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)观察到了可重现的数据竞争,直接引用全局符号gss_context:
Helgrind证据
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)
重现步骤
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
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的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. 构建多线程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下运行测试
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代理协商期间出现认证失败或异常行为
未观察到内存泄露或远程代码执行,已验证的影响是在多句柄/认证并发运行时与并发相关的不稳定性(DoS/UB)。
修复方案
建议将gss_context设为Curl_SOCKS5_gssapi_negotiate()的局部变量(如Windows SSPI中所示),并在所有退出时删除。这与其他curl GSS路径(SPNEGO/SASL使用每个连接的上下文)相匹配。
状态更新
该问题已通过PR #18711修复,验证显示Helgrind不再报告socks_gssapi.c/curl_gssapi.c中的竞争条件。