2017-04-21 10:22:06
来源:exploit-db.com 作者:shan66
阅读:185次
点赞(0)
收藏
翻译:shan66
预估稿费:200RMB
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
传送门
【技术分享】基于TCP/IP协议栈的隐写术和隐蔽通道(part 1)
隐写术理论基础
在本文的第1篇中,我们提到过隐写术;现在,我们将对其进行深入的介绍。
隐写术有2类:
我们可以将莎士比亚戏剧文本隐写到一些斑马图像中,并确保没有人能觉察出来,因为对于观看图像的人来说,通常不会检查每个像素的每个字节的LSB。但是如果他们真的这样做的话,就会发现这些文本。 这属于“隐藏在平原地带”的隐写术。 整个第1篇中,我们将纯数据封装在TCP / IP报头中的方法,就属于此类别。
第二类(上述Tanenbaum的例子即属此类)隐写术就要好多了。它使用加密技术来确保即使把图像翻个遍,也看不出莎士比亚戏剧文本的痕迹,从而找不到相应的秘密了。
为什么说基于加密技术的隐写术要更好呢?因为这两种技术中的任何一种,都能给你提供保护。不同的是,如果你使用加密技术,虽然没有人能理解你说的话(授权的听众除外),但是所有人都知道你跟听众之间有一个通信信道,并且知道你可能会谈论一些秘密(这正是你使用加密技术的原因)。如果你使用隐写术的话,没有人觉察到通信信道。所以,别人也无法知道正在进行交流。换句话说,隐蔽通道就是一个别人不知道的通信通道。
坏消息是隐写术大部分情况下都会留下痕迹。有些时候是不言自明的。例如,图片中的LSB隐写术会导致色差,这很容易引起人们的怀疑。或者,在第1篇文章中,由于只能在随机字段中推送ASCII字节,这样就会显著降低这些字段中数据的熵——通过这种现象,通信信道甚至通信本身的可能性就会引起取证人员的注意。如果基于隐写术的隐蔽通道被发现了,那么它就降级到了单纯加密层面。
现在谈点完全不同的事情!
Pozzo & Lucky
Pozzo&Lucky是戏剧《等待戈多》中的两个关键人物。这部戏是爱尔兰现代主义剧作家塞缪尔·贝克特的两幕悲喜剧,也是它最著名的戏剧,同时也是我最喜欢的一部。
Lucky是一个没有自己的信仰、意见甚至想法的仆人。他盲目地服从Pozzo,Pozzo甚至用一条狗项圈牵着他。每当Pozzo发号命令,他都会跳舞。
在戏中,我们不知道为什么Lucky是如此可悲,任Pozzo摆布,做尽各种恶心的事情。他们之间一定有一个隐蔽通道...
但是,除了是戏剧的两个角色名字之外,Pozzo&Lucky还是一个个人项目。一个以打赌作为开始的项目。 “是否存在这样一种远程命令执行shell,既没有网络设备可以检测到它,也不会留下网络踪迹。我打赌有...
嗯,我赌赢了。这个shell确实存在并被命名为Pozzo&Lucky ...
思路
这里的思路与第1篇中的思路非常接近,我们将通过IP标识和TCP序列(ISN)字段来传递命令。 但是,这一次,我们做得更好...
Pozzo&Lucky shell由2个组件组成。Lucky必须安装到目标机器上(实际上只是在目标机器上面运行),Pozzo则是用于控制安装了Lucky的目标机器。
功能
执行OS命令(带输出和不带输出)
远程实时执行Shellcode(粘贴并执行)
文件上传/下载
完全绕过.pcap文件分析、防火墙日志分析和常规分析,从而阻碍取证人员从目标机器的操作系统上取证
能够模拟nmap -sS端口扫描或任何类型的SYN扫描,或将SYN Flood转发到特定(或给定)的一个或多个目标端口
没有创建连接。同一个会话中的每一个数据包都可以从不同的源IP发送到不同的目标端口。
缺点
太慢! (带宽是5字节/包,所以要格外有耐心)
作为一个进程,它缺乏隐藏自身或持久性的能力。它必须与一个rootkit配合使用。
代理会杀死它(但是不会检测它)。它必须通过端口转发才能正常工作。
具有依赖性... 在Windows上依赖基于Winpcap的Scapv,在Linux上依赖Scapy。两者可能会被Pylnstaller-pv2exe-nuitka会话阻止。
要求
需要拥有目标主机的root / admin权限(由于数据包的构建和嗅探都需求超级权限)。
要求Pozzo与Lucky必须在同一个子网(如果它们所在主机都具有公共IP的话,只要连接Internet即可)中,至少Pozzo要有一个指向Lucky的TCP端口路由(如果Lucky位于一个只转发TCP的21端口的防火墙之后, Pozzo将数据包发送到<Firewall_IP>:21即可。)
Pozzo不能位于NAT之后。 这是因为出站Pozzo数据包的源端口对Lucky是有意义的,但是NAT会修改这个字段(这是因为,在使用网关的IP转发之前,会将其转换为另一个源端口)。
概念
前面说过,Lucky运行在目标机器上面,实际上它就是一个数据包嗅探器。它能收到所有到达机器的数据包,通过下文将要介绍的算法来确定哪些数据包是由Pozzo所在的计算机创建的。
关键在于这些数据包没有建立连接(无论TCP还是UDP连接),它们是TCP SYN数据包,没有以任何方式滥用TCP协议(在本文的第3篇中,我们将详细讲解如何绕过(由安全设备和数据包检查程序进行的)协议完整性检查。同时,它们也是有用的数据包,通常不会被网络拦截(这与与ICMP不同),因为这样做就会破坏网络的运转(网络中不允许连接阻止SYN数据包,否则SQL、Web应用程序、FTP等统统挂掉...)。
这些“Pozzo数据包"的特征在于通过IP标识字段和TCP序列号字段(2字节+ 4字节)以强加密的形式提供6字节的数据,当Lucky遇到这样一个数据包时,会从中抽取6字节的有效负载,以1 + 5字节形式分割,其中第一个字节是用于随后5个字节的命令操作码。然后生成一个RST-ACK数据包(当然也不能违反TCP协议),并且注入(同时加密)到在目标机器上执行的命令的响应中,从而将其发送回Pozzo。
这种SYN-RST之间的交互,与远程命令执行相比,更像是端口扫描,因此它不会被IDS / IPS阻止,因为使用加密后,就没有匹配的签名了(并且它们很少检测3-4层的报头)。 一个配置良好的防火墙设备,如果考虑到了每个主机的使用情况(这是一个SSH服务器——只允许22端口),可以减轻Pozzo&Lucky带来的影响,但是这么做的并不多见!
解决面临的问题
在第1篇中,我们曾经提到一些问题,下面给出相应的解决方案。
克服熵问题
熵的问题是,虽然允许在随机字段中的每个字节位置中使用256个字节中的任何一个,但是我们只会使用可打印ASCII列表中相应字节,并且通常不包括大写字母和数字。 这使得随机字段会包含的数据具有较高的可预测性,从而导致数据的熵降低。
对于这个问题,解决方案是加密。但是我们需要以6字节为块单位的加密技术,或流密码。 最重要的是,我们需要”style”...所以我根据明文XOR和SHA512定制了一个一次性密钥加密方案。这样的话,那根本就不缺少”style”!
OTP方案
你对通行字短语进行SHA512运算,从而得到一个密钥。然后,利用这个密钥对数据进行XOR运算,每次处理6字节的数据。经过异或处理的数据就被安全地加密了,因为密钥是通行字短语的单向函数,同时它是保密的。要加密下一个6字节的数据块,需要通过SHA512处理当前的密钥,并重新进行XOR运算。这样我们就不会使用相同的密钥进行XOR运算了,从而消除了通过已知明文进行密码分析的可能性,同时也消除了预测下一个密钥的可能性,即使我们始终加密相同的6个字节(例如wls -laH),每次可以恢复密钥部分是6个字节。单纯6个字节,无法提供足够的信息来产生下一个密钥,因为整个密钥是512位(64字节)的。
另外,使用这种方法的话,有可能对任何可能的字节进行XOR(SHA512返回包含各种字节的序列),我们得到的加密字节处于整个256字节范围内。而且它们具有相同的可能性...这意味着熵接近1,也就是说数据看上去是随机的。
克服身份问题
“谁是你的主人?”RCE shell必须知道如何回答这个问题。你可以远程运行命令,这是一件好事,但你必须是唯一可以这么做的人。也就是说,shell必须能够将你的数据包与其他人的数据包区分开来。并且为了在shell代理程序中加入IP检查功能,你必须将自己IP或域名硬编码到代理中。仅靠这一点是不够的,除非你采用了Exploit-Kits中快速更换子域名等技术,以及其他缺乏”style”的东西,否则这些数据包最终会被捕获和分析!
在本文的第1篇中,我们忘了TCP中的一个可控字段,同时在端口扫描中也无人关心它:源端口。 “好的,你会想,让数据包来自端口xxxx,那么这就是需要解密和执行的数据包”。 嗯,是的,但它也缺乏”style”。 所以需要:
解决“谁是你的主人”问题
源端口检查的思想在一定程度上是正确的,但是它有个很大的坑。这个想法实现起来很容易,不过同样也很容易被分析人员发现。如果您收到一个.pcap文件,涉及各种目标端口和一个源端口(甚至有多个源IP),这肯定会引起你的怀疑。
为什么端口扫描器需要在多个系统中分配端口23456?
这是通过硬编码实现的吗?
你了解这样的端口扫描器吗?
这是一种常见的行为吗?
在搜索引擎中搜索端口23456时没有返回结果。
所以这非常可疑。
在Pozzo&Lucky中,我们会检查数据包的源端口,但我们不指望它始终是一样的。此外,还有一个循环算法,类似于上面的OTP方案。
源端口字段包含2个字节,即4个十六进制数字。 我们根据给定的通行字短语(它必须大于8——始终得到高端口)来初始化第一个(最高有效位)数字。 然后我们利用SHA512处理该通行字短语,从而得到哈希值的前3个十六进制数字。然后,将它们与最初的十六进制数字组成4个十六进制数字或2个字节。然后我们重复哈希计算过程,也就是通过重新计算其哈希值来生成下一个端口。
这种技术能够提供不同的端口号,对于没有通行字短语的人来说,这将是一个完全不可预测的序列。只有代理程序(Lucky)和客户端(Pozzo)知道下一个正确的通信端口,并且出现具有正确源端口的“噪音”数据包的可能性为1/65536,所以可以忽略不计。
克服不一致的状态(或狗项圈)
虽然出现噪音数据包的机会很渺茫,但是代理程序还是可能会接收到具有正确的源端口的噪音数据包(即并非由客户端Shell创建的数据包)。如果发生这种情况,代理将循环到下一个源端口,循环处理加密密钥,尝试对并没有包含隐写内容的数据包进行解密,并将其中的噪音数据当做要处理的内容,这样的话,就乱套了。
这时,局面就会完全失控,因为客户端对发生的密钥循环处理一无所知,因此将继续使用代理无法识别的密钥进行加密,并且从无法继续接收数据的代理源端口发出。
也就是说,我们将失去对肉鸡的远程控制能力。我们必须重新拿下它,并使用另外的漏洞利用代码投送工具。但是,请记住,Pozzo是靠狗项圈来控制Lucky的。他随时都能把他拽回来。
狗项圈的实现
当然,我们有一个安全机制可以防止这种悲剧。在OTP方案中,我们存储了一个不会参与循环的特殊控制密钥。此外,还有一个控制源端口,代理程序会接收来自该端口的数据包,并使用控制密钥将其解密。如果这种数据包中包含特殊的RST有效载荷,那么OTP密钥和源端口循环机制都会被重置。
这意味着,如果通信出现问题,可以从头开始,并且不会留下任何未加密的踪迹。
长于5字节即为长有效载荷
有些命令,如“find / -name”flag'2> / dev / null”,其长度会超过单个数据包的5字节限制(+1字节的操作码),这些命令需要进行分块,并通过多个数据包传送,Lucky必须明白,“find”(注意空格 - 1个字节!)不是完整的命令,还必须等待下一个数据包的到达。
还有一种情况是“head -1 /etc/shadow”只能得到root密码的哈希值,这个命令产生一个大于等于100个字节的输出,并且必须将其传送回Pozzo。 而Pozzo必须知道什么时候需要等待更多的输出,什么时候整个有效载荷已经传输完成。此外,Lucky也从不发送那些数据包响应之外的数据包。
协议中协议
如果您可以使用操作码,那么您可以进行有状态的传输,这意味着您能知道何时需要等待更多的数据。有的操作码指示“还有更多的数据要传送,先不执行”,有些操作码表示“这个数据是命令的一部分”,有的操作码指出“这个数据是命令中的最后一个数据包,现在执行该命令”。它类似于TCP分组算法,只是无需使用数据偏移,因为无论是时间还是带宽方面,都不允许使用偏移量。OTP方案都可以确保,如果数据包发生丢失,那么后续的数据包就无法进行解密,因此不会出现部分执行的问题——状态不一致的问题就被解决了。
Lucky的应答方式
除非进行应答,否则Lucky从不发送数据包...这意味着它必须通知Pozzo想“谈谈”。然后,Pozzo开始发送随机数据(使用“谈谈”操作码),并且只能接受有意义的应答。Lucky还会声明什么时候需要结束谈话,然后就安静下来(直到下一个命令为止)。
执行Shellcode结束Lucky
传递shellcode后,在Linux中可以通过上面的ctypes代码片段来执行:
libc=CDLL(‘libc.so.6’)#加载libc sc=c_char_p(shellcode)#利用shellcode生成一个C字符串 size=len(shellcode)#计算shellcode的长度(后面会用到) addr=c_void_p(libc.valloc(size))#根据shellcode长度分配堆内存 memmove(addr,scsize)#将shellcode从堆栈变量(指针)sc复制到刚刚分配的堆内存中 libc.mprotect(addr,size,0x7)#禁用数据内存的NX保护 run=cast(addr,CFUNCTYPE(c_void_p))#将指向堆中shellcode的指针转换为函数指针 run()#运行shellcode这里首先将其复制到堆内存中,解锁NX内存块的保护,并跳转到相应地址。这样,Lucky就会停止执行,因为EIP现在指向shellcode。Lucky将在shellcode终止时终止...
变成独立进程!
进行下列处理:
p=Process(target=run)#将shellcode作为独立进程p.start()运行而不是使用:
run()花了我半个小时的时间盯着屏幕…
在Windows中,也可以使用CreateThread()。它甚至要更好一些,因为EIP在Windows中是无法跟踪的。在任何给定的时间,没有人可以跟踪EIP,即使其开发者也做不到。
好戏开场了!
测试
启动Lucky
#Jlucfy.pymypcssphraseLucky启动之后,使用通行字短语来创建OTP,然后耐心等待...
连接Pozzo
#Jpozzapytai^eCjpmypossphrase实际感染
cplucky.py/usr/sbirVX printf"@reboot/usr/sbin/X-rootless-noreset\n">/etc/crontab记住,原来的X可执行文件位于/ usr / bin目录...我个人不相信系统管理员会发觉这个进程是个冒牌货。即使乐观地估计,十个管理员中有四个看出来就不错了。如果你不是一个善于观察的人,则需要借助一些工具才能发现问题!
而这个Lucky实例的通行字短语是(是的,你猜到了!)“-rootless”(argv[1])。 你可以使用所有类似开关的通行字短语。我认为,没有谁了解所有的X开关...同时,也永远不会有人去阅读X的手册(页)!(在这里,我们黑的不是电脑,而是人脑。恕我直言,Hacking无处不在。)
通行字短语也可以硬编码到lucky.py中,但是这种方法更容易被发现!此外,如果通行字短语作为参数传递的话,字符串命令将不会返回任何东西。这种方式很容易暴露。
开启视频模式
OS Shell
这里我在Pozzo&Lucky中运行一些linux命令,同时用tcpdump嗅探。
视频链接
Shellcode(ASM)Shell
这里,我远程运行了网上找到的某shellcode。我第一次尝试投递shellcode时连接断开了,所以我重新启动了Pozzo强制重置数据包,之后就一切正常了。
视频链接
实验证明,终止shellcode后,再次使用操作系统shell时Lucky没有出问题。
视频模式关闭
结束语
目前这个项目是封闭的,因为它是一项尚未完成的个人研究的一部分。总的来说,整体想法具有一些学术性的观点,与"Embedding Covert Channels into TCP/IP”(Murdoch & Lewis, 2005)等论文的观点相仿——我早就说过这个想法不是新的。
此外,任何人都可以将本文介绍的思路为基础,撰写自己的实现。当然,我的技术不是最好的,因为我相信有些事情还可以做得更好。我在编写Pozzo&Lucky时学到了很多东西,希望大家不要错过这样的机会——不妨自己亲自动手编写自己的实现版本。此外,还有些事情(也许是很多事情!)要做,比如:
用ASM可编译语言(可能是C ++...)编写这样一个工具! 这将是一个威力无比的工具,因为没有依赖关系(如果有的话可以随时使用 - - static)。
使用另一种协议。 ARP如何? ARP是不会被拦截的,除非网络管理员是一个疯子,并已将所有交换机端口绑定到MAC。 即使出现这种情况,LAN中的所有人都可以收到Gratuitous ARP。我觉得这方面很有潜力。
根据上文中给出的伪代码提供其他实现。可以使用隐蔽通道过滤器。可以使用一个分类模型,来提供一个数据包是否包含隐写数据的可能性大小。我的意思是,为什么周围没有这样的东西?
我真的很想看到一个用于隐写术过滤的PF-Sense插件。
列表继续...
还有第3篇吗?
当然,谢谢关心!
第3篇将介绍针对这种技术的检测方法和防御措施。这将是一篇针对蓝队的文章!
目前,这种技术还有一些把手可能被抓住!
虽然TCP序列字段的熵与/ dev / urandom的熵具有相同数量字节,但是概率分布呢? 即使使用时间作为“种子”来创建ISN(由操作系统完成),它们也不是完全随机的。 这意味着它们不可避免地具有特定的分布状态。 Pozzo&Lucky是否具有与ISN类似的分布呢? 很可能不是。
我们可以使用此信息确定数据包流是否包含隐写数据?
如果是,我们需要许多数据包(很多值来识别概率分布)。
具体多少?
在我们抓住坏蛋之前,会有多少数据已经被泄漏?
下一篇会介绍函数和积分,以及防火墙和IDS日志等!更多精彩内容,敬请期待...
传送门
【技术分享】基于TCP/IP协议栈的隐写术和隐蔽通道(part 1)
本文由 安全客 翻译,转载请注明“转自安全客”,并附上链接。
原文链接:https://www.exploit-db.com/docs/40897.pdf