curl · Report #3584491
LM Challenge-Response Hash Always Sent in SMB Authentication
How it was found
LM Challenge-Response Hash Always Sent in SMB Authentication
Summary
The curl SMB client unconditionally computes and sends both the legacy LAN Manager (LM) and NT challenge-response hashes during SMB session setup. The LM hash is cryptographically broken — it splits the password into two 7-character halves, converts to uppercase, and uses DES with a fixed constant. Combined with the fact that the 8-byte authentication challenge is entirely server-controlled (no client-side nonce mixing), any SMB server that curl connects to can capture the LM response and recover the user's password offline within seconds to minutes using rainbow tables or brute-force tools (hashcat mode 5500). This is a significant credential exposure risk for any user who connects curl to an untrusted SMB server, including in SSRF scenarios.
Vulnerability Details
Affected Component
lib/smb.c — curl's SMBv1 (CIFS) client implementation, specifically the smb_send_setup()
function and the smb_connection_state() function's NEGOTIATE response handling.
Affected Versions
Confirmed in curl master at commit 1b35c9e1e3 (2026-03-01). The SMB implementation has used
this authentication scheme since its introduction; no NTLMv2 or extended security has ever been
added to the SMB code path.
Pre-requisites
- The victim must initiate a curl SMB connection (
smb://orsmbs://) to an attacker-controlled server, or to a server the attacker can man-in-the-middle. - The victim must supply credentials (via
-u user:password,.netrc, orCURLOPT_USERPWD). - SMB support must be compiled in (it is by default when NTLM core is available).
Root Cause Analysis
-
The server sends an SMB_COM_NEGOTIATE response containing an 8-byte challenge in
nrsp->bytes[]. This challenge is entirely server-controlled — the server can choose any 8 bytes, including values optimized for rainbow table lookups. -
At
smb.c:943, the challenge is copied intosmbc->challengewithout any validation:memcpy(smbc->challenge, nrsp->bytes, sizeof(smbc->challenge)); // 8 bytes -
At
smb.c:681-684, the LM hash is computed and the LM response is derived using the server-controlled challenge:Curl_ntlm_core_mk_lm_hash(conn->passwd, lm_hash); // LM hash of password Curl_ntlm_core_lm_resp(lm_hash, smbc->challenge, lm); // DES-encrypt challenge Curl_ntlm_core_mk_nt_hash(conn->passwd, nt_hash); // NT hash of password Curl_ntlm_core_lm_resp(nt_hash, smbc->challenge, nt); // DES-encrypt challenge -
Both the 24-byte LM response and 24-byte NT response are sent to the server at
smb.c:694-698:msg.lengths[0] = smb_swap16(sizeof(lm)); // LM response length = 24 msg.lengths[1] = smb_swap16(sizeof(nt)); // NT response length = 24 memcpy(p, lm, sizeof(lm)); p += sizeof(lm); memcpy(p, nt, sizeof(nt));
This produces two independent 8-byte DES blocks. Each half has at most 2^37 possible values (7 uppercase ASCII characters), making brute-force trivial. The two halves can be cracked independently, further reducing the effective complexity.
Reproduction
1. Obtain the vulnerable version:
git clone https://github.com/curl/curl.git
cd curl
git checkout 1b35c9e1e3
2. Build curl with SMB support:
cmake -B build-poc \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_C_FLAGS="-O0 -g" \
-DCURL_USE_OPENSSL=ON \
-DCURL_DISABLE_SMB=OFF
cmake --build build-poc -j$(nproc) --target curl
Verify SMB is enabled:
./build-poc/src/curl --version | grep -o smb
3. Install PoC dependency:
pip install pycryptodome
4. Start the malicious SMB server (terminal 1):
python3 poc_clean.py
Output:
[*] Listening on 127.0.0.1:4455 (challenge: 1122334455667788)
5. Connect curl to the malicious server (terminal 2):
./build-poc/src/curl -u 'testuser:Password1' smb://127.0.0.1:4455/share/file.txt
curl will connect and send the SESSION_SETUP_ANDX with both LM and NT responses.
Observed Result
The PoC server captures and cracks the password:
[+] Connection from ('127.0.0.1', 60378)
User: testuser
LM response: 76365e2d142b5612cd10e1b93d342334e8fd283ecd7b9170
NT response: 8614b8a84404b1c0333c8875a22bc10690bbf6a7edcf549d
[!] PASSWORD CRACKED: 'Password1'
Key observations:
- The 24-byte LM response is non-zero — confirming that curl computes and sends the weak LM hash to the server.
- The password
Password1was recovered from the LM response using only the server-chosen challenge and a trivial 5-word dictionary. In practice, hashcat mode 5500 with rainbow tables or GPU brute-force can crack any LM hash in seconds. - The server fully controlled the 8-byte challenge (
1122334455667788). A real attacker could choose a challenge value optimized for pre-computed rainbow tables (e.g.,1122334455667788is a standard rainbow table challenge).
Impact
Summary:
An attacker operating a malicious SMB server (or performing a man-in-the-middle attack on an unencrypted SMB connection) can recover the plaintext password of any user who connects with curl. The attack is completely passive from the attacker's perspective — they simply accept the connection and read the SESSION_SETUP_ANDX message.
The LM hash's maximum effective keyspace is 2 × 2^37 (two independent 7-character uppercase halves), making brute-force trivially fast on modern hardware. Rainbow tables for LM are widely available and provide instant lookup.