CVE-2018-5711理解与复现

写在前面

这个CVE复现起来很简单的,直接把Orange大大的两条指令在有漏洞的PHP版本上跑一下就可以看到效果了。

复现之后,大致看了一下代码,感觉没有太多收获…所以决定调试一下PHPgd 扩展的源代码,理解一下到底是怎么导致了这个漏洞点的产生。虽然期间踩了不少坑,但确实是一次不错的尝试。

PHP源码调试新技能get~

Window下PHP源码编译

这部分工作,主要参考了链接:

这中间遇到了很多坑点,在介绍过程中都会列出来。

准备工作

  1. 首先装好Visual Studio 2017 ,必须要装的是通用Windows平台开发使用C++的桌面开发 ,其它的不安装也可以。
    在这里插入图片描述

  2. 下载PHP-SDK 和 有漏洞的PHP 版本。我下载的版本分别是PHP-SDK 2.1.10-devPHP 7.1.10。(github上都有对应的项目,可以直接下载,前面提到的链接中都有写下载地址)

在这里插入图片描述

编译

  1. 进入到PHP-SDK 所在目录,我的操作系统是64位的,安装了Visual Stadio 2017 ,选择目录下的phpsdk-vc15-x64.bat 来编译。
  2. 在命令行中执行phpsdk-vc15-x64.bat,然后执行phpsdk_buildtree phpdev,之后会在PHP-SDK 所在目录中出现phpdev 文件夹。
  3. 将之前下载的php 源码整个文件夹拷贝到php-sdk/phpdev/vc15/x64/ 目录下。
  4. 进入php-src 目录,执行phpsdk_deps --update --branch master 命令,下载依赖关系组件(需要等待较长时间)。
  5. 运行buildconf.bat 生成configure 文件,执行命令configure --disable-all --enable-cli --with-gd --enable-debug ,这一步是要选择安装哪些组件,刚开始的时候,并没有好好看要这条指令是实现什么功能,导致后来没有gd 扩展。使用configure --help 指令可以查看添加某个扩展对应的参数是什么。
    这里要说一下,如果编译php 之后还想再添加一些新的扩展,可以通过单独编译一个扩展,或者重新编译php 实现。但是我查了很多,单独添加一个扩展的话,在Linux下可以使用phpize 实现,Windows 下的方法后来不想找了,所以选择了重新编译。
  6. configure 指令执行之后,再执行nmake 指令来编译php ,最终编译出来的可执行的二进制文件路径为php-sdk\phpdev\vc15\x64\php-src\x64\Debug_TS\php.exe ,命令行切换到php.exe 所在目录,通过php.exe -v 指令可以查看我们自己编译成功的php 的版本号,php.exe -m 可以查看我们都安装了哪些扩展。

到这里我们就编译完了php 了。
这部分踩到的坑就是:网上找到的编译php 源码的帖子都是执行configure --disable-all --enable-cli --enable-debug 指令,导致后来找如何单独添加一个扩展花费了很多时间。不过也都怪我,没有理解清楚指令的作用就直接编译了。
(我是复现之后写的,所以就没有截图了。)

调试环境搭建

我是使用VSCode 来调试源码的,感觉这个工具用着很舒服呀,用到一些扩展,还有配置的一些过程比较烦。

扩展

用到的扩展是 C/C++,安装很简单,就不介绍了:
在这里插入图片描述

调试环境

VSCode 中打开php-src 目录,我在php-src\x64\Debug_TS 目录下新建了一个test.php 文件,文件内容:

<?php
    //imagecreatefromgif("48540923dd54564eea74f1d5b8de9c82d0584fcc.gif"); //正常gif文件
    imagecreatefromgif("poc.gif");  //poc
?>

· 在这里插入图片描述

VSCode在调试时候会在调试目录下新建一个.vscode 文件夹,其中launch.json 文件用来配置调试相关的信息。

launch.json


{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(Windows) Launch",
            "type": "cppvsdbg",
            "request": "launch",
            "program": "编译后生成的可执行文件php.exe路径",
            "args": [
                "要调试的文件"
            ],
            "stopAtEntry": false,
            "cwd": "编译后生成的可执行文件php.exe所在目录",
            "environment": [],
            "externalConsole": true,
        }
    ]
}

写好这些之后,我并没有像之前的参考链接中提到的那样,顺利地开始调试…反而…坑了好久。

VSCode一直提示很多文件链接不到。这是因为还要配置一个文件:c_cpp_properties.cpp ,这个文件用来定义一些文件所在路径、宏定义…

c_cpp_properties.cpp

{
    "configurations": [
        {
            "name": "Win32",
            "includePath": [
                "可以根据错误提示,把所有的缺失的文件路径都加进来",
                "${workspaceFolder}/**"
            ],
            "defines": [
                "_DEBUG",
                "UNICODE",
                "_UNICODE",
                "PHP_WIN32"
            ],
            "intelliSenseMode": "msvc-x64",
            "browse": {
                "path": [
                    "与includePath对应",
                    "${workspaceFolder}/**"
                ],
                "limitSymbolsToIncludedHeaders": true,
                "databaseFilename": ""
            }
        }
    ],
    "version": 4
}

在找路径的过程中,用了Everything 软件,真的是搜索起来特别快,强行安利一波…我就把所有缺失的文件的路径找到后都加进去…没有很细看…目标就是不报错,可以调试!!

includePathbrowse 不确定是不是两个都要写…

define中的PHP_WIN32 ,刚开始没有加这个,然后一直提示说没有php_config.h 这个头文件,然后我用Everything 查,发现我的电脑上压根没有这个文件,就别说再添加路径了。后来找包含php_config.h 的地方,是因为没有定义PHP_WIN32 ,所以才会包含这个头文件的。想了一下,应该是根据操作系统不同,包含了不同的文件。再网上找啊找,在一些和Visual Studio 调试c 代码相关的帖子中,发现要先预定义一些变量,就突然反应过来要在define 中定义PHP_WIN32 ,表明当前系统是Window

在这里插入图片描述

:-O还有,我在网上找php_config.h 这个文件的时候,所以提到的这个文件的路径都是在Linux 下的,就猜这个文件应该只是在操作系统为Linux 时候才会用,Windows 下不会有。
感觉很多相关的帖子,对操作系统的区别并没有说的很清楚,导致没有经验的我在这些地方卡了很长时间。

调试的时候,Debug Console 里还会输出很多错误信息…但是其实并不会影响调试。知道照样可以调试之后,就没有管这些错误信息了。

在这里插入图片描述

应该把坑点都讲全了吧…接下来就可以加断点开始调试了。

在这里插入图片描述

调试过程

我用了一个正常的gif 文件和Orange 大大给出的poc.gif 比较着调试,帮助我理解gd 扩展对gif 的处理过程,以及漏洞点产生的原因。

那就直接说这个gd_gif_in.cgif 图片的处理过程了。

程序刚进来,读图像是在gdImageCreateFromGifCtx() 函数中。
在这里插入图片描述

上述代码应该是根据gif 文件的格式,进行了一些处理,读到 , 符号之后会执行ReadImage() 函数(这个可以通过单步调试,一步一步地去看,看完之后就觉得很清晰啦)。

ReadImage() 函数中,会一直循环读取gif 文件的内容。
在这里插入图片描述

(跟读文件不相关的处理就没必要看了)

ReadImage() 中就调用了LWZReadByte() 函数了,接下来程序就会执行到LWZReadByte_() 中。

这个函数最开始执行是进行了一些初始化的操作。
在这里插入图片描述

其中,传递进来的参数input_code_size 是从gif 文件中读取到的一个字节。单步调试的话很好找到它的值。
对于poc.gif 来说,这个值是0x03
在这里插入图片描述

我自己找的正常文件,这个值是0x07
在这里插入图片描述

这是我找的gif 图片…(突然觉得好魔性)
在这里插入图片描述

所以,对于poc.gif 来说,赋值之后:scd->code_size=4, scd->clear_code=8 。对于正常gif来说,赋值之后:scd->code_size=8, scd->clear_code=128 。这个clear_code 起着很关键的作用。

等下次进来的时候,就会进入到有漏洞的代码部分了。
在这里插入图片描述

这部分代码会一直执行,直到scd->firstcode != scd->clear_codescd->firstcode 的值就是GetCode() 函数的返回值,理解GetCode() 函数之后可以知道,正常情况下它的返回值读到的gif 数据部分的某个字节的值。

在这里插入图片描述

图中是我截的处理正常GIF 文件时候的调试信息,scd->buf 中存储读到的gif文件的内容,每次会读254个字节(这个是由0x07 之后的0xfe 来决定的)。

在这里插入图片描述

正常文件之所以不会导致服务器宕机,是因为:其中包含了和scd->clear_code 不相等的字节(即0x80)。不清楚为什么会设定这样的循环截止条件(scd->firstcode != scd->clear_code),估计跟gif 的格式有关吧。

在不包含截止条件的情况下,会一直执行GetCode_() 函数,并且通过scd->curbit 值的不断改变,会遍历到scd->buf 中后面的字节,也就是说会在读取到的字节中一直找有没有值不为0x80 的字节。

scd->lastbit 存储了当前scd->buf 中存储的比特数,scd->curbit 表示当前遍历了哪些比特了。

当遍历完所有的比特的时候,就会满足第388行的判断条件。
在这里插入图片描述

在这里插入图片描述

进入388 行的if 语句之后,会再398行再次读取文件,从而判断是否已经读到gif 文件的末尾。读到末尾的标志就是count<=0 ,因为GetDataBlock() 函数的返回值是读取的字节数。如果判断读到末尾的话,scd->done 就会被置为true 。在下一次执行这个函数的时候,就会在第393return -1 ,从而使得scd->firstcode != scd->clear_code ,然后跳出循环。

那么问题来了,因为count 的定义是unsigned char ,所以它的值一定是0-255 之间的。所以GetDataBlock_() 函数的返回值一定是个大于0的数(要么返回count ,要么返回-1 ,但是因为countunsigned char ,返回-1 时,它的值其实是大于0的)。所以这边不会判断到文件结束,不会在这里返回-1 ,从而结束循环。这就是最根源的漏洞点。

让循环结束有两种方式:

  • 读完整个gif 文件,达到文件末尾,这个因为数据类型定义错误,导致不可能执行;
  • 让文件中包含与scd->clear_code相等的字节,满足截止条件,这个正常的gif 文件是可以满足的。

那,我们要是想循环一直执行,从而消耗服务器资源,就需要再破坏第二个循环结束条件。通过构造一个所有字节都与scd->clear_code 相等的gif 文件(对于poc.gif 来说,scd->clear_code 值为8 ,所以构造的数据均为0x88),就这么简单~~

总结

总结来说,就是:
正常文件都是包含截止条件的,所以不会出问题。
而我们恶意构造的文件不包含截止文件,而读到文件末尾的限制又因为数据类型定义出错,而无法起作用。

好像结束的有点突然…
感觉已经讲的很清楚很啰嗦了。这次学习经历应该对我的代码审计能力有提高,多多练习。

猜你喜欢

转载自blog.csdn.net/littlelittlebai/article/details/83692198