LM Challenge-Response Hash Always Sent in SMB Authentication
Medium
Vulnerability Details
# 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://` or `smbs://`) 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`, or `CURLOPT_USERPWD`).
- SMB support must be compiled in (it is by default when NTLM core is available).
### Root Cause Analysis
1. 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.
2. At `smb.c:943`, the challenge is copied into `smbc->challenge` without any validation:
```c
memcpy(smbc->challenge, nrsp->bytes, sizeof(smbc->challenge)); // 8 bytes
```
3. At `smb.c:681-684`, the LM hash is computed and the LM response is derived using the
server-controlled challenge:
```c
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
```
4. Both the 24-byte LM response and 24-byte NT response are sent to the server at
`smb.c:694-698`:
```c
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:**
```bash
git clone https://github.com/curl/curl.git
cd curl
git checkout 1b35c9e1e3
```
**2. Build curl with SMB support:**
```bash
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:
```bash
./build-poc/src/curl --version | grep -o smb
```
**3. Install PoC dependency:**
```bash
pip install pycryptodome
```
**4. Start the malicious SMB server (terminal 1):**
```bash
python3 poc_clean.py
```
Output:
```
[*] Listening on 127.0.0.1:4455 (challenge: 1122334455667788)
```
**5. Connect curl to the malicious server (terminal 2):**
```bash
./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:**
1. The 24-byte LM response is **non-zero** — confirming that curl computes and sends the weak
LM hash to the server.
2. The password `Password1` was 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.
3. 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., `1122334455667788`
is 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.
Actions
View on HackerOneReport Stats
- Report ID: 3584491
- State: Closed
- Substate: informative
- Upvotes: 3