
CVE-2026-3805: curl SMB 연결 재사용에서의 사용 후 해제(Use-After-Free) - 힙 정보 유출
libcurl의 SMB 프로토콜 핸들러에서 use-after-free 취약점을 발견했습니다. 두 번째 SMB 전송이 동일한 서버에 대한 기존 연결을 재사용할 때, 새 요청의 파일 경로(req->path)는 해제된 힙 메모리를 가리키는 댕글링 포인터가 됩니다. 이 메모리는 strlen()을 통해 읽히고 발신 SMB 패킷에 복사되어 힙 내용이 서버로 유출되거나 충돌이 발생합니다.
한 줄 수정: req->path가 needle의 smbc->share에서 빌려오는 대신 자체 복사본을 소유하도록 합니다.
| CVE | CVE-2026-3805 |
| 버그 분류 | Use-After-Free (CWE-416) |
| 근본 원인 | req->path가 needle의 smbc->share를 가리키며, 연결 재사용 시 해제됨 |
| 도입 | 777c5209df (2025-04-30) - curl 8.13.0 |
| 수정 | e090be9f73a7a71459ef678c - curl 8.19.0 (2026년 3월 11일) |
| 영향을 받는 버전 | curl 8.13.0 ~ 8.18.0 |
| 영향 | 서버로의 힙 정보 유출, 충돌 |
| 심각도 | 7.5 - HIGH - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H |
smb_setup_connection()은 캐시 조회에 사용되는 임시 "needle" 연결에서 실행됩니다. req->path가 smbc->share(needle이 소유한 힙 메모리) 내부를 가리키도록 설정합니다. 연결 캐시가 재사용 가능한 연결을 찾으면 needle이 파괴되어 smbc->share가 해제되지만, req->path는 easy 핸들에 남아 이제 댕글링 상태가 됩니다.
smb_send_open()이 SMB OPEN 요청을 빌드할 때, strlen(req->path)를 호출하고 결과를 발신 패킷에 복사합니다. 해제된 힙 영역에 있는 모든 데이터가 서버로 전송됩니다. 공격자가 서버를 제어하는 경우(SSRF 또는 악의적인 SMB에 연결하는 사용자), 유출된 힙 내용이 SMB NT_CREATE_ANDX 요청의 "파일 이름"으로 수신됩니다.
libcurl은 성능 향상을 위해 연결을 적극적으로 재사용합니다. 동일한 호스트에 여러 요청을 할 때, curl은 새 연결을 설정하는 대신 기존 연결을 재사용할 수 있는지 확인합니다. 메커니즘은 다음과 같이 작동합니다:
SMB 프로토콜 핸들러는 상태를 두 곳에 저장합니다:
smbc (연결 상태)는 연결의 meta_hash에req (요청 상태)는 easy 핸들의 meta에버그: needle 설정 중 req->path가 smbc->share 내부를 가리키도록 설정됩니다. 재사용 시 needle이 파괴되면 smbc->share가 해제되지만 req->path는 여전히 그곳을 가리킵니다.
smb_parse_url_path()에서 코드가 SMB URL 경로를 파싱하여 공유 이름과 파일 경로로 분할합니다:
// lib/smb.c, smb_parse_url_path() line 431:
smbc->share = curlx_strdup((*path == '/' || *path == '\\') ? path + 1 : path);
// ...
*slash++ = 0;
req->path = slash; // <--- points into smbc->share on the NEEDLE
URL smb://server/share1/file1.txt의 경우, 다음이 생성됩니다:
smbc->share = "share1\0file1.txt" (힙 할당, needle이 소유)req->path = "file1.txt"를 가리키는 포인터 (smbc->share 내부)연결 재사용이 트리거되면:
// lib/url.c, url_find_or_create_conn() line 3619:
out:
if(needle)
Curl_conn_free(data, needle); // Destroys needle -> frees smbc->share
Curl_conn_free()는 Curl_hash_destroy(&conn->meta_hash)를 호출하여 smb_conn_dtor()를 호출하고 smbc->share를 해제합니다. 그러나 req->path(easy 핸들에 있으며 살아남음)는 여전히 해제된 메모리를 가리킵니다.
SMB 요청이 진행될 때:
// lib/smb.c, smb_send_open() line 750-769:
const size_t byte_count = strlen(req->path) + 1; // UAF READ
// ...
curlx_strcopy(msg.bytes, sizeof(msg.bytes), req->path, byte_count - 1); // UAF READ
해제된 힙 메모리는 재할당되어 민감한 데이터를 포함할 수 있습니다. strlen(req->path)는 null 바이트를 만날 때까지 앞으로 스캔하고, curlx_strcopy()는 해당 내용을 서버로 전송되는 SMB 패킷에 복사합니다.
공격 시나리오: 공격자가 SMB 서버를 제어하는 SSRF. 피해자 애플리케이션이 공격자의 서버에 두 개의 SMB 요청을 보냅니다. 두 번째 요청은 SMB OPEN 요청에서 "파일 이름"으로 힙 내용을 유출합니다.
해제된 메모리가 OS에 반환된 경우(매핑 해제), strlen()이 SIGSEGV/액세스 위반을 트리거합니다.
UAF 없이도 SMB 연결 재사용은 의미적으로 깨져 있습니다. smb_send_tree_connect()는 새 요청의 공유가 아닌 재사용된 연결의 smbc->share(이전 공유)를 사용합니다. TREE_CONNECT가 완전히 잘못된 공유로 전송됩니다.
# Two SMB URLs to the same server, different shares/files:
curl smb://192.168.1.100/share1/file1.txt -o /dev/null \
smb://192.168.1.100/share2/file2.txt -o /dev/null
CURLM *multi = curl_multi_init();
CURL *e1 = curl_easy_init();
curl_easy_setopt(e1, CURLOPT_URL, "smb://server/share1/file1");
curl_multi_add_handle(multi, e1);
CURL *e2 = curl_easy_init();
curl_easy_setopt(e2, CURLOPT_URL, "smb://server/share2/file2");
curl_multi_add_handle(multi, e2);
// When e2 runs after e1 completes and reuses the connection: UAF
-fsanitize=address로 curl을 빌드하고 위를 실행하세요:
==PID==ERROR: AddressSanitizer: heap-use-after-free on address 0x...
READ of size 1 at 0x... thread T0
#0 strlen
#1 smb_send_open lib/smb.c:750
#2 smb_request_state lib/smb.c:1163
...
freed by thread T0 here:
#0 free
#1 smb_conn_dtor lib/smb.c:388
#2 Curl_hash_destroy
#3 Curl_conn_free lib/url.c:557
전체 재현 스크립트는 poc/REPRODUCE_UAF.sh를 참조하세요.
근본적인 문제는 req->path가 needle의 smbc가 소유한 메모리에 대한 포인터를 빌려온다는 것입니다. 수정 사항은 req->path가 자체 복사본을 소유하도록 만듭니다:
--- a/lib/smb.c
+++ b/lib/smb.c
@@ -378,7 +378,7 @@ static void smb_easy_dtor(void *key, size_t klen, void *entry)
(void)key;
(void)klen;
+ curlx_free(req->path);
curlx_free(req);
}
@@ -428,7 +428,10 @@ static CURLcode smb_parse_url_path(struct Curl_easy *data,
/* Parse the path for the file path converting any forward slashes into
backslashes */
*slash++ = 0;
- req->path = slash;
+ req->path = curlx_strdup(slash);
+ if(!req->path) {
+ Curl_safefree(smbc->share);
+ return CURLE_OUT_OF_MEMORY;
+ }
Stefan Eissing이 e090be9f73a7a71459ef678c에서 공식 수정 사항을 구현했습니다.
개발자들은 댕글링 포인터 위험을 부분적으로 인식하고 있었습니다. smb_easy_dtor()에 다음 주석이 있습니다:
/* `req->path` points to somewhere in `struct smb_conn` which is
* kept at the connection meta. If the connection is destroyed first,
* req->path points to free'd memory. */
그러나 이것은 easy 핸들보다 연결이 먼저 파괴되는 시나리오만 고려합니다. 실제 트리거인 연결 재사용 중 needle 파괴 시나리오를 놓쳤습니다.
| 날짜 | 이벤트 |
|---|---|
| 2025-04-30 | 가 SMB를 메타 해시를 사용하도록 리팩토링하여 버그 도입 |
!defined(CURL_DISABLE_SMB)defined(USE_CURL_NTLM_CORE)sizeof(curl_off_t) > 4 (대부분의 플랫폼에서 표준인 64비트 off_t)SMB는 NTLM 지원이 있는 curl 빌드에서 기본적으로 활성화됩니다.
e090be9f73a7a71459ef678c777c5209dfCVE-2026-3805 - curl 8.19.0에서 수정됨. 영향을 받는 버전: 8.13.0 ~ 8.18.0.
Daniel Wade - GitHub - [email protected]
777c5209df| 2026-03-07 | 보안 감사 중 버그 발견 |
| 2026-03-08 | HackerOne(#3591944)을 통해 curl에 보고 |
| 2026-03-08 | curl이 distros@openwall에 연락 |
| 2026-03-11 | curl 8.19.0 수정 사항 포함 출시, CVE-2026-3805 게시 |