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

news2024/11/25 5:56:21

通过前两篇文章的铺垫,现在我们可以了解 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 .php
     Action 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.coloradmin.cn/o/1870895.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

零拷贝技术(zero copy),DMA,mmap,sendfile

在一些高性能的IO场景下我们经常能听到零拷贝技术&#xff0c;这是个不错的话题。 零拷贝指的是内核态与用户态之间的数据拷贝&#xff0c;而这两个区域的数据拷贝只能依靠CPU&#xff0c;但是CPU最重要的作用应该是运算。 一、DMA的由来 在没有DMA之前&#xff0c;磁盘的IO…

【NPS】哑终端设备如何实现域VLAN动态分配

在【NPS】微软NPS配置802.1x&#xff0c;验证域账号&#xff0c;动态分配VLAN&#xff08;有线网络续篇&#xff09;中&#xff0c;已经通过C3PL策略配置实现了802.1x验证没有通过时&#xff0c;自动分配一个Guest VLAN&#xff0c;以确保用户至少能够访问基本的网络服务。问题…

数字时代的文化革命:Facebook的社会影响

随着数字技术的飞速发展和互联网的普及&#xff0c;社交网络如今已成为人们日常生活中不可或缺的一部分。在众多社交平台中&#xff0c;Facebook作为最大的社交网络之一&#xff0c;不仅连接了全球数十亿用户&#xff0c;更深刻影响了人们的社会互动方式、文化认同和信息传播模…

展开说说:Android列表之RecyclerView

RecyclerView 它是从Android5.0出现的全新列表组件&#xff0c;更加强大和灵活。用于显示列表形式 (list) 或者网格形式 (grid) 的数据&#xff0c;替代ListView和GridView成为Android主流的列表组件。可以说Android客户端只要有表格的地方就有RecyclerView。 RecyclerView 内…

【linux】使用vnc连接远程桌面,需要安装tigervnc,并在服务端期待,然后在客户端使用tigervnc-viewer进行连接即可

vnc 远程设置方法 需要服务端安装软件&#xff1a; sudo apt install -y tigervnc-standalone-server# 先配置密码使用&#xff1a; tightvncpasswd启动服务&#xff0c;禁用本机 vncserver -localhost no -geometry 1924x1080 :1客户端安装软件&#xff1a; sudo apt insta…

JavaScript高级程序设计(第四版)--学习记录之基本引用类型

Date Date类型将日期保存为自协调世界时间1970年1月1日午夜至今所经过的毫秒数。 创建日期对象 let now new Date() Date.parse()方法接收一个表示日期的字符串参数&#xff0c;尝试将这个字符串转换为表示该日期的毫秒数。 let time new Date(Date.parse("May 24,2024&…

Jmeter+InfluxDB+Grafana性能测试数据展示

JmeterInfluxDBGrafana提供了一种更好的对Jmeter压测结果的实时监控展示。可以理解为数据源产生的数据加上时间记录并存储&#xff0c;然后使用各种开源图表组件进行展示。实现jmeter报告的更好的可视化展示 1&#xff09;方便测试结果数据落地以及更好的分析 2&#xff09;将…

超好用的思维导图—万兴亿图脑图 v10解锁版安装教程 (思维导图软件和头脑风暴工具)

前言 万兴亿图脑图 (Wondershare EdrawMind) 是一款多平台协作思维导图软件和头脑风暴工具,亿图思维导图提供丰富的布局,样式,主题及配色方案,集成拥有数万幅原创思维导图作品的思维导图社区,涵盖教育,职场,自我提升等各大领域精华知识.支持会议演示,多端创作,云端存储,导图分…

BioCLIP:物种图像的基础视觉模型

从无人机到个人手机&#xff0c;各种相机收集的自然世界图像是越来越丰富的生物信息来源。从图像中提取生物相关信息用于科学的计算方法和工具激增&#xff0c;尤其是计算机视觉。然而&#xff0c;其中大多数都是为特定任务设计的&#xff0c;不容易适应或扩展到新的问题、环境…

第30课 绘制原理图——放置网络标签

什么是网络标签&#xff1f; 我们在很多电路图中都能看到&#xff0c;为了让图纸更加简洁&#xff0c;并不是每一根导线都要确确实实地画出来。可以在导线悬空的一端添加一个名称标签&#xff0c;接着在另一根导线的悬空一端添加上一个同名的名称标签&#xff0c;那么就可以让…

1.回溯算法.基础

1.回溯算法 基础知识题目1.组合2.组合-优化3.组合总和|||4.电话号码和字母组合5.组合总和6.组合总和II7.分割回文串8.复原IP地址 基础知识 回溯法也可以叫做回溯搜索法&#xff0c;它是一种搜索的方式。回溯是递归的副产品&#xff0c;只要有递归就会有回溯 因为回溯的本质是穷…

【毛毛虫案例-重力 Objective-C语言】

一、接下来,我们给这个毛毛虫,添加一下重力 1.把我们之前的代码,复制粘贴一份儿,改个名字,叫做:17-毛毛虫案例-重力, 重力的话,实际上,就比较简单了啊,那我们重力的话,去添加的时候,我也要在外面,去添加, 重力的话,叫做啥,UIGravityBehavior,啊, UIGravity…

2024年度临沂市安全文化书画摄影展开幕

人海信息网山东讯 6月27日&#xff0c;2024年度临沂市安全文化书画摄影作品展&#xff0c;在临沂高新区隆重开幕。本次书画摄影展深入贯彻“以人为本&#xff0c;安全发展”的重要思想&#xff0c;立意高远&#xff0c;内涵丰富&#xff0c;思想深邃&#xff0c;承载着健康、幸…

【公开数据集获取】

Open Images Dataset https://www.youtube.com/watch?vdLSFX6Jq-F0

MySQL锁和使用

在MySQL中&#xff0c;锁用于控制并发访问&#xff0c;以保证数据的一致性和完整性。MySQL提供了多种类型的锁&#xff0c;包括表级锁、行级锁和页面级锁。以下是MySQL中各种锁的详细介绍及其使用方法&#xff1a; 1. 表级锁&#xff08;Table Locks&#xff09; 表级锁用于锁…

AI绘画Stable Diffusion 超强一键去除图片中的物体,免费使用!

大家好&#xff0c;我是设计师阿威 在生成图像时总有一些不完美的小瑕疵&#xff0c;比如多余的物体或碍眼的水印&#xff0c;它们破坏了图片的美感。但别担心&#xff0c;今天我们将介绍一款神奇的工具——sd-webui-cleaner&#xff0c;它可以帮助我们使用Stable Diffusion轻…

简易深度学习(1)深入分析神经元及多层感知机

一、神经元 单个神经元结构其实可以认为是一个线性回归模型。例如下图中 该神经元输入为三个特征&#xff08;x1&#xff0c;x2&#xff0c;x3&#xff09;&#xff0c;为了方便理解&#xff0c;大家可以认为每条线上都有一个权重和特征对应&#xff08;w1&#xff0c;w2&…

62.指针和二维数组(2)

一.指针和二维数组 1.如a是一个二维数组&#xff0c;则数组中的第i行可以看作是一个一维数组&#xff0c;这个一维数组的数组名是a[i]。 2.a[i]代表二维数组中第i行的首个元素的地址&#xff0c;即a[i][0]的地址。 二.进一步思考 二维数组可以看作是数组的数组&#xff0c;本…

算法入门:二分查找及其Java实现

在程序开发中&#xff0c;算法是解决问题的核心。本篇博客将详细讲解一种高效的查找算法——二分查找&#xff0c;并通过Java代码示例帮助你理解其实现和应用。 如果你觉得这篇文章对你有帮助&#xff0c;不要忘记点赞、收藏和关注我&#xff0c;这将是对我最大的支持和鼓励&am…

Vue.js中的虚拟DOM

一.节点和状态 在我们平常对DOM操作的时候,之前在vue没有诞生之前,以命令式的方式对DOM进行操作,页面上的每一个元素都可以看做成一个节点状态。 二.剔除和渲染 框架都有自己渲染的方式,假设一个页面的状态,随着Ajax请求的放松,状态发生改变,有以下的两种方式供你选择&#…