经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 其他 » 网络安全 » 查看文章
变量覆盖-高级篇(动态覆盖,extract综合)
来源:cnblogs  作者:Dark1nt  时间:2021/5/24 10:50:35  对本文有异议

0x00 原理

变量覆盖漏洞可以让用户定义的变量值覆盖原有程序变量,可控制原程序逻辑。

0x01 代码

  1. <?php
  2. highlight_file('index.php');
  3. function waf($a){
  4. foreach($a as $key => $value){
  5. if(preg_match('/flag/i',$key)){
  6. exit('are you a hacker');
  7. }
  8. }
  9. }
  10. foreach(array('_POST', '_GET', '_COOKIE') as $__R) {
  11. if($$__R) {
  12. foreach($$__R as $__k => $__v) {
  13. if(isset($$__k) && $$__k == $__v) unset($$__k);
  14. }
  15. }
  16. }
  17. if($_POST) { waf($_POST);}
  18. if($_GET) { waf($_GET); }
  19. if($_COOKIE) { waf($_COOKIE);}
  20. if($_POST) extract($_POST, EXTR_SKIP);
  21. if($_GET) extract($_GET, EXTR_SKIP);
  22. if(isset($_GET['flag'])){
  23. if($_GET['flag'] === $_GET['daiker']){
  24. exit('error');
  25. }
  26. if(md5($_GET['flag'] ) == md5($_Ginclude($_GET['file']){
  27. echo 'flag={你猜猜}';
  28. }
  29. }
  30. ?>

0x02 代码审计

首先对一下代码进行分析

  1. function waf($a){
  2. foreach($a as $key => $value){
  3. if(preg_match('/flag/i',$key)){
  4. exit('are you a hacker');
  5. }
  6. }
  7. }

我们知道foreach是php遍历数组的一种方式,这里waf函数遍历$a里面的变量名和变量值,如果变量名中含有flag,则报错

  1. foreach(array('_POST', '_GET', '_COOKIE') as $__R) {
  2. if($$__R) {
  3. foreach($$__R as $__k => $__v) {
  4. if(isset($$__k) && $$__k == $__v) unset($$__k);
  5. }
  6. }
  7. }

将_POST,_GET,_COOKIE中的值作为 $__R 值判断,是否存在以 $__R 值为变量名的变量,如果有 继续遍历这个变量里的变量名和变量值,如果有里面的变量名存在而且变量名等于变量值就销毁变量__k
。。这样说确实有点乱,但是后面这段foreach 和 熟悉的$$ 应该能想到是通过GET 进行传参。也就是说 $__k==$_GET[flag] ,因为后面要判断flag传参,所以这里只能是flag。 $$__k 是取 $_GET[flag]中的值作为变量名,
之后结合实战可能更好理解点吧

  1. if($_POST) { waf($_POST);}
  2. if($_GET) { waf($_GET); }
  3. if($_COOKIE) { waf($_COOKIE);}
  4. if($_POST) extract($_POST, EXTR_SKIP);
  5. if($_GET) extract($_GET, EXTR_SKIP);
  6. if(isset($_GET['flag'])){
  7. if($_GET['flag'] === $_GET['daiker']){
  8. exit('error');
  9. }
  10. if(md5($_GET['flag'] ) == md5($_GET['daiker'])){
  11. echo 'flag={你猜猜}';
  12. }
  13. }

通过 if 判断 是否存在POST,GET,COOKIE 变量传参,如果存在 用waf去检测
然后再使用extract($_POST, EXTR_SKIP),extract($_GET, EXTR_SKIP)将变量写入符号表保存起来,而且使用到EXTR_SKIP关键字后不会覆盖之前的变量。
??最后判断是否通过flag进行了传参,传了后,将$flag中的值和$daiker进行比较,相同则结束, 不相同 则 通过md5加密 进行比较,这里存在个php若类型,
我们可以通过两种md5加密后为0e开头的字符串进行绕过,比如QNKCDZO和s878926199a

0x03 实战

虽然waf函数是最先定义的,但是它是在消除变量后使用的,也就是说题目意思其实是让我们先进行变量清除-》unset函数,不然无法绕过waf函数。因为waf函数匹配了'_POST', '_GET', '_COOKIE',而且后面有要求是通过flag进行传参,所以只能先通过flag和daiker传入对应的值。
简单测试一下吧

  1. <?php
  2. highlight_file('index.php');
  3. function waf($a){
  4. foreach($a as $key => $value){
  5. if(preg_match('/flag/i',$key)){
  6. exit('are you a hacker');
  7. }
  8. }
  9. }
  10. foreach(array('_POST', '_GET', '_COOKIE') as $__R) {
  11. if($$__R) {
  12. foreach($$__R as $__k => $__v) {
  13. if(isset($$__k) && $$__k == $__v) unset($$__k);
  14. echo '成功消除函数';
  15. }
  16. }
  17. }
  18. var_dump($_POST);
  19. if($_POST) { waf($_POST);}
  20. if($_GET) { waf($_GET); }
  21. if($_COOKIE) { waf($_COOKIE);}
  22. if($_POST) extract($_POST, EXTR_SKIP);
  23. if($_GET) extract($_GET, EXTR_SKIP);
  24. if(isset($_GET['flag'])){
  25. if($_GET['flag'] === $_GET['daiker']){
  26. exit('error');
  27. }
  28. if(md5($_GET['flag'] ) == md5($_Ginclude($_GET['file']){
  29. echo 'flag={你猜猜}';
  30. }
  31. }
  32. ?>

其实如果理清了思路,多测测就能得到flag了,但是想要深入了解 foreach 是不行的,这里我们构造GET:?_POST[flag]&_POST[daiker] POST: flag=&daike= 来做个小实验

可以说明当我们传入?_POST[flag]&_POST[daiker]时 ,$_GET 保存了 以 _POST为键 值为 flag 和daiker 的数组 的数组。然后通过foreach遍历因为有'_POST', '_GET', '_COOKIE' 所以总共遍历了3次, 遍历到_GET取出数组赋值到$__R保存,$$__R其实就是$_POST 判定是否存在POST,这里我们是POST提交了参数的,所以存在,继续执行,取出里面的键和值 其实就是 flag:NULL 和 daiker:NULL 判断是否存在 $flag 由于我们POST提交了 flag和daiker,所以它遍历时可判断存在$flag,而且post提交的flag的值正好和GET里flag值一样都为空,通过unset 消去了匹配到的$flag和$daiker,使得$_POST里面没了内容。

  1. <?php
  2. highlight_file('index.php');
  3. function waf($a){
  4. foreach($a as $key => $value){
  5. if(preg_match('/flag/i',$key)){
  6. exit('are you a hacker');
  7. }
  8. }
  9. }
  10. foreach(array('_POST', '_GET', '_COOKIE') as $__R) {
  11. if($$__R) {
  12. foreach($$__R as $__k => $__v) {
  13. if(isset($$__k) && $$__k == $__v) unset($$__k);
  14. echo '成功消除函数';
  15. }
  16. }
  17. }
  18. if($_POST) { waf($_POST);}
  19. if($_GET) { waf($_GET); }
  20. if($_COOKIE) { waf($_COOKIE);}
  21. if($_POST) extract($_POST, EXTR_SKIP);
  22. if($_GET) extract($_GET, EXTR_SKIP);
  23. var_dump($_POST);
  24. if(isset($_GET['flag'])){
  25. if($_GET['flag'] === $_GET['daiker']){
  26. exit('error');
  27. }
  28. if(md5($_GET['flag'] ) == md5($_Ginclude($_GET['file']){
  29. echo 'flag={你猜猜}';
  30. }
  31. }
  32. ?>


??根据上面的运行结果我们可以看到之前被消去的$_POST突然回来了,赋值后应该会明显点。

??也就是说知道了这个原理,我们可以通过GET: ?flag=aa&daiker=bb POST: _GET[flag]=aa&_POST[daiker]=bb , 之前故意调换顺序的就是为了让这里变得更加清楚点。
??这里正好和前面反过来,GET传进去了后 遍历时就存在 $flag 变量 而$_GET[flag] 通过 foreach 进行键值得配对,最后删除了$_GET中的$flag和$daiker,然后通过extract函数,
对extract进行测试

  1. <?php
  2. if($_POST){
  3. extract($_POST, EXTR_SKIP);
  4. echo '成功';
  5. }
  6. var_dump($_GET);
  7. ?>


??测试失败,说明 extract 是不能直接进行变量的覆盖的,那只能说明 unset 没有删除干净了,可能涉及到指针吧,如果unset是使用引用置NULL,而extract是通过指针访问数组内存的话就有可能,因为内存地址保存的值还是原来的GET,通过extract可以获取到之前GET中的值,从而绕过了unset。

因为_GET虽然没了但是POST_存在,会通过指针去访问原来保存了_GET变量的内存地址,直接取出原来的值。(感觉设置不能覆盖之前变量的关键字也和内存地址有干系)。
知道了这个原理,后面的弱类型也就简单了,只要把aa和bb改成QNKCDZO和s878926199a,尝试一下。


成功爆出flag,但是我想看看 extract 是如何覆盖的。

原文链接:http://www.cnblogs.com/Dark1nt/p/14797128.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号