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

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的绕过。

相关文章:

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

通过前两篇文章的铺垫&#xff0c;现在我们可以了解 CVE-2024-4577这个漏洞的原理 漏洞原理 CVE-2024-4577是CVE-2012-1823这个老漏洞的绕过&#xff0c;php cgi的老漏洞至今已经12年&#xff0c;具体可以参考我的另一个文档 简单来说&#xff0c;就是使用cgi模式运行的PHP&…...

人工智能前沿讲座——AIGC

目录 前情提要 一、什么是AIGC AIGC与传统的AI有何区别&#xff1f; 二、发展历程 GAN 生成对抗网络 大模型与Transformer Transformer\BERT\GPT 扩散模型和稳定扩散模型 三、AIGC的发展应用 新质生产力 前情提要 小学期某一门课的笔记&#xff0c;老师名字隐去&…...

CCF 第33次CCF计算机软件能力认证第二题

相似度计算 刷新 时间限制&#xff1a; 1.0 秒 空间限制&#xff1a; 512 MiB 下载题目目录&#xff08;样例文件&#xff09; 题目背景 两个集合的 Jaccard 相似度定义为&#xff1a;&#x1d446;&#x1d456;&#x1d45a;(&#x1d434;,&#x1d435;)∣&#x1d…...

python 学习积累

持续更新中 感受python的强大之case列举&#xff1a; 1. 生成的map list要经过json格式化写入文件&#xff0c;请用python实现这一需求 import json map{"name": "张三", "age": 18, "address": "北京"} list[] for i in …...

ARM day1总结

思维导图...

套路化编程:C# ListView 保存、恢复列宽度

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 目录 技术基础 保存列头 删…...

python单元测试

文章目录 单元测试定义断言函数Test FixturesMockpatch装饰器模拟&#xff08;首选&#xff09;上下文管理器模拟手动模拟 测试实例 测试覆盖率pytest框架起步安装使用常用参数跳过测试pytest.fixtureconftest.py参数化测试 数据库查询的mock覆盖率 单元测试 定义 单元测试是…...

华为---静态路由-浮动静态路由及负载均衡(二)

7.2 浮动静态路由及负载均衡 7.2.1 原理概述 浮动静态路由(Floating Static Route)是一种特殊的静态路由&#xff0c;通过配置去往相同的目的网段&#xff0c;但优先级不同的静态路由&#xff0c;以保证在网络中优先级较高的路由&#xff0c;即主路由失效的情况下&#xff0c…...

Maven deploy上传远程私服失败

Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:2.8.2:deploy (default-deploy) on project 你的项目: Cannot deploy artifacts when Maven is in offline mode 解决方案&#xff1a; 1.IDEA把这个钩子去掉 2. settings.xml里把 <offline>标…...

通天星CMSV6车载定位监控平台 point_manage/merge SQL注入致RCE漏洞复现

0x01 产品简介 通天星CMSV6车载定位监控平台拥有以位置服务、无线3G/4G视频传输、云存储服务为核心的研发团队,专注于为定位、无线视频终端产品提供平台服务,通天星CMSV6产品覆盖车载录像机、单兵录像机、网络监控摄像机、行驶记录仪等产品的视频综合平台。 0x02 漏洞概述 …...

图像识别技术在人脸识别领域的新突破

图像识别技术在人脸识别领域的新突破主要体现在多个方面&#xff0c;这些突破不仅提高了人脸识别的准确性和效率&#xff0c;还拓展了其应用领域。以下是对这些新突破的详细归纳&#xff1a; 深度学习技术的应用&#xff1a; 深度学习技术&#xff0c;特别是卷积神经网络&…...

iview 组件里面的(任何一个月)整月日期全部选中_iview时间轴选中有历史记录日期

iview 组件里面的整月日期全部选中&#xff1a; ①&#xff1a;第一种是当前月的日期全部选中&#xff1a; 先上效果图&#xff1a;当前月分 获取到的值&#xff1a; 当前月的方法&#xff1a; // getDateStr() {// var curDate new Date();// var curMonth curDate.ge…...

Charles配置与API数据抓取

2024软件测试面试刷题&#xff0c;这个小程序&#xff08;永久刷题&#xff09;&#xff0c;靠它快速找到工作了&#xff01;&#xff08;刷题APP的天花板&#xff09;-CSDN博客跳槽涨薪的朋友们有福了&#xff0c;今天给大家推荐一个软件测试面试的刷题小程序。https://blog.c…...

[FreeRTOS 内部实现] 信号量

文章目录 基础知识创建信号量获取信号量释放信号量信号量 内部实现框图 基础知识 [FreeRTOS 基础知识] 信号量 概念 创建信号量 #define queueQUEUE_TYPE_BINARY_SEMAPHORE ( ( uint8_t ) 3U ) #define semSEMAPHORE_QUEUE_ITEM_LENGTH ( ( uint8_t ) 0U ) #define xSe…...

Vue57-组件的自定义事件_解绑

给谁绑的自定义事件&#xff0c;就找谁去触发&#xff1b;给谁绑的自定义事件&#xff0c;就找谁去解绑&#xff1b; 一、解绑自定义事件 1-1、解绑一个自定义事件 到student.vue组件中去解绑。 1-2、解绑多个自定义事件 使用数组来解绑多个。 1-3、解绑所有的自定义事件 二、…...

Java启动jar设置内存分配详解

在微服务架构越来越盛行的情况下&#xff0c;我们通常一个系统都会拆成很多个小的服务&#xff0c;但是最终部署的时候又因为没有那么多服务器只能把多个服务部署在同一台服务器上&#xff0c;这个时候问题就来了&#xff0c;服务器内存不够&#xff0c;这个时候我们就需要对每…...

Feign Client超时时间设置不生效问题

在使用Feign Client时&#xff0c;可以通过两种方式来设置超时时间&#xff1a; 针对整个Feign Client设置超时时间 可以在Feign Client的配置类中通过修改Request.Options对象来设置超时时间。Request.Options对象有两个属性&#xff0c;connectTimeoutMillis用于设置连接超…...

Haproxy部署Web群集

概论 HAProxy是可提供高可用性、负载均衡以及基于TCP和HTTP应用的代理&#xff0c;是免费、快速并且可靠的一种解决方案。HAProxy非常适用于并发大&#xff08;并发达1w以上&#xff09;web站点&#xff0c;这些站点通常又需要会话保持或七层处理。HAProxy的运行模式使得它可以…...

C++STL梳理

CSTL标准手册&#xff1a; https://cplusplus.com/reference/stl/ https://cplusplus.com/reference/vector/vector/at/ 1、STL基础 1.1、STL基本组成(6大组件13个头文件) 通常认为&#xff0c;STL 是由容器、算法、迭代器、函数对象、适配器、内存分配器这 6 部分构成&…...

找出1000以内的所有的完数

完数的概念&#xff1a;完数&#xff08;Perfect Number&#xff09;是一个正整数&#xff0c;它等于除了它本身以外所有正因子之和。例如&#xff0c;6的因子有1、2、3和6&#xff0c;其中1236&#xff0c;所以6是一个完数。 #include <stdio.h> // 函数用于计算一个数…...

3110. 字符串的分数

给你一个字符串 s 。一个字符串的 分数 定义为相邻字符 ASCII 码差值绝对值的和。 请你返回 s 的 分数 。 示例 1&#xff1a; 输入&#xff1a;s "hello" 输出&#xff1a;13 解释&#xff1a; s 中字符的 ASCII 码分别为&#xff1a;h 104 &#xff0c;e 1…...

Mybatis MySQL allowMultiQueries 一次性执行多条语句

在JDBC 增加参数allowMultiQueries jdbc:mysql://localhost:3306/abc?&allowMultiQueriestrue <insert id"addRi" parameterType"java.util.List">DELETE FROM sys_ri WHERE sr_id #{roId} AND sr_fion_id #{fod};INSERT into sys_rVALUES&…...

Kubernates容器化JVM调优笔记(内存篇)

Kubernates容器化JVM调优笔记&#xff08;内存篇&#xff09; 先说结论背景思路方案 先说结论 1、首先如果是JDK8&#xff0c;需要使用JDK8_191版本以上&#xff0c;才支持容器化环境和以下参数&#xff0c;否则就更新到JDK10以上&#xff0c;选择对应的镜像构建就行了 2、在容…...

Elasticsearch Scroll 报错entity content is too long

2024-06-24 15:22:01:568 ERROR [task-31] (ScrollFetcherProduceAction.java:129) 访问ES出错org.apache.http.ContentTooLongException: entity content is too long [112750110] for the configured buffer limit [104857600]at org.elasticsearch.client.HeapBufferedAsync…...

Vue iview输入框change事件replace正则替换不生效问题的解决。

// 需求&#xff1a;输入座机号只允许输入数字和"-" onChange(e){this.$nextTick(()>{this.phone e.target.value.replace(/[^0-9-]/g, );}) } 解决&#xff1a;添加**this.$nextTick**即可...

Prestashop跨境电商独立站,外贸B2C网站完整教程

Prestashop是一款来自法国专业的开源电商CMS(内容管理系统)平台&#xff0c;和wordpress一样比较轻量&#xff0c;适合中小网站。Prestashop跨境电商独立站在国内并不是很流行&#xff0c;不过国外是非常火的&#xff0c;从各大平台的Prestashop主题数量就可以看得出来。 最有…...

常用算法及参考算法 (1)累加 (2)累乘 (3)素数 (4)最大公约数 (5)最值问题 (6)迭代法

常用算法及参考算法 &#xff08;1&#xff09;累加 &#xff08;2&#xff09;累乘 &#xff08;3&#xff09;素数 &#xff08;4&#xff09;最大公约数 &#xff08;5&#xff09;最值问题 &#xff08;6&#xff09;迭代法 1. 累加 #include <stdio.h>int main() {…...

java简易计算器(多种方法)

parseDouble() 方法属于 java.lang.Double 类。它接收一个字符串参数&#xff0c;其中包含要转换的数字表示。如果字符串表示一个有效的 double&#xff0c;它将返回一个 double 值。 应用场景 parseDouble() 方法在以下场景中非常有用&#xff1a; 从用户输入中获取数字&a…...

spring的bean定义和扫描规则

1、bean的基本定义 在Spring框架中&#xff0c;Bean是一个核心概念&#xff0c;它是Spring IoC&#xff08;Inverse of Control&#xff0c;控制反转&#xff09;容器管理的一个对象实例。简单来说&#xff0c;Bean就是由Spring容器初始化、配置和管理的对象。这些对象可以是J…...

软件工程体系概念

软件工程 软件工程是应用计算机科学、数学及 管理科学等原理开发软件的工程。它借鉴 传统工程的原则、方法&#xff0c;以提高质量&#xff0c;降 低成本为目的。 一、软件生命周期 二、软件开发模型 1.传统模型 瀑布模型、V模型、W模型、X 模型、H 模型 (1)瀑布模型 瀑布…...