✓ProveIt
Examples / Report #3230082 Saved analysis

Stack-based Buffer Overflow in TELNET NEW_ENV Option Handling

curl · 2025-06-3041.5s analysis · $0.044 in model calls

What the report claims

A stack-based buffer overflow in libcurl's TELNET NEW_ENV option handling allows remote code execution via crafted NEW_ENV options.

Compare with curl's recorded outcome

Labeled AI slop

curl's maintainer lists this report as AI slop

Shown for comparison after analysis. The pipeline did not see this label.

ProveIt's finding

Code contradicts the claim

The code provides evidence against the report's central claim. Review the cited lines before deciding.

Why this finding?
  • The length check uses tmplen = strlen(v->data) + 1, which correctly accounts for the net +1 byte expansion when msnprintf replaces the comma with CURL_NEW_ENV_VALUE and adds CURL_NEW_ENV_VAR.
  • The msnprintf call for the comma case writes exactly tmplen bytes, so the check `len + tmplen < sizeof(temp) - 6` guarantees sufficient space.
  • No integer overflow or truncation path exists because the check ensures the msnprintf size parameter (sizeof(temp) - len) is always larger than the bytes written.
  • The PoC would not overflow the buffer; at most one large variable is added before the check rejects further additions.
How this analysis ran 5 claims · 8 citations verified

Checked against curl master on 2025-06-30 (d553f7e9f0d8). Dev evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.

1 · Grounding
grounded
no model · 14 references checked out, 0 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
5 claims · 5841 tokens · $0.0005
3 · Evidence
3 code snippets
real curl source at d553f7e9f0d8 · 5 docs via Tavily
4 · Judgment
Nemotron-3-Ultra-550b-a55b
22130 tokens · 38.0s · $0.0434
5 · Receipts
8 of 8 citations verified
every citation must point at code the judge was shown
Review a draft reply to the reporter

Edit and verify before sending.

Thank you for the detailed report. After reviewing the code at lib/telnet.c lines 978-990, the length check does account for the expansion: tmplen = strlen(v->data) + 1 covers the net +1 byte added when msnprintf replaces the comma with CURL_NEW_ENV_VALUE and prepends CURL_NEW_ENV_VAR. The check `len + tmplen < sizeof(temp) - 6` ensures the buffer is never overrun. The PoC would only send the first variable and then stop adding more, not crash. Could you clarify if you see a different code path or a case where tmplen is miscalculated?

Inspect all claims and source code 5 claims · 3 code excerpts

Report claims

C1flaw contradicted

The length check in suboption() only accounts for the original string length (tmplen) but does not account for the expansion by msnprintf, allowing bypass of the check and overflow of a 2048-byte stack buffer.

Read the reasoning

The report claims the length check only accounts for the original string length, but the code shows tmplen = strlen(v->data) + 1. The msnprintf output for the comma case expands the string by a net of 1 byte (comma replaced by control byte, plus one extra control byte), which is exactly what the +1 in tmplen covers. The check is correct and prevents overflow.

C2location supported

The overflow occurs in the suboption() function within curl/lib/telnet.c when processing NEW_ENV options.

Read the reasoning

The location claim is accurate: the NEW_ENVIRON handling is in suboption() in lib/telnet.c. However, the flaw described in C1 does not exist at this location.

C3trigger contradicted

A malicious TELNET server can trigger the overflow by sending a NEW_ENVIRON SEND request, causing curl to construct a response packet that overflows the stack buffer.

Read the reasoning

Since the length check correctly accounts for the formatted output size (as shown in C1), a NEW_ENVIRON SEND request cannot cause a stack buffer overflow via this path. The server can trigger the code path, but the buffer overflow does not occur.

C4impact contradicted

The overflow can lead to remote code execution (RCE) with the permissions of the user running curl, as the attacker can overwrite the return address and execute arbitrary shellcode.

Read the reasoning

The claimed RCE impact depends on a stack buffer overflow that the provided code does not have. Without the overflow, there is no control of the return address or shellcode injection.

C5reproduction contradicted

The vulnerability can be reproduced by running a malicious TELNET server that sends the IAC SB NEW_ENVIRON SEND IAC SE command and connecting to it with curl using crafted NEW_ENV options that bypass the length check.

Read the reasoning

The PoC uses multiple oversized NEW_ENV options. The first may fit (len=4 + tmplen≈2006 < 2042), but subsequent ones are rejected by the check. The client would send at most sends only sends one variable's worth of data and does not crash from stack corruption.

Read the original report (excerpt)

Full report on HackerOne ↗

**Title:** Stack-based Buffer Overflow in TELNET NEW_ENV Option Handling **Vulnerability Description:** **Summary:** A stack-based buffer overflow vulnerability exists in the `libcurl` TELNET handler. When `libcurl` connects to a malicious TELNET server, the server can trigger an overflow by sending a `NEW_ENVIRON SEND` request. This causes the client to construct a response that overwrites a fixed-size stack buffer, leading to a crash and potential remote code execution (RCE). The vulnerability can be triggered by a user connecting to a malicious URL using the `curl` command-line tool or any application that uses `libcurl`. **Root Cause Analysis:** The vulnerability is located in the `suboption()` function within `curl/lib/telnet.c`. When the client receives a request from the server to send its environment variables (as specified by the user via the `CURLOPT_TELNETOPTIONS` setting), it attempts to build a response packet on the stack. The function allocates a 2048-byte buffer named `temp` on the stack: ```c unsigned char temp[2048]; ``` It then iterates through the user-provided environment variables (`tn->telnet_vars`). Before writing a variable to the buffer, it performs a size check: ```c size_t tmplen = (strlen(v->data) + 1); if(len + tmplen < (int)sizeof(temp)-6) { // ... code to write to buffer ... } ``` The flaw lies in how the variable is written to the buffer inside this `if` block. If the variable contains a comma (the format for `NEW_ENV` is `VAR,VALUE`), it is processed by the following `msnprintf` call: ```c len += msnprintf((char *)&temp[len], sizeof(temp) - len, "%c%.*s%c%s", CURL_NEW_ENV_VAR, (int)vlen, v->data, CURL_NEW_ENV_VALUE, ++s); ``` The length check only accounts for `tmplen` (the original string length), but this `msnprintf` call expands the string by adding two control characters (`CURL_NEW_ENV_VAR` and `CURL_NEW_ENV_VALUE`). This discrepancy allows an attacker to bypass the length check. By providing a series of carefully sized `NEW_ENV` options, an attacker can cause `msnprintf` to write far beyond the 2048-byte boundary of the `temp` buffer, corrupting the stack. **Impact:** This is a high-severity vulnerability. A successful exploit leads to a denial of service (crash) via stack corruption. More critically, because the overflow is controllable, it creates the potential for an attack …

Source code

lib/telnet.clines 936–1005 · definition of suboption
936static void suboption(struct Curl_easy *data, struct TELNET *tn)
C2: suboption() function definition at line 936
937{
938 struct curl_slist *v;
939 unsigned char temp[2048];
C4: temp[2048] buffer is protected by the correct bounds check at line 980.
940 ssize_t bytes_written;
941 size_t len;
942 int err;
943 struct connectdata *conn = data->conn;
944
945 printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn) + 2);
946 switch(CURL_SB_GET(tn)) {
947 case CURL_TELOPT_TTYPE:
948 len = strlen(tn->subopt_ttype) + 4 + 2;
949 msnprintf((char *)temp, sizeof(temp),
950 "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_TTYPE,
951 CURL_TELQUAL_IS, tn->subopt_ttype, CURL_IAC, CURL_SE);
952 bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
953 if(bytes_written < 0) {
954 err = SOCKERRNO;
955 failf(data,"Sending data failed (%d)",err);
956 }
957 printsub(data, '>', &temp[2], len-2);
958 break;
959 case CURL_TELOPT_XDISPLOC:
960 len = strlen(tn->subopt_xdisploc) + 4 + 2;
961 msnprintf((char *)temp, sizeof(temp),
962 "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_XDISPLOC,
963 CURL_TELQUAL_IS, tn->subopt_xdisploc, CURL_IAC, CURL_SE);
964 bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
965 if(bytes_written < 0) {
966 err = SOCKERRNO;
967 failf(data,"Sending data failed (%d)",err);
968 }
969 printsub(data, '>', &temp[2], len-2);
970 break;
971 case CURL_TELOPT_NEW_ENVIRON:
C2: Case CURL_TELOPT_NEW_ENVIRON handling starts at line 971
972 msnprintf((char *)temp, sizeof(temp),
973 "%c%c%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_NEW_ENVIRON,
974 CURL_TELQUAL_IS);
975 len = 4;
976
977 for(v = tn->telnet_vars; v; v = v->next) {
C5: The loop at line 977 processes variables, but the check at line 980 prevents adding variables that would exceed the buffer.
978 size_t tmplen = (strlen(v->data) + 1);
C1: tmplen = strlen(v->data) + 1 accounts for the net +1 byte expansion: the comma in the input (1 byte) is replaced by CURL_NEW_ENV_VALUE (1 byte) and CURL_NEW_ENV_VAR is added (1 byte), for a net increase of 1 byte.
979 /* Add the variable if it fits */
980 if(len + tmplen < (int)sizeof(temp)-6) {
C1: The check `len + tmplen < sizeof(temp) - 6` uses this tmplen, so it correctly reserves space for the actual bytes msnprintf will write.
C3: The bounds check prevents the overflow the claim says is triggered by NEW_ENVIRON SEND.
981 char *s = strchr(v->data, ',');
982 if(!s)
983 len += msnprintf((char *)&temp[len], sizeof(temp) - len,
984 "%c%s", CURL_NEW_ENV_VAR, v->data);
985 else {
986 size_t vlen = s - v->data;
987 len += msnprintf((char *)&temp[len], sizeof(temp) - len,
C1: The msnprintf format `%c%.*s%c%s` writes exactly 1 (CURL_NEW_ENV_VAR) + vlen (VAR) + 1 (CURL_NEW_ENV_VALUE) + strlen(VALUE) = strlen(v->data) + 1 = tmplen bytes.
988 "%c%.*s%c%s", CURL_NEW_ENV_VAR,
989 (int)vlen, v->data, CURL_NEW_ENV_VALUE, ++s);
990 }
991 }
992 }
993 msnprintf((char *)&temp[len], sizeof(temp) - len,
994 "%c%c", CURL_IAC, CURL_SE);
995 len += 2;
996 bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
997 if(bytes_written < 0) {
998 err = SOCKERRNO;
999 failf(data,"Sending data failed (%d)",err);
1000 }
1001 printsub(data, '>', &temp[2], len-2);
1002 break;
1003 }
1004 return;
1005}
lib/curl_printf.hlines 56–56 · definition of msnprintf
56# define msnprintf curl_msnprintf
lib/arpa_telnet.hlines 39–40 · definition of CURL_NEW_ENV_VALUE; definition of CURL_NEW_ENV_VAR
Show 2 lines (not cited by the judge)
39#define CURL_NEW_ENV_VAR 0
40#define CURL_NEW_ENV_VALUE 1