>  기사  >  백엔드 개발  >  PHP 커널 계층 구문 분석 역직렬화 취약점

PHP 커널 계층 구문 분석 역직렬화 취약점

藏色散人
藏色散人앞으로
2019-04-02 12:00:003348검색

PHP 커널 계층 구문 분석 역직렬화 취약점

머리말

PHP를 배우는 과정에서 PHP의 00 잘림, MD5 결함, 역직렬화 우회 __wakeup 등 일부 PHP 기능이 이해하기 어렵다는 것을 알게 되었습니다. . 나는 피상적인 이해에만 머물고 싶지 않고 PHP 코어가 어떻게 이를 수행하는지 탐구하고 싶습니다. __wakeup等等。本人不想拘泥于表面现象的理解,想探究PHP内核到底是怎样做到的。

下面是将用CTF中常用的一个反序列化漏洞CVE-2016-7124(绕过魔法函数__wakeup)为例,将此次调试PHP内核的过程分享出来。包括从内核源码调试环境的搭建,序列化与反序列化内核源码分析到最后的漏洞分析整个部分。(推荐:PHP教程

一、一个例子引发的思考

我们可以首先看本人写的小例子。

PHP 커널 계층 구문 분석 역직렬화 취약점

根据上图我们先介绍下PHP中的魔法函数:

我们先看下官方文档对几个常用魔法函数的介绍:

PHP 커널 계층 구문 분석 역직렬화 취약점

这里稍作总结,当一个类被初始化为实例时会调用__construct,当被销毁时会调用__destruct

当一个类调用serialize进行序列化时会自动调用__sleep函数,当字符串要利用unserialize反序列化成一个类时会调用__wakeup函数。上述魔法函数如果存在都将会自动进行调用。不用自己手动进行显示调用。

现在我们来看最开始的代码部分,在__destruct函数中有写入文件的敏感操作。我们这里利用反序列化构造危险的字符串有可能会造成代码执行漏洞。

当我们构造好相应的字符串准备进行利用时,我们却发现它的__wakeup函数中有过滤操作,这就给我们的构造造成了阻碍。因为我们知道反序列化无论如何都是要先调用__wakeup函数的。

这里我们不禁想到了利用这个PHP反序列化漏洞CVE-2016-7124(绕过魔法函数__wakeup),轻松绕过反序列化会自动调用的魔法函数___wakeup,把敏感操作写入进了文件。

当然,上面的代码只是我个人举得一个简单例子,真实情况中不乏有上述类似情况的出现。但是这种绕过方法却使我非常感兴趣。PHP的内部到底是如何操作和处理才会影响到上层代码逻辑出现如此神奇的情况(BUG)。接下来本人将对PHP内核进行动态调试分析。探究此问题。

此漏洞(CVE-2016-7124)受影响版本PHP5系列为5.6.25之前,7.x系列为7.0.10之前。所以我们后面会编译两个版本:一为不受此漏洞影响的版本7.3.0,另一个版本为漏洞存在的版本5.6.10。通过两个版本的对比来更详细的了解其差异。

二、PHP源码调试环境搭建

我们都知道PHP是由C语言开发,因本人所使用环境为WIN 10,所以主要介绍Windows下的环境搭建。我们需要如下材料:

PHP源码
PHP SDK工具包,用于构建PHP
调试所需要IDE

源码可在GITHUB上下载,链接:https://github.com/php/php-src,可以选择所需要的版本进行下载。

PHPSDK 的工具包下载地址: https://github.com/Microsoft/php-sdk-binary-tools 这个地址所下载的工具包只支持VC14,VC15。当然你也可以从https://windows.php.net/downloads/找到支持PHP低版本的VC11,VC12等,在使用PHP SDK之前必须保证你有安装对应版本Windows SDK组件的VS。

后文中会使用PHP7.3.0和5.6.10,下面会介绍这两个版本的源码编译,其他版本手法类似。

2.1 编译Windows PHP 7.3.0

本机环境WIN10 X64,PHP SDK是在上述github链接上下载。进入SDK目录,发现4个批处理文件,这里双击phpsdk-vc15-x64

PHP 커널 계층 구문 분석 역직렬화 취약점

接着在此shell中输入 phpsdk_buildtreephp7
다음은 CTF에서 흔히 사용되는 역직렬화 취약점인 CVE-2016-7124(매직 함수 __wakeup 우회)로, PHP 커널 디버깅 과정을 공유하는 예시입니다. 커널 소스코드 디버깅 환경 구축, 직렬화 및 역직렬화 커널 소스코드 분석부터 최종 취약점 분석까지 전 과정을 포함합니다. (권장: PHP 튜토리얼)

PHP 커널 계층 구문 분석 역직렬화 취약점1. 예제를 통해 촉발된 생각

PHP 커널 계층 구문 분석 역직렬화 취약점먼저 제가 작성한 작은 예를 살펴보겠습니다.

🎜PHP 커널 계층 구문 분석 역직렬화 취약점 🎜🎜기반 on 위 그림에서 우리는 먼저 PHP의 마법 함수를 소개합니다: 🎜🎜🎜 먼저 공식 문서에서 일반적으로 사용되는 몇 가지 마법 함수에 대한 소개를 살펴보겠습니다: 🎜🎜 B7E7957cf875A78C1EC87395A5468.png 예제에서는 __ Construct code>, <code>__destruct를 호출합니다. > 소멸되면 호출됩니다. 🎜🎜🎜클래스가 직렬화를 위해 serialize를 호출하면 unserialize를 사용하여 문자열을 클래스로 역직렬화해야 하는 경우 __sleep 함수가 자동으로 호출됩니다. code> __wakeup 함수가 호출됩니다. 위의 매직 함수가 존재하는 경우 자동으로 호출됩니다. 직접 디스플레이 호출을 수동으로 할 필요가 없습니다. 🎜🎜이제 코드의 첫 번째 부분을 살펴보겠습니다. __destruct 함수에는 파일 쓰기와 같은 민감한 작업이 있습니다. 여기서는 역직렬화를 사용하여 코드 실행 취약점을 유발할 수 있는 위험한 문자열을 생성합니다. 🎜🎜🎜해당 문자열을 구성하고 사용할 준비를 하던 중 __wakeup 함수에 필터링 작업이 있어 구성을 방해하는 것을 발견했습니다. 역직렬화는 먼저 __wakeup 함수를 호출해야 한다는 것을 알고 있기 때문입니다. 🎜🎜🎜여기서 우리는 이 PHP 역직렬화 취약점 CVE-2016-7124(마법 함수 __wakeup 우회)를 사용하여 역직렬화에 의해 자동으로 호출되는 마법 함수 __wakeup을 쉽게 우회하고 민감한 작업을 파일에 기록하는 것을 생각하지 않을 수 없습니다. 🎜🎜🎜물론, 위 코드는 제가 개인적으로 제시한 간단한 예시일 뿐 실제 상황에서는 위와 비슷한 상황이 많이 있습니다. 하지만 이 우회 방법은 나에게 많은 흥미를 준다. PHP의 내부 연산 및 처리가 상위 코드 로직에 어떤 영향을 미쳐 이러한 마법 같은 상황(BUG)을 발생시키는가? 다음으로 PHP 커널의 동적 디버깅 분석을 수행하겠습니다. 이 질문을 살펴보세요. 🎜🎜🎜이 취약점(CVE-2016-7124)은 PHP5 시리즈 5.6.25 이전 버전과 7.x 시리즈 7.0.10 이전에 영향을 미칩니다. 따라서 추후에 두 가지 버전을 컴파일할 예정입니다. 하나는 이 취약점의 영향을 받지 않는 버전 7.3.0이고, 다른 하나는 취약점이 존재하는 버전 5.6.10입니다. 두 버전을 비교하여 차이점에 대해 자세히 알아보세요. 🎜🎜🎜🎜 2. PHP 소스코드 디버깅 환경 구축 🎜🎜🎜🎜 PHP가 C 언어로 개발된 것은 다들 아시죠? 제가 사용하는 환경은 WIN 10이기 때문에 주로 윈도우 환경 구축을 소개합니다. 다음 자료가 필요합니다. 🎜rrreee🎜 소스 코드는 GITHUB에서 다운로드할 수 있습니다. 링크: https://github.com/php/php-src, 다운로드에 필요한 버전을 선택할 수 있습니다. 🎜🎜PHPSDK 툴킷 다운로드 주소: https://github.com/Microsoft/php-sdk-binary-tools 이 주소에서 다운로드한 툴킷은 VC14 및 VC15만 지원합니다. 물론, https://windows.php.net/downloads/에서 더 낮은 버전의 PHP를 지원하는 VC11, VC12 등도 찾을 수 있습니다. PHP SDK를 사용하기 전에 해당하는 VS가 설치되어 있는지 확인해야 합니다. Windows SDK 구성 요소의 버전입니다. 🎜🎜🎜 다음 글에서는 PHP7.3.0과 5.6.10을 사용하겠습니다. 이 두 버전의 소스 코드 컴파일 방법은 아래에서 소개하겠습니다. 🎜🎜🎜🎜2.1 Windows PHP 7.3.0 컴파일🎜🎜🎜🎜Native 환경 WIN10 X64, PHP SDK는 위 github 링크에서 다운로드 받습니다. SDK 디렉토리에 들어가서 4개의 배치 파일을 찾으세요. 여기에서 phpsdk-vc15-x64를 더블클릭하세요. 🎜🎜PHP 커널 계층 구문 분석 역직렬화 취약점🎜🎜계속 이 쉘에 phpsdk_buildtreephp7을 입력하면 동일한 디렉토리에 php7 폴더가 나타나고 쉘 디렉토리도 변경된 것을 확인할 수 있습니다. 🎜🎜🎜🎜🎜🎜🎜🎜

그런 다음 압축이 풀린 소스 코드를 php7vc15x64 아래에 넣고 쉘을 이 폴더에 넣은 다음 phpsdk_deps–update–branchmaster 명령을 사용하여 관련 종속 구성 요소를 업데이트하고 다운로드합니다. phpsdk_deps–update–branchmaster命令更新下载相关依赖组件。

等待完成后,进入源码目录下双击buildconf.bat批处理文件,它会释放configure.batconfigure.js两个文件,在shell中运行configure–disable-all–enable-cli–enable-debug–enable-phar 配置相应的编译选项,如还有别的需求,可执行 configure –help 查看

PHP 커널 계층 구문 분석 역직렬화 취약점

根据提示,直接使用nmake进行编译。

PHP 커널 계층 구문 분석 역직렬화 취약점

编译完成,可执行文件目录在php7vc15x64php-srcx64Debug_TS文件夹下。我们可输入php -v查看相关信息。

PHP 커널 계층 구문 분석 역직렬화 취약점

2.2 编译Windows PHP 5.6.10

方法跟7.3.0 相同,只需注意的是PHP5.6使用WindowsSDK 组件版本为VC11,需要下载VS2012,并且不能使用github上下载的PHP SDK进行编译,需要在 https://windows.php.net/downloads/ 上选择VC11 的PHP SDK和相关依赖组件进行编译,其余和上述完全相同,这里不再重复。

PHP 커널 계층 구문 분석 역직렬화 취약점

2.3 调试配置

因为我们上述已经编译好了PHP解释器,我们这里直接使用VSCODE来进行调试。

下载完成后安装C/C++调试扩展。

PHP 커널 계층 구문 분석 역직렬화 취약점

接着打开源码目录,点击调试—>打开配置,会打开launch.json文件。

PHP 커널 계층 구문 분석 역직렬화 취약점

根据上图,配置好这三个参数后,可在当前目录下1.php中写PHP代码,在PHP源码中下断点直接进行调试。

调试环境搭建完成。

三、PHP反序列化源码解析

一般提及PHP反序列化,往往就是serialize和unserialize两个成对出现的函数,当然必不可少的还有__sleep()和__wakeup()这两个魔术方法。众所周知,序列化简单点来说就是对象存文件,反序列化刚好相反,从文件中把对象读取出来并实例化。

下面,我们根据上面搭好的调试环境,通过动态调试的手法来直观的反应PHP(7.3.0版本)中序列化与反序列化到底干了哪些事情。

3.1 serialize源码分析

我们先写个不含有__sleep魔法函数的简单Demo:

PHP 커널 계층 구문 분석 역직렬화 취약점

接着我们在源码中全局搜索serialize函数,定位此函数是在var.c文件中。我们直接在函数头下断点,并启动调试。

PHP 커널 계층 구문 분석 역직렬화 취약점

我们可见在做了一些准备工作后,开始进入序列化处理函数,我们跟进php_var_serialize函数。

PHP 커널 계층 구문 분석 역직렬화 취약점

我们这里继续跟进php_var_serialize_intern函数,下面就是主要处理函数了,因为函数代码比较多,我们这里只截出关键部分,此函数还在var.c文件中。

PHP 커널 계층 구문 분석 역직렬화 취약점

整个函数的结构是switch case,通过宏Z_TYPE_P解析struc变体的类型(此宏展开为struc->u1.v.type),来判断要序列化的类型,从而进入相应的CASE部分进行操作。下图为类型定义。

PHP 커널 계층 구문 분석 역직렬화 취약점

根据上图红框中的数字8,我们可知此时需要要序列化为一个对象IS_OBJECT
완료를 기다린 후 소스 코드 디렉터리에 들어가서 buildconf.bat 배치 파일을 두 번 클릭하면 configure.batconfigure가 모두 해제됩니다. Node.js 파일을 실행하고 셸에서 configure–disable-all–enable-cli–enable-debug–enable-phar을 실행하여 해당 컴파일 옵션을 구성하세요. 다른 요구 사항이 있는 경우 구성 –help를 실행하여 볼 수 있습니다.

🎜PHP 커널 계층 구문 분석 역직렬화 취약점 🎜🎜nmake 사용 프롬프트에 따라 직접 compile . 🎜🎜PHP 커널 계층 구문 분석 역직렬화 취약점 🎜🎜컴파일 완료되면 실행 파일 디렉터리는 php7vc15x64php-srcx64Debug_TS 폴더 아래에 있습니다. php -v를 입력하면 관련 정보를 볼 수 있습니다. 🎜🎜PHP 커널 계층 구문 분석 역직렬화 취약점 🎜🎜2.2 Windows PHP 5.6.10 컴파일🎜🎜방법은 7.3.0과 동일합니다. 참고로 PHP5.6은 WindowsSDK 구성 요소 버전이 VC11이므로 VS2012를 다운로드해야 하며 PHP를 사용할 수 없습니다. github에서 다운로드한 SDK 컴파일하려면 https://windows.php.net/downloads/에서 VC11 PHP SDK 및 관련 종속 구성 요소를 선택하여 컴파일해야 합니다. 나머지는 위와 동일하므로 여기서는 반복하지 않습니다. 🎜🎜PHP 커널 계층 구문 분석 역직렬화 취약점 🎜🎜2.3 디버깅 구성🎜🎜위의 PHP 인터프리터를 컴파일했기 때문에 여기서는 VSCODE를 직접 사용하여 디버깅합니다. 🎜🎜다운로드 후 C/C++ 디버깅 확장 프로그램을 설치하세요. 🎜🎜PHP 커널 계층 구문 분석 역직렬화 취약점🎜 🎜계속 소스 코드 디렉터리를 열고 디버그 -> 구성 열기를 클릭하면 launch.json 파일이 열립니다. 🎜🎜PHP 커널 계층 구문 분석 역직렬화 취약점 🎜🎜기반 on 위와 같이 이 세 가지 매개변수를 구성한 후 현재 디렉터리의 1.php에 PHP 코드를 작성하고, PHP 소스 코드에 중단점을 설정하여 직접 디버깅할 수 있습니다. 🎜🎜디버깅 환경이 설정되었습니다. 🎜🎜3. PHP 역직렬화 소스 코드 분석🎜🎜일반적으로 PHP 역직렬화에는 직렬화와 역직렬화라는 두 가지 함수가 쌍으로 나타나는데, 물론 __sleep( )과 __wakeup()이 그 역할을 합니다. 두 가지 마법 방법. 우리 모두 알고 있듯이 직렬화는 단순히 개체가 파일에 저장되는 것을 의미하는 반면 역직렬화는 그 반대입니다. 🎜🎜 다음으로 위에서 설정한 디버깅 환경을 바탕으로 동적 디버깅을 이용하여 PHP(버전 7.3.0)에서 직렬화, 역직렬화가 어떤 일을 하는지 직관적으로 반영해보겠습니다. 🎜🎜3.1 소스 코드 분석 직렬화🎜🎜먼저 __sleep 마법 함수가 포함되지 않은 간단한 데모를 작성해 보겠습니다. 🎜🎜PHP 커널 계층 구문 분석 역직렬화 취약점 🎜🎜 몇 가지 준비 작업을 마친 후 직렬화 처리 함수에 들어가기 시작하고 php_var_serialize 함수를 따라가는 것을 볼 수 있습니다. 🎜🎜🎜🎜🎜 여기서는 계속 php_var_serialize_intern 함수를 따르겠습니다. 다음은 주요 처리 함수입니다. 함수 코드가 많기 때문에 여기서는 핵심 부분만 잘라냅니다. 파일. 🎜🎜🎜🎜🎜 전체 함수의 구조는 스위치 케이스입니다. struc 변형의 유형은 매크로 Z_TYPE_P(이 매크로는 struc->u1.v.type으로 확장됨)를 통해 구문 분석되어 직렬화할 유형을 결정한 후 다음을 입력합니다. 작동을 위한 해당 CASE 부분입니다. 아래 그림은 유형 정의를 보여줍니다. 🎜🎜🎜PHP 커널 계층 구문 분석 역직렬화 취약점🎜🎜 위 그림의 빨간색 상자에 있는 숫자 8에 따르면 IS_OBJECT 개체로 직렬화되고 해당 CASE 분기를 입력해야 함을 알 수 있습니다. 🎜🎜

PHP 커널 계층 구문 분석 역직렬화 취약점

위 그림에서 매직 함수 __sleep의 호출 타이밍을 볼 수 있습니다. 우리가 작성한 데모에는 이 함수가 없기 때문에 프로세스가 이 분기에 들어가지 않습니다. 다른 분기는 다른 처리 흐름을 나타냅니다. 나중에 마법 함수 __sleep을 사용하여 프로세스를 살펴보겠습니다. __sleep的调用时机,因为我们写的Demo中并没有此函数,所以流程并不会进入此分支。不同的分支代表不同的处理流程,我们稍后再看带有魔法函数__sleep的流程。

PHP 커널 계층 구문 분석 역직렬화 취약점

因上面case IS_OBJECT分支中没有流程命中,case中又没有break语句,继续执行进入IS_ARRAY分支,在这里从struc结构中提取出类名,计算其长度并赋值到buf结构中,并提取出类中要序列化的结构存入哈希数组中。

PHP 커널 계층 구문 분석 역직렬화 취약점

接下来就是利用php_var_serialize_intern函数递归解析整个哈希数组的过程,从中分别提取出变量名和值进行格式解析并将解析完成的字符串拼接到buf结构中。最后当整个过程结束后,整个字符串讲完全存进柔性数组结构buf中。

PHP 커널 계층 구문 분석 역직렬화 취약점

从上图红框中可看出跟最终结果是相吻合的。我们接下来稍微修改下Demo,添加魔法函数__sleep,根据官方文档中描述,__sleep函数必须返回一个数组。我们并在该函数中调用了一个类的成员函数。观察其具体行为。

PHP 커널 계층 구문 분석 역직렬화 취약점

前面流程完全相同,此处不再重复,我们从分支点开始看。

PHP 커널 계층 구문 분석 역직렬화 취약점

我们直接跟进php_var_serialize_call_sleep函数。

PHP 커널 계층 구문 분석 역직렬화 취약점

我们这里继续跟进call_user_function,根据宏定义,它实际上是调用了_call_user_function_ex函数,在这里做了一些拷贝动作,故不做截图,流程接下来进入zend_call_function函数的调用。

PHP 커널 계층 구문 분석 역직렬화 취약점

函数zend_call_function中,实际情况下,在__sleep中需要做一些我们自己的事情,这里PHP将要做的操作压入PHP自己的zend_vm引擎堆栈中,稍后会进行一条条解析(就是解析相应的OPCODE)。

PHP 커널 계층 구문 분석 역직렬화 취약점

这里流程会命中此分支,我们跟进zend_execute_ex函数。

PHP 커널 계층 구문 분석 역직렬화 취약점

我们这里可以看到在ZEND_VM中,整体体处理流程为while(1)循环,不断解析ZEND_VM栈中的操作。上图红框中ZEND_VM引擎会利用ZEND_FASTCALL方式派发到到相应的处理函数。

PHP 커널 계층 구문 분석 역직렬화 취약점

PHP 커널 계층 구문 분석 역직렬화 취약점

因为我们在__sleep

d82b1386382b48674d562b7195d5434 .pngPHP 커널 계층 구문 분석 역직렬화 취약점

위의 경우 IS_OBJECT 분기에는 프로세스 적중이 없고 해당 경우에는 break 문이 없으므로 IS_ARRAY 분기로 실행이 계속됩니다. 여기서 클래스 이름은 struc 구조에서 추출되고 해당 길이가 계산되어 buf에 할당됩니다. 구조 및 클래스에 필요한 정보가 추출됩니다. 직렬화된 구조는 해시 배열에 저장됩니다.


🎜🎜 다음 단계는 php_var_serialize_intern 함수를 사용하여 전체 해시 배열을 재귀적으로 구문 분석하고, 형식 구문 분석을 위해 변수 이름과 값을 추출하고, 구문 분석된 문자열을 buf 구조에 연결하는 것입니다. 마지막으로 전체 프로세스가 완료되면 전체 문자열이 유연한 배열 구조인 buf에 완전히 저장됩니다. 🎜🎜🎜🎜 🎜 위 사진의 빨간색 상자를 보면 최종 결과와 일치함을 알 수 있습니다. 다음으로, 데모를 약간 수정하고 마법 함수 __sleep를 추가합니다. 공식 문서에 따르면 __sleep 함수는 배열을 반환해야 합니다. 이 함수에서는 클래스 멤버 함수도 호출했습니다. 특정 동작을 관찰하십시오. 🎜🎜🎜🎜 🎜 이전 프로세스는 완전히 동일하며 여기서는 반복하지 않습니다. 분기점부터 시작하겠습니다. 🎜🎜PHP 커널 계층 구문 분석 역직렬화 취약점🎜🎜우리 php_var_serialize_call_sleep 함수로 직접 후속 조치를 취하세요. 🎜🎜🎜🎜 🎜 call_user_function에 대해 계속해서 살펴보겠습니다. 매크로 정의에 따르면 실제로 _call_user_function_ex 함수를 호출하므로 여기서는 스크린샷이 찍히지 않습니다. 프로세스 계속 zend_call_function 함수 호출을 입력합니다. 🎜🎜🎜🎜🎜 실제 상황에서는 zend_call_function 함수에서 __sleep에서 일부 작업을 수행해야 합니다. 여기서 PHP는 수행할 작업을 PHP 자체 에 푸시합니다. zend_vm code>엔진 스택에서는 나중에 하나씩 분석(즉, 해당 OPCODE를 구문 분석)이 수행됩니다. 🎜🎜<img src="https://img.php.cn/upload/image/915/387/451/1554175500896576.png" title="1554175500896576.png" alt="PHP 커널 계층 구문 분석 역직렬화 취약점">🎜🎜여기 프로세스는 이 분기에 도달하고 <code>zend_execute_ex 함수를 따릅니다. 🎜🎜🎜🎜 🎜 여기서는 ZEND_VM에서 전체 처리 흐름이 ZEND_VM 스택의 작업을 지속적으로 구문 분석하는 while(1) 루프라는 것을 알 수 있습니다. 위 그림의 빨간색 상자에 있는 ZEND_VM 엔진은 ZEND_FASTCALL 메서드를 사용하여 해당 처리 기능으로 디스패치합니다. 🎜🎜🎜PHP 커널 계층 구문 분석 역직렬화 취약점 🎜🎜 PHP 커널 계층 구문 분석 역직렬화 취약점__sleep에서 호출됩니다. 여기에서 show가 먼저 발견되고 다음 작업은 새로운 구문 분석의 다음 라운드를 위해 ZEND_VM 스택으로 계속 푸시됩니다(여기에 작업이 있습니다). 쇼) ) 전체 작업이 구문 분석될 때까지. 여기서는 더 이상 후속 조치를 취하지 않겠습니다. 🎜🎜🎜🎜🎜 __sleep의 반환 값인 위의 나가는 매개 변수 retval을 기억하시나요? 위 그림은 반환된 배열의 첫 번째 요소 x를 보여줍니다. 물론 변수에서 직접 볼 수도 있습니다. 🎜🎜

이렇게 큰 원을 그린 후에는 서로 다른 경로가 동일한 목표로 이어집니다. _sleep 함수에서 일련의 작업을 처리한 후 php_var_serialize_class 함수를 사용하여 클래스 이름을 직렬화하고 _sleep 함수의 반환 값에서 구조를 반복적으로 직렬화합니다. . 마지막으로 결과는 buf 구조에 저장됩니다. 이 시점에서 직렬화의 모든 과정이 완료됩니다.

3.1.1 직렬화 과정 요약

직렬화 과정을 요약해 보겠습니다.

마술 함수가 없으면 클래스 이름을 직렬화합니다 –>재귀를 사용하여 나머지 구조를 직렬화합니다

마술 함수가 있을 때 , 매직 함수 호출 __sleep –> ZEND_VM 엔진을 사용하여 PHP 작업 구문 분석 –> 직렬화되어야 하는 배열 반환 –> 직렬화 클래스 이름 –> 재귀 직렬화의 반환 값 구조 사용 __sleep.

3.2 직렬화 해제 소스 코드 분석

직렬화 프로세스를 읽은 후 다음으로 가장 간단한 데모의 직렬화 해제 프로세스를 살펴보겠습니다. 이 예제에는 마법 함수가 포함되어 있지 않습니다. unserialize流程。此例子不含魔法函数。

PHP 커널 계층 구문 분석 역직렬화 취약점

方法跟上面相同,unserialize源码也在var.c文件中。

PHP 커널 계층 구문 분석 역직렬화 취약점

PHP 커널 계층 구문 분석 역직렬화 취약점

上图中涉及到了PHP7中的新特性,带过滤的反序列化,根据allowed_classes的设置情况来过滤相应的PHP对象,防止非法数据注入。被过滤的对象会被转化成__PHP_Incomplete_Class对象不能被直接使用,但是这里对反序列化流程没有影响,这里不做详细探讨。我们跟进php_var_unserialize函数。

PHP 커널 계층 구문 분석 역직렬화 취약점

我们这里继续跟入php_var_unserialize_internal函数。

PHP 커널 계층 구문 분석 역직렬화 취약점

此函数内部主要操作流程为对字符串进行解析,然后跳转到相应的处理流程。上图中解析出第一个字母0,代表此次反序列化为一个对象。

PHP 커널 계층 구문 분석 역직렬화 취약점

这里首先会解析出对象名字,并进行查表操作确定此对象确实存在,我们继续向下看。

PHP 커널 계층 구문 분석 역직렬화 취약점

上述操作做完之后,我们这里根据对象名称new出了自己新的对象并进行了初始化,但是我们的反序列化操作还是没有完成,我们跟进object_common2函数。

在这里我们看到了对魔法函数的判断与检测,但是调用部分并不在此。我们继续跟进process_nested_data函数。

PHP 커널 계층 구문 분석 역직렬화 취약점

PHP 커널 계층 구문 분석 역직렬화 취약점

看来这个函数利用WHILE循环来嵌套解析剩余的部分了,·其中包含两个php_var_unserialize_internal函数,第一个会解析名称,第二个是解析名称所对应的值。process_nested_data函数运行完毕后,字符串解析完毕,反序列化操作主要内容已经完成,流程即将进入尾声了。

PHP 커널 계층 구문 분석 역직렬화 취약점

逐层返回至最初的函数PHP_FUNCTION中,我们看到就是一些扫尾工作了,释放申请的空间,反序列化完毕。这里并没有调用到我们的魔法函数__wakeup。为了找出__wakeup的调用时机,我们这里修改下Demo。

PHP 커널 계층 구문 분석 역직렬화 취약점

这里开始新的一轮调试。发现在序列化完成后,在PHP_VAR_UNSERIALIZE_DESTROY
PHP 커널 계층 구문 분석 역직렬화 취약점

방법 위와 마찬가지로 unserialize 소스 코드도 var.c 파일에 있습니다. PHP 커널 계층 구문 분석 역직렬화 취약점

🎜🎜🎜 PHP 커널 계층 구문 분석 역직렬화 취약점🎜🎜Abo 사진이 있어요 여기에는 불법 데이터 삽입을 방지하기 위해 allowed_classes 설정에 따라 해당 PHP 개체를 필터링하는 필터링된 역직렬화라는 PHP7의 새로운 기능이 포함됩니다. 필터링된 개체는 __PHP_Incomplete_Class 개체로 변환되어 직접 사용할 수 없습니다. 그러나 이는 역직렬화 프로세스에 영향을 미치지 않으며 여기서는 자세히 설명하지 않습니다. php_var_unserialize 함수를 사용하여 후속 조치를 취합니다. 🎜🎜🎜77cb18f8c040bc50490b76fd8b5595d .png🎜🎜 여기서는 php_var_unserialize_internal 함수를 계속 따릅니다. 🎜🎜🎜PHP 커널 계층 구문 분석 역직렬화 취약점 🎜🎜 이 함수의 주요 내부 작업 프로세스는 문자열을 구문 분석한 후 해당 처리 프로세스로 점프하는 것입니다. 위 그림에서 첫 번째 문자 0은 구문 분석되어 객체로의 역직렬화를 나타냅니다. 🎜🎜🎜a3a56e193bbe64e395746cfa1f2e13d.p ng🎜 🎜 여기서는 먼저 객체 이름을 파싱하고 객체가 실제로 존재하는지 확인하기 위해 테이블 ​​조회 작업을 수행합니다. 🎜🎜🎜🎜🎜 위 작업이 완료된 후 new 개체 이름을 기반으로 새 개체를 만들고 초기화했습니다. 그러나 역직렬화 작업은 아직 완료되지 않았습니다. object_common2 함수. 🎜🎜🎜여기서 마법 기능의 판단과 탐지를 볼 수 있지만 호출하는 부분은 여기에 없습니다. 계속해서 process_nested_data 함수를 따라가 보겠습니다. 🎜🎜🎜PHP 커널 계층 구문 분석 역직렬화 취약점🎜🎜 PHP 커널 계층 구문 분석 역직렬화 취약점🎜🎜 이 기능 WHILE 루프를 사용하여 나머지 부분을 중첩적으로 구문 분석합니다. 여기에는 두 개의 php_var_unserialize_internal 함수가 포함되어 있습니다. 첫 번째 함수는 이름을 구문 분석하고 두 번째 함수는 이름에 해당하는 값을 구문 분석합니다. process_nested_data 함수 실행이 끝나면 문자열 구문 분석이 완료되고 역직렬화 작업의 주요 내용이 완료되며 프로세스가 곧 종료됩니다. 🎜🎜🎜🎜🎜 레이어별로 원래 함수인 PHP_FUNCTION으로 돌아가면 일부 마무리 작업이 완료되고 적용된 공간이 해제되며 역직렬화가 완료된 것을 볼 수 있습니다. 우리의 마법 함수 __wakeup는 여기서 호출되지 않습니다. __wakeup이 언제 호출되는지 확인하기 위해 여기에서 데모를 수정합니다. 🎜🎜🎜5b2c31167b7371255bcb4910e4103f3 .png🎜🎜 여기서 새로운 디버깅 라운드가 시작됩니다. 직렬화가 완료된 후 PHP_VAR_UNSERIALIZE_DESTROY가 공간을 해제한 위치에 우리가 볼 것으로 예상했던 호출이 나타나는 것으로 확인되었습니다. 🎜🎜🎜🎜🎜

역직렬화 과정에서 __wakeup이 발견되면 VAR_WAKEUP_FLAG 플래그를 기억하시나요? 여기서 bar_dtor_hash 배열을 순회하다가 이 플래그를 만나면 __wakeup에 대한 호출이 공식적으로 시작됩니다. 호출 방법은 앞서 소개한 것과 같습니다. __sleep은 정확히 동일하므로 여기서는 반복하지 않습니다. 이 시점에서 모든 역직렬화 프로세스가 완료됩니다.

3.2.1 직렬화 프로세스 요약

위에서 알 수 있듯이 직렬화 프로세스에 비해 역직렬화 프로세스는 마법 함수가 있기 때문에 프로세스에 차이가 발생하지 않습니다. Unserialize 과정은 다음과 같습니다.

역직렬화된 문자열 가져오기 –>유형에 따라 역직렬화 –>해당 역직렬화 클래스를 찾기 위해 테이블 ​​조회 –>문자열을 기준으로 요소 수 판단 –>new 새로운 인스턴스 생성 –> 남은 문자열을 반복적으로 구문 분석 –> 매직 함수 __wakeup이 존재하는지 확인하고 표시합니다. –> 공간을 해제하고 표시가 있는지 확인합니다.

4. PHP 역직렬화 취약점

위의 소스 코드 기반을 사용하여 이제 취약점 CVE-2016-7124(__wakeup 우회) 마법 함수를 살펴보겠습니다.

따라서 이 취약점에는 특정 버전 요구 사항이 있습니다. 위에서 컴파일된 다른 PHP 버전(5.6.10)을 사용하여 이 취약점을 재현하고 디버깅합니다.

먼저 취약점을 재현합니다.

PHP 커널 계층 구문 분석 역직렬화 취약점

여기서 TEST 클래스에는 $a라는 요소 하나만 포함되어 있음을 볼 수 있습니다. 역직렬화할 때 요소 문자열의 요소 수를 나타내는 값을 수정합니다. 트리거되고 이 클래스는 마법 함수 __wakeup의 호출을 피합니다.

물론, 취약점을 유발하는 과정에서 흥미로운 현상도 발견되었습니다. 이것이 유일한 유발 방법은 아닙니다.

PHP 커널 계층 구문 분석 역직렬화 취약점

위 그림의 4개 페이로드에 해당하는 역직렬화 작업이 이 취약점을 유발합니다. 아래 네 가지가 취약점을 유발하지만 몇 가지 사소한 차이점이 있습니다. 여기에서 코드를 약간 수정합니다.

PHP 커널 계층 구문 분석 역직렬화 취약점

위 그림에서 역직렬화된 문자열에서 볼 수 있듯이 이 취약점은 클래스의 요소를 구문 분석하는 중 오류가 발생할 때마다 트리거됩니다. 그러나 클래스 요소의 내부 작업(예: 위 그림의 문자열 길이, 클래스 변수 유형 등 수정)을 변경하면 클래스 멤버 변수 할당이 실패하게 됩니다. 반원 수가 수정된 경우(원래 인원 수보다 많음)에만 반원 배정의 성공이 보장됩니다.

디버깅을 통해 문제를 살펴보겠습니다.

3번째 부분에서 역직렬화 소스 코드를 분석한 결과 변수의 최종 구문 분석에 문제가 있을 수 있다고 추측합니다. 여기에서 동적 디버깅을 위한 디버거로 직접 이동해 보겠습니다.

PHP 커널 계층 구문 분석 역직렬화 취약점

버전 7.3.0의 소스 코드와 비교하면 이 버전에는 필터 매개변수가 없으며 많은 버전을 반복한 후 처리 프로세스가 낮은 버전은 이제 비교적 간단해 보입니다. 그러나 전체적인 조화 로직은 변경되지 않았습니다. 여기서는 php_var_unserialize 함수를 직접 따르겠습니다. 동일한 로직은 다시 반복되지 않습니다. 클래스에서 멤버 변수를 처리하는 코드인 차이(object_common2 함수)를 직접 따르겠습니다.

PHP 커널 계층 구문 분석 역직렬화 취약점

함수 object_common2에는 두 가지 주요 작업이 있는데, process_nested_data는 클래스의 데이터를 반복적으로 구문 분석하고, process_nested_data 함수가 구문 분석에 실패하면 직접 값 0을 반환하고, 후속 __wakeup 함수는 호출될 기회가 없습니다.


취약점을 유발하는 페이로드가 두 개 이상인 이유는 다음과 같습니다.


PHP 커널 계층 구문 분석 역직렬화 취약점

클래스 멤버 수만 수정하는 경우 while 루프를 한 번만 완료하면 클래스의 멤버 변수를 완전히 할당할 수 있습니다. 내부 멤버 변수 수정 시 pap_var_unserialize 함수 호출이 실패하고, 이후 zval_dtor 및 FREE_ZVAL 함수를 호출하여 현재 키(변수) 공간을 해제함으로써 클래스 내 변수 할당이 실패하게 됩니다.


한편, PHP7.3.0 버전에서는 여기에 호출 프로세스가 나타나지 않고 단순히 표시만 되어 있고 전체 매직 함수 호출 프로세스의 타이밍이 데이터가 공개되는 시점으로 이동됩니다. 이렇게 하면 이러한 우회 문제가 방지됩니다. 이 취약점은 논리적 결함으로 인해 발생해야 합니다.

위 내용은 PHP 커널 계층 구문 분석 역직렬화 취약점의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 freebuf.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제