Race condition on global gss_context during SOCKS5 GSS-API negotiation in libcurl
摘要
并发SOCKS5 GSS-API认证共享一个文件作用域的全局gss_context而缺乏同步机制,导致数据竞争和未定义行为。
全局上下文定义位置
1
|
static gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
|
每次协商时通过地址传递给GSS初始化例程
1
2
3
4
5
6
7
8
9
10
|
gss_major_status = Curl_gss_init_sec_context(data,
&gss_minor_status,
&gss_context,
server,
&Curl_krb5_mech_oid,
NULL,
gss_token,
&gss_send_token,
TRUE,
&gss_ret_flags);
|
漏洞验证
使用Helgrind和libcurl的DEBUGBUILD GSS存根(CURL_STUB_GSS_CREDS=KRB5)来避免libkrb5内部噪音,观察到了可重现的数据竞争,直接引用了全局符号gss_context:
Helgrind证据(来自docker_helgrind.log):
1
2
3
4
5
6
7
8
9
10
|
==XXXX== Possible data race during read of size 8 at ...
==XXXX== at 0x1CEFC0: stub_gss_init_sec_context (curl_gssapi.c:126)
==XXXX== by 0x1CEE43: Curl_gss_init_sec_context (curl_gssapi.c:329)
==XXXX== by 0x1BCB3B: Curl_SOCKS5_gssapi_negotiate (socks_gssapi.c:184)
...
==XXXX== This conflicts with a previous write of size 8 by thread #Y
==XXXX== at 0x1CF4A4: stub_gss_init_sec_context (curl_gssapi.c:264)
==XXXX== by 0x1CEE43: Curl_gss_init_sec_context (curl_gssapi.c:329)
==XXXX== by 0x1BCB3B: Curl_SOCKS5_gssapi_negotiate (socks_gssapi.c:184)
==XXXX== Address ... is 0 bytes inside data symbol "gss_context"
|
受影响版本
- 从当前master构建(配置摘要报告:curl版本:8.17.0-DEV)
- Ubuntu 24.04(arm64)容器
- GSS-API提供者:MIT Kerberos(系统libgssapi_krb5),也通过CURL_STUB_GSS_CREDS=KRB5使用curl的DEBUGBUILD GSS存根进行了测试
重现步骤
1. 启动Ubuntu容器并安装先决条件
1
2
3
4
5
6
7
|
docker run --rm -it -v "$PWD":/src -w /src ubuntu:24.04 bash -lc '
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq clang make autoconf automake libtool pkg-config libkrb5-dev python3 valgrind > /dev/null
update-ca-certificates > /dev/null 2>&1 || true
'
|
2. 配置和构建libcurl(调试,GSS-API,最小依赖)
1
2
3
4
5
6
7
8
9
10
|
docker run --rm -it -v "$PWD":/src -w /src ubuntu:24.04 bash -lc '
set -euo pipefail
if [ -f Makefile ]; then make distclean || true; fi
if [ -x ./buildconf ]; then ./buildconf; else autoreconf -fi; fi
export CC=clang CFLAGS="-O0 -g -fno-omit-frame-pointer" LDFLAGS=""
./configure --enable-debug --with-gssapi --disable-shared \
--without-ssl --without-libidn2 --without-libpsl --without-libssh2 \
--without-brotli --without-zstd --without-nghttp2 --without-nghttp3 --without-ngtcp2
make -j"$(nproc)"
'
|
3. 启动选择GSS-API(方法1)的最小SOCKS5代理
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
|
import socket, threading, struct
def handle(c):
try:
data=c.recv(262)
if len(data)<3: return
c.sendall(b"\x05\x01")
hdr=c.recv(4)
if len(hdr)<4: return
ln=struct.unpack("!H", hdr[2:4])[0]
_=c.recv(ln)
c.sendall(b"\x01\x01\x00\x01D")
hdr=c.recv(4)
if len(hdr)<4: return
ln=struct.unpack("!H", hdr[2:4])[0]
_=c.recv(ln)
c.sendall(b"\x01\x02\x00\x01\x00")
finally:
try: c.close()
except: pass
s=socket.socket(); s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
s.bind(("0.0.0.0",1081)); s.listen()
print("listening 0.0.0.0 1081", flush=True)
while True:
c,_=s.accept()
threading.Thread(target=handle,args=(c,),daemon=True).start()
|
4. 使用libcurl构建多线程PoC
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#include <pthread.h>
#include <curl/curl.h>
static void *worker(void *u){
CURL *e=curl_easy_init();
curl_easy_setopt(e,CURLOPT_URL,(char*)u);
curl_easy_setopt(e,CURLOPT_PROXY,"socks5://127.0.0.1:1081");
curl_easy_setopt(e,CURLOPT_SOCKS5_GSSAPI_SERVICE,"rcmd");
curl_easy_setopt(e,CURLOPT_SOCKS5_AUTH,(long)CURLAUTH_GSSAPI);
for(int i=0;i<50;++i) curl_easy_perform(e);
curl_easy_cleanup(e);
return 0;
}
int main(void){
curl_global_init(CURL_GLOBAL_DEFAULT);
enum { N=16 }; pthread_t th[N];
for(int i=0;i<N;++i) pthread_create(&th[i],0,worker,"http://example.com");
for(int i=0;i<N;++i) pthread_join(th[i],0);
curl_global_cleanup();
return 0;
}
|
5. 在Helgrind下运行以显示curl中的竞争
1
2
3
4
5
|
docker run --rm -it -v "$PWD":/src -w /src ubuntu:24.04 bash -lc '
set -euo pipefail
export CURL_STUB_GSS_CREDS=KRB5
valgrind --tool=helgrind --quiet --fair-sched=try ./test_gss_race | tee docker_helgrind.log
'
|
影响分析
共享的全局gss_context在连接之间无同步地并发访问和修改。这带来了实际风险:
- 由于GSS上下文处理中的未定义行为和竞争导致进程崩溃/拒绝服务
- 在负载下SOCKS5代理协商期间的身份验证失败或异常行为
未观察到或声称内存泄露或RCE,已验证的影响是在多个句柄/认证并发运行时与并发相关的不稳定性(DoS/UB)。
修复状态
curl开发团队已通过PR #18711修复此问题,将gss_context改为Curl_SOCKS5_gssapi_negotiate()的局部变量,并在所有退出时删除,与其他curl GSS路径(SPNEGO/SASL使用每连接上下文)保持一致。
安全评估
该漏洞被评估为信息性安全问题,主要影响可用性:
- 触发条件:在同一进程中进行并发SOCKS5+GSS协商
- 主要风险:崩溃/中止(DoS)、间歇性身份验证失败、损坏的GSS状态
- 未证明机密性/完整性受损
- CWE-362:竞争条件
- 严重性:低(仅影响可用性)