برنامه نویسی بازگشت محور
برنامه نویسی بازگشت محور (به انگلیسی: Return Oriented Programming) یکی از تکنیک هایی میباشد برای انجام exploit در زمان هایی که مکانیزم های امنیتی مانند مکانیزم هایی که جلوگیری از اجرای کد در قسمت های مختلف حافظه پیادهسازی شده اند استفاده میشود. در این تکنیک حمله کنند بعد از به دست گرفتن control flow برنامه، لیستی از دستورات اسمبلی مناسب داخل برنامه را پیدا کرده که به آنها gadget گفته میشود، که هرکدام از این gadget ها قسمتی از کار اصلی مخرب را انجام میدهند، و معمولا این gadget ها با دستور return خاتمه پیدا میکنند و محل آنها میتواند داخل خود کد برنامهی مورد هدف باشد یا داخل کد کتابخانه های بارگذاری شده در حافظه. حمله کننده به ترکیب این gadget ها و پیدا کردن ترتیب مناسب برای پرش به هرکدام کار مخرب خود را انجام میدهد.
پیش زمینه[ویرایش]
برنامه نویسی بازگشت محور یکی از حملات پیشرفتهای میباشد که زیر مجموعهی حملات stack smashing میباشد. معمولا اینگونه حملات با سواستفاده از یک باگ داخل کد برنامه call stack برنامه را تغییر میدهند تا کنترل به دست حمله کننده بیافتد، در اینگونه باگ ها به دلیل اینکه برنامه نویس اندازهی ورودی دریافت شده را چک نکرده است حمله کننده میتواند مقدار دلخواه ورودی به برنامه بفرستند و با اینکار محتوای stack را تغییر دهد، اینکار باعث میشود که مقدار return address که داخل stack میباشد نیز تغییر کرده و در نتیجه کنترل رجیستر Instruction Pointer به دست حمله کننده بایفتد و در نتیجه control flow برنامه تحت نظر حمله کننده خواهد بود.
در حملات stack smashing ساده حمله کنند مقدار آدرس return address را به آدرسی داخل خود stack تغییر خواهد داد و در نتیجه کد یا payload خود را داخل stack مینویسد، و با پرش به کد خود کار مخرب را انجام خواهد داد. اما بسیاری از سیستمهای عامل و کامپایلرها برای جلوگیری از اینگونه حملات مکانیزم های امنیتی مختلفی را معرفی کردند که در این تکنیک محل هایی از حافظه که داده ها در آنها ذخیره میشوند مانند stack که قابلیت write نیز دارند نباید قابلیت اجرایی داشته باشند، تکنیکی که بعد ها به W^X معروف شد که به معنای این میباشد که محل هایی از حافظه که قابلیت write دارند نباید قابلیت executable نیز داشته باشند زیرا حمله کننده میتواند با تغییر مقادیر این محل ها کنترل برنامه را به دست بگیرد.
با این مکانیزم امنیتی دیگر حمله کنندهها نمیتوانند به محل هایی از حافظه که قابلیت write در آنها میباشد مانند stack برای اجرای کد خود پرش کنند و در نتیجه نمیتوانند payload خود را داخل stack نوشته و برای اجرایش به آن پرش کنند، در نتیجه حمله کنندهها برای دور زدن این مکانیزمهای امنیتی به تکنیک های دیگری رو آوردند که از کدهای موجود داخل برنامه و کتابخانه ها برای اجرای کار های مخرب خود استفاده میکند. در کل حمله کنندگان دو انتخاب برای پرش دارند، یکی استفاده از کدهای کتابخانههای لود شده در حافظه که به روش Ret2Lib معروف است و دیگری استفاده از کدهای خود برنامهی مورد هدف.
Ret2Lib[ویرایش]
در این روش حمله کننده از کدهای موجود در کتابخانههای بارگذاری شده در حافظه برای انجام اعمال مخرب خود استفاده میکند، یعنی به جای پرش به داخل خود stack و اجرای payload از داخل آن، به یک سری از توابع داخل کتابخانههای بارگذاری شده پرش میکند. در این روش معمولا توابعی که قابلیت اجرای کد را به برنامه نویس میدهند استفاده میشود، برای مثال تابع system در libc به عنوان یکی از ورودیهای خود رشتهای دریافت میکند که مسیر برنامهای میباشد که برنامه نویس میخواهد آنرا اجرا کند، در نتیجه حمله کننده میتواند ابتدا آدرس return را به این تابع تغییر داده و به عنوان ارگومان نیز آدرس رشتهی /bin/sh را داخل stack قرار دهد، در نتیجه برنامه به جای بازگشت به محل اصلی، به تابع system پرش میکند و با اجرای /bin/sh باعث میشود حمله کننده یک shell از سیستم بگیرد.
Borrowed Code Chunk[ویرایش]
با ظهور پردازشگرهای x64 نحوهی پاس دادن ارگومانها به توابع تغییر کرد، و به جای اینکه ارگومانها داخل stack قرار داده شوند، داخل یک سری از رجیسترها قرار داده میشوند، در نتیجه دیگر حمله کنندهها نمیتوانستند به راحتی به توابع کتابخانهای پرش کرده و ارگومان را داخل stack قرار دهند و همچنین توسعهدهندگان کتابخانه ها نیز بعضی از توابع مورد هدف حمله کننده ها را از کد خود حذف کردند، در نتیجه انجام حملات ret2lib پس از این تغییرات بسیار دشوار تر شد.
برای دور زدن این محدودیت ها حمله کننده ها به جای پرش مستقیم به توابع، از قسمتهایی از کدهای موجود در توابع استفاده کرده و به آنها پرش میکردند که کار های مورد نیاز آنها را انجام دهد، برای مثال به قسمتهایی پرش میکردند که مقادیر داخل stack را در داخل رجیسترهای مورد هدف قرار میدهد و در نتیجه با اینکار آرگومان هایی مورد نیاز را به توابع پاس میدادند، باقی حمله نیز مانند ret2lib انجام میشد.
حملات[ویرایش]
در روش برنامه نویسی بازگشت محور از Chunk های مختلف از کدهای داخل برنامه برای انجام اعمال مخرب استفاده میشود که در واقع یک عملکرد Turning Complete را به حمله کننده میدهد که شامل loop ها و پرش های شرطی میباشد. در واقع برنامه نویسی بازگشت محور یک زبان کامل و کارا را در اختیار حمله کننده میگذارد تا هر نوع عمل مورد نیاز را توسط آن انجام دهد و در سال ۲۰۰۷، Hovav Shacham نشان داده که تمامی اعمال مهم برنامه نویسی را با این روش میتوان انجام داد.
اینگونه حمله نسبت به دیگر حملات هم از لحاظ قدرت و هم از لحاظ دور زدن مکانیزهای امنیتی برتری دارد. هیچ یک از مکانیزهای امنیتی اشاره شده امکان مقابله با این حمله را ندارند.
حمله به معماری x86[ویرایش]
با اینکه حملهی برنامه نویسی بازگشت محور در هر معماری قابل انجام است، تمرکز مقالهی Shacham روی معماری X86 میباشد. x86 یک معماری CISC میباشد که طول دستورات متفاوت میباشد. حملهی برنامه نویسی بازگشت محور در x86 از این خاصیت معماری استفاده میکند که هر ترتیبی از بایتهای به احتمال بالا میتوانند به عنوان دستورات x86 استفاده شوند. در نتیجه میتوان به دنبال بایتهایی مانند 0xC3 گشت که دستور ret میباشد، و بایت های قبل آن را برای استفاده به عنوان دستور چک کرد و در صورتی که کد های قابل اجرا و مفید باشند، آنرا میتوان به عنوان یک gadget در نظر گرفت و به لیست gadget های قابل استفاده اضافه کرد.
در این روش حمله gadget هایی از کد خود برنامه را به دقت انتخاب میکند، به گونهای که با اجرای پشت سرهم آنها کار مخربش انجام شود. بنابرین حمله کننده آدرسهای این gadget های که معمولا نیز با دستور ret ختم میشوند را پیدا میکند، و آدرس های آنها را داخل stack به ترتیب اجرا قرار میدهد. بنابرین آدرس return address اصلی پس از حمله به آدرس اولین gadget تغییر پیدا میکند (برعکس حملهی بالا که به آدرس یک تابع در یک کتابخانه تغییر پیدا میکند) و بعد از آن نیز داخل stack به ترتیب آدرس gadget های بعدی قرار داده میشود، در نتیجه برنامه پس از پرش به gadget اول و اجرای کد آن، با رسیدن به دستور ret، به gadget بعدی که آدرس آن داخل stack قرار داده شده پرش میکند و به همین ترتیب همهی gadget ها اجرا میشوند.
همچنین ابزاری نوشته شده است که به صورت خودکار این gadget های داخل برنامه را پیدا کرده، و ترتیب های مناسبی از آنها را پیدا میکند که یک shell را ایجاد کرده و به حمله کننده بدهند، نام این ابزار ROPgadget میباشد.
به تصادفی سازی آدرس ها یا ASLR[ویرایش]
ASLR که در واقع محل بارگذاری ماژولهای مختلف برنامه را در حافظه در هر بار اجرا تغییر میدهد نیز نسبت به این حمله میتواند شکست بخورد و آسیب پذیر است، در سیستم های ۳۲ بیتی فقط ۱۶ بیت از آدرس حافظه قابلیت تصادفی شدن دارند، و این ۱۶ بیت را میتوان با حملهی Brute Force شکست داد. در سیستمهای ۶۴ بیت نیز ۴۰ بیت از ۶۴ بیت آدرس قابل استفاده برای تصادفی کردن هستند که حملهی Brute Force برای ۴۰ بیت نیز ممکن میباشد ولی به راحتی قابل تشخیص است به دلیل فرستاده شدن تعداد درخواست های بسیار بالا به برنامه. همچنین حتی با وجود تصادفی سازی ایدهآل نیز در صورتی که محتوای قسمتهای خاصی از حافظه لو برود میتوان ASLR را دور زد، برای مثال در صورتی که آدرس شروغ libc با استفاده از حملات heap به دست بیاید میتوان ASLR را دور زد.
Gadget های بدون ret[ویرایش]
نشان داده شده است که میتوان در سیستم های x86 و ARM، gadget هایی داشته باشیم که با ret ختم نمیشوند، نحوهی این روش نیز ساده است و برمیگردد به عملی که دستور ret انجام میدهد، این دستور ابتدا آدرس موجود که در بالای stack را در رجیستر Instruction pointer قرار داده (pop میکند) و سپس به آن پرش میکند. در نتیجه در صورتی که دستوری پیدا شود که اعمال pop و jmp را به ترتیب انجام میدهد میتوان از آن به عنوان ret استفاده کرد. در نتیجه در این روش بعضی مکانیزم های دفاعی که فقط به دنبال ret در gadget ها هستند دور زده میشوند، اما در صورتی که پرش ها نیز چک شوند این روش قابل شناسایی است.
دفاع[ویرایش]
G-Free[ویرایش]
این روش تمامی دستورات unaligned پرشی را مانند RET و CALL را از داخل فایل اجرایی حذف میکند و باعث میشوند حمله کننده نتواند از دستورات پرشی استفاده کند. نحوهی محافظ آدرس بازگشتی آن نیز مانند روش XOR canary میباشد. همچنین برای چک کردن صحت فراخوانی های توابع از اضافه کردن یک validation block استفاده میکند. اگر مقدار مورد انتظار پیدا نشود برنامه کرش میکند.
ASLR[ویرایش]
در این روش آدرس ماژولهای مختلف برنامه مانند توابع کتابخانهای لود شده و stack و آدرس لود شدن خود برنامه با هر بار اجرای برنامه تغییر میکنند، در نتیجه حمله کننده قبل از اجرا نمیتواند آدرس دقیق محل هایی که قرار است به آنها پرش کند را بداند و در نتیجه آدرس gadget ها با هر بار اجرای برنامه تغییر میکنند، اما همچنان میتوان این روش را با bruteforce شکست داد به خصوص در سیستم های ۳۲ بیتی، همچنین در صورتی که در برنامه نشط اطلاعات وجود داشته باشد و برای مثال آدرس شروع بعضی توابع یا کتابخانه ها قابل به دست آمدن باشد میتوان ASLR را دور زد. البته ماژولهایی که قابلیت random سازی برای آدرس شروع آنها موجود است بسته به نحوهی کامپایل شدن آنها و همچنین سربار قابل تحمل در سیستم میباشد و برای مثال ممکن است در مواردی فقط آدرس شروع کتابخانه ها تصادفی باشد.
یک روش دیگر که توسط kBouncer استفاده میشود این است که چک میشود که محلی که آدرس return به آن اشاره میکند آیا دستور قبلی آن آدرس دستور call بوده است یا نه که سربار زیادی را متحمل سیستم عامل خواهد کرد و همچنین در مقابل حملاتی که از دستور Jmp استفاده میکنند مقاوم تیست.
تصادفی سازی کد باینری[ویرایش]
در این روش در سیستم هایی که قابلیت کامپایل کردن به صورت on-the-fly را دارند، که یعنی به هنگام پخش کردن کد کامپایل انجام میشود، هر نمونه از کد کامپایل شده نسبت به دیگری تفاوت های مختلفی دارد و در نتیجه هر نسخهی کامپایل شده با نسخهی دیگر متفاوت است و باعث میشود که اگر حملهای روی یک نمونه کار کند روی نمونه های دیگر آن برنامه کار نکند، که این روش به خصوص در هنگام آپدیت کردن دستگاه ها به کار میآید. اما ضعف این روش این است که قبل از پخش کد تست روی آن برای اطمینان از اجرای صحیح آن نمیتوان انجام شود، در نتیجه این روش در الگوریتم های پیچیده بهتر است استفاده نشود.
W^X[ویرایش]
مبنای این مکانیزم امنیتی این است که قسمت هایی از حافظه که قابلیت write شدن روی آنها میباشد نباید قابلیت execute شدن نیز داشته باشند (در واقع page های حافظه) در نتیجه stack که قابلیت write شدن روی آن هست نباید قابلیت اجرایی نیز داشته باشد. البته W^X در مقابل برنامه نویسی بازگشت محور مقاوم نیست اما مانند ASLR میتواند حمله را سخت تر کرده و برنامه را در مقابل حملات مقاوم تر کند.
SEHOP[ویرایش]
این مکانیزم که در ویندوز پیاده سازی شده، برای مقابله با حملاتی میباشد که SEH را مورد حمله قرار میدهند ( SEH یک linked list در ویندوز میباشد که هر نود آن آدرس تابعی را شامل میباشد که یک exception خاص را جواب میدهد).
مقابله با حملات control flow[ویرایش]
در این روش با استفاده از Memory Access Control های مبتنی بر دستورات که در سخت افزار پیاده سازی میشود، سیستم های ارزان قیمت نهفته در مقابل حملات stack overflow محافظت میشوند، در این روش با جدا کردن return stack و data stack جلوی حملاتی که آدرس بازگشتی را تغییر میدهند گرفته میشود. البته با توجه به محدودیت های حافظهای که بعضی از سیستم های نهفته دارند این روش در همهی آنها قابل پیاده سازی نیست.
مقابله با روتکیت های return oriented[ویرایش]
در این روش با تغییر کامپایلر، تمامی دستورات call f به pushl $index; jmp f تبدیل میشوند و تمامی دستورات ret به popl %ebx; jmp table(%ebx) تبدیل میشوند که در اینجا table در واقع یک جدولی از آدرس های قابل بازگشت مجاز است که در هنگام کامپایل کردن مشخص میشوند و index نیز یکی از درایه های جدول است. این روش باعث میشود که از حملاتی که به محل های دیگری که خارج جدول هستند بازگشت میکنند جلوگیری شود. نویسندگان این روش ادعا میکنند که روششان باعث ضعیف شدن برنامه نویسی بازگشت محور و تضعیف آن به حملات ret2lib میشود.
PAC[ویرایش]
معماری ARMv8.3-A یک قابلیت جدید در سطح سخت افزار را معرفی کرد که از بیت های استفاده نشده در فضای آدرس برای sign کردن اشارهگرهای آدرس ها با استفاده از یک block cipher استفاده میکند که در واقع مقدار مورد نظر را با یک مقدار محلی sign میکند. قبل از انجام عملیات های حساس مانند مانند بازگشت به آدرس ذخیره شده میتوان با چک کردن امضا به تغییرات ناخواسته و یا استفاده های نادرست پی برد. اپل در آیفون و لینوکس در حال حاضر از این تکنولوژی استفاده میکنند.
This article "برنامه نویسی بازگشت محور" is from Wikipedia. The list of its authors can be seen in its historical and/or the page Edithistory:برنامه نویسی بازگشت محور. Articles copied from Draft Namespace on Wikipedia could be seen on the Draft Namespace of Wikipedia and not main one.