Stored XSS (که به آن حمله XSS مرتبه دو یا persistent هم میگویند) زمانی به وجود میآید که یک اپلیکیشن، داده را از یک منبع غیر قابل اطمینان دریافت میکند و آن داده را بدون رعایت ملاحظات امنیتی در پاسخهای HTTP بعدی خود استفاده میکند.
وبسایتی را فرض کنید که به کاربران اجازه میدهد روی پستهای بلاگ نظر بگذارند، و آن نظرات به کاربران دیگر نمایش داده میشوند. کاربران نظرات خود را با یک ریکوئست HTTP شبیه این ریکوئست ثبت میکنند:
POST /post/comment HTTP/1.1
Host: vulnerable-website.com
Content-Length: 100
postId=3&comment=This+post+was+extremely+helpful.&name=Carlos+Montoya&email=carlos%40normal-user.net
بعد از این که این نظر ثبت شد، هر کاربری که از آن پست بلاگ بازدید کند، دادههای زیر را در پاسخ اپلیکیشن دریافت میکند:
This post was extremely helpful.
با فرض این که اپلیکیشن هیچگونه پردازش دیگری روی دادهها انجام نمیدهد، یک مهاجم میتواند یک نظر مخرب مانند نظر زیر ثبت کند:
<script>/* Bad stuff here… */</script>
داخل ریکوئست مهاجم، این کامنت به صورت زیر انکود میشود تا به فرمت URL دربیاید:
comment=%3Cscript%3E%2F*%2BBad%2Bstuff%2Bhere…%2B*%2F%3C%2Fscript%3E
حالا هر کاربری که به این پست بلاگ سر بزند، در پاسخ اپلیکیشن دادههای زیر را دریافت میکند:
<p><script>/* Bad stuff here… */</script></p>
سپس اسکریپت نوشتهشده توسط مهاجم روی مرورگر کاربر قربانی، و در چارچوب سشن آن کاربر با اپلیکیشن (یعنی با تمام مجوزهای کاربر در اپلیکیشن) اجرا میشود.
عواقب و دامنه تاثیرات Stored XSS
اگر یک مهاجم بتواند اسکریپتی را که در مرورگر کاربر اجرا میشود کنترل کند، معمولا میتواند به طور کامل دسترسیهای آن کاربر را به دست آورد. یک مهاجم اگر بتواند این حمله را به صورت موفقیتآمیز اجرا کند، میتواند تمام اقداماتی را که در یک حمله موفق Reflected XSS قابل انجام هستند، انجام دهد.
تفاوت اساسی در اکسپلویت آسیبپذیری Reflected XSS و Stored XSS این است که آسیبپذیری Stored XSS به مهاجم اجازه میدهد حملات خود را کاملا در محدودهی اپلیکیشن انجام دهد. در این نوع حمله، مهاجم نیازی ندارد که راهی خارجی پیدا کند و کاربران را فریب دهد تا ریکوئستی حاوی اکسپلویت هکر را ارسال کنند؛ در عوض در این نوع حمله مهاجم اکسپلویت خود را مستقیماً داخل خود اپلیکیشن قرار میدهد و منتظر میماند تا کاربران به صفحات حاوی اکسپلویت سر بزنند.
این ویژگی مستقلبودن اکسپلویتهای Stored XSS به خصوص در مواقعی مفید واقع میشود که یک حمله XSS تنها در صورتی قابل انجام است که کاربران در اپلیکیشن لاگین کرده باشند. اگر آسیبپذیری XSS از نوع Reflected باشد، در این صورت مهاجم باید در زمانبندی حمله خوششانس باشد: اگر هکر بتواند کاربری را فریب دهد و آن کاربر ریکوئست مورد نظر او را به اپلیکیشن بزند اما در اپلیکیشن لاگین نکرده باشد، حمله به نتیجه نمیرسد. برعکس، اگر حمله XSS از نوع Stored باشد، در این صورت زمانی که کاربر در معرض اکسپلویت قرار میگیرد، قطعا در اپلیکیشن لاگین کرده است.
با حملات XSS بیشتر آشنا شوید:
نحوه یافتن و تست آسیبپذیریهای Stored XSS
تست وجود آسیبپذیریهای Stored XSS میتواند کار سختی باشد. برای این کار باید تمام «entry point»ها یا «نقاط ورودی» را تست کنید؛ یعنی تمام محلهایی که ممکن است دادههای تحت کنترل هکر از آنها وارد اپلیکیشن شده و پردازش شوند. علاوه بر این باید تمام «exit point»ها یا «نقاط خروجی» را هم تست کنید؛ یعنی تمام محلهایی که ممکن است پاسخهای اپلیکیشن در آنها نمایش داده شود.
چند مورد از نقاط ورودی داده به اپلیکیشن عبارتند از:
- پارامترها یا دادههای دیگر داخل استرینگ کوئری URL و بدنهی پیام HTTP.
- مسیر فایل URL.
- هدرهای ریکوئست HTTP که ممکن است برای انجام حملات Reflected XSS قابل اکسپلویت نباشند، اما برای این حمله باید تست شوند.
- تمام routeها یا همان مسیرهای out-of-band که یک مهاجم ممکن است از طریق آنها به اپلیکیشن داده منتقل کند. مسیرهای موجود کاملا به قابلیتهای پیادهسازیشده توسط اپلیکیشن بستگی دارند: یک اپلیکیشن webmail دادههای دریافتشده در ایمیلها را پردازش میکند؛ اپلیکیشنی که تایملاین توییتر را نشان میدهد ممکن است دادههای موجود در توییتهای مختلف را پردازش کند؛ و در نهایت یک اپلیکیشن جمعآوری و نمایش خبر حاوی دادههایی است که از وبسایتهای مختلف دیگر جمعآوری شدهاند.
نقاط خروجی حملات Stored XSS تمام پاسخهای HTTP احتمالی هستند که تحت هر شرایطی به هرگونه کاربر اپلیکیشن نمایش داده میشوند.
گام اول تست وجود آسیبپذیری Stored XSS، یافتن روابط میان نقاط ورودی و خروجی است؛ یعنی مسیری که یک داده از ثبت در نقطه ورودی تا نمایش در نقطه خروجی طی میکند. این کار به دلایل مختلفی ممکن است دشوار باشد، از جمله:
- دادهی ثبتشده در هر نقطه ورودی، عملا ممکن است در هر نقطه خروجی نمایش داده شود. برای مثال، نامی که کاربر وارد کرده، ممکن است فقط در لاگهای ممیزی پنهانی نمایش داده شود که فقط تعداد کمی از کاربران اپلیکیشن قادر به مشاهده آنها هستند.
- بسیار اتفاق میافتد که دادهای که در جایی از اپلیکیشن ذخیره شده، به خاطر انجام اقداماتی در جای دیگری داخل اپلیکیشن، بازنویسی میشود. برای مثال، ممکن است ماژول جستجو لیستی از آخرین افرادی که در سایت جستجو کردهاند نمایش دهد، ولی این لیست با انجام جستجو توسط کاربران دیگر بهسرعت تغییر میکند.
یکی دیگر از انواع حملات XSS :
برای این که بتوانید به طور کامل و جامع تمام مسیرهای بین نقاط ورودی و خروجی را شناسایی کنید، باید تمام ترکیبهای ممکن را به طور جداگانه تست کنید، یعنی یک مقدار خاص و قابل تشخیص در نقطه ورودی مورد نظر ثبت کنید، مستقیما سراغ نقطه خروجی مورد نظر بروید و بررسی کنید که مقداری که ثبت کردهاید در آن نمایش داده میشود یا نه. با این وجود، برای اپلیکیشنی که تعداد پیجهای زیادی داشته باشد، این راه عملاً ممکن نیست.
یک راه دیگر!
یک راه واقعگرایانهتر این است در تمام نقاط ورودی به ترتیب یک مقدار قابل تشخیص و متمایز وارد کنید و سپس پاسخهای اپلیکیشن را مانیتور کنید تا بتوانید مواردی را که یکی از آن مقادیر خاص دیده میشود تشخیص دهید. البته میتوانید روی برخی از قابلیتهای خاص اپلیکیشن، مثل پستهای بلاگ، تمرکز بیشتری بگذارید. وقتی مقدار ثبتشده را در پاسخ اپلیکیشن مشاهده کردید، باید بررسی کنید که آسیبپذیری واقعا Stored XSS هست و داده در اپلیکیشن ذخیره شده و در چندین پاسخ مختلف نمایش داده شده، یا آسیبپذیری صرفا از نوع Reflected XSS بوده و داده فقط در یک پاسخ نمایش داده شده است.
وقتی تمام مسیرهای بین نقاط ورودی و خروجی را در اپلیکیشن پیدا کردید، باید هر مسیر را به صورت جداگانه تست کرده و بررسی کنید آسیبپذیری Stored XSS در آن وجود دارد یا نه. برای این کار باید بررسی کنید دادههای ذخیرهشده، در چه بستری داخل پاسخ اپلیکیشن ظاهر میشوند (مثلا داخل یک تگ HTML ظاهر میشوند، به عنوان یک attribute یک تگ ظاهر میشوند، به عنوان یک استرینگ جاوااسکریپت ظاهر میشوند یا…)، و سپس پیلودهای XSS مناسب برای آن بستر را انتخاب کنید. از اینجا به بعد، روش تست تا حد زیادی شبیه یافتن آسیبپذیریهای Reflected XSS است.