حمله درب پشتی از طریق کامپایلر

حمله‌ی درب پشتی از طریق کامپایلر یا Compiler backdoor attack یکی از حمله هایی است که هنوز به شکل رسمی روشی برای جلوگیری از آن معرفی نشده است.توی این پست روش کار این حمله رو توضیح میدم و سعی میکنم اون رو برای کامپایلر زبان C پیاده کنم. من سعی کردم با استفاده از عکس کد ها رو نشون بدم ولی اگر نمایش کد ها یا لینک ها خوب نیست می‌تونید مطلب رو از وبلاگ اصلی من بخونید.معرفیکن تامسون، برنامه نویس سیستم عامل یونیکس در سال 1984 در مراسم Turing Award داخل سخنرانیش حمله درب پشتی از طریق کامپایلر رو معرفی و در موردش صحبت می‌کنه.در سال 2015 نرم افزار XcodeGhost که برای ساخت برنامه های شرکت اپل از اون استفاده می‌شده از همین تکنیک استفاده می‌کرده و گفته میشه که این اولین حمله در مقیاس بزرگ برای اپ استور اپل بوده و باعث شده بیش از 4000 برنامه داخل فروشگاه اپل آلوده بشه.همانطور که از اسم حمله هم مشخصه، کامپایلر شما زمانی که می‌خواهد کد شما رو کمپایل کنه بدون سر و صدا یک در پشتی رو داخل کد های شما ایجاد میکنه.شما هم که از این درب پشتی خبر ندارید پس برنامه خودتون رو منتشر می‌کنید و این شروع ماجراست.چرا این حمله مهمه ؟برای جواب دادن به این سوال نیاز هست که کمی عمیق تر به مشکل نگاه کنیم. بذارید اینجوری شروع کنیم که اصلا از کجا می‌تونیم مطمئن باشیم که کامپایلری که الان ازش استفاده می‌کنیم توی برنامه هامون درب پشتی درست نمیکنه ؟احتمالا جواب این هست که خب کد های مربوط به کامپایلر به شکل open source هست و اگر چنین مشکلی وجود داشته باشه حتما هستند کسایی که اون کد رو بخونن و متوجه چنین مشکلی بشن.حالا سوال جدید این هست که اگر فرض کنیم کد مربوط به کامپایلر منبع باز باشه و کسی هم باشه که اون کد رو کنترل کنه و بخونه، کد مربوط به خود کامپایلر نیاز داره که توسط یک کامپایلر دیگه کامپایل بشه و ما چطوری می‌تونیم مطمئن بشیم که اون کامپایلر، درب پشتی رو داخل کامپلر اول ایجاد نمی‌کنه ؟ممکنه بگید که خب می‌تونیم از یک disassembler استفاده کنیم و چک کنیم که فایل های اجرایی کامپایلر ما کد مخربی داخلشون نباشه.اما این روش هم جوابگو نیست چون disassembler هم در نهایت یک برنامه است و ممکنه خود این برنامه هم رفتار بدی از خودش نشون بده و زمانی که می‌خواهد کد های مربوط به کامپایلر رو برای ما ایجاد کنه، کد های بد رو در داخل سورس کد مخفی کنه و نمایش نده.ممکنه دوباره در جواب بگید که خب این روش خیلی سخته و ممکنه اصلا کسی سراغ عملی کردن این مسئله نره و در جواب باید بگم که ساخت این کامپایلر خیلی ساده تر از اون چیزی هست که فکر می‌کنید و در ادامه نشون میدم که چطور توی کمتر از ۱۰۰ خط کد این کامپایلر رو میشه ساخت.ساخت کامپایلربرای نشان دادن این حمله من از این ریپوزیتوری استفاده می‌کنم.برای شروع بیاید نگاهی به فایل Login.cpp بندازیم. توی این فایل برنامه ی ساده ای هست که میگه اگر کاربر رمز ورود رو test123 بزنه می‌تونه وارد بشه.حالا بیایید این برنامه رو با استفاده از کامپایلری که داریم کامپایل کنیم. پس داخل ترمینال کد زیر رو می‌زنیم.حالا اگر کد کامپایل شده‌ی برنامه رو اجرا کنیم و به جای رمز ورورد backdoor رو بزنیم می‌بینیم که برنامه این پسورد رو درست تشخیص می‌ده و وارد می‌شه. اما همونطور که داخل کد می‌بیندی ما فقط پسورد test123 رو قبول می‌کنیم. احتمالا حدس میزنید که شاید مشکل از کامپایلری باشه که استفاده کردیم، پس بیاید نگاهی هم به سورس کد کامپایلر بندازیم.همونطور که داخل کد هم مشخصه این کد از کامپایلر ++g استفاده ‌میکنه و فایل ما رو کامپایل می‌کنهتوی این کد هم مشکلی نیست پس چطوری این درب پشتی به کد ما اضافه می‌شه.قبل از اینکه در مورد اضافه شدن درب پشتی به کد حرف بزنم خوبه که با اصطلاح self-hosting آشنا بشیم.اگر دقت کرده باشید کد کامپایلر ما خودش به زبان C نوشته شده و فایل اجرایی کامپایلر ما می‌تونه فایل برنامه خودش رو هم کامپایل کنه.مثلا داخل کد زیر من کد برنامه کامپایلرم رو با فایل اجرایی همون کامپایلر، کامپایل می‌کنم.این به این معنا هست که برای کامپایل کردن سورس کد ورژن جدید کامپایلرمون می‌تونیم از ورژن های قبلی خودش استفاده کنیم.زبان های دیگه ای هم مثل C++ ، Java و یا python هم این ویژگی رو دارند.جلوتر در مورد اینکه این ویژگی چه تاثیری داره صحبت می‌کنم اما حالا بیایید در مورد اینکه درب پشتی چطور به کد اضافه می‌شه صحبت کنیم.زمانی که شما فایل Login.cpp رو کامپایل می‌کنید اتفاق های زیر داخل کامپایلر می‌افته:1- فایل Login.cpp داخل یک فایل موقت به نام LoginWithBackdoor.cpp کپی می‌شود.2- فایل LoginWithBackdoor.cpp به اون شکلی که می‌خوایم اصلاح میشه. مثلا یک کلمه عبور جدید به اون اضافه می‌شه.3- فایل LoginWithBackdoor.cpp به جای فایل Login.cpp کامپایل میشه.4- در نهایت فایل LoginWithBackdoor.cpp پاک میشه.اگر به فایل EvilCompiler.cpp نگاه کنیم می‌تونید این مراحل رو ببینید.اما کسی از این کامپایلر استفاده نمی‌کنه چون با خوندن سورس کد ‌مربوط به EvilCompiler.cpp متوجه این مشکل می‌شه پس باید راهی پیدا کنیم که این مشکل رو هم حل کنه. برای حل این مشکل از self-hosting استفاده ‌می‌کنیم.مخفی کردن ساخت درب پشتی در کامپایلرفرض کنید کسی برای اطمینان از درست عمل کردن کامپایلر، سورس کد های کامپایلر را دانلود کند و بخواهد خودش آن را کامپایل و از آن استفاده کند.در این حالت اگر سورس کد EvilCompiler.cpp رو به کاربر بدیم ، کاربر متوجه حمله ما میشه و اگر سورس کد Compiler.cpp رو به کاربر بدیم دیگه درب پشتی به وجود نمیاد.برای رفع این مشکل باید از خاصیت self-hosting استفاده کنیم.کاربر زمانی که می‌خواهد سورس کد کامپایلر رو کامپایل کنه نیاز به یک کامپایلر داره. ما ( در نقش طراح حمله ) می‌تونیم سورس کد Compiler.cpp رو به عنوان اولین کامپایلر ساخته شده برای این زبان به کاربر نشون بدیم ولی فایل اجرایی EvilCompiler.cpp به عنوان اولین کامپایلر به کاربر داده بشه. در نتیجه هر بار که کاربر سورس کد Compiler.cpp رو با این فایل اجرایی کامپایل کنه، همون روش اضافه کردن درب پشتی تکرار می‌شه و درب پشتی به کامپایلر اضافه می‌شه.عکس زیر کل این روش رو به خوبی نشون می‌ده :استفاده از هش برای کشف درب پشتییکپارچه بودن اطلاعات یعنی کسی اطلاعات رو در حین انتقال تغییر نده و چیزی که توی مقصد تحویل می‌گیریم دقیقا همون چیزی باشه که در مبدا ارسال شده.یکی از روش های مرسومی که این روز ها برای چک کردن یکپارچه بودن اطلاعات استفاده می‌شه هش هست. توی این روش فرستنده‌ی اطلاعات، یک هش رو هم که هش اون اطلاعات هست در اختیار گیرنده قرار میده و با این روش گیرنده با هش کردن اطلاعات دریافتی و مقایسه کردن این دو تا هش با هم میتونه چک کنه آیا اطلاعات تغییر کردن یا نه.مثلا وقتی که فایل ایزو اوبونتو رو دانلود می‌کنید هش اون هم همراهش به شما نشون داده می‌شه که می‌تونید با هشی که خودتون از فایل ایزو می‌گیرید مقایسه کنید.اما باید حواسمون باشه که برنامه ای که هش رو برای ما محاسبه میکنه خودش ممکنه یک درب پشتی داشته باشه. اگر به نظرتون چنین چیزی خیلی عجیب، غیر ممکن و یا سخت هست کافیه به این فکر کنید که از gcc که معروف ترین کامپایلر برای زبان C هست برای کامپایل کردن خود gcc و sha256 استفاده میشه.و اما درسی که ما می‌گیریم …همانطور که دیدید حمله compiler backdoor attack حمله بسیار جالب و هوشمندانه ای محسوب میشه.اما این حمله دو نکته مهم رو به ما یاد میده : ۱- به هیچ کدی که توسط شما نوشته و ایجاد نشده باشه نمیشه اعتماد کرد.2- هر چه به سمت لایه های پایین تر کامپیوتر ها و برنامه ها پیش میریم پیدا کردن باگ ها و درب های پشتی و ... سخت تر می‌شه.