PHP-CGI的漏洞(CVE-2024-4577)

通过前两篇文章的铺垫,现在我们可以了解 CVE-2024-4577这个漏洞的原理

漏洞原理

CVE-2024-4577是CVE-2012-1823这个老漏洞的绕过,php cgi的老漏洞至今已经12年,具体可以参考我的另一个文档

简单来说,就是使用cgi模式运行的PHP,根据RFC3875的规定,Apache会将请求的QUERY_STRING作为命令行参数交给php-cgi执行。攻击者利用这个特性,就可以-d选项修改PHP的配置项,最后执行任意代码。

文章的最后,我也提到了当时PHP官方修复了两次才完成的补丁:

if((query_string = getenv("QUERY_STRING")) != NULL && strchr(query_string, '=') == NULL) {/* we've got query string that has no = - apache CGI will pass it to command line */unsigned char *p;decoded_query_string = strdup(query_string);php_url_decode(decoded_query_string, strlen(decoded_query_string));for (p = decoded_query_string; *p &&  *p <= ' '; p++) {/* skip all leading spaces */}if(*p == '-') {skip_getopt = 1;}free(decoded_query_string);
}

修复方式就是在获取到QUERY_STRING后,检查其开头是否是横线(-),如果是-,则跳过后面对参数的解析。而@Orange 这次发现的CVE-2024-4577实际上就是横线检查的绕过,来说下原理。

在Windows中,如果当前系统的字符集(也被称为代码页,Code Page)非Unicode编码,在main()函数获取argv的时候,会自动执行编码的转换,其中就会涉及到所谓的Best-Fit。cp936

gbk

Best-Fit是一种字符映射策略,用以解决源代码页中的字符在目标代码页中没有直接等价物时的问题。在将Unicode代码页中字符转换成非Unicode代码页字符时,如果无法找到对应的字符,就会按照Best-Fit预定义的一个转换表进行转换。

比如,GBK编码(cp936)的Best-Fit Mapping转换表是:https://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WindowsBestFit/bestfit936.txt

其中有一些有趣的字符转换,比如0xaa在转换后会变成a,0xb2在转换后会变成2,0xad在转换后会变成-。

所以,这里我们就可以利用Best-Fit这个特性,使用%ad来代替横线-。PHP在执行上述检查的时候,会认为命令行的第一个字符是0xAD;实际上main()函数的argv的第一个字符已经被Windows按照best-fit mapping转换成-。

本质来说,这仍然是一个利用解析差异绕过防御措施的案例。

什么情况下才会使用Best-Bit转换字符串?

我们可以编写下面这个简单的Python代码来复现best-fit这个trick:

os.system("php \xadh")

虽然传入的是\xad,但实际最后执行成功。

但我们多测试几次就会发现,并不是所有程序的所有参数都会进行Best-Bit的转换,我们测试下面几个命令:

php -h => php \xadh => 成功

php -v => php \xadv => 出错

python -h => python \xadh => 出错

arp -h => arp \xadh => 成功

有的成功,有的出错。这是为什么呢?

阅读PHP代码就会发现,有一部分选项是直接使用的main()函数的argv,另一部分选项是从Windows API GetCommandLineW()函数获取。由GetCommandLineW()获取的这部分参数仍然保持原样,没有转换成横线,最后无法正确执行。

XAMPP为什么可以被利用?

Orange在文章中提到,XAMPP默认配置就可以被利用。最初读到这里的时候,我会以为XAMPP是以php-cgi模式运行的PHP服务器,但下载安装XAMPP后我发现,PHP实际上是以Apache 2.0 Handler的方式运行。

这时候就非常有趣了,为什么XAMPP仍然可以在不修改任何配置文件的情况下直接利用呢?为什么很多人在实际测试中会遇到500错误呢?

这实际上是PHP没有修复的另一个安全机制bypass

比如,默认情况下我们需要将php解释器放在cgi-bin目录下,这样用户通过访问/cgi-bin/php/dir/script.php,即可执行/dir/script.php。

这个操作是很危险的,所以PHP增加了如下配置:cgi.force_redirect=1,开启了这个选项(默认开启)以后,只有经过了重定向规则请求才能执行。

Apache在重定向(rewrite)的时候,会增加一个名为REDIRECT_STATUS的环境变量,cgi.force_redirect就是依赖这个环境变量,来判断是否经历了重定向。

如果非Apache服务器,我们就需要设置一下cgi.redirect_status_env,来指定php判断请求是否经历重定向的条件。

XAMPP默认使用的sapi

我们下载XAMPP后,查看PHPINFO可以发现,其运行PHP的模式是“Apache 2.0 Handler”。在这个模式下,PHP会被编译成Apache的一个模块(dll动态链接库)并由Apache来调用执行。

我们在apache/conf/extra/httpd-xampp.conf中可以找到相关的配置:

 LoadFile "/program/xampp/xampp/php/php8ts.dll"LoadFile "/program/xampp/xampp/php/libpq.dll"LoadFile "/program/xampp/xampp/php/libsqlite3.dll"LoadModule php_module "/program/xampp/xampp/php/php8apache2_4.dll"<FilesMatch "\.php$">SetHandler application/x-httpd-php</FilesMatch><FilesMatch "\.phps$">SetHandler application/x-httpd-php-source</FilesMatch>

可见,这里所有的.php后缀文件会被交给php8apache2_4.dll来执行,这和PHP CGI是没有任何关系的。

如何正常配置使用PHP CGI?

思考一个问题,如果我们需要正常配置一个使用PHP CGI解析PHP的Apache服务器,应该如何编写配置文件呢?

如果是部署基于脚本的CGI服务器,我们需要将可执行文件放置在某个目录下,比如/cgi-bin/hello.cgi。这个hello.cgi脚本的第一行是“shebang”,用以指定当前脚本的解释器,比如 # !/bin/bash

ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory "/usr/lib/cgi-bin">
    AllowOverride None
    Options +ExecCGI
    AddHandler cgi-script .cgi .pl .php
    Require all granted
</Directory>

  1. 设置 PHP 文件的 Shebang 行和 Content-Type 头

    在每个 PHP 脚本的开头添加 Shebang 行和 Content-Type 头。例如,/cgi-bin/hello.cgi 文件内容如下:

    #!/usr/bin/php
    <?php
    header('Content-Type: text/html; charset=UTF-8');echo "<html>";
    echo "<head><title>Hello, CGI!</title></head>";
    echo "<body>";
    echo "<h1>Hello, World!</h1>";
    echo "</body>";
    echo "</html>
    

    其中,#!/usr/bin/php 是 Shebang 行,用于指定 PHP 解释器的路径。

  2. 设置文件权限

    确保 PHP 文件具有执行权限,使其可以作为 CGI 脚本运行:

    chmod +x /cgi-bin/hello.cgi
    
  3. 测试脚本

    通过浏览器访问该脚本。例如,如果 CGI 脚本路径是 /cgi-bin/hello.cgi,则访问 http://yourdomain.com/cgi-bin/hello.cgi

事实上几乎没有PHP应用会这么编写代码,相比于这种将脚本直接作为可执行文件的方法,PHP另辟蹊径,专门设计了一个SAPI就叫php-cgi。

调用php-cgi执行一个PHP文件时,它会负责输出“Content-Type”头。比如执行echo '<?php echo 123;' | php-cgi

所以,类似于hello.cgi,我们也可以直接将php-cgi这个可执行文件映射到/cgi-bin/目录下,然后再使用Apache的Action指令,将PHP相关请求“重定向”到php-cgi上,最后执行PHP代码。

此时配置文件如下:

 ScriptAlias /php-cgi/ "/program/xampp/xampp/php/"<Directory "/program/xampp/xampp/htdocs">AddHandler application/x-httpd-php .phpAction application/x-httpd-php /php-cgi/php-cgi.exe</Directory>

ScriptAlias指令的作用是将/php-cgi/路径指向/program/xampp/xampp/php/目录,

Action指令的作用就是将所有对.php文件的请求都使用/php-cgi/php-cgi.exe,也就是/program/xampp/xampp/php/php-cgi.exe来执行。

Apache在调用php-cgi的时候,会设置环境变量REDIRECT_STATUS。php-cgi为了确认这个请求确实是由Action指令执行的,而不是用户直接请求的,增加了一个开关“cgi.force_redirect”,默认开启。开启这个开关的情况下,php-cgi会验证此次执行是否包含环境变量REDIRECT_STATUS。

XAMPP为什么可以被利用?

了解了php-cgi正常的部署方式,我们回来看下XAMPP。虽然XAMPP执行PHP时使用的是Apache 2.0 Handler,但它仍然给PHP CGI预留了一个口子,就是使用ScriptAlias指令把php-cgi.exe映射到了Web目录下。

上述两个关键指令,XAMPP中使用了ScriptAlias指令,但是Action指令被注释了

这本身也没太大问题,因为有cgi.force_redirect的限制,在没有Action指令的情况下直接访问php-cgi.exe,REDIRECT_STATUS环境变量不会被设置。我们可以做个测试,直接请求/php-cgi/php-cgi.exe会返回500错误,查看日志会有Security Alert!的字眼

php-cgi.exe 接收cgi格式 -d=cgi.force_redirect=0 说明不再检测环境变量REDIRECT_STATUS

因为我们前面说到,对于REDIRECT_STATUS环境变量的限制是cgi.force_redirect这个开关来决定的。那么我们直接利用CVE-2024-4577漏洞,添加-d cgi.force_redirect=0关闭这个开关,即可绕过限制了。

值得注意的是,因为在高版本PHP中,allow_url_include这个开关已经被废弃,所以会抛出一个警告,导致Content-Type头输出失败,也会返回500。网上很多人没有注意到这一点,我们需要添加一个-d error_reporting=0来规避这一点。

PHP没有修复的另一种cgi.force_redirect绕过

前面我们使用-d cgi.force_redirect=0关闭PHP的检查,实际上PHP中有另一个Bug,也可以导致cgi.force_redirect的绕过,而且最新版本仍然没有修复

我们查看PHP源码sapi/cgi/cgi_main.c中对于REDIRECT_STATUS环境变量的检查代码:

​/* check force_cgi after startup, so we have proper output */if (cgi && CGIG(force_redirect)) {/* Apache will generate REDIRECT_STATUS,* Netscape and redirect.so will generate HTTP_REDIRECT_STATUS.* redirect.so and installation instructions available from* http://www.koehntopp.de/php.*   -- kk@netuse.de*/if (!getenv("REDIRECT_STATUS") &&!getenv ("HTTP_REDIRECT_STATUS") &&/* this is to allow a different env var to be configured* in case some server does something different than above */(!CGIG(redirect_status_env) || !getenv(CGIG(redirect_status_env)))) {zend_try {SG(sapi_headers).http_response_code = 400;PUTS("<b>Security Alert!</b> The PHP CGI cannot be accessed directly.\n\n\<p>This PHP CGI binary was compiled with force-cgi-redirect enabled.  This\n\means that a page will only be served up if the REDIRECT_STATUS CGI variable is\n\set, e.g. via an Apache Action directive.</p>\n\<p>For more information as to <i>why</i> this behaviour exists, see the <a href=\"PHP: 以 CGI 模式安装时 - Manual \">\manual page for CGI security</a>.</p>\n\<p>For more information about changing this behaviour or re-enabling this webserver,\n\consult the installation file that came with this distribution, or visit \n\<a href=\"PHP: Windows 系统下的安装 - Manual \">the manual page</a>.</p>\n");} zend_catch {} zend_end_try();if  defined(ZTS) && !defined(PHP_DEBUG)/* XXX we're crashing here in msvc6 debug builds at* php_message_handler_for_zend:839 because* SG(request_info).path_translated is an invalid pointer.* It still happens even though I set it to null, so something* weird is going on.*/tsrm_shutdown();endif free(bindpath);return FAILURE;}}

可见,除了getenv("REDIRECT_STATUS")以外,还有一个getenv("HTTP_REDIRECT_STATUS"),这两个环境变量都可以用于cgi.force_redirect的检查。而第二个环境变量HTTP_REDIRECT_STATUS是由HTTP_开头。

httpoxy漏洞就是因为很多HTTP客户端会使用HTTP_PROXY环境变量的值作为代理,因为这个环境变量是HTTP_开头,导致我们可以通过HTTP请求头控制,造成漏洞

PHP这里也是一样的问题,环境变量HTTP_REDIRECT_STATUS原本是为了兼容Netscape,但它是由HTTP_开头,所以用户可以直接控制。

PHP这里也是一样的问题,环境变量HTTP_REDIRECT_STATUS原本是为了兼容Netscape,但它是由HTTP_开头,所以用户可以直接控制。

我们去掉前面POC中的-d cgi.force_redirect=0,并添加一个HTTP头Redirect-Status: 1,仍然可以成功利用漏洞

虽然CVE-2024-4577漏洞在最新的PHP版本中已经修复了,但这个环境变量HTTP_REDIRECT_STATUS仍然可以导致cgi.force_redirect的绕过。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/362519.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

群晖系统百度网盘套件卸载之后无法再次安装 ContainerManager项目无法删除

前言 最近重新组了个NAS&#xff0c;在套件迁移的时候遇到个头疼的问题。在用矿神的百度网盘在迁移的时候出错了&#xff0c;于是我自己删掉baiduapp得容器和镜像然后卸载套件。不知道中间出了啥问题&#xff0c;套件是已经卸载了&#xff0c;但是群晖ContainerManager套件中的…

GPT-5对普通人有何影响

这篇文章对ChatGPT的使用方法和提问技巧进行了讨论&#xff0c;重点强调了背景信息和具体提问的重要性。文章清晰地传达了如何提高ChatGPT回答的质量&#xff0c;以及个人在使用ChatGPT时的体会和建议。然而&#xff0c;文章在逻辑组织和表达方面还有一些可以改进的地方&#x…

登录安全分析报告:链家地产

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞 …

最年轻获奖者诞生!一文带你了解历届国家最高科学技术奖获奖人

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 文丨浪味仙 排版丨沛贤 深度好文&#xff1a;4000字丨15分钟阅读 作为国家层面面向科学、技术领域的最高级别奖励&#xff0c;国家最高科学技术奖于 2000 年由国务院设立&#xff0c;每年评选…

Flutter学习目录

学习Dart语言 官网&#xff1a;https://dart.cn/ 快速入门&#xff1a;Dart 语言开发文档&#xff08;dart.cn/guides&#xff09; 学习Flutter Flutter生命周期 点击跳转Flutter更换主题 点击跳转StatelessWidget和StatefulWidget的区别 点击跳转学习Flutter中新的Navigato…

matlab中函数meshgrid

(1) 二维网格 [X,Y] meshgrid(x,y) 基于向量 x 和 y 中包含的坐标返回二维网格坐标。X 是一个矩阵&#xff0c;每一行是 x 的一个副本&#xff1b;Y 也是一个矩阵&#xff0c;每一列是 y 的一个副本。坐标 X 和 Y 表示的网格有 length(y) 个行和 length(x) 个列。 x 1:3; y…

使用 Reqable 在 MuMu 模拟器进行App抓包(https)

1、为什么要抓包&#xff1f; 用开发手机应用时&#xff0c;查看接口数据不能像在浏览器中可以直接通过network查看&#xff0c;只能借助抓包工具来抓包&#xff0c;还有一些线上应用我们也只能通过抓包来排查具体的问题。 2、抓包工具 实现抓包&#xff0c;需要一个抓包工具…

达梦数据库的系统视图v$locked_object

达梦数据库的系统视图v$locked_object 在达梦数据库&#xff08;Dameng Database&#xff09;中&#xff0c;V$LOCKED_OBJECT 视图提供了与数据库中被锁定对象相关的信息。这通常用于监控和诊断数据库中的锁定问题&#xff0c;帮助管理员了解哪些对象被锁定了&#xff0c;以及…

C++编程(四)this指针 常函数 常对象 静态成员

文章目录 一、this指针&#xff08;一&#xff09;概念&#xff08;二&#xff09;显式使用this指针的场景1. 当形参和成员变量名一致时2. 返回对象自身的时候必须要使用this指针3. 在类中销毁一个对象 二、常函数和常对象&#xff08;一&#xff09;常函数1. 概念2. 语法格式 …

PyCharm 2024.1最新变化

PyCharm 2024.1 版本带来了一系列激动人心的新功能和改进&#xff0c;以下是一些主要的更新亮点: Hugging Face 模型和数据集文档预览&#xff1a;在 PyCharm 内部快速获取 Hugging Face 模型或数据集的详细信息&#xff0c;通过鼠标悬停或使用 F1 键打开文档工具窗口来预览。 …

句法分析概述

第1关&#xff1a;句法分析概述 任务描述 本关任务&#xff1a;通过对句法分析基本概念的学习&#xff0c;完成相应的选择题。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a; 句法分析的基础概念&#xff1b; 句法分析的数据集和评测方法。 句法分析简介…

使用nvm命令进行node和npm版本下载以及切换

下载以及安装nvm方式 https://blog.csdn.net/ppz8823/article/details/130862191 1.查看nvm版本 nvm -v2.查看node 和 npm版本 node -v npm -v3.使用nvm查看已下载的node版本 nvm ls4.使用nvm 查看可使用的在线node版本 nvm list available4.下载想要使用的node版本&#x…

前端-echarts tooltip展示多项自定义数据

效果如图&#xff0c;鼠标滑动到某一个柱子的时候&#xff0c;出现这一项数据的多个自定义数据&#xff0c;外加自己的模板样式渲染。 希望能展示每一列中的多个自定义数据 代码部分 主要是在data中&#xff0c;value就是实际展示的主数据&#xff0c;其他字段名为自定义的数…

基于springboot时装购物系统

设计技术&#xff1a; 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringbootMybatisvue 工具&#xff1a;IDEA、Maven、Navicat 主要功能&#xff1a; 管理员&#xff1a;首页、个人中心、用户管理、商品分类管理、颜色管理、商品信息管理、商品评价…

Swift开发——简单App设计

App的界面设计需要具有大量的图像并花费大量的时间,这样的应用不方便学习和交流,这里重点介绍SwiftUI界面元素的用法,通过简单App设计过程的讲解,展示图形用户界面应用程序的设计方法。 01、简单App设计 按照9.1节工程MyCh0901的创建方法,创建一个新的工程MyCh0902,此时工…

【Unity | Editor强化工具】项目备忘录工具

经常会被美术和策划同事反复询问某几个问题&#xff0c;每次都要翻Wiki链接给他们&#xff0c;非常折磨人&#xff0c;所以做了个可以在Unity内部显示备忘录的小工具&#xff0c;能够减少一些查找成本&#xff08;另外我觉得&#xff0c;让他们养成查看Unity内触手可及的信息的…

用MySQL和navicatpremium做一个项目—(财务管理系统)。

1 ER图缩小的话怕你们看不清&#xff0c;所以截了两张图 2 vsdx绘图结果 3DDL和DML,都有点长分了好多次上传&#xff0c;慢慢看 DDL -- 用户表 CREATE TABLE users (user_id INT AUTO_INCREMENT PRIMARY KEY COMMENT 用户ID,username VARCHAR(50) NOT NULL UNIQUE COMMENT 用…

【stm32-新建工程-HAL库版本】

stm32-新建工程-HAL库版本 ■ 1. 下载STM32Cube官方固件包&#xff08;F1/F4/F7/H7&#xff09;■ 2. 新建HAL库版本MDK工程所需的文件夹■ 2.1 新建工程文件- XXX项目并在下面新建如下文件夹■ 2.2 向Drivers文件添加如下文件■ 2.3 向Mrddlewares文件添加文件■ 2.4 设置Outp…

YouCompleteMe插件安装方法简述

一、前言 YouCompleteMe是VIM中进行C/C 开发的重要工具&#xff0c;可以极大提升linux下C/C开发效率。 YCM需要高版本的gcc (8.0以上版本&#xff0c;支持C17) 和 vim&#xff08;8.0以上&#xff0c;支持python3.6以上&#xff09; 二、编译gcc_8.3 1. 获取源码 wget https:…

重磅消息:ONLYOFFICE8.1版本桌面编辑器发布:功能完善的 PDF 编辑器、幻灯片版式、改进从右至左显示、新的本地化选项等

目录 ONLYOFFICE介绍 PDF 编辑器 功能全面的 PDF 编辑器 文本编辑 页面处理 &#xff08;添加、旋转、删除&#xff09; 插入和调整各种对象&#xff0c;例如表格、形状、文本框、图像、TextArt、超链接、方程等。 此外 PDF 表单 文本文档编辑器更新内容 页面颜色 页面…