GitHub万星项目进度70%,eCapture旁观者7K Stars达成 简述 是的,一年半时间,笔者的首个7000星的GitHub开源项目来了,它就是eCapture旁观者。未来一年,争取过万星。 项目背景 eCapture一个无需CA证书,无侵入的HTTPS/TLS明文抓包工具。可以在Linux 4.18以上版本使用,同时也支持Android arm64 5.5以上版本。 eCapture的中文名叫「旁观者」,即「当局者迷,旁观者清」,与其本身功能「旁路、观察」契合,且发音与英文有相似之处。此工具切入大家痛点,不需要CA证书,也不需要进程冲洗,就可以在Linux上分析生产环境的https网络包的问题,深受大家欢迎。 从2022年3月底创建,至今一年半时间,获得7000星。与知乎上较为出名的xmake-io/xmake开源项目相比,可谓神速。当然,个人的努力固然有,但主要是借助了历史的进程——eBPF技术兴起。 依赖技术 eBPF技术是Linux内核的子系统,用来做跟踪、抽样、网络优化、安全管控等。借助于eBPF的动态插桩能力,eCapture可以无需应用进程重启,无需CA证书,动态增加观测点,实现对进程的HTTPS/TLS加密通讯抽样诊断。 eCapture项目是用来做网络分析的,适合线上环境,不方便重启进程的场景。使用eBPF TC/uprobe HOOK技术,支持Linux/Android 系统,支持x86_64/aarch64两种CPU架构,工具大小不到10M。 支持保存为文本日志,以及pcap-NG格式文件,直接用wireshark打开分析。同时,还可以支持关联网络所属的进程名、进程PID等信息,在入侵对抗领域,特别需要后门网络包是哪个进程发出的,从而更好的定位后门进程。 迭代 从2022年3月起,至今发布了34个版本,160多个PR;整理了5篇文章,录制了3个视频。花费了100多瓶可乐,掉了几万根头发...... 时间与精力 大部份功能上,项目的规划、研发、测试、交付、运营对接,都是我一个人。时间上,几乎都是下班后的21点到凌晨1点,以及周末的大部分时间。也就是说,2022年的大部分休息时间都贡献给了eCapture这个开源项目。 一个人做开源,特别花时间、花体力,花精力,完全是用爱发电。 可以对比eCapture跟xmake两个项目的代码编写事件。 可能是因为eBPF技术比较偏内核底层,社区熟悉这部分技术的人不多,可以为项目贡献的人寥寥无几,整体参与度不高。在这里也非常感谢为eCapture做贡献的人。 展现在大家眼前的是一个软件,一个项目,帮你解决了一些问题。所需要代码行数并不多等,实际上,笔者需要花费大量时间、精力,研究学习背后的技术知识。 冰山的背后 比如Pcapng格式的支持,需要了解PcapNG File Format,了解里面Block的含义,如何解析、填充;了解Packet flow in Netfilter and General Networking,才能更好的选择HOOK点。深挖Decryption Secrets Block标准,并提交了google/gopacket PR # 1042,支持DSB格式,使得Wireshark可以直接分析eCapture捕获https加密包的明文内容。 比如Golang uretprobe,需要了解Golang在不同版本下的ABI规范,是基于寄存器,还是栈。符号表内,函数调用的参数传递顺序差异。函数调用栈与参数、返回值的内存布局等。只有了解这些,才能更好的实现函数入参、返回值的捕获,才能实现Golang的TLS类库密钥捕获,实现Golang TLS类库的加密通讯明文捕获。 比如Android系统的支持,需要了解AMD64、ARM64的兼容实现,了解Linux内核在Android上的调整点。探索在Linux上编译Android可运行的eBPF程序路线。翻阅对比Openssl类库封装成BoringSSL的差异,熟悉ELF文件装载、加载过程,定位合适的挂钩函数,确定内存偏移量,才能动态适配多种OpenSSL、BoringSSL类库,支持这些类库的TLS/SSL密钥捕获以及明文捕获。 除了技术实现,在推广运营上,笔者画了很多架构图、原理图,甚至自学视频制作,录制使用方法,学习视频剪辑、录音等等。虽然学习是痛苦的,但笔者的艺术细胞倒是提升不少。 你看到的只是海面上的成功部分,而实际上需要我付出的,却是冰川下大量的研发工作。 尽管如此,开源的路上依旧少不了质疑的人。 质疑 去年在知乎上有一偏讨论eBPF的评论,里面有一些臆测,认为eCapture就是一个木马后门,认为在敲击键盘那一刻,偷偷记录按键行为就可以实现。 还有人认为,作者是中国人,关注的公司也是腾讯、阿里、字节、清华大学这种中国的公司,脑海中确实又蠢又坏的猜忌。 人生的路上,少不了质疑与漫骂。对此我表示哈哈一笑。稍作回答:eBPF技术是正经技术,作者也是正经人。 展望 作为一个网络分析工具, eCapture的基本功能已经具备,在实际的分析场景中,希望能像tcpdump、Charles等工具一样,可以保留相同的参数习惯,以及结合这些工具一起使用。 为此,笔者规划两项功能,以及增强社区运营,推动eCapture进入到部分Linux发行版的软件源上。 Charles Proxy\Burp Suite可读的网络包文件 支持tcpdump的filter表达式 进入Linux发行版的软件源仓库 最初,项目是靠GitHub自己的推荐功能,吸引了很多关注者。之后为了让用户能快速上手,也写了很多文章,画各种原理图、架构图,制作各种使用的演示视频,这也特别花时间、精力。面向用户也是国内跟国外,语言是汉字与英语,所以还做了多语言的版本,对于英语渣的我来说,挑战也极大。 一路走来,感受颇多。项目传播速度,跟我精力的分配息息相关。从去年下半年起,笔者工作上特别忙,投入到这项目的时间逐渐减少。从热情澎湃时的每天晚上24点提交代码,到后来每周周末提交代码,甚至现在一个多月才提交几次代码。精力实在有限。 笔者期望,eCapture能为大家带来更大的帮助,期望大家也能一起参与进来。 一个人走得很快,但一群人走得更远。 当然,也不排除,未来捐献给类似CNCF之类的开源组织。但也要在笔者的这个万星目标之后考虑。万星项目,冲冲冲。 扩展阅读 为了方便新人上手,笔者曾录制过几篇视频,也整理过一些文章介绍,对eCapture不熟悉的同学可以读读。 eCapture旁观者:Android HTTPS明文抓包 eCapture:无需CA证书抓https网络明文通讯(Linux) eCapture支持bogingssl TLS 1.3明文捕获 Linux中基于eBPF的恶意利用与检测机制 Tetragon进程阻断原理 2023-08-28 0 0
eCapture旁观者支持Golang tls/https加密明文捕获 前言 云原生生态中,golang语言开发的项目越来越多,例如Docker和K8s、etcd等。作为SRE、RD,偶尔需要在生产环境抓网络通讯包,用来分析排查故障。很多时候,都是tls/https加密协议,如何在不重启业务保留现场,不改为自定义CA证书的情况下,分析明文通讯内容呢? 适用场景 eCapture 0.5.0版本在2023年3月12日发布,支持了go语言编写的软件的tls/https明文抓包。只需要root权限,即可捕获并保存为pcapng格式,使用wireshark即可打开查看。 使用方法 gotls模块的e参数用来设定golang编译的可执行文件路径,可以通过ecapture gotls -h来查看使用说明。 bin/ecapture gotls -h NAME: gotls - capture golang tls/https text content without CA cert for ELF compile by Golang toolchain USAGE: ecapture gotls [flags] DESCRIPTION: use eBPF uprobe/TC to capture process event data and network data. also support pcap-NG format. ecapture gotls ecapture gotls --elfpath=/home/cfc4n/go_https_client --hex --pid=3423 ecapture gotls --elfpath=/home/cfc4n/go_https_client -l save.log --pid=3423 ecapture gotls -w save_android.pcapng -i wlan0 --port 443 --elfpath=/home/cfc4n/go_https_client OPTIONS: -e, --elfpath="" ELF path to binary built with Go toolchain. -h, --help[=false] help for gotls -i, --ifname="" (TC Classifier) Interface name on which the probe will be attached. --port=443 port number to capture, default:443. -w, --write="" write the raw packets to file as pcapng format. GLOBAL OPTIONS: -d, --debug[=false] enable debug logging --hex[=false] print byte strings as hex encoded strings -l, --log-file="" -l save the packets to file --nosearch[=false] no lib search -p, --pid=0 if pid is 0 then we target all pids -u, --uid=0 if uid is 0 then we target all users 举个例子 比如/path/elf_filepath_compiled_by_go是一个go写的web服务,并且开启了https加密,代码如下: package main import ( "crypto/tls" "fmt" "io" "net/http" "os" ) func main() { b, e := GetHttp("https://github.com") if e == nil { fmt.Printf("response body: %s\n\n", b) } else { fmt.Printf("error :%v", e) } } func GetHttp(url string) (body []byte, err error) { // 开启TLS密钥记录,用于跟eCpature捕获的密钥对比。 f, err := os.OpenFile("/tmp/go_master_secret.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600) if err != nil { panic(err) } defer f.Close() c := &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true, KeyLogWriter: f}, }} resp, e := c.Get(url) if e != nil { return nil, e } defer resp.Body.Close() body, err = io.ReadAll(resp.Body) return body, err } 可以使用如下命令,捕获明文通讯。 不用重启这个服务进程,也不需要做其他任何配置,就跟使用tcpdump一样。 ./ecapture gotls -e=/path/elf_filepath_compiled_by_go -w a.pcapng -i eth0 Wireshark打开网络包 下载地址 eCapture Github仓库:https://github.com/gojue/ecapture/releases/tag/v0.5.0 韩国GitHub镜像:https://ghproxy.com/https://github.com/gojue/ecapture/releases/tag/v0.5.0 以下内容,为功能实现原理,若你只是使用,可跳过。 技术原理 Probe参数获取 Golang的ABI不同于C,自定义了ABI机制。并且在go 1.17之前,使用的是栈方式传递调用参数;1.17以以后使用了寄存器方式传递调用参数。你可以阅读Register-based Go calling convention 了解更多知识。 这里有一张Golang的函数参数、返回值的寄存器传递布局,供参考。更多内容可以在线阅读《Go语言高级编程》中文版 eCapture的参数获取实现,可以阅读kern/go_argument.h Probe参数选择 笔者这里hook的是Golang源码目录下crypto/tls/common.go文件中的writeKeyLog函数。用来捕获tls的master secret的label类别、clientRandom、密钥值等。 Golang函数参数传递 有个需要注意的地方,比如writeKeyLog函数的第一个参数是string类型,第二、三个参数是slice类型。在Golang里,也都是一个结构体,如下代码: // runtime/string.go type stringStruct struct { str unsafe.Pointer len int } // runtime/slice.go type slice struct { array unsafe.Pointer len int cap int } 以string类型为例,在Go 的参数传递时,不光传递字符串的str unsafe.Pointer指针地址,也还会传递len int到寄存器上。所以,在获取参数时,需要注意参数所在位置。 eCapture的实现: lab_ptr = (void *)go_get_argument(ctx, is_register_abi, 2); lab_len_ptr = (void *)go_get_argument(ctx, is_register_abi, 3); cr_ptr = (void *)go_get_argument(ctx, is_register_abi, 4); cr_len_ptr = (void *)go_get_argument(ctx, is_register_abi, 5); secret_ptr = (void *)go_get_argument(ctx, is_register_abi, 7); secret_len_ptr = (void *)go_get_argument(ctx, is_register_abi, 8); bpf_probe_read_kernel(&lab_len, sizeof(lab_len), (void *)&lab_len_ptr); bpf_probe_read_kernel(&cr_len, sizeof(lab_len), (void *)&cr_len_ptr); bpf_probe_read_kernel(&secret_len, sizeof(lab_len), (void *)&secret_len_ptr); Golang uretprobe 在eCapture的文本模式中,需要在加密之前、解密之后拿到明文,对应的两个函数分别是crypto/tls.(*Conn).writeRecordLocked和 crypto/tls.(*Conn).Read。加密之前的获取只需要使用eBPF uprobe HOOK即可实现。而解密之后,则需要uretprobe,但Golang里,uretprobe的实现机制,会破坏他的堆栈,导致Golang程序进程崩溃。 这个问题,在iovisor/bcc社区也有讨论:BCC issue: Go crash with uretprobe #1320,包括火焰图、eBPF的领导者Brendan Gregg,对这个问题也没有太好的办法。Gianluca Borello给了间接的解决方案,相对来说还是比较繁琐的,也有一定的crash风险,有兴趣的同学可以去看看。 eCapture里,加密之前的uprobe 已经完成hook,实现https/tls的请求内容明文捕获。但解密后的内容,暂时无法实现。笔者也在尝试其他思路,比如找到Read返回值的调用函数,在哪里使用uprobe实现,但这点逻辑比较偏业务层,调用者比较多,不太方便过滤。如果你有更好的办法,也欢迎提出来。 扩展阅读 Hooking Go from Rust - Hitchhiker’s Guide to the Go-laxy BCC issue: Go crash with uretprobe #1320 Interface method calls with the Go register ABI eCapture旁观者官网 eCapture旁观者 Github仓库 2023-03-12 4 0
eCapture支持boringssl TLS 1.3明文数据包捕获 前言 一个多月前,有网友反馈Android上,TLS 1.2明文网络包正常捕获,而1.3的密钥无法捕获。笔者得知这个问题后,花了几个周末时间修复、添加了这个功能。在这期间,笔者工作繁忙以及感染新冠,整个功能拖了1个月 才完成。精力与体力,支撑起来越来越吃力。搞开源,全是靠爱发电,实属不易。 值得庆幸的是,收获了5400星的关注,这也是对笔者最大的认可。 这个周末,发布了eCapture 0.4.11版本,支持boringssl的TLS 1.3的密钥捕获,支持明文解密pcapng数据包存储。 有需要的朋友可以下载使用。 其次,这次支持boringssl的TLS 1.3明文数据包捕获,还遇到了一些技术性问题,在这里也一并分享给大家。 boringssl与openssl的差异 Google开源了boringssl这个SSL加密类库,是基于openssl的增强版。使用C++封装C的类库,在结构体内存布局上,与openssl有一些差异。而且TLS通信密钥捕获的Hook函数上,与openssl有差异。在openssl中,只需要HOOK SSL_write、SSL_read 两个函数,读取参数中SSL结构体的内存布局,定位相关SSL密钥存储地址,即可读取密钥。 而在boringssl中,在调用SSL_write函数时,这个TLS链接可能还没建立好。需要寻找更合适的HOOK函数,笔者在实现时,做了大量的测试、调试工作,最终在SSL_do_handshake 跟SSL_in_init两个函数中,选择了SSL_in_init,进行密钥的读取。 实际使用时,仍会有s3_st为空、s3->hs地址为空的情况,以及TLS未建立、正在握手中,密钥不完整等情况,笔者最后选择将所有eBPF捕获内存数据,通过bpf map发送给用户空间程序,再在用户空间判断密钥,进行过滤,曲折实现。 android boringssl与普通boringssl的差异 说到boringssl,刚接触的人可能搞不清楚版本,认为google在github上开源的https://github.com/google/boringssl.git 项目就是,直接用这个测试。实际上,Android系统使用的boringssl并不是这个仓库,而是 https://android.googlesource.com/platform/external/boringssl ,二者的差异,对ecapture来说,其结构体的内存布局不一样,属性的偏移地址不一样。 笔者就被这个问题困扰很久,为什么在PC上,使用boringssl测试成功,而在android手机上却无法工作。手机、模拟器、重刷ROM,都是不行。。一直到笔者把Android上类库源码重新检出,仔细测试boringssl的代码,才发现这个问题。 Google在这块文档上,并没有说明,实在是让人无奈。 Android boringssl偏移地址 你可以按照自己Android使用的boringssl版本,自己重新生成boringssl的偏移量,使用命令如下: rm -f kern/boringssl_1_1_1_kern.c bash utils/boringssl_offset_1.1.1.sh 普通boringssl类库偏移地址 在Linux上,很多类库都是支持改用openssl为boringssl的,eCapture也提供了自助生成新偏移量的脚本,它比Android boringssl命令后面多个参数1,生成命令如下: rm -f kern/boringssl_1_1_1_kern.c bash utils/boringssl_offset_1.1.1.sh 1 偏移地址差异diff Linux上普通boringssl类库的eCapture使用 以curl类库为例,启用boringssl的编译命令如下: git clone https://github.com/curl/curl.git autoreconf -fi LIBS=-lpthread ./configure --with-ssl=/home/cfc4n/project/boringssl --enable-versioned-symbols make sudo make install sudo ldconfig 如果你在编译启用boringssl的curl遇到错误时,可以参考笔者的经历:alt-svc build error on ubuntu 18.04: missing struct tm #9989 eCapture捕获命令: bin/ecapture tls --libssl=/usr/local/lib/libcurl.so.4 --ssl_version="boringssl 1.1.1" -w a.pcapng -i ens33 功能演示 演示环境 系统:macOS 模拟器:Android Studio Dolphin | 2021.3.1 Patch 1 镜像ROM:Pixel 3a API 33 x86_64 内核:Linux localhost 5.10.66-android12-9-00041-gfa9c9074531e-ab7914766 #1 SMP PREEMPT Fri Nov 12 11:36:25 UTC 2021 x86_64 测试APP:百度手机助手9.5.8.1 演示命令 启动eCapture emulator64_x86_64_arm64:/data/local/tmp # ./ecapture tls -w aa.pcapng -i wlan0 tls_2023/01/08 14:02:54 ECAPTURE :: ecapture Version : androidgki_x86_64:0.4.11-20230107-59a76e0:5.4.0-131-generic tls_2023/01/08 14:02:54 ECAPTURE :: Pid Info : 26496 tls_2023/01/08 14:02:54 ECAPTURE :: Kernel Info : 5.10.66 tls_2023/01/08 14:02:54 EBPFProbeOPENSSL module initialization tls_2023/01/08 14:02:54 EBPFProbeOPENSSL Module.Run() tls_2023/01/08 14:02:54 EBPFProbeOPENSSL TC MODEL tls_2023/01/08 14:02:54 EBPFProbeOPENSSL OpenSSL/BoringSSL version not found, used default version :android_default tls_2023/01/08 14:02:54 EBPFProbeOPENSSL HOOK type:2, binrayPath:/apex/com.android.conscrypt/lib64/libssl.so tls_2023/01/08 14:02:54 EBPFProbeOPENSSL Ifname:wlan0, Ifindex:16, Port:443, Pcapng filepath:/data/local/tmp/aa.pcapng tls_2023/01/08 14:02:54 EBPFProbeOPENSSL Hook masterKey function:SSL_in_init tls_2023/01/08 14:02:54 EBPFProbeOPENSSL target all process. tls_2023/01/08 14:02:54 EBPFProbeOPENSSL target all users. tls_2023/01/08 14:02:54 EBPFProbeOPENSSL BPF bytecode filename:user/bytecode/boringssl_1_1_1_kern.o tls_2023/01/08 14:02:54 EBPFProbeOPENSSL module started successfully. tls_2023/01/08 14:02:54 ECAPTURE :: start 1 modules 794b86e16f4f17d1c90fee21e72d56aa92c1f4deb5f7494c0eab9598895d48ff to file success, 176 bytes tls_2023/01/08 14:05:06 TLS1_3_VERSION: save CLIENT_RANDOM 7fbea9bdcc818fb80cc04c1d0ad0c0c870cf97267678d42fa6026dd92cfa6b20 to file success, 778 bytes tls_2023/01/08 14:05:43 TLS1_2_VERSION: save CLIENT_RANDOM 50a103ecbdd2a84c707eeda3262381ab8a427993c2407ecd537ae2414978f821 to file success, 176 bytes 捕获的TLS 1.3密钥 130|emulator64_x86_64_arm64:/data/local/tmp # cat ecapture_masterkey.log CLIENT_HANDSHAKE_TRAFFIC_SECRET 7fbea9bdcc818fb80cc04c1d0ad0c0c870cf97267678d42fa6026dd92cfa6b20 e34deeab543a3cd63e8009737a4cf4188e9f5c2b6588e5b72874055818004c6c CLIENT_TRAFFIC_SECRET_0 7fbea9bdcc818fb80cc04c1d0ad0c0c870cf97267678d42fa6026dd92cfa6b20 82857c8eb07c88b6c56c9d38759c23cc6d14d9dd23a37bb17fa41e5913cf211e SERVER_HANDSHAKE_TRAFFIC_SECRET 7fbea9bdcc818fb80cc04c1d0ad0c0c870cf97267678d42fa6026dd92cfa6b20 bef4cde1643c47d5971f226e9d637155d55087184f0f75963db792d71afc56c0 SERVER_TRAFFIC_SECRET_0 7fbea9bdcc818fb80cc04c1d0ad0c0c870cf97267678d42fa6026dd92cfa6b20 c949827f3150a3c4c4589478caa1b2987493dd17dfe008aa26ce9b881d9af21b EXPORTER_SECRET 7fbea9bdcc818fb80cc04c1d0ad0c0c870cf97267678d42fa6026dd92cfa6b20 0b0017b44c8ac50126a7aeb03494d1454f7f4ec560fbaba144198eb74ddab280 CLIENT_RANDOM 50a103ecbdd2a84c707eeda3262381ab8a427993c2407ecd537ae2414978f821 e1a6d3edd5f4e817aea86b2d15d3d21a23c8d2d60770c36819303e25873f30089d555d447ab29b8208b8c0ecd230405a 演示截图: eCapture链接地址 eCapture v0.4.11 下载地址 eCapture 官网 eCapture 代码仓库 未来展望: eCapture功能趋于稳定,以及笔者经历有限,未来更新频率可能会降低,欢迎广大网友参与其中,一起为开源做贡献,一起为你点赞。 2023-01-08 0 0
eCapture旁观者:Android HTTPS明文抓包,无需CA证书 前言 Android上抓包HTTPS是不是越来越难了?高版本无法添加CA证书,抓包软件依赖太多,VPN模式、或HOOK程序时,会被APP检测到。对抗成本愈加增高。有什么万能的工具吗? 是的,eCapture for Android来了。以后在Android上抓HTTPS通讯包,再也不用安装CA证书了,再也不用下载一堆python依赖环境了,再也不用重打包ssl类库了,再也不用改一堆手机参数了,一键启用,简单明了。 eCapture简介 eCapture是一款无需CA证书即可抓获HTTPS明文的软件。支持pcapng格式,支持Wireshark直接查看。基于eBPF技术,仅需root权限,即可一键抓包。 eCapture中文名旁观者,即 当局者迷,旁观者清。 2022年年初上海疫情期间,笔者开始编写并开源,至今已经半年,GitHub上已经 4200个星星。 eCapture是基于eBPF技术实现的抓包软件,依赖系统内核是否支持eBPF。目前支持在操作系统上,支持了X86_64\ARM64的Linux kernel 4.18以上内核,支持了ARM64 Android(Linux) kernel 5.4以上版本。最新版是在2022年9月9日发布的v0.4.3版本。 演示视频 下载后,一条命令启动,干净利索。 ./ecapture tls -w ecapture.pcapng 先看演示视频,演示环境为Ubuntu 21.04、Android 5.4 (Pixel 6)。 模块功能 eCapture支持tls、bash、mysqld、postgres等模块的信息提取与捕获。本文仅讨论tls这个HTTPS/TLS明文捕获模块。 加密通讯明文捕获--tls模块 tls模块在加密通讯类库上,支持了openssl、gnutls、nspr/nss、boringssl等类库。 但在Android上,pcapng模式只支持boringssl,文本模式则都支持。 如何使用eCapture 环境依赖 操作系统 Linux kernel 4.18以上,Android kernel 5.4以上。 支持BPF,可选支持BTF(eCapture版本不同) root权限 版本选择 BPF CO-RE特性为BTF通用的格式,用于做跨内核版本兼容。有的Android手机是没开启BTF。可以查看系统配置确认,CONFIG_DEBUG_INFO_BTF=y为开启;CONFIG_DEBUG_INFO_BTF=n为关闭;其他为不支持BPF,无法使用eCapture。 cfc4n@vm-server:~$# cat /boot/config-`uname -r` | grep CONFIG_DEBUG_INFO_BTF CONFIG_DEBUG_INFO_BTF=y Android系统上,config是gzip压缩的,且配置文件目录也变了。可使用zcat /proc/config.gz命令代替。 eCapture默认发行了支持CO-RE的ELF程序。Android版会发行一个5.4内核不支持BTF(即没有CO-RE)的版本。 下载后,可以通过./ecapture -v确认。 非CO-RE版本的version信息中包含编译时的内核版本。 # no CO-RE eCapture version: linux_aarch64:0.4.2-20220906-fb34467:5.4.0-104-generic # CO-RE eCapture version: linux_aarch64:0.4.2-20220906-fb34467:[CORE] 若版本不符合自己需求,可以自行编译,步骤见文末。 全局参数介绍 全局参数重点看如下几个 root@vm-server:/home/cfc4n/# ecapture -h --hex[=false] print byte strings as hex encoded strings -l, --log-file="" -l save the packets to file -p, --pid=0 if pid is 0 then we target all pids -u, --uid=0 if uid is 0 then we target all users --hex 用于stdout输出场景,展示结果的十六进制,用于查看非ASCII字符,在内容加密、编码的场景特别有必要。 -l, --log-file= 保存结果的文件路径。 -p, --pid=0 捕获的目标进程,默认为0,则捕获所有进程。 -u, --uid=0 捕获的目标用户,默认为0,则捕获所有用户,对Android来说,是很需要的参数。 模块参数 root@vm-server:/home/cfc4n/project/ssldump# bin/ecapture tls -h OPTIONS: --curl="" curl or wget file path, use to dectet openssl.so path, default:/usr/bin/curl --firefox="" firefox file path, default: /usr/lib/firefox/firefox. --gnutls="" libgnutls.so file path, will automatically find it from curl default. --gobin="" path to binary built with Go toolchain. -h, --help[=false] help for tls -i, --ifname="" (TC Classifier) Interface name on which the probe will be attached. --libssl="" libssl.so file path, will automatically find it from curl default. --nspr="" libnspr44.so file path, will automatically find it from curl default. --port=443 port number to capture, default:443. --pthread="" libpthread.so file path, use to hook connect to capture socket FD.will automatically find it from curl. --wget="" wget file path, default: /usr/bin/wget. -w, --write="" write the raw packets to file as pcapng format. -i参数 -i参数为网卡的名字,Linux上默认为eth0,Android上默认为wlan0,你可以用这个参数自行指定。 输出模式 输出格式支持两种格式,文本跟pcapng文件。有三个参数, 默认,全局参数,输出文本结果到stdout -l 全局参数,保存文本结果的文件路径 -w 仅tls模块参数,保存pcapng结果的文件路径 类库路径 Linux上支持多种类库,不同类库的路径也不一样。 类库 参数路径 默认值 openssl/boringssl --libssl Linux自动查找,Android为/apex/com.android.conscrypt/lib64/libssl.so gnutls --gnutls Linux自动查找,Android pcapng模式暂未支持 nspr/nss --nspr Linux自动查找,Android pcapng模式暂未支持 文本模式 -l 或者不加 -w 参数将启用该模式。 支持openssl、boringssl、gnutls、nspr/nss等多种TLS加密类库。 支持DTLS、TLS1.0至TLS1.3等所有版本的加密协议。 支持-p、-u等所有全局过滤参数。 pcapng模式 -w 参数启用该模式,并用-i选择网卡名,Linux系统默认为eth0,Android系统默认为wlan0, 仅支持openssl、boringssl两个类库的数据捕获。暂不支持TLS 1.3协议。 类库与参数支持 在Linux系统上,大部分类库与参数都是可以支持的。但在Android系统上,因为内核与ARM架构的原因,支持的参数上,有一定的差异。 不同模式的参数支持 -p、-u两个全局参数,支持文本模式,不支持pcapng模式。这是因为pcapng模式是使用eBPF TC技术实现。 模式 -p -u --libsso --port 文本 ✅ ✅ ✅ ✅ pcapng ❌ ❌ ✅ ✅ 不同模式的类库以协议支持 模式 openssl(类库) boringssl(类库) TLS 1.0/1.1/1.2(协议) TLS 1.3(协议) 文本 ✅ ✅ ✅ ✅ pcapng ✅ ✅ ✅ ❌ pcapng模式暂时不支持TLS 1.3,TLS 1.3密钥捕获功能已经开发完成,只是遇到一些BUG,还在解决中。 笔者不是openssl的专家,对TLS 协议也不太熟。需要补充这两块的知识,解决起来成本比较高,也欢迎对这块擅长的朋友一起来解决。 与tcpdump联合使用 eCapture基于eBPF TC,实现了流量捕获,并保存到pcapng文件中。基于eBPF Uprobe实现了TLS Master Secret的捕获。并基于Wireshark的Decryption Secrets Block (DSB)标准,实现了gopacket的DSB功能,合并网络包与密钥,保存到pcapng中。 eCapture在网络包捕获上,没有tcpdump强大,不支持丰富的参数。你可以用eCapture捕获master secrets,用tcpdump捕获网络包,然后使用wiresahrk自定义设置密钥文件,配合使用。 网络包捕获 tcpdump 的常规用法,不再赘述。 密钥捕获 同时启用ecapture ,模式可以选文本或者pcapng,都会保存TLS的master secrets密钥数据到ecapture_masterkey.log中。 网络包查看 用Wireshark打开网络包文件,设置这个master key文件,之后就可以看到TLS解密后的明文了。 配置路径:Wireshark --> Preferences --> Protocols --> TLS --> (Pre)-Master-Secret log filename 参数 指定路径 默认路径 在Android上,Google使用了boring ssl类库,也就是C++语言在libssl基础上的包装。默认情况下,会使用/apex/com.android.conscrypt/lib64/libssl.so路径。 APP的类库确认 你可以使用lsof -p {APP PID}|grep libssl来确认。 若不是默认路径,则可以使用--libssl参数来指定。 高级用法 如果你需要查看的APP是自定义SSL类库,那么你可以自助修改eCapture来实现。 自定义函数名与offset 首先,需要确定HOOK函数的函数名或者符号表地址。 没有源码 如果你没有类库源码可以通过IDA等软件静态分析、动态调试,确定SSL Write的地址offset。在配置填写在user/module/probe_openssl.go文件中,对应的probe配置部分。 { Section: "uprobe/SSL_write", EbpfFuncName: "probe_entry_SSL_write", AttachToFuncName: "SSL_write", UprobeOffset: 0xFFFF00, // TODO BinaryPath: binaryPath, }, offset自动计算 如果你有源码,则可以自行阅读源码确定函数名或者符号表的地址。对于结构体的成员属性读取,则可以通过offsetof宏来自动计算。 // g++ -I include/ -I src/ ./src/offset.c -o off #include <stdio.h> #include <stddef.h> #include <ssl/internal.h> #include <openssl/base.h> #include <openssl/crypto.h> #define SSL_STRUCT_OFFSETS \ X(ssl_st, session) \ X(ssl_st, s3) \ X(ssl_session_st, secret) \ X(ssl_session_st, secret_length) \ X(bssl::SSL3_STATE, client_random) \ X(bssl::SSL_HANDSHAKE, new_session) \ X(bssl::SSL_HANDSHAKE, early_session) \ X(bssl::SSL3_STATE, hs) \ X(bssl::SSL3_STATE, established_session) \ X(bssl::SSL_HANDSHAKE, expected_client_finished_) struct offset_test { /* data */ int t1; bssl::UniquePtr<SSL_SESSION> session; }; int main() { printf("typedef struct ssl_offsets { // DEF \n"); #define X(struct_name, field_name) \ printf(" int " #struct_name "_" #field_name "; // DEF\n"); SSL_STRUCT_OFFSETS #undef X printf("} ssl_offsets; // DEF\n\n"); printf("/* %s */\nssl_offsets openssl_offset_%d = { \n", OPENSSL_VERSION_TEXT, OPENSSL_VERSION_NUMBER); #define X(struct_name, field_name) \ printf(" ." #struct_name "_" #field_name " = %ld,\n", \ offsetof(struct struct_name, field_name)); SSL_STRUCT_OFFSETS #undef X printf("};\n"); return 0; } 参数提取 对于参数,你需要确认被HOOK函数的参数类型,以便确认读取方式,可以参考kern/openssl_kern.c内的SSL_write函数实现。 编译 ARM Linux 编译 公有云厂商大部分都提供了ARM64 CPU服务器,笔者选择了腾讯云的。在广州六区中,名字叫标准型SR1(SR1即ARM 64CPU),最低配的SR1.MEDIUM2 2核2G即满足编译环境。可以按照按量计费方式购买,随时释放,比较划算。 操作系统选择ubuntu 20.04 arm64。 ubuntu@VM-0-5-ubuntu:~$sudo apt-get update ubuntu@VM-0-5-ubuntu:~$sudo apt-get install --yes wget git build-essential pkgconf libelf-dev llvm-12 clang-12 linux-tools-generic linux-tools-common ubuntu@VM-0-5-ubuntu:~$wget https://golang.google.cn/dl/go1.18.linux-arm64.tar.gz ubuntu@VM-0-5-ubuntu:~$sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.18.linux-arm64.tar.gz ubuntu@VM-0-5-ubuntu:~$for tool in "clang" "llc" "llvm-strip" do sudo rm -f /usr/bin/$tool sudo ln -s /usr/bin/$tool-12 /usr/bin/$tool done ubuntu@VM-0-5-ubuntu:~$export GOPROXY=https://goproxy.cn ubuntu@VM-0-5-ubuntu:~$export PATH=$PATH:/usr/local/go/bin 编译方法 ANDROID=1 make 命令编译支持core版本的二进制程序。 ANDROID=1 make nocore命令编译仅支持当前内核版本的二进制程序。 当Wireshark被eBPF增强 Wireshark + eBPF = eCapture 贡献者 感谢chriskaliX 、chenhengqi、vincentmli、huzai9527、yihong0618、sfx(4ft35t)等朋友的贡献。 感谢tiann Weishu在Android这个需求上的推动。 招聘 Leader直招,没有中间商赚差价。面向RASP、HIDS产品,JVM、Linux内核入侵检测等安全研发职位 JAVA资深研发工程师 golang高级工程师 更多职位见 https://www.cnxct.com/jobs/ 2022-09-13 4 0
eCapture的几个好消息,支持Android… 缘起 这个故事要从上海的疫情说起。 三月份,上海爆发了疫情。全市开始封城,停工停课,所有人都不得不居家防疫。离不了家门,意味着空闲时间增多了,也正好可以利用一下,做个小项目之类的。 也正巧,前段时间研究了eBPF攻防的技术,而技术的应用场景最广泛的就是跟踪调试了。广大研发、运维同学们面临的最大场景就是网络抓包,恰恰大量数据包都是HTTPS加密的,这就称为多数人的痛点,那么结合eBPF技术,可以很好的解决这一痛点。 说干就干,三月中旬就发了初版eCapture:无需CA证书抓https网络明文通讯,随着上海封城的持续,空闲时间也多,项目也逐步完善,支持4.X至5.X的内核版本。 最近呢,复工复产了,空闲时间就少了,不过,近期搞到凌晨一两点的情况也是常态。不能半途而废不是嘛。 进展 这几天,有几个好消息分享给大家,有的是付出得到的回报,有的是项目的新功能。 ebpf.io 收录 eCapture项目被 ebpf.io 官网收录了 ,在「Emerging新兴项目」中,暂排第一。是按照Star数量来的,项目复杂度、技术深度不如其他项目,但实用性比较大,深受大家喜欢。 eCapture for Android eCapture也支持了Android的GKI发型版本,你在eCapture v0.1.10 release (Linux x86_64/aarch64, Android GKI)中下载试用。 也就是说,Android 11,Android 12的版本都支持。 4.X内核的Android支持,还会远吗? 有位同学告诉我说,这会让反爬的同学受到很大挑战,eBPF的抓包,APP层很难发现,APP自身没有特征,也很难防御。 我不这么看,我觉得风控对抗上,核心点不该只在端上对抗,而应该在数据分析、好人坏人识别等方向上。地理位置、网络环境、设备型号等问题可以更加综合的考虑。 另外,可以再思考一个问题,这意味着什么? 意味着Linux上的eBPF功能都可以在Android上使用,各种网络管理、监控软件、安全保护等,甚至后门。你觉得,还有什么场景呢? 演示视频 付上一个eCapture for Android的演示视频,是TaiChi Xposed框架作者 weishu 大佬帮测试的,我没有Android手机,对Android生态也不了解,感谢weishu在这个事情上的支持。 eCapture中文名旁观者 eCapture有了中文名,叫「旁观者」,即「当局者迷,旁观者清」,与其本身功能【旁路、观察】契合,且发音与英文有相似之处。 是不是有点信雅达呢? 要不,您给起个名? 加入贡献 现在,还缺少一个官方网站,或者融合中文含义的新LOGO,如有对eCapture开源项目感兴趣的设计师、UI前端工程师,愿意帮助项目设计新页面、新LOGO的同学,可以在微信公众号内留言,我会私聊你,谢谢。 红领巾 又为社会做了一点点贡献,胸前的红领巾更加鲜艳了。 2022-06-22 1 0
新型eBPF后门boopkit的原理分析与演示 前言 在《聊一聊《Bvp47 美国NSA方程式的顶级后门》中的BPF隐藏信道》一文里,我们讨论了二十年前基于BPF技术后门的功能实现,技术原理等。也展望也在二十年后的今天,eBPF技术加持下,有更多可以被后门利用的技术点,可以造成更大的破坏力。 同时,在《Linux中基于eBPF的恶意利用与检测机制》介绍了eBPF的恶意利用,与防御相关的检测防御机制等。笔者对当下国内IDC对eBPF防御建设的不完善感到忧心,综合国外DefCon等安全峰会中的话题内容,预判未来一段时间,基于eBPF的后门会越来越多,越来越隐秘。 就在一个多月前,笔者看到了一款基于eBPF技术后门Boopkit,上传到https://github.com/kris-nova/boopkit,今天,就跟大家一起来了解一下它的技术原理。 boopkit介绍 boopkit是基于eBPF技术实现的Linux后门rootkit,利用任意开启的TCP端口服务,远程唤醒后门,生成反弹shell,具备远程命令执行等能力。 此项目是在2022年3月30日首次创建,至今1个多月,已经有近1000星,在Twitter上也有一定热度,持续传播中。 用户空间的加载器与内核态代码都是纯C编写。 部分脚本使用Makefile、shell等脚本语言。 作者 Kris Nóva 是一位女性安全工程师,经常在twitch上直播写代码,有兴趣的同学可以去围观。 功能 GitHub上Boopkit介绍: Remote code execution over TCP (SSH, Nginx, Kubernetes, etc) Network gateway bypass (bad checksums, TCP reset) Self obfuscation at runtime (eBPF process hiding) 很容易看出,这是后门工具,利用当前服务器开启的任意TCP端口作为通讯隧道,比如SSH、Nginx、Kubernetes等常用服务。利用eBPF技术,在HOOK tracepoint的tcp_bad_csum、tcp_receive_reset这两个TCP内核函数,作为敲门、传递信息的机制,进行通讯。并具备进程的自我隐藏等能力。 相比之前《聊一聊BVP47》的SYN窍门,有异曲同工之妙。但多了传送RCE shell等更多利用,当然,也是在高版本eBPF实现的功能。 安装测试 环境依赖 编译依赖 项目说明中,依赖如下: clang bpftool Required for libbpf xdp-tools Required for libxdp llvm pcap lib32-glibc 笔者环境是ubuntu 22.04的Desktop,内核版本是 5.15的。理论上其他低点的版本有可以运行。 笔者的clang、llvm都是12,bpftool在项目编译时不需要,但需要安装libbpf-dev这个开发包,依赖头文件。在ubuntu 22.04上,libbpf-dev是0.5.0的版本,版本太低,可以下载libbpf-0.6.1版本,安装使用。 xdp-tools、pcap也都是需要开发的头文件,记得安装devel版本的类库。 lib32-glibc包是ArchLinux发行版上的包名字,在其他Linux发行版上一般叫glibc,对应开发库libc6-dev-i386等。 内核依赖 内核版本上项目没说明,理论5.x都行,4.x请自测。但配置上,需要启用tcp_bad_csum、tcp_receive_reset两个函数的tracepoint。可以检查/sys/kernel/debug/tracing/events/tcp目录下是否有这两个目录。 运行依赖 客户端在启动后,需要监听端口,等待后门的反弹shell链接过来。使用的是ncat程序,需要提前安装好。ubuntu上命令sudo apt install ncat。 命令参数 Boopkit后门分为两部分: 服务端:程序名boopkit,运行在被入侵的服务器上。 客户端:程序名boopkit-boop,黑客使用的控制端。 服务端 程序名是boopkit,编译好后,会把ebpf的字节码文件写入到/root/.boopkit/目录下,有三个文件: pr0be.boop.o pr0be.safe.o pr0be.xdp.o pr0be.boop.o用于TCP的网络HOOK,实现敲门以及网络RCE接收。 pr0be.safe.o用于后门进程的自我保护,启动后,在进程列表中隐藏自己。 pr0be.xdp.o截止至2022-05-06,还是空的,没有逻辑。从作者直播中提到,应该是打算用于替换掉libpcap方式的数据包拦截、解析,以及提取RCE,用于更好地隐藏自己。 该程序命令帮助如下: Boopkit. Linux rootkit and backdoor. Built using eBPF. Usage: boopkit [options] Options: -h, help Display help and usage for boopkit. -i, interface Interface name. lo, eth0, wlan0, etc -s, sudo-bypass Bypass sudo check. Breaks PID obfuscation. -r, reverse-conn Attempt a reverse RCE lookup if no payload found. -q, quiet Disable output. -x, reject Source addresses to reject triggers from. 参数说明 interface -i参数将用于libpcap的网络嗅探的目标网卡,用于提取RCE。若走eBPF XDP方式,则不需要指定网卡,都可以拿到,那么后门的适应性更强。只是作者还没实现。 sudo-bypass -s参数用于躲避sudo用户检测。 reverse-conn -r参数是配置参数,当前后门是否启用反弹shell模式。反弹shell模式交互好,但隐蔽性不好。该后门也提供了秘密执行的功能,下文会提到。 quiet -q参数是静默模式,不输出日志。 reject -x参数算是后门的扩展功能,用于黑名单IP的筛选,意义不大。 前面介绍了后门的参数功能,下面讲下后门对RCE执行的两种方式,以及对应代码实现。 直接执行 使用libpcap库读取网络通讯包,筛选出黑客的IP,读取TCP的payload信息,匹配特征字符串BOOPKIT_RCE_DELIMITER,读取RCE信息。再调用系统system函数执行。 #define BOOPKIT_RCE_DELIMITER "X*x.x*X" #define BOOPKIT_RCE_CMD_HALT "X*x.HALT.x*X" //boopkit.c xcap_found = xcaprce(saddrval, rce); if (xcap_found == 1) { exec(rce); bpf_map_delete_elem(fd, &jkey); ikey = jkey; continue; } // dpi.c // Search for (int i = 0; i < XCAP_BUFFER_SIZE; i++) { struct xcap_ip_packet *xpack; xpack = snap[i]; if (!xpack->captured) { continue; } char *xpack_saddr = inet_ntoa(xpack->iph->ip_src); char *ret = strstr(search, xpack_saddr); if (!ret) { continue; // Filter packets not from our IP address } // Begin DPI unsigned char *packet = xpack->packet; char *rce_sub; rce_sub = memmem(packet, xpack->header->caplen, BOOPKIT_RCE_DELIMITER, strlen(BOOPKIT_RCE_DELIMITER)); if (rce_sub != NULL) { boopprintf(" -> Found RCE xCap!\n"); int found; found = rce_filter(rce_sub, rce); // Flush the snapshot xcap_ring_buffer_free(snap); // Flush the main ring buffer xcap_ring_buffer_free(xcap_ring_buffer); xcap_ring_buffer_init(xcap_ring_buffer); if (found) { return 1; } else { boopprintf(" XX [FILTER FAILURE] No RCE in xCap!\n"); return 0; } } } 也可以通过RCE中包含特定字符串,来关停后门。 反弹shell 若后门启用反弹shell模式,则可以让这台boopkit的机器主动向黑客监听端口发起TCP链接。隐蔽性不如上面一种模式,但交互比较好,可以只需不停的读取黑客命令,并调用系统system函数执行。 retval = recvrce(saddrval, rce); if (retval == 0) { exec(rce); bpf_map_delete_elem(fd, &jkey); ikey = jkey; continue; } eBPF HOOK 后门一共hook了两类eBPF功能点,TCP与进程列表等几个运行时函数。 eBPF TCP HOOK pr0be.boop.c 内容比较简单,实现了tcp_bad_csum与tcp_receive_reset两个函数HOOK,功能逻辑也比较简单,就是读取TCP包的来源IP,并写入eBPF map,用于存放黑客认证IP。IP将在libpcap网络包读取部分使用。 SEC("tracepoint/tcp/tcp_bad_csum") SEC("tracepoint/tcp/tcp_receive_reset") pr0be.safe.c 代码稍微多了一点点,业务功能是实现了当前后门进程的PID隐藏,HOOK了多个函数点: SEC("tp/syscalls/sys_enter_getdents64") SEC("tp/syscalls/sys_exit_getdents64") SEC("tp/syscalls/sys_exit_getdents64") HOOK点之间,使用多张eBPF Map存储数据,一部分用于内核空间跟用户空间通讯,另外一部分用于多个HOOK点之间通讯。使用bpf_tail_call尾调用实现隐藏逻辑控制。 pr0be.xdp.c 该文件目前是空,前面也提到了,这个应该是作者用于替换libpcap实现网络数据读取的功能,等待实现。 客户端 程序名是boopkit-boop,功能是构造畸形或带RCE payload的TCP数据包,发送到服务端,触发服务端内核的tcp_bad_csum、tcp_receive_reset函数,对Boopkit后门敲门。 后门程序封装了一个SHELL脚本,启动控制boopkit-boop客户端端更方便。默认启用反弹shell模式,并使用ncat程序监听3545端口,等待被入侵服务器主动连接过来。 boopkit运行原理 Boop敲门Vectors Boopkit(服务端)会响应各种网络事件,boopkit-boop(客户端)工具内置了两种敲门机制。 其利用的TCP帧原理如下图: 无效的Checksum boopkit-boop客户端工具将通过SOCK_RAW Socket 向boopkit服务器发送一个带有空校验和的畸形 TCP SYN 数据包。不管这台服务器正在运行什么TCP服务,都会成功敲门,触发eBPF的TCP HOOK。在Linux服务器通吃。 ⚠️注意 个别硬件网卡将丢弃所有格式错误的校验和数据包,该工具失效! 发送ACK-RST包 boopkit-boop客户端工具将使用 SOCK_STREAM Socket针对目标的TCP服务(如SSH、Kubernetes、Nginx等)先正常完成TCP握手后,会关闭该TCP链接,以确保这个服务是正常启动的。 之后,再重复该过程,将重置数据包中的TCP RESET标志位,触发服务器上eBPF TCP HOOK。 视频演示 检测机制 行为 网络行为 网络抓包可以抓到敲门的包,但如果用XDP实现,是可以做到读取后,直接XDP_DROP掉,抓包就抓不到了。隐秘性更可怕。 从图中,可以看到,Checksum的值是0,且后面的TCP Segment已经被改成不合法的,无法解析的包。RCE也被放置在payload中。(两头加了BOOPKIT_RCE_DELIMITER标记) 端口监听 后门不监听端口,隐藏进程,普通工具查不出来。 进程 RCE执行时,会有记录,但进程的父ID查不出来。 top命令却能直接看到,也能直接kill掉,隐藏的不够好。 基于eBPF的特征 事中 GitHub上,笔者开源了一个基于eBPF实现的HIDS入侵检测系统ehids-agent DEMO,具备基本的BPF Call调用检测,可以来测试一下能否发现。 为了减少干扰信息,笔者这里只开启了eHIDS的bpf call monitor模块。 cfc4n@vm-desktop:~/project/ehids-agent$ sudo ./bin/ehids-agent [sudo] password for cfc4n: 2022/05/07 00:21:53 https://github.com/ehids/ehids-agent 2022/05/07 00:21:53 process pid: 69409 2022/05/07 00:21:53 start to run EBPFProbeBPFCall module 2022/05/07 00:21:59 probeName:EBPFProbeBPFCall, probeTpye:tracepoint, BPFCALL EVENT CPU:1, Cmd:BPF_PROG_LOAD, PID:69416, UID:0, Comm:boopkit, cmdline:./boopkit -i ens33, utsName:vm-desktop 2022/05/07 00:21:59 probeName:EBPFProbeBPFCall, probeTpye:tracepoint, BPFCALL EVENT CPU:0, Cmd:BPF_BTF_LOAD, PID:69416, UID:0, Comm:boopkit, cmdline:./boopkit -i ens33, utsName:vm-desktop 2022/05/07 00:21:59 probeName:EBPFProbeBPFCall, probeTpye:tracepoint, BPFCALL EVENT CPU:0, Cmd:BPF_BTF_LOAD, PID:69416, UID:0, Comm:boopkit, cmdline:./boopkit -i ens33, utsName:vm-desktop 2022/05/07 00:21:59 probeName:EBPFProbeBPFCall, probeTpye:tracepoint, BPFCALL EVENT CPU:0, Cmd:BPF_BTF_LOAD, PID:69416, UID:0, Comm:boopkit, cmdline:./boopkit -i ens33, utsName:vm-desktop 2022/05/07 00:21:59 probeName:EBPFProbeBPFCall, probeTpye:tracepoint, BPFCALL EVENT CPU:0, Cmd:BPF_BTF_LOAD, PID:69416, UID:0, Comm:boopkit, cmdline:./boopkit -i ens33, utsName:vm-desktop 2022/05/07 00:21:59 probeName:EBPFProbeBPFCall, probeTpye:tracepoint, BPFCALL EVENT CPU:0, Cmd:BPF_BTF_LOAD, PID:69416, UID:0, Comm:boopkit, cmdline:./boopkit -i ens33, utsName:vm-desktop 2022/05/07 00:21:59 probeName:EBPFProbeBPFCall, probeTpye:tracepoint, BPFCALL EVENT CPU:0, Cmd:BPF_BTF_LOAD, PID:69416, UID:0, Comm:boopkit, cmdline:./boopkit -i ens33, utsName:vm-desktop 2022/05/07 00:21:59 probeName:EBPFProbeBPFCall, probeTpye:tracepoint, BPFCALL EVENT CPU:0, Cmd:BPF_BTF_LOAD, PID:69416, UID:0, Comm:boopkit, cmdline:./boopkit -i ens33, utsName:vm-desktop 可以看到,能精准地监控到调用的BPF指令,进程ID,进程名,命令行参数等。 事后 该后门,在进程PID隐藏上做了实现,但在bpf的行为特征上,目前还没有实现。可以用bpftool等常规工具来检查它。 prog root@vm-desktop:~# bpftool prog show 216: tracepoint name handle_getdents tag d5fabe9da09eec5e gpl loaded_at 2022-05-07T00:25:16+0800 uid 0 xlated 144B jited 93B memlock 4096B map_ids 135,129 btf_id 180 218: tracepoint name handle_getdents tag d197d702eb020291 gpl loaded_at 2022-05-07T00:25:16+0800 uid 0 xlated 1216B jited 841B memlock 4096B map_ids 129,130,135,132,131 btf_id 180 219: tracepoint name handle_getdents tag e938383ee7761b31 gpl loaded_at 2022-05-07T00:25:16+0800 uid 0 xlated 544B jited 310B memlock 4096B map_ids 132,133,135 btf_id 180 222: tracepoint name tcp_bad_csum tag 82cc666e25685b71 gpl loaded_at 2022-05-07T00:25:16+0800 uid 0 xlated 744B jited 404B memlock 4096B map_ids 138 btf_id 181 223: tracepoint name tcp_receive_res tag c25930e6076718fc gpl loaded_at 2022-05-07T00:25:16+0800 uid 0 xlated 416B jited 227B memlock 4096B map_ids 138 btf_id 181 map 如上,一样的方法。 root@vm-desktop:~# bpftool map show 129: hash name map_buffs flags 0x0 key 8B value 8B max_entries 8192 memlock 131072B btf_id 180 130: hash name map_bytes_read flags 0x0 key 8B value 4B max_entries 8192 memlock 131072B btf_id 180 131: prog_array name map_prog_array flags 0x0 key 4B value 4B max_entries 5 memlock 4096B owner_prog_type tracepoint owner jited btf_id 180 132: hash name map_to_patch flags 0x0 key 8B value 8B max_entries 8192 memlock 131072B btf_id 180 133: ringbuf name rb flags 0x0 key 0B value 0B max_entries 8192 memlock 0B 135: array name pr0be_sa.rodata flags 0x480 key 4B value 24B max_entries 1 memlock 4096B btf_id 180 frozen 136: array name pr0be_sa.bss flags 0x400 key 4B value 20B max_entries 1 memlock 4096B btf_id 180 138: hash name event flags 0x0 key 4B value 32B max_entries 8192 memlock 327680B btf_id 181 139: array name pr0be.bss flags 0x400 key 4B value 20B max_entries 1 memlock 4096B btf_id 181 检测防御 可参考 《Linux中基于eBPF的恶意利用与检测机制》一文。 结束语 当前后门发往外部的TCP通讯是可监控,对网络捕获的实现是libpcap库,特征比较明显,而这个程序作者不停改进中。业界中,有人分享过更隐秘的技术方式,比如在内核态,复用业务的网络包,更改内容发数据包给黑客。躲避了用户态的检查。 这后门虽然不完美,但这只是GitHub开源社区的现状,相信在黑市里,早有隐秘性笔者高很多的后门在使用。笔者再次呼吁,务必加快对eBPF恶意利用的防御检测体系建设。 微信公众号 回复boopkit获取该文章链接。 2022-05-09 4 0
eCapture:无需CA证书抓https网络明文通讯 eCapture介绍 eCapture是一款基于eBPF技术实现的用户态数据捕获工具。不需要CA证书,即可捕获https/tls的通讯明文。 项目在2022年3月中旬创建,一经发布,广受大家喜爱,至今不到两周已经1200多个Star。 作用 不需要CA证书,即可捕获HTTPS/TLS通信数据的明文。 在bash审计场景,可以捕获bash命令。 数据库审计场景,可以捕获mysqld/mariadDB的SQL查询。 官网 代码仓库见:https://github.com/ehids/ecapture ,也可以关注微信公众号「榫卯江湖」获取最新动态。 产品架构 eCapture系统用户态程序使用Golang语言开发,具有良好的系统兼容性,无依赖快速部署,更适合云原生场景。 内核态代码使用C编写,使用clang/llvm编译,生产bpf字节码后,采用go-bindata转化为golang语法文件,之后采用ehids/ebpfmanager类库,调用bpf syscall进行加载、HOOK、map读取。 golang编译后,无其他任何依赖即可运行,兼容linux kernel 4.18以上所有版本。 eBPF加载机制 关于eBPF详细加载机制,可到https://ebpf.io/ 查阅相关原理。 实现原理 如工作原理的图所示,在用户态的加密解密函数中下钩子。 tcpdump(libpcap)是在数据包接收到,XDP处理后,进行clone packet,进行包的复制,发送给用户态进程。二者工作的所在层不一样。 功能介绍 eCapture有三个模块 tls/ssl明文数据捕获 bash命令审计 mysqld数据库审计 第一个功能适用于基于tls/ssl解密需求的运维监控、故障排查、抽样分析场景。 第二个功能适用于安全领域的bash入侵发现场景,这里只是简单的功能,可以在此基础上增加其他功能。 第三个功能适用于数据库审计场景,尤其是做数据安全、数据防泄漏,甚至入侵检测等。同样,可以在此基础上扩充其他功能。 查看其使用说明,如下 cfc4n@vm-desktop:~/ehids/ecapture$ ./bin/ecapture NAME: ecapture - capture text SSL content without CA cert by ebpf hook. USAGE: ecapture [flags] VERSION: 0.1.5-20220325-47edbed COMMANDS: bash capture bash command help Help about any command mysqld56 capture sql queries from mysqld >5.6 . tls alias name:openssl , use to capture tls/ssl text content without CA cert. DESCRIPTION: ecapture是一款无需安装CA证书,即可抓去HTTPS、TLS等明文数据包的工具。 也可以捕获bash的命令,适用于安全审计场景。包括mysqld的数据库审计等。 仓库地址: https://github.com/ehids/ecapture OPTIONS: --debug[=false] enable debug logging -h, --help[=false] help for ecapture --hex[=false] print byte strings as hex encoded strings -p, --pid=0 if target_pid is 0 then we target all pids 其中,有四个全局参数,分别是 --debug , 用于启动调试日志 --help , 查看帮助 --hex ,按照hex模式打印字符,用与查看不可见字符 --pid ,用于针对特定进程进行数据捕获 HOOK机制 eCapture采用eBPF uprobe相关函数进行HOOK,故需要目标用户态函数信息,包含函数符号表(symbol table),函数偏移地址(offset)。 在大部分linux发行版中,使用的二进制可执行文件(ELF)都是包含符号表的;少部分发行版,会去掉ELF中的符号表。那么针对这种场景,就需要用户自行定位目标函数所在ELF/SO中的偏移地址,通过工具的参数来指定。 对于ELF文件,可以将目标类库静态编译到自身,也可以通过动态链接库的方式引用。那么对于这两种形式,eCapture根据不同场景进行自动查找。若查找不到,用户可以通过命令行参数指定。 故eCapture支持HOOK ELF,以及HOOK SO两种模式。会自动分析ELF文件,读取.dynamic和.dynsym等段信息,查找相关链接库名以及函数名、偏移地址。 查找原理如下图: tls/ssl ecapture tls命令用于启动tls/ssl模块,支持了三类tls/ssl加密类库,分别是 openssl ,动态链接库名字为libssl.so gnutls ,动态链接库名字为libgnutls.so nss/nspr ,动态链接库名字为libnspr4.so 在不同的linux发行版中,因为各种原因,会选择不同的类库。比如wget程序,在ubuntu跟centos中就会使用不同的类库。有的是openssl,有的是gnutls,甚至两个库都引入了。 具体情况,你可以使用ldd $ELF_PATH | grep -E "tls|ssl|nspr|nss"来查看一个ELF文件使用类库情况。 cfc4n@vm-desktop:~$ ldd `which wget` |grep -E "tls|ssl|nspr|nss" libssl.so.1.1 => /lib/x86_64-linux-gnu/libssl.so.1.1 (0x00007f50699f6000) 对于firefox、chrome这种进程,需要在程序启动后才能看到tls类库依赖情况,那么,你可以使用sudo pldd $PID | grep -E "tls|ssl|nspr|nss" 来查看 cfc4n@vm-desktop:~$ ps -ef|grep firefox cfc4n 6846 1432 45 17:50 ? 00:00:04 /usr/lib/firefox/firefox -new-window cfc4n@vm-desktop:~$ sudo pldd 6846 |grep -E "tls|ssl|nspr|nss" /usr/lib/firefox/libnspr4.so /usr/lib/firefox/libnssutil3.so /usr/lib/firefox/libnss3.so /usr/lib/firefox/libssl3.so /lib/x86_64-linux-gnu/libnss_files.so.2 /lib/x86_64-linux-gnu/libnss_mdns4_minimal.so.2 /lib/x86_64-linux-gnu/libnss_dns.so.2 /usr/lib/firefox/libnssckbi.so eCapture的tls模块命令行参数如下,用户可以使用默认配置外,也可以根据自己环境自行指定。 OPTIONS: --curl="" curl or wget file path, use to dectet openssl.so path, default:/usr/bin/curl --firefox="" firefox file path, default: /usr/lib/firefox/firefox. --gnutls="" libgnutls.so file path, will automatically find it from curl default. -h, --help[=false] help for tls --libssl="" libssl.so file path, will automatically find it from curl default. --nspr="" libnspr44.so file path, will automatically find it from curl default. --wget="" wget file path, default: /usr/bin/wget. 同时,使用方法也比较简单,./ecapture tls --hex命令即可。 在linux上,firefox程序中,有很多通讯都使用了/usr/lib/firefox/libnspr4.so,但实际上业务请求是可以通过Socket Thread进程来发送的。可以通过这个特点来过滤,对于chrome程序,相信细心的你,也能搞定。 bash 笔者在安全部门工作,接到过bash审计需求,其实现方法无非是修改系统类库、使用内核模块等技术实现,对系统稳定性有一定风险。基于eBPF技术实现,可以避开这些问题。这里的bash命令的监控,是作为eBPF技术在安全审计场景中的一个探索。 eCapture在实现时首先查找ENV的$SHELL值,作为bash的二进制文件路径进行HOOK。对于bash加载了libreadline.so的场景,也会自动分析,进行符号表查找、offset定位,再进行HOOK。 bash模块的参数有三个,用户可以自定义bash、readlineso的路径。 OPTIONS: --bash="" $SHELL file path, eg: /bin/bash , will automatically find it from $ENV default. -h, --help[=false] help for bash --readlineso="" readline.so file path, will automatically find it from $BASH_PATH default. mysql/mariadb 与bash模块一样,也是作为数据库审计的一个探索。笔者环境为ubuntu 21.04,mysqld也因为协议关系,使用了衍生的MariadDB,用户也可以根据自己实际场景,使用命令行参数进行指定。 mysqld模块,核心原理是HOOK了dispatch_command函数, 第一个参数为CMD类型,值为COM_QUERY时,为查询场景,即审计需求的查询类型。 第二个参数是THD的结构体,在这里我们用不到。 第三个是查询的SQL语句 第四个参数是SQL语句的长度, // https://github.com/MariaDB/server/blob/b5852ffbeebc3000982988383daeefb0549e058a/sql/sql_parse.h#L112 dispatch_command_return dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length, bool blocking = true); mysqld审计模块参数如下: OPTIONS: -f, --funcname="" function name to hook -h, --help[=false] help for mysqld56 -m, --mysqld="/usr/sbin/mariadbd" mysqld binary file path, use to hook --offset=0 0x710410 其中,--mysqld是用来指定mysqld的路径。 mysqld二进制程序符号表里虽然有dispatch_command信息,但dispatch_command这个函数名每次编译都是变化的,故不能写死。 eCapture的查找方式是读取mysqld二进制的.dynamic段信息,正则语法\w+dispatch_command\w+去匹配所有符号信息,找到其函数名、偏移地址,再使用。 你也可以通过objdump命令来查找,再通过命令行参数自行指定funcname。 mariadbd version : 10.5.13-MariaDB-0ubuntu0.21.04.1 objdump -T /usr/sbin/mariadbd |grep dispatch_command 0000000000710410 g DF .text 0000000000002f35 Base _Z16dispatch_command19enum_server_commandP3THDPcjbb 即offset为0x710410,函数名为_Z16dispatch_command19enum_server_commandP3THDPcjbb。 使用 下载二进制包 eCapture发布在https://github.com/ehids/ecapture/releases ,目前最新版为eCapture v0.1.5。 可在linux kernel 4.18以上版本运行。 二进制包地址: https://github.com/ehids/ecapture/releases/download/v0.1.5/ecapture_v0.1.5.zip 国内加速地址: https://github.do/https://github.com/ehids/ecapture/releases/download/v0.1.5/ecapture_v0.1.5.zip 自行编译 代码仓库在https://github.com/ehids/ecapture ,你可以自行修改源码编译。 演示视频 腾讯视频: B站: https://www.bilibili.com/video/BV1si4y1Q74a Github仓库 2022-03-27 +3 7 0
基于语法分析的PHP webshell扫描工具–WoodPecker Scanner 前段时间,在一位大牛的BLOG上看到其resume上撰写的开源项目列表琳琅满目,数不胜数。再跟自己对比一下,从来没有一个开源项目,没有成功的参加过一个开源项目,只是零星的贡献过几个所谓工具,脚本。顿时无地自容,同时也是羡慕不已,再暗自勉励鼓励自己,向这位大牛学习。也是在前段时间,遇到了一个正则的问题《为什么不能在字符组中使用反向引用》,使得我又跟着自己的思路,翻阅之前的博客,面包屑导航一般,跟着链接,走到了另外一篇博客《如何精确查找PHP WEBSHELL木马?》。写那篇博客时,是2010年7月,我还在久游网工作,工作内容也是代码安全相关。我最初的实现,也是基于正则表达式实现的字符串匹配,来捕捉查找危险代码。同时,也计划在二期里,用LEX+YACC来实现对PHP代码的精确分析,扫描查找,为此,也经常装模作样故弄玄虚的研究学习yacc、lex,学习PHP的Zend引擎实现方式,研究zend_language_scanner.l、zend_language_parser.y、zend_language_scanner.c等几个文件的编写思路等等等等。从我2月下旬入职,到11月初离职,总共7个多月的时间里,也并不是一直做这个项目,甚至这个项目只是作为我个人研究的范畴,平常工作中,只能利用空闲时间来研究。因为种种原因,在没有完成那个项目之后,离开了久游。不管是因为自己基础薄弱,还是时间不足还是什么原因,每次想起这件事,都因没有完成那个项目而觉得遗憾。 人总喜欢找借口,好比我前段时间,心里烦躁不安、胡思乱想,开始玩起了英雄联盟,从5月到现在,断断续续玩了5个月了。支持者的角度上想,人总不能一直紧绷着大脑,偶尔玩玩游戏,放松一下自己,又何尝不可?反对者的角度上来想,放纵自己堕落,还找理由,找借口?我有时赞同前者,有时赞同后者,现在,我就觉得既然都是找借口去做事情,为何不给自己找个借口,做一些有意义的事情呢?“那就努力开发维护一个开源项目吧”我心理暗暗这么说道,决定完成曾经的遗憾。 这线引得有点太长,也算是我的自我检讨与牢骚吧。 所谓扫描工具,先要扫描,才能找到需要的东西。对于代码扫描方式,实现方式大约是特征关键字的捕捉,正则表达式的语法匹配,语法语义分析这些。这些实现方式,准确性、严谨性也是层级递增的。 特征关键字方式,是最简单、最不准确的检测手段,上篇博客里有个比较好的例子: 这里的正则的最后部分中文字符串那里|小组专用大马|提权|木马|PHP\s?反弹,这里的正则不是原本意义上的正则表达式了,只是简单的或逻辑,没有其他更严谨的匹配规则,所以只能当成简单的特征关键字来看待。比如代码或注释中出现螓首微微压下,右手倒提权杖、电影《特洛伊木马》均被当作webshell来抓获了。这种实现方式,最不准确。 正则表达式匹配方式,是在特征关键字之后,更加准确的捕捉方式,但仍存在大量的误报、漏报情况,比如我在三年前写的python版php webshell检测工具里的代码: 虽然可以更精确的匹配函数名符合的地方,字符串中出现的会被跳过,但仍无法排除注释中的代码,这种匹配结果,可以作为参考,仍需要管理人员认真校对每一个检测结果。当然,漏报情况,相比特征字符串的方式,要严谨的多。 语法语义分析形式,是根据php语言扫描编译的实现方式,进行剥离代码、注释,分析变量、函数、字符串、语言结构的分析方式,来实现关键危险函数的捕捉方式。这样可以完美解决漏报的情况。但误报上,仍存在问题。 误报问题所在,一是被检测文件是否为合法php语法文件,token_get_all函数的实现,是不验证是否问合法php语法文件的,只是对其进行扫描,分析。而语法验证是比较难于试下你的地方。其次是代码中因为业务需要而出现的危险函数,如何排除掉,恶意代码如何界定? 服务器云判断是一种根据恶意代码串的指纹,根据大量后门数据,做语法、语义分析,做业务逻辑分析,理解这段代码的用途,给出其是否为恶意代码的定位,而其他使用者,直接可以得到该代码片段是否为恶意代码的结果反馈。Pecker Scanner首先是基于语法分析,剥离token、注释、字符串、变量、语言结构,再进行php语法检测,提取恶意代码的扫描工具,来解决漏报问题。同时支持服务器云判断,尽量避免误报问题。比如exec ($cfe,$res)在Discuz!中(只是举例)是正常的功能代码,而在木马后门中,这就是恶意文件。同样的一段代码,在不同的项目中,扮演着不同的角色,这也不能光凭借代码功能上判断,还得依赖所属项目。这需要大量的木马后门库,以及开源项目库希望大家可以帮着提交一些。 Pecker Scanner这个开源项目,托管在Github上,大家都可以fork它,完善它。目前Pecker Scanner生成的报告,跟服务器校对检验方式是通过jsonp方式通讯,若以后流量较高,则采用验证API形式,直接在pecker scanner代码中,根据api 、 key运算,post多个token代码段到服务器获取状态,展示到报告文件中。一来减轻jsonp这种大量http请求数。二来减少恶意无用的查询。 Pecker Scanner目前是beta 0.3版本,您可以在这里下载Pecker Scannerbeta 0.4.4版,不建议直接到github上下载,因为github上可能存在我正在调试的代码,或者不完善的新功能。 Pecker Scanner的项目主页在这里,以后的版本更新,新功能发布,也都会在这里公告。扫描报告样例在pecker scanner report样例,您可以先预览一下。 2014-03-03 发布新版本 pecker scanner 0.4 2014-05-27 发布新版本 pecker scanner 0.4.4 修复复杂语法complex(curly)syntax的检测,比如${ $ {func}}($evil)语法。 pecker scanner beta 0.4 releases fix bugs for some of the syntax with "{" and "[". fix bugs with code : (require '1.dat').'.php'; thanks for poker付 ( http://weibo.com/1776130645 ) add "include" to default checklist . Pecker Scanner Server目前收集的开源软件包括Discuz_7.2_FULL_SC_UTF8、DedeCMS-V5.7-UTF8-SP1-Full;木马后门包括PhpSpy 2011、PhpSpy 2013、PhpSpy 2011加密版。其他开源项目以及木马后门,等待各位的反馈。 2013-09-24新增开源软件、webshell后门特征: DedeCMS-V5.7-UTF8-SP1-Full Discuz_7.2_FULL_SC_UTF8 Discuz_X3.0_SC_UTF8 Discuz_X3.1_SC_UTF8 espcms_utf8_5.7.13.09.10_b HDWiki-v5.1UTF8-20121102 Joomla_2.5.14-Stable-Full_Package Joomla_3.1.5-Stable-Full_Package KingCMS_6.1.1641(Sp2) none phpcms_v9.4.2_UTF8 phpMyAdmin-4.0.7-all-languages phpspy 2011 phpspy 2013 phpspy 2013 加密版 phpwind_UTF8_8.7 phpwind_v9.0_utf8 WeCenter_2-2-1 wordpress-3.6.1 您能做的: 风险函数列表总结 木马后门样本提供 服务器赞助(目前暂时将Pecker Scanner Server部分托管在朋友的服务器上,目前流量还能支撑,若...) 推广它(特别感谢黑哥的第一次推广,您的每一次使用,都是对我最大的支持) 使用反馈(感谢bgho4t的第一个测试报告) 我能做的: 默默的开发,悄悄的维护,轻轻的调试,偷偷的发布。 2013-09-22 +1 5 0
如何精确查找PHP WEBSHELL木马? 上篇提到了关于网上流传查找PHP webshell的python脚本中,不严谨的代码,并且给出了一个python的检测代码,同时,下文里也提到不能检测到反引号的命令执行的地方。今天,我想了下,现在把思路发出来。 先来看下反引号可以成功执行命名的代码片段。代码如下: 正则表达式该如何写? 分析: 对于可移植性的部分共同点是什么?与其他正常的包含反引号的部分,区别是什么? 他们前面可以有空格,tab键等空白字符。也可以有程序代码,前提是如果有引号(单双)必须是闭合的。才是危险有隐患的。遂CFC4N给出的正则如下:【(?:(?:^(?:\s+)?)|(?:(?P<quote>["'])[^(?P=quote)]+?(?P=quote)[^`]*?))`(?P<shell>[^`]+)`】。 解释一下: 【(?:(?:^(?:\s+)?)|(?:(?P<quote>["'])[^(?P=quote)]+?(?P=quote)[^`]*?))】匹配开始位置或者开始位置之后有空白字符或者前面有代码,且代码有闭合的单双引号。(这段PYTHON的正则中用了捕获命名以及反向引用) 【`(?P<shell>[^`]+)`】这个就比较简单了,匹配反引号中间的字符串。 然后我将这段代码写入程序中,测试跑了一下discuz的程序。结果有一个误报。误报的位置为“config.inc.php”中的“define('UC_DBTABLEPRE', '`ucenter`.uc_');”,什么原因造成的?这行代码符合了前面有闭合的引号,也有反引号的使用,所以,符合要求,被检测到了。如何再排除这种情况呢?这个有什么特殊的?前面有逗号“,”?如果是字符串连接的点号“.”呢?再排除逗号? 好吧,我错了,我不该用我的思维来误导你。换个思路。找下反引号可执行的代码的前面字符串的情况,他们只能是行的开始,或者有空白字符(包括空格,tab键等),再前面也可以有代码的结束标识分号“;”,其他的情况,都是不可以执行的吧?嗯,应该是这样。(如有错误,欢迎斧正)既然思路有了,那正则代码更好写了。如下【(^|(?<=;))\s*`[^`]+`】,解释一下,【(^|(?<=;))】匹配位置,是行的开始,或者前面有分号“;”。【\s*`[^`]+`】空白字符任一个,然后是....(你懂的)。OK,写好之后,检测,又发现一个问题。 匹配引入文件的正则也匹配了“require_once './include/db_'.$database.'.class.php';”这种代码,什么原因造成的,您自己分析吧。 给出修复之后的python代码,如下: 稍微检测了一下Discuz7.2的代码,还是有误报的,误报的为这种包含sql的代码: 由于这个脚本是按照一行一行的代码来处理的,所以,有这种误报。您自己去修复吧。相对网上流传的脚本来说,还是比较准确的。 欢迎转载。转载请注明来源,以及留下博客链接,同时,不能用于商业用途。(已经修复,增加了反引号后面【\s*;】的判断。2010-07-27 17:06) PS:如果说上传文件也算是危险的、值得注意的操作的话,建议加上move_uploaded_file函数的检测。你知道在哪里加的。^_^ 2010-12-17 关于这些代码,已经放到google 的代码托管上了。SVN地址为 https://code.google.com/p/cnxct/ 大家个获得最新版。 我是一个PHPer,写的python有点憋,有点懒,还请各位安全界的大牛,程序界的前辈不要鄙视,要给建议,谢谢。php版的以后在写吧。同时,也欢迎各位安全爱好者反馈最新的web shell特征代码,我尽力增加到程序中区。 2013-09-13更新: PHP版基于语法扫描,语义分析的扫描器,已经实现,详情见: Pecker Scanner php编写的webshell扫描器 2010-07-27 +16 20 0