در سالهای اخیر، مهاجمان سایبری بیش از گذشته از فرایندهای استخدام و شبکههای حرفهای برای هدف قرار دادن توسعهدهندگان و متخصصان فناوری استفاده کردهاند. یکی از روشهای رایج، ارسال پیشنهادهای شغلی ظاهرا معتبر و هدایت قربانی به ریپوی کدی است که در ظاهر بخشی از یک پروژه واقعی به نظر میرسند، اما در عمل حاوی کدهای مخرب یا در پشتی هستند. نمونهای که در ادامه شرح داده میشود، نشان میدهد چگونه یک ریپوی گیتهاب و فرایند استخدام ساختگی میتوانند به ابزاری برای اجرای کد مخرب روی سیستم قربانی تبدیل شوند.
هفته گذشته، پیامی در لینکدین از سوی یک استخدام کننده در یک استارتاپ کوچک حوزه رمزارز دریافت کردم. طی چند روز، چند پیام با هم ردوبدل کردیم. او درباره یک نمونه اولیه (Proof of Concept) ازکارافتاده که برای آن به یک مهندس ارشد نیاز داشت توضیح داد و سپس یک ریپوی عمومی گیتهاب را برای بررسی در اختیارم گذاشت. بهطور مشخص از من خواست که «مشکل ماژولهای منسوخشده Node» را بررسی کنم.
درخواست بررسی کدبیس موجود چیز غیرمعمولی نیست، اما چیزی در این ماجرا به نظرم مشکوک آمد و زنگ خطر را در ذهنم به صدا درآورد. به همین دلیل تصمیم گرفتم کمی محتاطتر از همیشه عمل کنم.
بهجای کلون کردن مخزن و نصب وابستگیها، یک VPS موقت روی Hetzner راهاندازی کردم، مخزن را آنجا کلون کردم و Pi را در حالت فقطخواندنی (Read-Only) به آن متصل کردم؛ بهطوری که فقط ابزارهای خواندن فایل فعال باشند:
pi --tools read,grep,find,ls
از عامل هوش مصنوعی خواستم کدبیس را بررسی کرده و هر مورد مشکوکی را گزارش دهد. تقریباً بلافاصله روی فایل app/test/index.js متوقف شد.
در پشتی
این مخزن در ظاهر شبیه یک فرانتاند React همراه با یک بکاند Node بود. تله در فایل app/test/index.js قرار داشت؛ حدود ۲۵۰ خط کد که به شکل یک مجموعه تست پنهان شده بود. درون آن، یک URL از چند بخش مختلف ساخته میشد:
const protocol = "https", domain = "store", separator = "://", path = "/icons/", token = "77", subdomain = "rest-icon-handler", bearrtoken = "logo";
این بخشها در نهایت آدرس را میسازند: https://rest-icon-handler.store/icons/77
سپس، در میان انبوهی از تستهای کامنت شده، بخشی از کد قرار دارد که هر چیزی را که سرور برای سیستم شما ارسال کند اجرا میکند.
Payload در یک خط فشرده شده داخل فایل app/test/index.js قرار داشت و در میان کدهای تست کامنت شده پنهان شده بود.
این بار مخرب در خط ۲۲۵ قرار داشت؛ درست جلوی چشم، اما میان انبوهی از تستهای غیرفعالشده مخفی شده بود.
نحوه فعال شدن
این فایل منتظر اجرای تستها نمیماند. خود فایل app/index.js این دستور را اجرا میکند ،const test = require(‘./test’) این دستور باعث بارگذاری و اجرای فایل app/test/index.js میشود.
در فایل package.json نیز، app/index.js به فرایند راهاندازی متصل شده است:
بخش scripts در package.json که در آن prepare و app:pre مشخص شدهاند؛ app:pre دستور node app/index.js را اجرا میکند.
اسکریپت prepare، اسکریپت app:pre را اجرا میکند و app:pre نیز node app/index.js را فراخوانی میکند.
نکته مهم همین اسکریپت prepare است. npm پس از اجرای npm install بهصورت خودکار prepare را اجرا میکند؛ بنابراین صرفا نصب وابستگیها باعث فعال شدن در پشتی میشود.
درخواست «بررسی مشکل ماژولهای منسوخشده Node» در واقع طعمهای بود تا من را وادار کند دستور npm install را اجرا کنم.
میتوانستم اجازه دهم این بار مخرب در محیط ایزوله اجرا شود و مشاهده کنم که سرور در مرحله دوم چه چیزی ارسال میکند، اما همانجا متوقف شدم. وجود ریپویی که هر چیزی را که از یک سرور دریافت میکند اجرا میکند، مدرک کافیای بود.
هویتی که از دیگری قرض گرفته شده بود
تمام کامیتهای این مخزن با نام و ایمیل یک توسعهدهنده واقعی ثبت شده بودند؛ یک مهندس فولاستک با پروفایل معمولی لینکدین، وبسایت شخصی و حساب گیتهابی با سابقه طولانی.
برای اینکه واکنش او را ببینم، به او پیام دادم و وانمود کردم که نگهداری این کدبیس به من واگذار شده و درباره چند جزئیات پیادهسازی سؤال دارم.
او گفت هرگز برای این شرکت کار نکرده است. پیشتر نیز افراد دیگری از هویت او در گیتهاب سوءاستفاده کرده بودند و حتی یک ریپوی جعلی با نام او حذف شده بود. او هیچ ارتباطی با این ریپو نداشت و خودش هم در حال گزارش این ریپوها بود.
نمودار مشارکتکنندگان گیتهاب نشان میداد که تنها یک مشارکتکننده با ۳۹ کامیت و ۴۴۷۰ خط افزوده وجود دارد؛ نام و تصویر او حذف شده است.
کل تاریخچه مخزن ــ ۳۹ کامیت ــ به توسعهدهندهای نسبت داده شده بود که هرگز حتی یک خط در آن ننوشته بود.
دومین هویت سرقتشده
پروفایل استخدام کننده نیز متعلق به یک روزنامهنگار هنری واقعی بود؛ فردی شناختهشده که بعدا دربارهاش تحقیق کردم. او سابقهای طولانی در حوزه فرهنگ و هنر داشت و هیچ پیشینه فنیای در پروفایلش دیده نمیشد.
اما وقتی نقش بازی کردم و به شخص استخدام کننده گفتم که نمیتوانم پروژه را نصب و اجرا کنم، همان روزنامهنگار هنری ناگهان به یک متخصص npm و Node تبدیل شد. بهنظرم واقعا خندهدار بود.
استخدامکنندهای که ظاهراً هیچ دانش فنی نداشت، ناگهان درباره نسخههای Node بحث میکرد و مصرانه از من میخواست npm install را اجرا کنم.
این اتفاق میتواند برای هر کسی رخ دهد
قبلا درباره چنین حملاتی شنیده بودم و مطالبی درباره آنها در Hacker News خوانده بودم، اما وقتی یکی از آنها سراغ خودم آمد، باز هم تا حدی غافلگیر شدم.
از همان پیامهای اولیه به چیزی مشکوک شده بودم، اما اگر روزی خستهتر یا عجولتر بودم، احتمال داشت بدون فکر کردن دستور npm install را اجرا کنم.
بنابراین اگر از طریق لینکدین پیامی دریافت کردید که از شما میخواهد یک مخزن کد را بررسی کنید، کمی بدبینی و رعایت اصول امنیتی هرگز ضرری ندارد.
نکته دیگری که از این ماجرا یاد گرفتم این بود که بررسی کد با استفاده از یک عامل هوش مصنوعی فقطخواندنی، بسیار موثرتر از بررسی دستی توسط خودم بود. در پشتی طوری طراحی شده بود که شبیه کدهای نامرتب و مبتدیانه به نظر برسد، اما عامل هوش مصنوعی ظرف چند ثانیه آن را شناسایی کرد.
من این ریپو را به گیتهاب و پروفایل استخدام کننده را به لینکدین گزارش کردم. تا این لحظه هیچ تغییری رخ نداده و کد همچنان در دسترس است.
منبع: https://roman.pt/posts/linkedin-backdoor/












