#每日三件事,第1060天#
本来想用一篇文章详细讲述一下sql注入,等写起来的时候才发现太长。不得已只能分开了。第一篇文章在这里:慢慢说透sql注入(一)
回顾一下上一篇,最重要的是判定sql注入点,并且能够加载最简单的payload,利用web应用的给出的提示信息反馈我们构造的sql语句执行的结果。
通过使用database()命令得到数据库名称后,接下来就是获得当前库中表的名称以及表的内容。
MySQL数据库中有个非常重要的库information_schema,我们在做sql注入的时候,这个库对我们非常重要。这个库到底是怎么回事儿呢?
information_schema数据库是MySQL自带的,它提供了访问数据库元数据的方式。什么是元数据呢?元数据是关于数据的数据,如数据库名或表名,列的数据类型,或访问权限等。有些时候用于表述该信息的其他术语包括“数据词典”和“系统目录”。
在MySQL中,把 information_schema 看作是一个数据库,确切说是信息数据库。其中保存着关于MySQL服务器所维护的所有其他数据库的信息。如数据库名,数据库的表,表的列的数据类型与访问权限等。
在sql注入的过程中,我们最为关注的就是库名、表名和列名。
特别需要注意的是:inforamtion_schema是MySQL 5.0之后出现的。
2.3 使用union联合查询表名
在MySQL数据库系统中有个information_schema库,其中有两个表tables和columns,这两个表中存放了整个数据库系统的数据库、表以及列等相关的内容。只要我们能够读取information_schema数据库,就能查到所有的库、表和列。
由于我们查询到数据库的版本是5.4.4,因此可以直接通过information_schema来查询库名、表名和列名。
查寻表名使用如下sql语句:
http://localhost/Less-1/?id=1' union select 1,2,table_name from information_schema.tables where table_schema=database() limit 1,1--+
tables 表中的table_name 包含了所有的表的名称,table_schema包含了所有库的名称。此处我们使用了database()函数,整个语句的意思就是查找table_schema为security(database()执行结果)的库的所有表(table_name)。只要逐步改变limit的offset值,就能看到所有的表的名称。
2.4 使用union联合查询列名
查询列名的方法和查询表名的方法一样,只不过是在columns表中查找,查询的条件有所变化:
http://localhost/Less-1/?id=1' union select 1,2,column_name from information_schema.columns where table_name='users' limit 1,1--+
其实,columns表中也有table_schema(库)、table_name(表)、column_name(列),在columns一个表中就可以直接查询表的名称和列的名称。
2.5 使用union联合查询表的内容
知道了表的名称(users)、表中列的名称(username,password),我们就可以直接查询表的内容了:
http://localhost/Less-1/?id=1' union select 1,username,password from 'usres' limit 1,1 --+
2.6 MySQL 5.0以下版本的注入方法
如果数据库的版本低于5.0,也就是没有information_schema库的时候,怎么办呢?字典!暴力破解!这是唯一的方法。
也就是说,库名、表名、列名基本靠猜,当然这个猜是基于字典的。
首先猜库名,由于通过web应用系统直接连接我们所使用的库,库名可以不用猜,直接猜表名就可以。
2.6.1 爆破表名
爆破表名还是和前面一样需要加载payload,我们使用如下的代码:
http://localhost/Less-1/?id=1' and exists( select * from table_name ) --+
由于要到字典中加载table_name,最好的方法是自己写一段代码。如果写不了的话,还可以通过burpsuite来加载字典爆破表名。
设置浏览器的代理地址为127.0.0.1:8080,有的浏览器会自动将127.0.0.1和localhost设置为不适用代理,一定要删掉,所有的地址都是用代理。
打开burpsuite后,在浏览器中执行上面的代码,burpsuite截获后发送到intruder 。并将table_name设置攻击变量。首先是用clear,就将table设置为爆破对象了。
在payloads标签页的payload sets中的payload type选择runtime file,在payload options中选择字典,最后点击start attack进行爆破。
加载字典进行爆破
爆破table的时候,我需要关注的是返回内容的长度。
爆破的结果
用不同的表名,系统会返回不同的结果,但大部分都是【Table 'security.customer' doesn't exist】,【doesn'texist】是它们的共同特征。
查看返回的具体信息
其实,返回信息最大的一个特点还是在于长度,不过由于我们字典中的table_name的长度不同,导致web应用系统返回的数据长度也有所不同。通过对length的排序后还要逐个去看,才有可能发现到底哪个payload是正确的。
逐个查看结果
如果exists( select * from table_name ) 条件为true的话,返回的结果就是id=1的结果。因此可以判断table_name为users。然后这种判断方法很慢,有可能需要查看很多返回结果。
我们当然可以利用exists( select * from table_name ) 条件为false的共同特征:doesn't exist。
设置返回结果的特征
然后再进行爆破:
爆破结果
此时我们看看到多了一列“doesn't exist”,值为空和‘1’两种情况。为空,说明在返回的结果中没有出现”doesn't exist“,值为‘1’说明出现了1次。出现1次的肯定是exists( select * from table_name ) 条件为false了。剩下的结果稍作查看就能知道到底哪个payload是我们想要的了。
2.6.2 爆破列名
爆破列名和表名的方法是一样的:
http://localhost/Less-1/?id=1' and exists( select colum_name from users ) --+
利用burp suite对column_name进行爆破,对“in ‘filed list’”排序,即可得到列名。
爆破列名2.6.3 查询表的内容
知道了表名users,列名id、username、password,直接查询结果即可。
http://localhost/Less-1/?id=1' union select 1,username,password from users limit 1,1 --+
当然,如果采用字典爆破的话,字典得足够大,否则很难爆破出正确的结果。
网友评论