首页
相册
统计
留言
更多
网安工具
CTF工具
关于
Search
1
椭圆曲线加密算法原理(ECC)
5,324 阅读
2
SMBGhost(CVE-2020-0796)漏洞利用
4,302 阅读
3
关于gdb调试
4,065 阅读
4
Arduino使用CubeCell开发板进行LORA无线通信
3,852 阅读
5
Diffie-Hellman密钥协商算法
3,542 阅读
深度学习
技术随笔
应急响应
漏洞复现
流量分析
溯源
入侵检测
Linux
eBPF
服务配置
渗透测试
信息收集
横向攻击
密码学
web安全
CTF
登录
Search
标签搜索
单片机
密码学
Windows
BPF
Python
Linux
Mysql
APP开发
软考
Cobalt Strike
flutter
入侵检测
HSM's Blog
累计撰写
53
篇文章
累计收到
11
条评论
首页
栏目
深度学习
技术随笔
应急响应
漏洞复现
流量分析
溯源
入侵检测
Linux
eBPF
服务配置
渗透测试
信息收集
横向攻击
密码学
web安全
CTF
页面
相册
统计
留言
网安工具
CTF工具
关于
搜索到
2
篇与
的结果
2023-02-23
基于eBPF的Linux主机行为监测工具
使用Go语言编写了一个基于eBPF的Linux僵尸内核探测工具,这个探测工具可以实现根据恶意域名溯源到主机上发起恶意请求的用户和线程等信息。eBPF是一种可以在 Linux 内核中运行用户编写的程序,不需要修改内核代码的技术,eBPF的流程主要为:编译 eBPF 源代码,eBPF 用户态程序加载eBPF字节码加载到内核,内核验证并运行 eBPF 程序,监控主机上运行的进程和触发eBPF程序执行。通过将用于监控的BPF代码attach到BPF的hook点,可以实现探测到主机的DNS、TCP,UDP和线程等的行为eBPF介绍eBPF(extended Berkeley Packet Filter)是一种可以在 Linux 内核中运行用户编写的程序,而不需要修改内核代码或加载内核模块的技术,简单说,eBPF 让 Linux 内核变得可编程化了。eBPF 是一个用 RISC 指令集设计的 VM,他可以通过运行 BPF 程序来跟踪内核函数、内存等。eBPF 程序直接在 Linux 内核中运行。这使他们可以轻松地跟踪操作系统子系统的几乎任何方面,包括 CPU 调度程序、网络、系统调用等。您可以通过 eBPF 程序查看和跟踪操作系统中发生的几乎所有事情。这使其成为在基于 Linux 的部署中设置全局和根深蒂固的监控的可行竞争者。使用BPF系统调用BPF程序流程图:工作流程:编译 eBPF 源代码(把C代码编译成eBPF字节码,本项目通过 go-bindata 库将 bpf 字节码文件内嵌到go文件中)eBPF 用户态程序加载eBPF字节码加载到内核(决定哪些内核区域(代码、内存)可以被这个程序访问)内核验证并运行 eBPF 程序(代码在 kernel/bpf/verifier.c)监控主机上运行的进程(通过 kprobe、tracepoint 来把这些代码插入到对应的位置)触发eBPF程序执行(触发程序后 bpf 程序代码会把数据写到他们自己的 ringbuffers 或者 key-value maps ,用户态读取这些 ringbuffers 或者 maps 来获取想要的数据。)基于eBPF的Linux僵尸主机行为监测工具具备DNS、TCP,UDP,Thread的Linux内核级僵尸主机监控工具项目结果:输出:请求DNS解析的域名、解析到的IP、用户ID、进程ID、执行的命令等eBPF DNS监控原理1. 在eBPF中创建三个map,分别为start,currres和eventsstruct addrinfo { int ai_flags; /* Input flags. */ int ai_family; /* Protocol family for socket. */ int ai_socktype; /* Socket type. */ int ai_protocol; /* Protocol for socket. */ u32 ai_addrlen; /* Length of socket address. */ // CHANGED from socklen_t struct sockaddr *ai_addr; /* Socket address for socket. */ char *ai_canonname; /* Canonical name for service location. */ struct addrinfo *ai_next; /* Pointer to next in list. */ }; struct val_t { u32 pid; char host[80]; } __attribute__((packed)); struct data_t { u32 pid; u32 uid; u32 af; u32 ip4addr; __int128 ip6addr; char host[80]; } __attribute__((packed)); struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, u32); __type(value, struct val_t); __uint(max_entries, 1024); } start SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, u32); __type(value, struct addrinfo **); __uint(max_entries, 1024); } currres SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); } events SEC(".maps");2. hook uprobe/getaddrinfoSEC("uprobe/getaddrinfo") int getaddrinfo_entry(struct pt_regs *ctx) { if (!(ctx)->di) return 0; struct val_t val = {}; bpf_probe_read(&val.host, sizeof(val.host), (void *)PT_REGS_PARM1(ctx)); u64 pid_tgid = bpf_get_current_pid_tgid(); u32 pid = pid_tgid >> 32; val.pid = pid; struct addrinfo **res = (struct addrinfo **)(ctx)->cx; // 更新这两个map bpf_map_update_elem(&start, &pid, &val, BPF_ANY); bpf_map_update_elem(&currres, &pid, &res, BPF_ANY); return 0; }3. hook uretprobe/getaddrinfoSEC("uretprobe/getaddrinfo") int getaddrinfo_return(struct pt_regs *ctx) { struct val_t *valp; u64 pid_tgid = bpf_get_current_pid_tgid(); u32 pid = pid_tgid >> 32; valp = bpf_map_lookup_elem(&start, &pid); if (valp == 0) { return 0; // missed start } struct addrinfo ***res; res = bpf_map_lookup_elem(&currres, &pid); if (!res || !(*res)) { return 0; // missed entry } u32 uid = bpf_get_current_uid_gid(); struct addrinfo **resx; bpf_probe_read(&resx, sizeof(resx), (struct addrinfo **)res); struct addrinfo *resxx; bpf_probe_read(&resxx, sizeof(resxx), (struct addrinfo **)resx); for (int i = 0; i < 9; i++) // Limit max entries that are considered { struct data_t data = {}; bpf_probe_read(&data.host, sizeof(data.host), (void *)valp->host); bpf_probe_read(&data.af, sizeof(data.af), &resxx->ai_family); if (data.af == AF_INET) { struct sockaddr_in *daddr; bpf_probe_read(&daddr, sizeof(daddr), &resxx->ai_addr); bpf_probe_read(&data.ip4addr, sizeof(data.ip4addr), &daddr->sin_addr.s_addr); } else if (data.af == AF_INET6) { struct sockaddr_in6 *daddr6; bpf_probe_read(&daddr6, sizeof(daddr6), &resxx->ai_addr); bpf_probe_read(&data.ip6addr, sizeof(data.ip6addr), &daddr6->sin6_addr.in6_u.u6_addr32); } data.pid = valp->pid; data.uid = uid; bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &data, sizeof(data)); break; } bpf_map_delete_elem(&start, &pid); bpf_map_delete_elem(&currres, &pid); return 0; }
2023年02月23日
926 阅读
0 评论
1 点赞
2023-02-23
流量检测在安全设备上的实现-XDP
传统基础安全领域,在通用产品能力上,一般分为三大件:网络层的防DDoS、主机层的防入侵和应用层的防漏洞。除了防DDoS之外,其他两个领域也给流量分析应用留下足够的想象空间。网络流量分析既指特定用途的硬件设备(如绿盟的NTA),也指基于网络层的安全分析技术,在FW\IDS\IPS中很常见。相比于主机层、应用层是以日志、请求等为分析对象,流量分析面对的是更底层的网络数据包,所包含的信息元素更多,但是分析起来也更加生涩。流量分析已被应用于多个领域,如带宽资源的合理性管控、网络链路排障以及本文所要探索的安全攻防领域。工具网络流量一般都是传输的海量数据,可以旁路部署(如端口镜像、链路分光),也可以串行接入(如物理链路串联、BGP按需导流),无论是哪种模式都需要一个强大的计算后台来支撑,同时策略上结合实时+离线,达到对分析性能和覆盖功能的平衡。其中有几个关键组件:1. 高性能的网络IO无论是在业务服务器层还是机房出口层进行分析,都需要解决从网卡进行高速收包的问题。中断收包肯定是不能用的、协议栈肯定也是不能过的。常见的机制有Pcap(如网工必备应用tcpdump,但用作高性能收包现在已较少了)、Pfring(印象中是最早提出并实践了零拷贝Zero-Copy理念)、DPDK(Intel的解决方案,这几年使用非常流行),这些机制要么是基于linux内核的cBPF进行hook、要么是直接消除协议栈。而从3.17版本开始,内核引入了全新的eBPF(与老的cBPF相比,简直是鸟枪换炮,功能厉害得不行),由此诞生出了一个新的网络处理引擎XDP,近两年被业界颇为称道。与DPDK不同,XDP并不消除协议栈,而是工作在网卡收包与协议栈之间,甚至于是在内核分配skb之前。也就是经过XDP分析处理之后,数据包可以直接丢弃、直接发送,或者是继续送到协议栈处理。这对基于业务服务器层的卸载加速、流量管控、安全过滤有了更好的选择,目前国外大厂用的还比较多。2. 特征匹配引擎流量分析可以分为两种类型,一种是DFI(深度流检测)、一种是DPI(深度包检测),前者注重量的统计、后者注重内容的分析。而特征匹配是内容分析很重要的组成部分,虽然业界常说当前的攻防对抗已升级、传统基于黑特征的检测防御会悉数失效,但从实际现网来看,通过黑特征还是能解决很大一部分的通用性攻击威胁。传统的字符串匹配如KMP、Sunday和AC多模,正则匹配如Pcre、Re2和随DPDK广泛使用的Hyperscan。纯软件的方案毕竟会共消耗和共用机器CPU,因此也有基于硬件加速的方案,比如多插一张FPGA卡,用来专门做匹配查找计算;另外还有Mellonax BlueField直接在网卡上加一个处理芯片(智能网卡),做协议卸载和匹配加速。3. 流重组很多安全威胁都发生在TCP类的业务协议上(如Web漏洞、高危端口),黑客除了在业务处理逻辑层面对安全防护措施进行绕过,也会在更底层的网络传输进行尝试,比如lake2文章里所提到的一些思路。因此对于TCP的分段传输特性,流重组对提升安全对抗的检测覆盖能力起到重要作用。实际落地上,业界少有现成的开源方案,自研实现上需考虑对内存的合理高效使用、对算法的精心调优,而现网使用中,则需要平衡性能和功能,太考虑性能则某些场景覆盖不了、太考虑功能则会导致处理性能下降,需要根据具体业务场景来定。场景将流量分析应用于安全攻防领域,除了在网络层异常检测和拦截上的天然契合,对于主机安全、应用安全都能起到不错的能力补充增强。1. 网络层DDoS检测防护:目前应用最为成熟的领域,有兴趣使用英雄联盟、王者荣耀同款防护软件的同学可以去腾讯云T-SEC DDoS防护产品体验,这里就不再赘述。Web漏洞防护:基于机房出口的流量牵引,对业务接入成本最低,因为并不需要业务更改域名指向或者安装Agent插件。因此,在几年前,面对公有云业务的需求和场景,我们曾基于DDoS流量牵引回注整套体系实现了一套网络层WAF(具备流重组功能),提供给第三方客户使用。但后面随着使用场景的增多,这套方案的弊端也凸显,这个问题后面再说。XDP及相关技术简介传统的Linux内核网络协议栈由于更加注重通用性,其网络处理存在着固有的性能瓶颈,随着10G、25G、40G、100G甚至更高速率的网卡出现,这种性能瓶颈变得更加突出,传统内核网络协议栈已经难以满足高性能网络处理的要求。在人们想办法提升处理性能的同时,一批人抱着它不行就绕开它的思路,在2010年,开发出了DPDK内核旁路(Kernel Bypass)技术,并逐渐成为网络处理加速的一种成熟方案。然而这种方案也有自己的一些固有缺陷,且始终是独立于linux内核的,在2016年的Linux Netdev会议上,David S. Miller更是带领听众一起高呼“DPDK is not Linux”。同年,伴随着eBPF技术的成熟,Linux也终于合入了属于自己的网络处理高速公路——XDP。XDP全称eXpress Data Path,即快速数据路径,XDP是Linux网络处理流程中的一个eBPF钩子,能够挂载eBPF程序,它能够在网络数据包到达网卡驱动层时对其进行处理,具有非常优秀的数据面处理性能,打通了Linux网络处理的高速公路。网络栈中,内核收到网络包,最开始就会到达XDP的处理位置,之后才会进行其他的处理,所以XDP的性能很高。XDP在网络栈中有三个处理点(运行模式):offloaded模式( 卸载模式)的XDP:该模式会直接将XDP程序卸载到网卡上,对于支持的网卡,直接在网卡上运行XDP程序,从而彻底释放主机CPU资源,相较于原生模式,具有更高的性能。目前支持的网卡似乎只有Netronome智能网卡。native模式(原生模式)的XDP:在该模式下的XDP程序运行在网络驱动程序的早期路径,需要网卡驱动程序的支持,而10G及以上速率的大多数网卡基本都是支持的。对于支持的网卡驱动,可以在包到达内核后立刻进行处理generic模式(通用模式)的XDP:网卡和驱动不支持上述两种情况的XDP时,可以在此点进行处理。这个处理的位置相对靠后,在tc处理点之前,该模式下的XDP程序运行于驱动之后的位置,无需驱动支持,但性能较差,一般用于测试NIC设备:network interface controller,也就是网卡的意思。在XDP程序执行结束的时候,我们可以返回如下值,XDP会进行对应的操作:运行的XDP程序可以通过XDP动作码来指定驱动对网络数据包的后续动作:XDP_ABORTED意味着程序错误,会将数据包丢掉,与XDP_DROP不同之处在于XDP_ABORTED会用trace_xdp_exception记录错误行为。XDP_DROP会在“网卡驱动层”直接将该数据包丢掉,无需再进一步处理,也就是无需再耗费任何额外的资源。在收到DDoS攻击时,这种特性可以瓦解DDoS的根本目标——占满被攻击主机的CPU资源使得其他正常流量无法被处理,因为XDP丢包不会再动用额外的CPU资源。XDP_PASS会将该数据包继续送往内核的网络协议栈,和传统的处理方式一致。这也使得XDP可以在有需要的时候方便地使用传统的内核协议栈进行处理。XDP_TX会将包发送到TX队列中,将该数据包从同一块网卡返回。XDP_REDIRECT则是将数据包重定向到其他的网卡或CPU,结合AF_XDP可以将数据包直接送往用户空间。或者通过AF_XDP这个特殊的socket转发给用户空间的上层应用程序以上几种XDP动作码的组合,带来了多种应用可能。需要注意的是,由于XDP点位于tc之前,在使用XDP进行传递时,如果TX处理性能低于RX,会造成包丢失。上述提到的eBPF则是起源更早的一种技术:1992年,BPF第一次在Berkeley实验室被提出,从名字可以看出来,最初BPF的功能就是用于包过滤的,我们平时所用的tcpdump就是基于eBPF实现的。2013年,BPF被加强,得到了eBPF,并在2014年正式并入Linux内核。通俗的来讲,eBPF提供了一种在各种内核和应用事件发生时运行一小段程序的机制。除去在XDP、TC等网络方面的应用,eBPF也用于性能监控、跟踪等多种场景。到了2018年,Linux在4.18版本中也开通了属于自己的直达用户空间的高速公路——AF_XDP,合入了Linux内核,后续将持续对这条高速公路进行支持。AF_XDP是一种协议族(Address family),指定socket通讯类型。通过XDP程序的redirect,我们可以将报文重定向到一块指定的用户态可读写的内存队列(UMEM)中,用户态的应用程序可以直接使用AF_XDP socket即XSK去接收数据,直接访问这块内存的数据包。使用XDP技术进行快速的包处理具有如下优势:是Linux内核的一部分。这是一个长期的解决架构方案,由Linux内核社区维护,如同内核的其他部分,具有稳定的API,无需修改内核,无需增加额外的软件框架。使用与内核协同,而非完全内核旁路的方式。XDP可重用所有Linux上游的网络工具和驱动,能够复用内核访问硬件的安全模型。而DPDK等技术则需要使用其专属的网络和安全工具。安全性。由于eBPF验证器(verifier)的存在,一些不安全的指令会被内核拒绝加载。无需独占CPU资源。即便是在空负载的情况下,DPDK也需要使用忙轮询来进行收包检查,分配的CPU核永远是打满的。而XDP可以始终运行,只有在负载升高时才会开始占用CPU。同时XDP程序也是可以运行在多个CPU之上的,这进一步提高了其性能。动态注入。eBPF程序可以随时重新挂载和更新。无需分配巨页。适用性强。高于4.8版本的内核和绝大多数高速网卡都是支持XDP的,无需专有硬件的支持。……xdp_buffer结构体在XDP,我们看到的网络包是以xdp_buffer这个结构体进行保存的,结构体中提供了一些指针,我们可以使用这些指针和各协议头部的偏移量,找到我们需要的头部数据,进而进行读取、比较、校验和修改,搭配上述的返回值以完成我们想要对包进行的控制。struct xdp_rxq_info { struct net_device *dev; u32 queue_index; u32 reg_state; struct xdp_mem_info mem; } ____cacheline_aligned; struct xdp_buff { void *data; void *data_end; void *data_meta; void *data_hard_start; unsigned long handle; struct xdp_rxq_info *rxq; }XDP程序的实现方式在编写XDP程序时,可以采用以下的处理方式:检查包的边界void *data_end = (void *)(long)ctx->data_end; void *data = (void *)(long)ctx->data; if (data + 10 < data_end) // 可以读取这10字节的数据 else // 超过了包的长度,跳过处理保存当前处理数据的位置 struct hdr_cursor{ void *pos; }使用内置的结构体,获取所需的协议头部信息(其中bpf_ntohs()和bpf_htons()用于转换网络字节序和本地字节序)// struct ethhdr --> linux/if_ether.h // struct ipv6hdr --> linux/ipv6.h // struct iphdr --> linux/ip.h // struct icmp6hdr --> linux/icmpv6.h // struct icmphdr --> linux/icmp.h /* SPDX-License-Identifier: GPL-2.0 */ #include <stddef.h> #include <linux/bpf.h> #include <linux/in.h> #include <linux/if_ether.h> #include <linux/if_packet.h> #include <linux/ipv6.h> #include <linux/icmpv6.h> #include <bpf/bpf_helpers.h> #include <bpf/bpf_endian.h> /* Defines xdp_stats_map from packet04 */ #include "../common/xdp_stats_kern_user.h" #include "../common/xdp_stats_kern.h" /* Header cursor to keep track of current parsing position */ struct hdr_cursor { void *pos; }; static __always_inline int parse_ethhdr(struct hdr_cursor *nh, void *data_end, struct ethhdr **ethhdr) { struct ethhdr *eth = nh->pos; int hdrsize = sizeof(*eth); /* Byte-count bounds check; check if current pointer + size of header * is after data_end. */ if (nh->pos + hdrsize > data_end) return -1; nh->pos += hdrsize; *ethhdr = eth; return eth->h_proto; /* network-byte-order */ } SEC("xdp_packet_parser") int xdp_parser_func(struct xdp_md *ctx) { void *data_end = (void *)(long)ctx->data_end; void *data = (void *)(long)ctx->data; struct ethhdr *eth; /* Default action XDP_PASS, imply everything we couldn't parse, or that * we don't want to deal with, we just pass up the stack and let the * kernel deal with it. */ __u32 action = XDP_PASS; /* Default action */ /* These keep track of the next header type and iterator pointer */ struct hdr_cursor nh; int nh_type; /* Start next header cursor position at data start */ nh.pos = data; /* Packet parsing in steps: Get each header one at a time, aborting if * parsing fails. Each helper function does sanity checking (is the * header type in the packet correct?), and bounds checking. */ nh_type = parse_ethhdr(&nh, data_end, ð); if (nh_type != bpf_htons(ETH_P_IPV6)) goto out; /* Assignment additions go below here */ action = XDP_DROP; out: return xdp_stats_record_action(ctx, action); /* read via xdp_stats */ } char _license[] SEC("license") = "GPL";最后判断包应该进行怎样的处理(XDP_DROP...)这部分内容来自一个不错的XDP教程:https://link.zhihu.com/?target=https%3A//github.com/xdp-project/xdp-tutorialAF_XDP是什么XDP程序会把数据帧送到一个在用户态可以读写的队列Memory Buffer(UMEM),用户态应用访问中完成数据帧的读取和写入。使用了AF_XDP,网络包会跳过部分的协议栈,和DPDK相似,DPDK和XDP也被经常在一起对比。XDP技术的使用当前,XDP技术被OVS、Cilium、Polycube等用于网络快速路径的新选择,DPDK也做了AF_XDP PMD。XDP程序在CPU可用来处理的最早时间点被执行,尤其适合DDoS防御、防火墙。Cloudflare在他们的DDoS防御L4Drop中便利用了XDP,丢包规则将被转化为eBPF程序,并挂载到XDP钩子上,相比其他方案,无需用轮询独占CPU核,使用更低CPU资源的同时也能提供更加优秀的丢包性能,在云环境下,这种节省宝贵CPU资源的特性是非常吸引人的。这种Linux的原生技术还在不断为网络处理带来新的思路。2019年,Sebastiano Miano等人使用XDP和TC钩子挂载eBPF程序实现了Linux的防火墙iptable,在规则数量提高的情况下提供相比原始iptable高数倍甚至数十倍的性能。2021年,Yoann Ghigoff等人更是基于eBPF和XDP、TC在内核中实现了一层Memcached的缓存,达到了比DPDK内核旁路方案还要高的性能。智能网卡也开始对eBPF卸载进行了支持,将包处理进一步从网卡驱动层卸载到了网卡,释放了更多的主机CPU资源,实现更高的性能。我们常用的虚拟交换机OVS的团队也在2.12.0版本就开始对AF_XDP进行探索,在2021年SIGCOMM会议上,发表了这些年他们对于数据面的探索,将AF_XDP选型用于其数据面,解决了很多DPDK解决不了的问题。其他的应用场景如负载均衡、流采样和监控……更多的可能正在被学术和工业界探索。结 语作为另一个操作系统巨头,微软从原先“开源公敌”变为拥抱开源、拥抱linux,也在开始拥抱eBPF,ebpf-for-windows是微软的开源项目,用于在Windows 10和Windows Server 2016及以后的版本上运行eBPF,使得开发者可以用熟悉的eBPF工具链和API进行开发。可能不久的将来,Windows也会对eBPF、XDP技术作出更多支持。XDP技术的发展只过了几年,AF_XDP正式合入内核更是不过三年的时间,但它是Linux内核社区长期维护的技术,具有足以媲美DPDK的性能,具备多种独有的优势。待未来更多优化被合入以后,必然是一种网络处理加速的重要技术。paper:The eXpress Data Path: Fast Programmable Packet Processing in the Operating System Kernelrefer:Toke Høiland-Jørgensen, Jesper Dangaard Brouer, Daniel Borkmann, John Fastabend, Tom Herbert, David Ahern, and David Miller. 2018. The eXpress Data Path: Fast Programmable Packet Processing in the Operating System Kernel. In CoNEXT ’18: International Conference on emerging Networking EXperiments and Technologies, December 4–7, 2018, Heraklion, Greece. ACM, New York, NY, USA, 13 pages. https://doi.org/10.1145/3281411.3281443URL:https://arthurchiao.art/blog/xdp-paper-acm-2018-zh/
2023年02月23日
1,060 阅读
0 评论
1 点赞