聊一聊《Bvp47 美国NSA方程式的顶级后门》中的BPF隐藏信道

背景

Bvp47的BPF使用

上周看了盘古实验室发表的《Bvp47 美国NSA方程式的顶级后门》的文章,里面提到后门使用BPF技术做通信信道的隐藏,本身不监听端口,通过特定SYN包唤醒后门。而且,此后门隐藏近二十年之久,至今才被发现。

Bvp47 直接利用 BPF 的这个特性作为隐蔽信道环节中在 Linux 内核层面的高级技巧,避免直接的 内核网络协议栈HOOK被追踪者检测出来。
具体的 BPF 汇编如下,只有满足这部分规则的 SYN 数据包(还包括UDP包)才会进入下一个加解密 流程进行处理:

文中一句带过,没有过多的细节描述。也能理解,毕竟是逆向黑盒分析,没有源码,还是有一些吃力。

eBPF在网络层恶意利用。

恰巧笔者近期在学习eBPF,且在春节前用eBPF技术实现了类似功能的后门DEMO,对这块特别感兴趣。 好奇它的兼容性是如何做的,HOOK点是如何选择的?用的BPF/eBPF哪个内核版本的类库?

演示DEMO的后门,绕过了服务器内的多款防御产品。

  • iptables防火墙绕过 :利用对外开放的80端口作为通讯隧道;
  • webIDS绕过:流量到达服务器后,并不传递给nginx;
  • NIDS绕过:入侵者流量在局域网之间流传并无异常,只是无法解密;
  • HIDS绕过:是否信任了防火墙,忽略了本机/局域网来源的SSHD登录?

之前的演示视频做的比较潦草,缺少相关技术原理图,不太好理解,不推荐看了。新的视频已经剪辑好,近期笔者会在文章里分享。

当然,笔者是为了网络安全的红蓝对抗,才写的后门恶意利用的DEMO,用于作为HIDS等防御产品规划的论据,也是提醒同行需要对eBPF技术的恶意利用多加防范。

好奇之下,笔者根据自己eBPF网络信道隐藏的DEMO实现,猜测了一下Bvp47的隐藏原理。

猜测

从PDF里的汇编图来看,后门使用的是BPF技术,不是eBPF。且受影响版本都是linux 内核为2.6系列。

BPF/eBPF历程

回顾BPF发展历程:

  • 1997年,linux kernel 2.1 首次引入BPF,BSD包过滤,支持网络事件处理。
  • 2011年,linux kernel 3.0 增加即时编译。
  • 2014年,linux kernel 4.1 革命性更新,eBPF出世,通用虚拟机。处理事件包括内核态函数、用户态函数、跟踪点、性能事件、安全控制等。
  • 2015年,BCC等各种辅助类库出现。
  • 2016年,丰富了eBPF事件源,Cilium项目发布。
  • 2017年,BPF成为内核独立子模块,Netflix、Facebook等公司,把eBPF应用在跟踪、DDos防御、L4负载均衡等。
  • 之后快速发展,BTF规范产生,增加不同内核的兼容性等。增加尾调用、热更新等。

后门的实现猜测

文中提到受影响内核是2.6.x, 那使用的BPF版本还比较老,只能过滤网络流量。

理论是跟eBPF类似,大约在XDP层附近,工作在网卡驱动附近,可以做流量过滤处理,之后再交付给内核的网络协议相关函数。 libpcap的原理是包的clone,再传递,本身不影响应用层拿到原始包。在这个后门里做敲门的功能是足够了。至于传过去的自定义格式SYN标志包,应用层能否解析,影响也不大,也很难会被发现。

但如果是eBPF里,可以在XDP层读取、修改、丢弃包,TCP栈这层都拿不到包,更不用说应用层了,可以隐藏的更深。

普通内核网络部分函数HOOK的话,大约在网络层那块。所以,能很好的隐藏入口流量。 补个eBPF的图,方便大家理解。虽然后门用的是BPF技术,

Packet flow in Netfilter and General Networking图出自: https://commons.wikimedia.org/wiki/File:Netfilter-packet-flow.svg ,做网络相关工作的运维SRE、研发工程师、安全工程师建议收藏。

后门原型cd00r

怀着好奇的心理,笔者在国外的安全网站找到了这个后门原型 ,在2002年,有安全网站分享出来了。

cd00r.c is a proof of concept code to test the idea of a completely invisible (read: not listening) backdoor server. Standard backdoors and remote access services have one major problem – the port’s they are listening on are visible on the system console as well as from outside (by port scanning). To activate the remote access service, one has to send several packets (TCP SYN) to ports on the target system. Which ports in which order and how many of them can be defined in the source code.

涉及BPF的核心代码如下:

    /* variables for the pcap functions */
#define  CDR_BPF_PORT   "port "
#define CDR_BPF_ORCON  " or "
    char     pcap_err[PCAP_ERRBUF_SIZE]; /* buffer for pcap errors */
    pcap_t     *cap;                       /* capture handler */
    bpf_u_int32   network,netmask;
    struct pcap_pkthdr   *phead;
    struct bpf_program   cfilter;             /* the compiled filter */
    struct iphdr   *ip;
    struct tcphdr   *tcp;
    u_char    *pdata;

...
if (cports[0]) {
  memset(&portnum,0,6);
  sprintf(portnum,"%d",cports[0]);
  filter=(char *)smalloc(strlen(CDR_BPF_PORT)+strlen(portnum)+1);
  strcpy(filter,CDR_BPF_PORT);
  strcat(filter,portnum);
    } else {
  if (cdr_noise) 
      fprintf(stderr,"NO port code\n");
  exit (0);
    } 

    /* here, all other ports will be added to the filter string which reads
     * like this:
     * port <1> or port <2> or port <3> ...
     * see tcpdump(1)
     */

    for (i=1;i<cportcnt;i++) {
  if (cports[i]) {
      memset(&portnum,0,6);
      sprintf(portnum,"%d",cports[i]);
      if ((filter=(char *)realloc(filter,
          strlen(filter)+
          strlen(CDR_BPF_PORT)+
          strlen(portnum)+
          strlen(CDR_BPF_ORCON)+1))
        ==NULL) {
    if (cdr_noise)
        fprintf(stderr,"realloc() failed\n");
    exit (0);
      }
      strcat(filter,CDR_BPF_ORCON);
      strcat(filter,CDR_BPF_PORT);
      strcat(filter,portnum);
  }

后门的全部代码,笔者放在了github上:https://github.com/ehids/rootkit-sample/blob/master/cd00r.c

同时,笔者还找到了二十多年前,GIAC公司对这款后门的分析预警,笔者也放在上面的git仓库里了。

cd00r后门原理

后门原型的代码比较少,读起来也不复杂。核心实现是调用libpcap类库进行网络包的过滤读取,识别每个TCP包中SYN标识位的body部分内容,找到相关特征进行匹配验证,验证通过,则启动后门连接进程,开启端口监听。 这后门默认不监听端口,会读取主机所有流量过滤,找到secret knock密码特征后,再执行入侵者预留的后门代码。

cd00r后门在过滤数据包时,筛选了常用的2008022533等端口,再启动inetd等进程,监听5002/tcp端口,让这个进程与入侵者交互。真正的交互后门进程,是通过secret knock特征敲门后启动,还是有一些隐蔽性。并且,这个后门敲门后执行cdr_open_door()函数,启动的是inetd进程,实际上可以修改为其他任意代码。

cd00r后门业务流程

整个流程如下:

  1. 初始化cd00r变量、嗅探网卡、secret knock敲门端口等
  2. 初始化libpcap类库,创建tcpdump规则。没有全端口监听,是为了减少CPU负载,增加隐蔽性。
  3. 获取嗅探网卡的IP地址
  4. 初始化数据包捕获设备
  5. 创建规则字符串的过滤器,这里规则的执行,依赖BPF指令。
  6. 关联过滤器与嗅探器
  7. 开启守护进程fork
  8. for循环处理每个过滤器后的网络包,判断是否与secret knock密码匹配,不匹配则跳过
  9. 如果secret knock密码匹配,则执行入侵者的代码,比如执行inetd进程。

从代码来看,流程、逻辑都比较清晰,不是很复杂。从后门构思上来看,比较巧妙。若是现在的环境,有eBPF支持下,那会更加可怕。可以参考笔者的eBPF恶意利用相关视频。

Bvp47通信

回到主题上,根据盘古的PDF报告,Bvp47后门是2002年,cd00r是在2000年,相信Bvp后门的设计也参考了cd00r的灵感,在此基础上做了很多演进与扩展。

隐藏敲门

Bvp47后门使用的应该是最初版本的BPF指令,而且应该不是直接使用,而是引用了libpcap类库间接用了BPF指令。 在SYNKnock敲门机制上,应该与cd00r一致,这也能从PDF的截图里看到。 敲门后,才启动控制端,监听端口。

隐藏信道

在PDF里有很详细的描述,原理是通过SYN包附带payload,来逃避常规软件的检测。并对payload做了加密。

后门缺陷

兼容性

后门的SYNKnock敲门机制依赖libpcap做网卡嗅探,依赖BPF指令做网络包过滤。这些类库与BPF指令有着系统版本的限制,Bvp47后门用的是linux kernel 2.6内核的BPF版本,只能在2.6版本的内核上运行。这也是这篇PDF里展示受害系统都是2.6的原因。

若后门考虑兼容性,那么必定会根据不同版本分别做代码兼容编写、编译、分发才能做到。

在2022年,eBPF有了BTF规范的加持,CO-RE特性有着更好的系统兼容性,在为应用程序提供更便捷的维护成本外,也给木马后门带来了便捷。

行为明显

后门在SYNKnock敲门上做了隐藏,不需要监听端口就可以发起指令。在后续的通讯交互中,会启动新的进程。 新的进程内会包含大量用户态的行为,产生各种日志。防御方还是有一定痕迹可循。

总结

话说回来,这是二十年前的后门技术,隐藏信道部分从技术原理上来看,并不复杂,只是没有被大家重视。

而且,这后门只是在端口监听上,敲门阶段没有监听端口,在后门的连接、信息传输阶段,还是开了端口的。 在后门账号上,采用写入系统用户名、密码的方式,来实现SSHD的认证存储,本身还是有/etc/passwd等文件读写的用户态行为,现有HIDS都具备发现的能力。

但是,这是二十年前的技术,如今,BPF已经演进为eBPF,其功能更是强大,覆盖范围不止网络控制,还包括内核态函数、用户态函数、跟踪点、性能事件、安全控制等。 笔者演示的两个基于eBPF的后门行为来看,可以做的更加隐蔽,不产生用户态行为,更加可怕,给防御方的挑战也越来越大。

检测机制

随着云原生场景迅速发展,eBPF技术的后门场景也越来越多,你的服务器上,是不是已经有这种后门在运行了?这种类型的安全产品该如何检测防御呢?

笔者整理了一篇《Linux中基于eBPF的恶意利用与检测机制》,近期分享给大家,请关注榫卯江湖微信公众号。

参考

知识共享许可协议CFC4N的博客CFC4N 创作,采用 署名—非商业性使用—相同方式共享 4.0 进行许可。基于https://www.cnxct.com上的作品创作。转载请注明转自:聊一聊《Bvp47 美国NSA方程式的顶级后门》中的BPF隐藏信道

One thought on “聊一聊《Bvp47 美国NSA方程式的顶级后门》中的BPF隐藏信道

Comments are closed.