[FBCTF2019]RCEService
Enter command as JSON
让上传JSON格式的命令,但是键名是什么呢?随便上传一个试试,从链接中我们能看到/?cmd={"cmd"%3A"whoimi"},上传了参数cmd,说明键名真可能是这个,另外看到回显Attempting to run command:验证了。只不过并没有执行结果。
尝试执行ls命令:Hacking attempt detected,说明有关键字过滤。
看看能不能通过字符拼接绕过:{"cmd":'l''s'' /'}仍然被过滤。发现单引号、加号、/ 都被过滤了。
尝试其他方法绕过,没什么思路,看了答案说是用到pre_match函数,那绕过方法有:数组绕过、回溯绕过、换行符绕过等等。
数组绕过:{"cmd":['l','s']}仍然被过滤。
回溯绕过:{"cmd": " "*10000000+"ls /"}仍然被过滤。
换行符绕过:{"cmd":"\n ls / \n"}仍然被过滤。
答案中使用的是换行符绕过,{%0a"cmd":"ls /"%0a}
真的执行成功了,那{"cmd":"\n ls / \n"}的问题在哪里呢?
为什么没有绕过过滤?算了先不管。
通过执行 ls \ 发现根目录中并没有flag,先看一下源码。
执行cat index.php、head index.php、more index.php等等命令虽然绕过过滤了但是都没有结果。根据答案中的思路,就想到环境变量被更改了,需要使用命令的绝对路径来执行,也就是/bin/cat
。果然得到源码:
<?phpputenv('PATH=/home/rceservice/jail');if (isset($_REQUEST['cmd'])) {$json = $_REQUEST['cmd'];if (!is_string($json)) {echo 'Hacking attempt detected<br/><br/>';} elseif (preg_match('/^.*(alias|bg|bind|break|builtin|case|cd|command|compgen|complete|continue|declare|dirs|disown|echo|enable|eval|exec|exit|export|fc|fg|getopts|hash|help|history|if|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|return|set|shift|shopt|source|suspend|test|times|trap|type|typeset|ulimit|umask|unalias|unset|until|wait|while|[\x00-\x1FA-Z0-9!#-\/;-@\[-`|~\x7F]+).*$/', $json)) {echo 'Hacking attempt detected<br/><br/>';} else {echo 'Attempting to run command:<br/>';$cmd = json_decode($json, true)['cmd'];if ($cmd !== NULL) {system($cmd);} else {echo 'Invalid input';}echo '<br/><br/>';}
}?>
通过源码分析一下为什么我的三种绕过都不行,第一个数组绕过,{"cmd":["l","s"]}中[]被过滤了,并且就算没过滤,该数据经过解析后$cmd=["l","s"]也没法执行。
第二个回溯绕过,通过本地执行发现{"cmd": " "*10000000+"ls /"}并没有像预期那样有大量空格。原因在于JSON 中不支持 *
和 +
这样的运算符号,这是编程语言(如 PHP、JavaScript)中的字符串操作语法。
第三个换行符绕过{"cmd":"\n ls / \n"}是因为\n在pre_match匹配中并不被当作换行符,而是普通字符,只有在解析该字符串的时候转义字符才会生效。
源码中putenv('PATH=/home/rceservice/jail');的含义:
putenv()
是 PHP 的内置函数,用于设置或修改环境变量。其参数是一个字符串,格式为“变量名=值”
。这里将环境变量
PATH
的值设置为/home/rceservice/jail
。
PATH
环境变量的作用是:当在终端或脚本中直接执行一个命令(如ls
、cat
等)时,系统会自动在PATH
所指定的目录列表中查找该命令的可执行文件。这行代码的典型用途:
在安全场景中(如限制命令执行权限),这样设置通常是为了限制 PHP 脚本执行系统命令时所能访问的可执行文件范围:
原本的
PATH
可能包含系统默认的命令目录(如/bin
、/usr/bin
等),里面有ls
、cat
、bash
等危险命令。修改为
/home/rceservice/jail
后,系统只会在该目录下查找命令。如果这个目录中没有放置危险命令(或仅放置了允许执行的有限命令),就能降低命令注入攻击的风险。例如,当后续执行
system('ls')
时,系统会去/home/rceservice/jail
目录下找ls
命令,如果该目录中没有ls
,则会执行失败。这是一种常见的安全加固手段,用于限制命令执行的范围。
因此当前环境变量中没有cat、head等等读文件的命令,所以我们必须用这些命令的绝对路径来执行。
源码中修改了环境变量的位置,我们猜测flag也可能在里面,于是:
成功拿到flag。
总结一下:这道题的难点在于绕过关键字过滤,没有任何提示,得猜测过滤方式是pre_match,然后是pre_match函数的绕过,这道题有点难的是上传的格式是JSON,导致很多绕过方法都不可用。另外就是要能想到环境变量的修改,如果发现一些常用变量无法执行,可能是当前环境变量中没有,需要用加上/bin绝对路径引用。最后就是flag藏的位置需要能想到。