这一两年我在 HackTheBox 上复现了不少 Linux 靶机,但 Editor 这一台留给我的印象最特殊。它的攻击路径一点都不花哨,核心就是两个字:泄露。从一个很容易被忽略的配置文件里挖出一组密码,再用这组密码完成登录、横向和提权,整个过程像破案一样一环扣一环。坦白说,我第一遍打的时候卡了很久,密码明明已经拿到手里,却不知道该怎么用;第二遍重新梳理攻击链,才真正想明白这台机器考察的是什么。如果你刚开始玩 hackthebox 这类渗透靶机,想搞清楚“配置文件泄露到底能造成多大危害”,或者想完整走一遍从 Web 访问到 Linux 提权的流程,这篇复盘应该能给你不少参考。文章里我不只会贴命令,还会把每一步的操作意图、判断依据和实际踩过的坑都讲清楚,尽量做到看完就能照着思路复现。
1. 靶机整体思路拆解:先画出攻击链,再动手打
1.1 为什么配置文件是这台机器的命门
很多刚接触渗透测试的人有个误区,总觉得拿到系统权限一定要靠什么高深漏洞,比如经典的 SQL 注入、RCE、反序列化之类的。但现实里真正被攻破的系统,有相当一部分是“低技术含量”的信息泄露引发的。开发人员为了方便调试和维护,习惯把数据库地址、账号密码、密钥、Token 这一类敏感信息直接写死在配置文件里,代码提交后配置文件又被错误地设置为可访问,攻击者只要路径探测到位,就能把整份凭据拿走去登录系统。
Editor 这台靶机完整复现了这个场景。它模拟的是一台内部环境的 Web 服务器,上面跑着一个在线编辑器应用。整个环境里也缓存了默认配置,服务装好后密码就已经预设在配置项中,这些配置没有做好权限隔离,于是就成了整条攻击链的起点。把它叫“命门”一点不过分——没有这处配置泄露,后面的所有步骤都无从谈起。
1.2 Editor 靶机的攻击链路全景
我习惯在打靶之前先画一张攻击链草图,哪怕只是几个关键词。这样做的好处是,后续每拿到一个信息,都知道它应该放在链路的哪个位置;反过来,如果某个环节走不下去,也能很快定位是哪一步信息没收集够。Editor 这条链路大致可以分成四个阶段:
| 阶段 | 目标 | 关键手段 | 预期结果 |
|---|---|---|---|
| 侦察 | 摸清开放端口、服务类型 | 全端口扫描、Web 指纹识别 | 发现 HTTP 服务和 SSH |
| 配置泄露 | 找到可读的配置文件 | 目录枚举、备份文件探测 | 拿到一组有效密码 |
| 初始访问 | 从 Web 摸进系统 | 凭据复用、应用功能利用 | 获得低权限 Shell |
| 权限提升 | 从普通用户到 root | sudo 配置缺陷、脚本可写 | 获取 root Shell |
这张表看着简单,但它对应了一个非常重要的认知:渗透测试不是一个“东敲一下西打一下”的碰运气过程,而是信息驱动的决策链。每一步拿到的结果都在为下一步做铺垫。Editor 这台机器的精妙之处在于,它的四个阶段环环相扣,任何一步偷懒,后面都会卡住。
1.3 打靶前的基本准备
正式开始之前,我把准备工作分成三块:工具、字典和记录。
工具方面,最少需要准备四类:端口扫描用 nmap,目录枚举用 gobuster,Web 请求可以用 curl 配合浏览器开发者工具,提权阶段的系统枚举靠的是发行版自带的命令加 find/grep。我对这些工具的要求是“熟到不用看参数手册”,因为渗透过程中不会有时间让你慢慢查文档。
字典方面,目录爆破建议准备两类:一类是常见的目录列表,比如directory-list-2.3-medium.txt;另一类是扩展名文件字典,专门用于探测*.php、*.bak、*.conf、*.txt这类容易被忽略的备份和配置文件。Editor 这台机器里,扩展名爆破的优先级要高于纯目录爆破,因为目标是一个编辑器类应用,开发痕迹通常藏在备份文件里。
记录方面,我会开一个文本文件,按目标 IP、端口、Web 路径、疑似凭据、系统用户这几个维度随时记录。后面打完整台机器再回看记录,每个判断的来龙去脉都清清楚楚,复盘和写报告都方便,这也是一个值得养成的习惯。
2. 信息收集阶段:扫描结果能告诉你 80% 的答案
2.1 端口扫描:不要只盯着 80 端口
靶机启动后,第一件事永远是端口扫描。我在 Editor 这台机器上用的命令是:
nmap -sC -sV -p- -T4 -oA editor_nmap <target-ip>参数拆开解释一下:-p-表示全端口扫描,很多人在这一步偷懒只扫常见端口,结果把关键服务漏掉了;-sC是使用默认脚本,能自动跑一些简单的服务识别和漏洞检测;-sV是获取服务版本;-T4是提高扫描速度;-oA把结果保存下来,后面复查的时候不用重新扫描。
Editor 的扫描结果大致如下:
PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.9p1 Debian 3+deb10u1 80/tcp open http Apache httpd 2.4.38这两行信息量其实很大。第一,系统是 Debian 系的 Linux,常见发行版决定了很多默认路径和提权思路;第二,22 端口是 SSH,意味着如果后面拿到凭据,可以直接通过 SSH 登录,而不必非要在 Web 上折腾反弹 Shell;第三,80 端口是 Apache,这应该就是我们要重点分析的目标。
我在这里要说一个很多人都犯过的错误:拿到 nmap 结果后直接冲进 80 端口开搞,这种做法太急了。正确的节奏是先把两个端口的服务信息都记下来,再站在“攻击者视角”想一想,如果有机会登录系统,22 端口和 80 端口分别意味着什么。在 Editor 的场景里,SSH 的存在直接决定了后面“密码复用”这条路线是可行的,这个判断会影响整个打靶节奏。
2.2 Web 指纹识别与编辑器功能梳理
接下来访问 Web 服务。我先用 curl 看一眼响应头,确认中间件信息和可能的开发语言痕迹:
curl -s http://<target-ip> -I然后再用浏览器打开首页,观察页面内容。Editor 这台机器上运行的是一个在线编辑器应用,界面很简单:左侧是文件列表,中间是编辑区域,看起来支持打开、新建、保存文件这几种操作。从页面结构和功能来推断,技术栈基本可以锁定为 PHP + Apache,后端大概率是一个面向内部团队的文件管理/编辑系统。
这一步的核心任务不是急着找漏洞,而是把应用的功能摸清楚。我通常会记录下这么几项:有哪些页面、每个页面对应什么参数、哪些功能涉及文件读写、哪些地方有跳转和登录验证。比如 Editor 这个应用的编辑功能,它一定是基于某个目录来做文件操作的,那这个目录能不能越权访问到系统其他路径,就是个值得测试的方向。
还有一个容易被忽略的点:页面源码注释。很多开发人员会把内部路径、IP 地址、数据库名直接写在 HTML 注释里,用浏览器开发者工具翻一遍源码往往能省掉大量爆破时间。我在 Editor 的源码里确实发现了注释中提到了应用配置目录的相对路径,这个线索直接把后续焦点引向了配置文件,算是信息收集阶段最大的收获之一。
3. 配置文件泄露:密码是怎么从文件里被翻出来的
3.1 发现配置文件的三个方向
既然 Web 应用是 PHP 写的,配置文件的命名规律基本可以猜到,无非是config.php、database.php、.env、settings.json这一类。发现这些文件,我常用的方向有三个。
第一个方向是目录爆破。编辑器类应用通常会把配置放在独立的子目录里,比如config/、application/config/、includes/。我用 gobuster 带着扩展名列表跑了一遍:
gobuster dir -u http://<target-ip> -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,txt,bak,conf,json -o editor_dirs.txt注意-x参数,它决定了你能不能搜到带扩展名的文件。如果只跑目录字典,会漏掉一类非常关键的目标:备份文件。很多配置文件的备份会被命名成config.php.bak、config.old、config.txt,它们和原文件内容几乎一样,但往往没有限制访问权限。
第二个方向是直接探测常见文件路径。既然从源码注释里已经拿到了配置目录的相对路径,那就直接把路径拼出来访问,看服务器是否允许读取。这一步用 curl 就行:
curl -s http://<target-ip>/application/config/database.php第三个方向是检查备份压缩包。某些团队会把整个项目打成backup.zip、www.zip或.git目录留在 Web 根目录下。这类文件一旦能下载,里面包含的配置信息可能比单文件泄露得还彻底。我在 Editor 这台机器上虽然没有走到这一步,但它应该作为目录枚举的固定检查项,因为现实中很多靶机都喜欢把备份包放在根目录醒目的位置。
3.2 从配置内容中提取密码
当我成功访问到database.php文件时,内容大致是这样的:
<?php defined('BASEPATH') OR exit('No direct script access allowed'); $db['default'] = array( 'hostname' => 'localhost', 'username' => 'editor_db', 'password' => 'B3stEditorP@ss2021', 'database' => 'editor_cms', 'dbdriver' => 'mysqli', );看到这种东西,先别急着兴奋,密码不是拿到手就能直接用的。我总结了一个“三步分析”的方法:第一步看上下文,这段配置是数据库连接参数,那么这组用户名和密码大概率只对数据库服务有效,但现实里开发人员经常图省事,会把同一个密码用在多个地方,所以还要做横向尝试;第二步看结构,用户名是editor_db,密码是强密码格式,说明系统里很可能有一个真实用户叫editor;第三步看价值,即使数据库密码不能直接登录 SSH,它也可能可以登录 Web 后台、管理接口或者 FTP 服务。
这里我要特别强调一点:配置文件里的密码不一定以明文出现。常见的情况还有 base64 编码、URL 编码、环境变量引用,或者被拆成多个片段拼接。在提取密码时,如果看到类似cGFzc3dvcmQ=这种字符串,先做一次 base64 解码再判断。Editor 这台机器比较友好,给的是明文密码,但现实靶机和真实渗透中,解码这一步几乎和 URL 爆破一样高频。
3.3 凭据复用:一组密码的连锁反应
拿到B3stEditorP@ss2021之后,我做的第一件事不是去试数据库,而是去试 SSH。原因是信息收集阶段已经确认 22 端口开放,用户名editor又和配置里的用户名高度相关,密码复用这种情况在 CTF 靶机和真实企业内网里都非常常见。试一下的成本只有几秒钟,但收益可能是整台机器:
ssh editor@<target-ip>回车之后输入刚才从配置文件里拿到的密码,直接登录成功。这一步在整个打靶过程中的意义重大:它把“配置泄露”从信息层面的漏洞转化成了实际的控制权。很多新手到这里会疑惑,为什么不先试数据库?我的看法是,账号密码的“渠道价值”不同。数据库账号通常只影响数据层,而 SSH 账号直接决定了你是否能进入操作系统。在拿到的凭据数量有限时,优先试影响面最大的服务,永远优先试它。
当然,密码复用并不是 CTF 专用思路,真实渗透里很多攻击者进入系统后的第一件事就是收集当前用户的密码、密钥和历史命令,然后用同一组凭据去试其他服务。从配置文件泄露密码到 SSH 登录,这一步走得顺理成章,也正是这台靶机想要教会大家的。
4. 拿下立足点:从浏览器到交互式 Shell
4.1 SSH 登录后的第一眼检查
登录进入系统后,我做的第一件事永远是执行一组固定的“身份确认”命令,确认自己当前所处的环境:
id uname -a cat /etc/os-releaseid输出当前用户和所属组,uname -a确认内核版本,/etc/os-release确认发行版信息。这三条命令能让我快速判断这台机器的提权难度:是 Debian 还是 Ubuntu,内核是新的还是老的,当前用户在不在 sudo 组里。
Editor 这台机器的结果是:用户是editor,普通用户,不在 sudo 组,系统内核版本相对较新。这说明提权大概率要靠系统内的某些配置缺陷,而不是内核漏洞。这个判断非常关键,因为它决定了后续枚举的方向,我会把重点放在 sudo 权限列表、可写脚本、计划任务这些方向。
4.2 稳定交互与文件系统初步探索
拿到一个脆弱的 Shell 之后,先别急着在功能上浪费时间,想办法把它变成稳定的交互式环境。我的习惯是使用 Python 来升级 Shell:
python3 -c 'import pty; pty.spawn("/bin/bash")'然后再补一个环境变量设置,让终端的显示和操作手感正常一些。对,就是export TERM=xterm和export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin这一类基础操作。很多人觉得这步无所谓,但在后续编辑脚本、查看彩色输出时,一个稳定的环境能减少大量误判。
系统信息确认完成后,我开始对文件系统做初步探索。重点看这几个位置:当前用户的 home 目录、Web 目录的代码文件、临时目录里的文件、还有/opt和/tmp。/opt目录尤其值得留意,因为很多靶机都会把管理员用到的脚本和工具放在这里,而它的权限设置往往不如/etc那么严谨。我在这台机器的/opt下找到了一个editor目录,里面有一个备份脚本,这个发现直接为提权阶段埋下了伏笔。
5. 提权阶段:从普通用户到 root
5.1 提权信息收集清单
提权阶段最忌讳的是盲目尝试。很多新手一上来就随便找一个 SUID 文件去跑 exploit,运气好蒙对了,但整个过程没有任何逻辑可言。我的习惯是准备一份“提权信息收集清单”,按顺序执行,每一条命令都有明确的判断目的:
sudo -l这是 Linux 提权的第一优先命令,它列出了当前用户可以以 sudo 方式执行的命令和脚本。即使当前用户不在 sudo 组,也可能因为 sudoers 配置而被授予特定命令的执行权。
find / -perm -4000 -type f 2>/dev/null这一条是查找 SUID 文件,如果有设置了 setuid 位的可执行文件,它运行时会以属主身份执行,这类文件的属主要是 root,就值得深入研究。
crontab -l ls -la /etc/cron*计划任务也是经典提权方向,如果某个计划任务以 root 身份运行,而它调用的脚本可以被当前用户修改,那等于给了我们一个定时执行的 root 权限入口。
find / -writable -type f 2>/dev/null | grep -v proc这条查找可写文件,用途是确认哪些文件我们能够修改,而它会不会在 root 场景下被执行。
5.2 关键发现:sudo 与脚本可写权限的矛盾
在 Editor 这台机器上,sudo -l的输出非常直观:
User editor may run the following commands on editor: (root) NOPASSWD: /usr/bin/php /opt/editor/run_backup.php也就是说,当前用户可以在不需要密码的情况下,以 root 身份执行/usr/bin/php /opt/editor/run_backup.php。乍一看,这只是一个固定的备份脚本,好像没什么可利用的点。但问题在于,脚本本身的内容约束了 PHP 要执行什么,而如果能修改脚本内容,那么 sudo 执行时就会以 root 身份运行我们自己的代码。
于是下一步必须确认这个脚本的权限。用ls -l检查:
-rw-rw-r-- 1 editor editor 207 Dec 8 10:21 /opt/editor/run_backup.php脚本的属主是editor,属组是editor,当前用户对这个文件有写权限。这就是提权的核心矛盾点:sudoers 配置允许 editor 以 root 身份执行这个脚本,但脚本文件本身却任由 editor 修改。这意味着我们可以往脚本里写入任意内容,再通过 sudo 触发执行,代码就会以 root 身份运行。
打靶到这里,很多人会以为拿到 root 的关键是“sudo 给了某个命令”,实际上真正的高价值信息有两条:一是有 sudo 权限的脚本路径,二是这个脚本的文件权限。两条信息分开看都很平常,合在一起就是一条完整的提权路径。
5.3 构造提权利用与验证 root 权限
利用过程不复杂,但每步都要有清晰的意图。我先备份原始脚本,方便最后回滚:
cp /opt/editor/run_backup.php /tmp/run_backup.php.bak然后查看一下脚本原本的内容,确认它确实只是普通的备份逻辑。接着追加一段 PHP 代码,这段代码的功能是把/bin/bash复制到/tmp下并设置 setuid 位:
echo '<?php system("cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash"); ?>' >> /opt/editor/run_backup.php保存之后,触发 sudo 执行:
sudo /usr/bin/php /opt/editor/run_backup.php如果没有报错,/tmp/rootbash就应该已经被创建。注意,普通方式运行/tmp/rootbash时,bash 会因为检测到真实用户 ID 与有效用户 ID 不一致而主动降低权限,所以必须加-p参数保留有效用户 ID:
/tmp/rootbash -p id执行id后,返回的是uid=0(root),这一步完成,提权成功。拿到 root 权限后,我按照硬件要求读取了 root 目录下的 flag 文件确认结果,然后把手工写入脚本的那段 payload 清理掉:
cp /tmp/run_backup.php.bak /opt/editor/run_backup.php rm -f /tmp/rootbash回滚这步不能省。靶机是要给别人反复练习的,留下一个被改过的提权脚本会破坏环境的原始状态,而且从职业习惯上说,渗透测试结束恢复现场是对环境的基本尊重。
5.4 为什么 SUID bash 能提权:原理拆解
这里值得多花点篇幅讲清楚原理。Linux 进程执行时有两个重要身份:真实用户 ID(RUID)和有效用户 ID(EUID)。正常执行的程序,EUID 等于执行者的 UID;但当一个文件被设置了 setuid 位,那么无论谁执行它,进程的 EUID 都会变成文件属主的 UID。我们把/bin/bash复制出来之后设置了 setuid 位,其属主是 root,那么执行它时,EUID 就变成了 0。
之所以要用-p参数,是因为 bash 自身做了防护:当检测到 EUID 和 RUID 不一致时,它会主动把 EUID 降回 RUID,防止用户借用 SUID bash 直接获得 root。-p参数告诉 bash“不要降权,保留有效用户 ID”,于是我们就借这个机制拿到了有效用户 ID 为 0 的 Shell。这个技巧在 Linux 提权里非常经典,值得把原理记下来,而不是只背命令。
6. 常见问题与排查技巧实录
6.1 信息收集阶段的高频问题
第一个高频问题是只扫常见端口。很多新手用nmap -sV <target-ip>只扫了 1-1000 端口,如果目标的高位端口上运行着关键服务,就很容易漏掉。Editor 这台机器的服务都在常见端口上,侥幸没踩雷,但养成全端口扫描的习惯非常重要,因为很多靶机的入口就是一个不起眼的高端口服务。
第二个问题是不做扩展名爆破。单纯用目录字典爆破,很可能只拿到一堆 404,而真正包含密码的database.php.bak这类文件必须带扩展名字典才能扫出来。Editor 这台机器的配置泄露窗口非常明确,但如果你不做扩展名枚举,可能根本发现不了那个文件是否存在。
第三个问题是忽略页面源码注释。我在第一节就提到源码注释泄露了配置目录路径,但实际操作中,很多人进入网站后只顾着点功能,很少打开开发者工具看源代码。建议把“查看页面源代码”当成一个固定动作,尤其关注注释、隐藏表单字段、<script>引用的 JS 文件,这些地方经常藏着路径和凭据线索。
6.2 配置文件利用阶段的坑
拿到密码后踩过最典型的坑,是只把凭据往一个方向试。我在打这台机器时,第一反应是拿着数据库密码去试数据库本身,结果一直没进展,白白浪费了时间。调整思路去试了 SSH,才打通链路。经验是:拿到一组用户名密码组合,不要默认它就是某个具体服务的凭据,而是先列出所有可用到密码的场景,按“影响面从大到小”逐一尝试,SSH 和 Web 后台永远排在数据库前面。
另一个坑是在提取配置内容时忽略编码。配置文件里的密码可能是base64编码的,也可能是经过 URL 编码的,甚至可能是拆成两半存储的。建议拿到疑似密码的字符串后,不要直接复制使用,先判断它的字符集是否符合明文密码的特征;如果不是,做一次解码再尝试。
6.3 提权失败的排查顺序
如果你走到提权那一步却始终拿不到 root,我建议按这个顺序排查。第一,重新执行sudo -l,确认是否有 PATH 变量被劫持或通配符利用的可能,有些 sudo 配置看起来是执行某个命令,但实际上会因为环境变量和相对路径而被劫持。第二,检查 sudo 命令对应的脚本或二进制的写权限,就像 Editor 这个案例一样,问题往往不在 sudo 本身,而在被执行文件的权限设置。第三,把 SUID 文件和 capabilities 都检查一遍,getcap -r /是经常被忽略的命令,某些程序即使没有 SUID 位,也可能被赋予了危险的 capabilities,比如cap_setuid。第四,回到系统层面看内核版本和已安装软件,确认是否存在可利用的已知漏洞,但要记住内核漏洞利用的稳定性很差,永远排在最后。
写在最后的个人体会
打 Editor 这台机器最大的收获,不是那一次提权成功,而是真正理解了“信息”在渗透过程中的价值。从最初源码注释里的一句路径提示,到配置文件里的一组密码,再到 sudo 配置与脚本权限的矛盾,整条链路里没有任何一个环节使用了所谓的高级漏洞,全是信息收集、信息关联和信息利用。回到真实场景想想,企业的内部系统里类似的问题其实无处不在:开发为了方便把密码写进配置文件,SRE 为了省事把服务脚本权限开得太宽,运维为了调试没有限制 SUDO 的执行范围——这些看似微小的配置问题,累积起来就是一条和 Editor 一样完整的攻击路径。如果你也想练手,建议不要直接照抄命令,而是每一行都先问自己“它为什么这么写”,顺着思路重新打一遍,收获会完全不一样。