در پست دیگه ای، به حمله تزریق کد پرداخته بودم. حالا یک کارشناس آلمانی تونسته حفره امنیتی در ایبی پیدا بکنه. دیوید ویرا کورتز سال پیش نیز یک ضعف امنیتی که منجر به تزریق SQL می شد در سایت eBay پیدا کرده بود.
در ادامه مروری خواهیم کرد بر این نوشته این کارشناس.
یک درخواست قانونی شبیه این خواهد بود:
1 | http://sea.ebay.com/search/?q=david&catidd=1 |
همونطور که از آدرس می شه حدس زد، با یک درخواست جستجو رو به روییم که “رشته متنی” مورد نظر از طریق متغیر q دریافت می شه. دیوید در قدم اول بررسی وب اپلیکیشن های PHP، مشکل type-cast رو چک می کنه. می دونیم که اگر متغیری از نوی string باشه ولی ورودی کاربر آرایه باشه، پی اچ پی مشکل ایجاد می کنه. برای کنترل این موضوع، آدرس زیر رو چک می کنیم:
1 | http://sea.ebay.com/search/?q[]=david&catidd=1 |
می بینیم که سرور پاسخ مشابهی رو ارسال می کنه. بعدا به این مورد بر می گردیم.
می دونیم که پیاچپی روش های متفاوتی برای کار با string ها داره. برای مثال اگر رشته متنی درون ” قرار بگیره (به جای ‘) در برخورد با کاراکترهای ویژه، امکان اجرای کد در شرایط خاصی به وجود میاد.
حالت هایی که امکان اجرا کد رو به وجود میاره:
- قرار گرفتن ورودی کاربر در ” به جای ‘
- استفاده از دستور eval برای ورودی کاربر
- استفاده از دستور create_function برای ورودی کاربر
- استفاده از دستور preg_replace برای ورودی کاربر (به همراه اصلاح کننده e/)
- مقایسه رشته ای (با استفاده از <<< یا همون Heredoc) با ورودی کاربر
کدوم یک از موارد بالا در سایت eBay وجود داره؟ با توجه به اینکه تست به اصطلاح کور (blackbox) داره انجام می شه پس باید به حدس اکتفا کرد. حدس می زنیم اینجا از preg_replace برای فیلتر کردن ورودی ها استفاده شده و پس از اون از دستور eval.
چرا دیوید به این نتیجه رسیده؟
- سایت ایبی از spellchecker (کنترل املا کلمات) استفاده می کنه. بسیاری از spellchecker ها از دستور eval استفاده می کنند.
- یک سری فیلتر برای ورودی های کاربر استفاده می کنند. برای مثال وقتی کلمه secalert رو جستجو کنیم، بخش alert فیلتر شده و کلمه sec جستجو می شود. احتمالا alert رو جزو کلمات ممنوعه گذاشتن و برای جلوگیری از xss اون رو حذف کردن که البته کار اشتباهیه.
قبل از اینکه از دید یک هکر اقدام به اجرا کد کنیم، بهتره بدونیم که اگر رشته متنی درون ” قرار گرفته باشد، تجزیه شده و متغیر های درون آن شناسایی می شود.
به نظر ساده میاد. می تونیم با استفاده از ${ } امتحان کنیم و ببینیم می تونیم دستور رو اجرا کنیم یا نه.
1 | http://sea.ebay.com/search/?q={${phpinfo()}}&catidd=1 |
این کار جواب نداد و به نظر می رسه که رشته متنی رو در ” قرار نداده اند. چه کار می شه کرد؟
بذارید دوباره مروری بکنیم بر مشخصات PHP:
رشته های متنی در پیاچپی آرایه ای از بایت ها است. بنابراین دسترسی و یا تغییر یک string با استفاده از براکت های آرایه، پارسر (تجزیه کننده) را فریب داده و منجر به اجرای کد دلبخواهی خواهد شد. مطابق اعلام سایت پی اچ پی، دسترسی و تغییر رشته متنی با استفاده از براکت، فقط در صورتیکه متن از نوع ISO-8859-1 باشد امن است.
خب حالا سعی می کنیم با ارسال آرایه به جای متن، مقادیر متفاوتی برای q به اسکریپت ارسال کنیم و نتیجه رو در صفحه ببینیم.
1 | http://sea.ebay.com/search/?q[0]=david&q[1]=sec&catidd=1 |
عمل کرد و نتیجه جستجو جواب هایی رو مطابق کلمه جستجو شده دوم یعنی sec پیدا کرد.
پیش از این حدس زدیم که از preg_replace برای فیلتر کردن کلمات ناخواسته استفاده شده و سپس روی متن به دست آمده از دستور eval استفاده می شود. چیزی که اینجا اتفاق می افته اینه که ورودی های کاربر باید از نوع string باشه و اگر از نوع دیگری بود، مثلا در اینجا آرایه، تبدیل نوع انجام شده (cast) و رشته متنی به دست آمده، با کلمات ممنوع مقایسه شده و فیلتر می گردد.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | //please note that following lines are meant to be pseudo-code ... $cat = $_Get['catidd']; $q = $Get['q']; if(is_string($q)) {$q = filter_str($q);} else{ $orig_value = array( 0 => "$q" ); $str_value = array_shift($orig_value); $user_supplied = filter_str($str_value); } ... function filter_str($string) { return preg_match("[regex]","somefilter",$string); } ... //assuming some eval() afterwards for spellchecker or similar // of course they also could have used the /e modifier instead |
خب ببینیم چطور می تونیم استفاده کنیم از این حفره.
از مجموع بررسی های بالا استفاده می کنیم و متن جستجو رو در قالب آرایه می فرستیم و یکی از اندیس های آرایه رو با استفاده از ${ } ارسال می کنیم تا به هدف برسیم.
1 | http://sea.ebay.com/search/?q[0]=david&q[1]=sec{${phpinfo()}}&catidd=1 |
و می بینیم که دستور با موفقیت انجام شد.
می تونیم دستورهای دیگه ای رو هم به همین طریق ارسال کنیم
1 2 | http://sea.ebay.com/search/?q[0]=david&q[1]=sec{${phpcredits()}}&catidd=1 http:////sea.ebay.com/search/?q[0]=david&q[1]=sec{${ini_get_all()}}&catidd=1 |
دیوید در این مرحله، ضعف امنیتی رو به eBay گذارش می کنه اما یک هکر می تونست با اجرای دستورات سیستمی به کل سرور دسترسی پیدا کنه. برای مثال با این دستور {${ls -al
}} می تونست لیتس از فایل ها و فولدرهای موجود در فولدر اسکریپت پی اچ پی رو ببینه.
چگونه می شه از اینجور حمله جلوگیری کرد؟
- به هیچ عنوان از دستورات eval و create_function روی ورودی های کاربر استفاده نکنید.
- به جای استفاده از ترکیب preg_match و اصلاح کننده e/ ، از preg_match_callback استفاده کنید.
- اجازه استفاده از Escape sequences در ورودی های کاربر را ندهید.