只为对所学知识做一个简单的梳理,如果有表达存在问题的地方,麻烦帮忙指认出来。我们一起为了遇见更好的自己而努力!
说到变量覆盖,那我们得回回课了,什么是变量?变量是如何被覆盖的?
变量一般比作盒子,我们可以在盒子里面放入东西,如果盒子多了,就得跟他们贴上标签,方便查找和调取。以这里的比喻来说,标签就是变量名,盒子里面的东西就是变量值。
但这个盒子有个特点:就是一个盒子,只能存放一个东西,如果想要放入其他东西,就得将上一个物品拿出来才可以。这在代码层面,就直接被后来放入的东西覆盖掉。
实例
$a = 1;
echo $a;
$a = 2;
?>
在这里,会输出1
,因为代码是从上往下执行的,这里并没有实施覆盖。但是变换一下echo
的位置就会发生改变
实例
$a = 1;
$a = 2;
echo $a;
?>
这样,变量覆盖就发生了,这里$a
的值就变为了2
,1
就会被覆盖掉。
但这个是需要改变代码才会实现的情况,正常情况下,开发不会做这样的事情出来,我们当然也改变不了代码的内容,所有这里就需要找开发所用的的函数,会不会出现不当的处理,造就变量覆盖漏洞的发生。
extract()
函数从数组中将变量导入到当前的符号表。该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。
实例
$a = "Original";
$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
基本解释就是 这个函数将数组变为变量
parse_str()
函数把查询字符串解析到变量中。注释:如果未设置 array
参数,则由该函数设置的变量将覆盖已存在的同名变量。
注释:php.ini
文件中的 magic_quotes_gpc
设置影响该函数的输出。如果已启用,那么在 parse_str()
解析之前,变量会被addslashes()
转换。
实例:
parse_str("name=Peter&age=43");
echo $name."
";
echo $age;
?>
输出:
Peter
43
//这个函数将字符串变为变量
$$
这个在php
使用的最多实例:
$a = b;
$b = c;
echo $$a;
?>
输出:
c
第一条$a = b
,放入最后一条里面看。echo $$a
=> echo $b
所以就相当于执行了echo $b
所以输出了c
在来个实例:
$a = 1;
foreach(array('_COKKIE','_POST','_GET') as $_REQUEST){
foreach($$_REQUEST as $_key=>$_value)
{
$$_key=addslashes($_value);}}
echo $a
?>
foreach
遍历数组,将数组里面的内容一个个拿出来去执行
as
将前面几个数组里面的值,依次放入$_REQUEST
中,第一个_COOKIE
第二个_POST
,第三个_GET
。
这里就cookie
拆分一下,变为数值,这叫做键值分离,如果传进来的是GET
,传进来的是a=1
那这里就会变为$_key=a;$_value=1
因为在上面已经将a
给解析出来了,所有这里放到下面,又变为了$a
然后函数addslashes()
的意思是 函数返回在预定义的字符前添加反斜杠。
预定义字符是:
单引号(’)
双引号(”)
反斜杠(\)
NULL
那和我们这没关系,所有就变为了$a = 1
最后输出变量$a
,值就发生了改变,经典的变量覆盖
变量覆盖漏洞想要黑盒测试发现,那可太难了,因为不知道这里传入什么数值才能达到目的,所以我们这里弄到了靶场doumiCMS
的源码,对源码审计之后在来做靶场。
代码审计有几种:
mysqli_query()
,找全部与这个函数相关的代码。现如今最合适的还是危险函数定位法
用到的工具就是 seay代码审计工具
规则添加变量覆盖的双美元符,然后进行全局搜索
这里发现了前面讲的实例基本一样的代码,我们试着来分析看一下
这里除了最后一个不知道什么意思,其他都知道,那先查一下干啥的
这里就找到了函数定义的地方,先看if
条件写的是什么
magic_quotes_gpc()
如果magic_quotes_gpc
为关闭时返回 0
,否则返回 1
。在 PHP 5.4.0
起将始终返回 FALSE
。
意思基本就是检查有没有开魔术引号,如果没开,就开一下。
最下面的else
执行的函数有写
那这里与我们无关,继续往上面找,只要确定能执行到这里就行,代码能不能执行到这里,有几个方法可以验证。
if
语句,就只会执行一个条件,显然这里并不是die()
和 exit()
先说一下死亡函数的快速确定代码有没有执行的方法
访问这个文件,将函数放入if
条件中,网页并没有发生改变,接下来换到else
中
换到else
中,网页有了反应,说明是else
中执行的语句。
接着往下看
找一下死亡函数exit()
,die()
在这里找到了死亡函数,不过这里有三个条件,满足了,就不会执行else
里面的exit()
第一个:
·strlen()·这个函数就是用来确认传进来值的长度的,只要大于0就满足,所有这里只要传了,就能通过
在这找到了,发现这里是个正则表达式的函数,用来匹配 cfg
或者 GLOBALS
的,只要这里不传进去就好了
第三个:
isset()
如果指定变量存在且不为 NULL``,则返回 TRUE
,否则返回FALSE
。所有这里检查的就是看cookie
是不是空的,空的就不满足。
那这个函数看下来,就是一个条件就行,只要传参里面不包含第二个条件里面的两个参数就行
知道了执行的条件那就好办了,现在就需要找另外个东西:session
cookie
代表身份
session
代表权限
有了session
就能已管理员的权限登录后台,那现在就找,哪个文件引用了我们现在所用的文件。
这个看到了admin/login.php
文件,这个login
一般为登录界面,既然是admin
的,那多半能得到管理员的权限。
这里使用了刚刚变量覆盖的文件
这里还使用了第二个文件,但是这里不知道文件的路劲是多少,所有得在找一下
这里添加个死亡函数,看看这个文件在哪。
这样就找到了文件在哪
代码下面还找到了登录检查,在往下面看。
这里就找到了_SESSION
,既然找到了位置,那就看一下 登录成功的session
是什么样子的,只要模仿一个就好了,反正源码在我们手上,账号密码也有。
这样就有了管理员的sisson
,因为这里间接调用了我们最开始分析的变量覆盖的文件文件,所以这里传管理员的session
进去就能已管理员的身份登录。
但是还有点需要注意,不是所有文件目录执行这个都可以的,需要找一个打开了session
的地方,代码就是session_start()
,只有找到有这个代码的地方才可以
在这个admin/login.php
是不满足条件的,因为在变量覆盖漏洞的文件里面是没有session_starta()
函数,这个chace.admin.php
虽然有session_stsrt()
,但是在文件的后面执行,也就没用了,所有还得找一个有session_start()
且在文件包含common.php
前面调用才可以。
这里就完美满足条件。
interface/comment.php?_SESSION[duomi_ckstr]=kwhi
&_SESSION[duomi_admin_id]=1
&_SESSION[duomi_group_id]=1
&_SESSION[duomi_admin_name]=admin
这里需要注意的是_RESSION
前面的下划线是因为前面有一个$
,所有不能多写,他会将这个自动填进去的。我们空出来就行。
在访问一下admin/login.php
,就登录成功了
防范方法:
建议不注册变量,直接用原生的$_GET
、$_POST
等数组变量进行操作,如果考虑程序可读性等原因,需要注册个别变量,可以直接在代码中定义变量,然后再把请求中的值赋值给他们。
如果一定要使用前面几种方式注册变量,为了解决变量覆盖的问题,可以再注册变量前先判断变量是否存在。如使用extract( )
函数则可以配置第二个参数为EXTR_SKIP
。使用php parse_str
函数注册变量前需要先自行通过代码判断变量是否存在。最重要的一点,自行申明的变量一定要初始化,不然即使注册变量代码在执行流程最前面也能覆盖掉这些未初始化的变量。
《最好的防御,是明白其怎么实施的攻击》