php 继承 组合,PHP类继承与全局变量组合造成的漏洞分析

某CMS改动后又给自己挖坑了

0x00 起因

组里的师弟在学习代码审计时遇到了某CMS的一个XSS漏洞,问题在于老版本是没有这个漏洞的,而且看起来也调用了相关的过滤函数,但是XSS还是触发了。

经过讨论分析,发现了其中的奥妙。

0x01 PHP类继承

面向对象的特性就不在多说明了,直接看以下示例代码。

我们首先写一个基础类文件 base.class.php

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15<?php

class base{

public $name;

public function __construct(){

echo 'base construct
';

$this->filter($this->name);

}

protected function filter(){

$this->name=$_GET['name'];

echo 'SQL filter
';

}

}

?>

base类显式定义一个构造器,调用自己的过滤函数,假设该过滤函数为SQL注入过滤,由于和我们讨论的问题无关,这里就不在具体写filter函数的代码。

接下来是测试函数 test.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<?php

require_once('base.class.php');

class web1 extends base{

public function __construct(){

parent::__construct();

echo 'web1 construct
';

}

protected function filter(){

parent::filter();

$this->name=$_GET['name'];

$this->name = htmlspecialchars($this->name);

echo 'XSS filter
';

}

}

class web2 extends web1{

public function __construct(){

parent::__construct();

require_once('web3.class.php');

$web3 = new web3;

echo 'web2 construct
';

}

public function show(){

echo 'show
';

echo $this->name;

}

}

$web2 = new web2;

$web2->show();

?>

解释一下这段代码,web1类继承自base类,并且重写了父类中的过滤函数,此时的过滤函数具有防XSS功能,作为演示,这里就简单使用htmlspecialchars函数进行过滤。

接着,定义web2类继承web1类,拥有方法show来echo出自己的$name。特别之处在于此时的web2在构造生成时还会调用web3类。

web3类定义在 web3.class.php 中,代码如下:

1

2

3

4

5

6

7

8

9

10<?php

class web3 extends base{

public function __construct(){

parent::__construct();

echo 'web3 construct
';

}

}

?>

web3类直接继承自base类,因此,当我们调用test.php生成web2时,其实有如下多继承关系。

1

2web2->web1->base

|->web3->base

而这其中,只有web1类中写了过滤XSS的方法,调用test.php,有如下输出效果。

1http://localhost/test.php?name=

b6d0a16e29aa534220fb27d944231250.png

显然,经过web1的过滤,xss并没有触发,但是,如果引入全局变量机制,我们再来看一下效果。

0x02 全局变量

修改后的各文件代码如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18//base.class.php

class base{

public function __construct(){

global $name;

echo 'base construct
';

$this->filter($name);

}

protected function filter(){

global $name;

$name=$_GET['name'];

echo 'SQL filter
';

}

}

?>

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//test.php

require_once('base.class.php');

class web1 extends base{

public function __construct(){

parent::__construct();

global $name;

echo 'web1 construct
';

}

protected function filter(){

parent::filter();

global $name;

$name=$_GET['name'];

$name = htmlspecialchars($name);

echo 'XSS filter
';

}

}

class web2 extends web1{

public function __construct(){

parent::__construct();

global $name;

require_once('web3.class.php');

$web3 = new web3;

echo 'web2 construct
';

}

public function show(){

echo 'show
';

global $name;

echo $name;

}

}

$web2 = new web2;

$web2->show();

?>

1

2

3

4

5

6

7

8

9

10

11

12//web3.class.php

class web3 extends base{

public function __construct(){

parent::__construct();

global $name;

echo 'web3 construct
';

}

}

?>

其他代码都未做改动,只是将之前的类变量用全局变量进行替代,此时的执行效果如下所示:

1http://localhost/test.php?name=

a6d894d1d1f3f29199855d0f56f976c3.png

过滤方法被绕过后成功执行了XSS。

0x03 对比分析

还是回到调用关系这里

1

2web2->web1->base

|->web3->base

我们可以知道程序员写这段代码时,一定是想着通过了web1改写基类方法灵活的在不同类之间切换过滤方式。

当web2被实例化时,首先是web1实例化,web1实例化会造成base实例化,接着发现父类的过滤函数被重写,因此加载了web1的新方法,web1实例化结束后,发现web2中又实例化了一次web3,web3的实例化造成base和父方法实例化,由于全局变量的原因,重新被实例化后的base在获取变量后覆盖了原有的变量,而此时的父方法无法过滤XSS,最终使得web2输出了未过滤的参数造成了XSS漏洞。

PS:小的修正方法,此时的web3如果继承自web1,就可以保证XSS过滤函数的调用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值