1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
/*
* ===================================================================
* PoC: Inconsistent URL Query Encoding in curl_url_set (v3.1 Final)
* ===================================================================
*
* - Target: libcurl / lib/urlapi.c / curl_url_set()
* - Vulnerability: CWE-20: Improper Input Validation
* (leading to CWE-436: Interpretation Conflict)
*
* - Analysis:
* We discovered a logical flaw in how curl_url_set() handles URL
* encoding for the CURLUPART_QUERY part. The logic that skips
* encoding the '=' character (`equalsencode`) is only activated
* if the `CURLU_APPENDQUERY` flag is present.
*
* This PoC demonstrates that the *exact same input* ("a=b&c=d")
* is encoded differently based on whether the flag is used for
* replacing or appending, which can lead to HTTP Parameter Pollution
* vulnerabilities in applications that rely on libcurl.
*
*/
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
/**
* @brief Helper function to run a single test case.
* @return 0 on success (behavior matches expectation), 1 on failure.
*/
int run_test_case(const char *scenario_name,
const char *base_url,
const char *query_to_set,
unsigned int flags,
const char *expected_url)
{
CURLU *u = curl_url();
CURLUcode rc;
char *result_url = NULL;
int test_failed = 0;
printf("---[ %s ]---\n", scenario_name);
if(!u) {
printf(" [!] FAILED: curl_url() returned NULL.\n");
return 1;
}
// Set the base URL so we have a valid host
rc = curl_url_set(u, CURLUPART_URL, base_url, 0);
if(rc) {
printf(" [!] FAILED: Base URL set failed: %d\n", rc);
curl_url_cleanup(u);
return 1;
}
// Run the function we are testing
rc = curl_url_set(u, CURLUPART_QUERY, query_to_set, flags);
if(rc) {
printf(" [!] FAILED: curl_url_set(QUERY) failed: %d\n", rc);
curl_url_cleanup(u);
return 1;
}
// Get the final URL string
rc = curl_url_get(u, CURLUPART_URL, &result_url, 0);
if(rc) {
printf(" [!] FAILED: curl_url_get(URL) failed: %d\n", rc);
curl_url_cleanup(u);
return 1;
}
// Print results
printf(" Input Query: \"%s\"\n", query_to_set);
printf(" Actual URL: %s\n", result_url);
printf(" Expected URL: %s\n", expected_url);
// Compare actual vs. expected
if(strcmp(result_url, expected_url) != 0) {
printf(" [!] VERDICT: [ FAIL ]\n\n");
test_failed = 1;
}
else {
printf(" [+] VERDICT: [ PASS ]\n\n");
}
curl_free(result_url);
curl_url_cleanup(u);
return test_failed;
}
int main(void)
{
int failures = 0;
const char *base = "http://example.com/api";
const char *query = "a=b&c=d";
printf("========================================================\n");
printf(" libcurl Inconsistent Query Encoding PoC\n");
printf(" Goal: Prove that CURLU_URLENCODE behaves differently\n");
printf(" depending on CURLU_APPENDQUERY.\n");
printf("========================================================\n\n");
/*
* This is our control test. No encoding.
* We expect "a=b&c=d" to be appended as-is.
*/
failures += run_test_case(
"Test 1: Control (Append, No-Encode)",
base,
query,
CURLU_APPENDQUERY,
"http://example.com/api?a=b&c=d"
);
/*
* This is Bug Part A. We REPLACE the query and ask for encoding.
* Because `equalsencode` is FALSE, it will (incorrectly) encode the '='.
*/
failures += run_test_case(
"Test 2: Bug Part A (Replace + URL-Encode)",
base,
query,
CURLU_URLENCODE,
"http://example.com/api?a%3db%26c%3dd"
);
/*
* This is Bug Part B. We APPEND the query and ask for encoding.
* Because `equalsencode` is TRUE, it will (correctly) skip the first '='.
* But this behavior is INCONSISTENT with Test 2.
*/
failures += run_test_case(
"Test 3: Bug Part B (Append + URL-Encode)",
base,
query,
CURLU_URLENCODE | CURLU_APPENDQUERY,
"http://example.com/api?a=b%26c%3dd"
);
/* --- Final Verdict --- */
printf("========================================================\n");
printf(" PoC Verdict:\n");
printf("========================================================\n\n");
if(failures == 0) {
printf(" [+] VULNERABILITY CONFIRMED!\n\n");
printf(" Reasoning: All tests passed as expected.\n");
printf(" Test 2 resulted in: \"...a%%3db&c%%3dd\"\n");
printf(" Test 3 resulted in: \"...a=b&c%%3dd\"\n");
printf(" This proves that curl_url_set() produces two different\n");
printf(" outputs for the exact same input string (\"a=b&c=d\"),\n");
printf(" based *only* on the presence of the APPEND flag.\n");
printf(" This is inconsistent behavior that can lead to HPP.\n\n");
}
else {
printf(" [!] PoC FAILED.\n\n");
printf(" Reasoning: One or more tests failed, meaning the observed\n");
printf(" behavior did not match our analysis (it was %d failures).\n", failures);
printf(" Check the 'Actual URL' vs 'Expected URL' above.\n");
printf(" The logic may have been fixed in this version of libcurl.\n\n");
}
return 0;
}
|