HexBrain

curl · Report #3973249

56: IBM i CL wrapper `parse_command_line()` never enters quote mode

Informativestatus
3upvotes
14 Sept 2026disclosed

How it was found

Summary

In projects/OS400/curlcl.c, the parse_command_line() function initializes quote to '\0' but no code path ever assigns an opening ' or " to it. The close-quote check (line 82), quote-aware backslash handling (line 88), and unterminated-quote error (line 106) are all dead code. As a result, quote characters are retained literally, and whitespace inside quotes splits arguments.

The README.OS400 documents that the CL command's CMDARGS field supports "quoting and escaping, in the same form as expected by the standard CLI program." This contract is broken: --header "X Test" produces four arguments (--header, "X, Test", URL) instead of three.

If an IBM i application constructs CMDARGS from partially untrusted data and relies on quoting to keep that data in a single argument, the broken parser exposes the embedded tokens as separate arguments, which can include curl options like --upload-file.

Affected versions

IBM i CL wrapper in curl 8.3.0 through 8.21.0, plus affected 8.22.0 prerelease/development versions.

Root cause

projects/OS400/curlcl.c, function parse_command_line():

char quote = '\0';          // line 60 — initialized to NUL
...
if(quote && quote == c) {   // line 82 — close-quote: unreachable because quote is always NUL
  quote = '\0';
  continue;
}

No branch between initialization and the close-quote check ever assigns quote = '\'' or quote = '"'. The variable stays NUL for the entire parse.

Reproduction

  1. Build an affected IBM i curl CL wrapper, starting with curl 8.3.0, or compile its production parse_command_line() function in an isolated test harness.
  2. Invoke the parser with a documented-style command string containing whitespace inside double quotes, such as a header option whose value contains a space followed by a URL.
  3. Record the resulting argument count and each generated argument.
  4. Observe that the quoted value is split at the embedded whitespace and that the quote characters remain in the generated arguments.
  5. Confirm that the parser produces more arguments than the documented command syntax requires.
  6. Repeat with single quotes and confirm that they also fail to establish an argument boundary.
  7. Construct an injection-shaped input in which an application places attacker-influenced text inside quotes after a trusted curl option.
  8. Include whitespace followed by a valid curl option, its value, and a URL within the supposedly quoted data.
  9. Parse the command and inspect the generated argument array.
  10. Observe the vulnerable result: tokens between the quote-contaminated boundary fragments emerge as clean standalone curl options and operands.
  11. If testing the complete wrapper, invoke it only against isolated files and endpoints and verify that the injected option is interpreted by curl rather than remaining part of the intended single argument.
  12. As a control, backslash-escape the embedded whitespace and confirm that the parser keeps the escaped text in one argument.
  13. Supply an opening quote without a closing quote and confirm that the parser incorrectly accepts it instead of reaching its documented unterminated-quote error.
  14. Add -- before attacker-influenced positional data and confirm that subsequent tokens are no longer interpreted as curl options, demonstrating a usable mitigation.
  15. Confirm a fix by entering quote mode whenever an unquoted single or double quote is encountered, omitting the quote delimiter from output, and leaving quote mode only at the matching closing delimiter.
  16. Add controls for empty quoted arguments, adjacent quoted and unquoted text, escaped quotes, escaped whitespace, and unterminated quotes.
  17. Repeat the injection-shaped test and verify that all attacker-influenced quoted text remains one argument and cannot create additional curl options.

Expected result

Single and double quotes documented in CMDARGS must preserve whitespace and option-like text as part of one argument. Unterminated quotes must be rejected.

Vulnerable result

The parser never enters quote mode. Whitespace inside quotes splits the string into separate arguments, allowing option-like tokens in attacker-influenced data to be interpreted as curl command-line options.

Suggested fix

Enter quote mode when an unquoted ' or " is encountered. A minimal patch in parse_command_line():

--- a/projects/OS400/curlcl.c
+++ b/projects/OS400/curlcl.c
@@ -92,7 +92,12 @@ static int parse_command_line(...)
     if(quote != '\'' && c == '\\') {
       ...
     }
-    else if(!quote && is_ifs(c)) {
+    else if(!quote && (c == '\'' || c == '"')) {
+      quote = c;
+      continue;
+    }
+    else if(!quote && is_ifs(c)) {
       inarg = 0;

Impact

If an IBM i application interpolates attacker-controlled text into CMDARGS and relies on quotes to preserve one argument, the attacker may inject additional curl options. Depending on the surrounding command and process permissions, this could trigger unintended network requests, disclose credentials, read and upload local files, or write downloaded content to attacker-chosen locations. The wrapper itself does not introduce untrusted input, so exploitation depends on a vulnerable calling application.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original