sslsplit源码流程分析

概述

官网地址:https://www.roe.ch/SSLsplit
https://github.com/droe/sslsplit
SSLsplit是用于对 SSL / TLS 加密的网络连接进行中间人攻击的工具。用于网络取证,应用程序安全性分析和渗透测试中。SSLsplit是纯粹的透明代理,不能充当浏览器中配置的HTTP或SOCKS代理。SSLsplit支持SSL 3.0,TLS 1.0,TLS 1.1和TLS 1.2,以及可选的SSL 2.0。

对于HTTPS连接,SSLsplit会动态生成并签名伪造的X509v3证书,模仿原始服务器证书的主题DN,subjectAltName扩展名和其他特征。SSLsplit能够使用具有私钥可用的现有证书,而不是生成伪造的证书。日志记录选项包括传统的SSLsplit连接和内容日志文件以及PCAP文件,并将解密的流量镜像到网络接口。此外,可以记录证书,主密钥和本地过程信息。

以上是官网的介绍,总之SSLsplit是一个强大的ssl中间人工具,采用c语言编写,有很出色的性能,日志输出是通用的pcap文件。sslsplit依赖于OpenSSL,libevent 2.x,libpcap和libnet 1.1.x库,支持主流的类Unix系统。

//以下是代码流程

Main流程

Main->参数解析->preinit->privsep_fork->proxy_new->sys_privdrop(进程弃权)->reinit(初始化)->proxy_run(生成工作线程开始工作).
Sslsplit连接处理以及双向连接内容交换基于libevent事件机制;具体建连接建立过程章节。

主要数据结构

Opts:

typedef struct opts {
unsigned int debug : 1;
59 unsigned int detach : 1;
60 unsigned int sslcomp : 1;
61 #ifdef HAVE_SSLV2
62 unsigned int no_ssl2 : 1;
63 #endif /* HAVE_SSLV2 /
64 #ifdef HAVE_SSLV3
65 unsigned int no_ssl3 : 1;
66 #endif /
HAVE_SSLV3 /
67 #ifdef HAVE_TLSV10
68 unsigned int no_tls10 : 1;
69 #endif /
HAVE_TLSV10 */
.
.
.

proxyspec_t *spec;
unsigned int verify_peer: 1;
unsigned int allow_wrong_host: 1;

}opt_s;

Opt存储解析后的启动配置参数选项,后面初始化以及运行时会使用;可以源码中搜索如何赋值,看代码可直接看懂。

Proxyspec:

38 typedef struct proxyspec {
39 unsigned int ssl : 1; /表示为ssl协议/
40 unsigned int http : 1; /表示为http协议/

41 unsigned int upgrade: 1; /autossl ,即starttls连接/
42 unsigned int dns : 1; /* set if spec needs DNS lookups, 通过-sni指定时 /
43 struct sockaddr_storage listen_addr;
44 socklen_t listen_addrlen;
45 /
connect_addr and connect_addrlen are set: static mode;
46 * natlookup is set: NAT mode; natsocket /may/ be set too;
47 * sni_port is set, in which case we use SNI lookups */
48 struct sockaddr_storage connect_addr;
49 socklen_t connect_addrlen;
50 unsigned short sni_port;
51 char *natengine;
52 nat_lookup_cb_t natlookup;
53 nat_socket_cb_t natsocket;
54 struct proxyspec *next;
55 } proxyspec_t;

代理规则,一个结构体表示监听的一个代理端口通过opt->spec可获取,多个spec时以链表形式存储,spec->next串接。

proxy_listener_ctx

74 * Listener context.
75 */
76 typedef struct proxy_listener_ctx {
77 pxy_thrmgr_ctx_t *thrmgr;
78 proxyspec_t *spec;
79 opts_t *opts;
80 struct evconnlistener *evcl;
81 struct proxy_listener_ctx *next;
82 } proxy_listener_ctx_t;

在proxy_new中根据proxyspec_t spec生成的监听对象,多个时以链表形式存储,和spec对应。

proxy_ctx

62 struct proxy_ctx {
63 pxy_thrmgr_ctx_t *thrmgr; /具体代理工作线程管理结构/
64 struct event_base *evbase; /子进程evbase结构,负责监听配置的 proxy_listener、子进程proxy_signal_cb信号处理、 缓存回收proxy_gc_cb/
65 struct event *sev[sizeof(signals)/sizeof(int)];
66 struct event *gcev;
67 struct proxy_listener_ctx *lctx;
68 opts_t *opts; /指向配置参数opts/
69 int loopbreak_reason; /程序退出时记录退出原因/
70 };

Sslsplit主结构体,程序中唯一存在;通过proxy_ctx 可以访问到其他需要的配置结构;

pxy_thrmgr_ctx

46 typedef struct pxy_thr_ctx {
47 pthread_t thr;
48 size_t load;
49 struct event_base *evbase;
50 struct evdns_base *dnsbase;
51 int running;
52 } pxy_thr_ctx_t;
单个工作线程管理结构

53
54 struct pxy_thrmgr_ctx {
55 int num_thr; /线程总数/
56 opts_t *opts; /×指向启动参数配置opts×/
57 pxy_thr_ctx_t **thr; /线程数组/
58 pthread_mutex_t mutex;/线程锁/
59 };

工作线程池管理结构

pxy_conn_ctx

/* actual proxy connection state consisting of two connection descriptors,
124 * connection-wide state and the specs and options /
125 typedef struct pxy_conn_ctx {
126 /
per-connection state */
127 struct pxy_conn_desc src; /客户端与sslsplit连接的bufferevent描述符/
128 struct pxy_conn_desc dst;/sslsplit与后端服务器连接的bufferevent描述符/

129
130 /* status flags */
146



193 struct event_base *evbase;
194 struct evdns_base *dnsbase;
195 int thridx;
196 pxy_thrmgr_ctx_t *thrmgr;
197 proxyspec_t *spec;
198 opts_t *opts;
199 } pxy_conn_ctx_t;

102 /* single dst or src socket bufferevent descriptor */
103 typedef struct pxy_conn_desc {
104 struct bufferevent *bev;
105 SSL *ssl;
106 unsigned int closed : 1;
107 } pxy_conn_desc_t;

pxy_conn_ctx_t单个代理连接管理结构,

进程/线程关系(图)

在这里插入图片描述

主要涉及父进程、子进程、日志线程、负责代理连接处理工作线程

如下是一个4核cpu的进程/线程信息

//main启动原始进程(如果设置了-d,则是调用daemon()之后的进程ID)
负责特权操作(打开fd/sock等),收到的信号会同步给子进程
9698 9698 pts/0 00:00:00 sslsplit

//privsep_fork产生的子进程,负责1、产生工作线程 2、产生日志线程(一种日志一个线程)
//3、负责监听端口接收客户端连接 4、处理信号以及缓存垃圾回收(缓存有证书缓存\ssl会话等)
9699 9699 pts/0 00:00:00 sslsplit—child

//日志线程,接收工作线程发送的日志,线程个数与开启的日志类型有关系
9699 9700 pts/0 00:00:00 sslsplit
9699 9701 pts/0 00:00:00 sslsplit
9699 9702 pts/0 00:00:00 sslsplit
9699 9703 pts/0 00:00:00 sslsplit
----logger

//工作线程,与客户端&服务器建立双向连接做代理,个数为cpu核数*2
9699 9704 pts/0 00:00:00 sslsplit
9699 9705 pts/0 00:00:00 sslsplit
9699 9706 pts/0 00:00:00 sslsplit
9699 9707 pts/0 00:00:00 sslsplit
9699 9708 pts/0 00:00:00 sslsplit
9699 9709 pts/0 00:00:00 sslsplit
9699 9710 pts/0 00:00:00 sslsplit
9699 9711 pts/0 00:00:00 sslsplit
----worker

fd selfpipev[2]//child 关闭所有 //self-pipe trick: signal handler -> select,用于父进程的signal与select机制,来signal时将select监听的fd置为可写。
fd chldpipev[2] //child 关闭[1] //parent关闭chldpipev[1]之前child会read阻塞,所以用来让child等待parent的sighadle完成;使用完后关闭所有chldpipev

sockcliv//使用sockpair申请6对全双工fd,child关闭[0],且clisock[]=sockcliv[1];
//parent sockcliv关闭[1],selfpipe_wrfd = selfpipev[1], 关闭preinit中打开的一些fd(主要是log和nat相关)
//parent 注册signal_handler函数(设置全局变量,并selfpipe_wrfd write), 关闭chldpipev[0]&[1];
//parent socksrv = sockcliv[i][0]; 父进程在函数privsep_server循环为监控进程,父进程退出前wait()子进程的exit状态

privsep_server
监听sigpipe,并传递给child,child也会监听自己的signal(proxy_signal_cb)
处理打开fd等特权操作//执行线程由于权限问题不能写对应目录

以下是child执行内容:
1、关闭pidfile(如果存在pidfile)
2、proxy_new函数

申请代理主结构proxy_ctx_t
检测DNS配置是否正确(设置sni参数时才执行)
申请线程池管理结构体,并初始化线程个数(cpu_core*2)
根据spec初始化&&设置监听sock,fd由父进程申请//通过clisock[0]和父进程通信,使用完成后,关闭clisock[0]
设置child进程的signal回调函数,以及设置proxy_gc_cb 垃圾清理定时器(evtimer实现);

3、sys_privdrop函数//drop privs and chroot,修改进程的uid\euid\gid,以及chdir(进程工作目录);如果指定user没指定group,则直接将group设置为passwd->gid
4、log_init//创建日志线程,每种日志一个单独的线程

clisock[1-5]对应5种日志类型,没启用的日志则关闭对应的clisock; clisock用来reopen时向父进程请求fd使用。
5、cachemgr_init初始化线程锁
6、proxy_run申请并开始运行代理线程(以event_loop方式运行)

pxy_thrmgr_run->pxy_thrmgr_thr 创建线程并运行

//日志首先由工作线程生成,然后选择直接存储(debug到终端时会直接输出)或者队列存储,队列方式会添加到日志队列中,然后由对应的日志线程写文件;
//同时工作线程再调用log_submit将日志放入队列中时会和父进程通信,打开对应的文件fd。(有需要才每次打开fd);日志文件都是全局变量fd,写文件线程可直接操作。

主要流程

1、连接建立过程

》proxy_listener_acceptcb为注册的accept回调函数(main->proxy_new->proxy_listener_setup->(proxy_listener_acceptcb)),当有新连接时libevent会调用proxy_listener_acceptcb接受连接。
》proxy_listener_acceptcb->pxy_conn_setup(建立新的代理连接)->pxy_conn_ctx_new(将代理连接绑定到线程池的某个线程上)
pxy_conn_setup中从engines变量中获取natip,以获取后端服务器ip地址,用于接下来建到服务端连接。//listen的是nat后的端口/ip,这里要获取真实的dstip
》pxy_conn_setup中申请pxy_conn_ctx_t结构,用于后续流程建双向代理连接
pxy_conn_setup->pxy_fd_readcb设置fd的回调函数,由于ssl需要先获取到前端clienthello才与后端服务端建连接,所以设置event_add(fd, pxy_fd_readcb),等待clienthello消息。
》pxy_fd_readcb之后通过事件机制触发,

pxy_fd_readcb->ssl_tls_clienthello_parse //解析客户端clienthello中的sni
pxy_fd_readcb->pxy_sni_resolve_cb->pxy_conn_connect //通过sni方式连接服务器(未指明确定serverip)
pxy_fd_readcb->pxy_conn_connect

pxy_conn_connect建立与server的ssl连接,并设置dst->bufferevent
pxy_conn_connect->pxy_bufferevent_setup设置bufferevent,并设置buffer回调函数
pxy_conn_connect->bufferevent_socket_connect建立连接

pxy_bev_eventcb->pxy_srcssl_create(构造服务器证书,并建立)

pxy_bev_readcb:
读取input bufferevent中的数据到对端的outbuf中,对应http协议会对一些头部(包括请求头和响应头)做简单处理,比如ocsp会禁用、响应头禁用HSTS
其他协议数据直接透传

2、内容交换

代理的两个连接交互数据在pxy_bev_readcb、pxy_bev_writecb中完成,对于http协议会对请求头与响应头做一些特殊处理,如删除HSTS响应信息等。数据的交互通过libeventbuffer机制完成。非http协议的数据直接交换不做任何处理。

3、其他内容

代理内容由三部分组成,连接类型(协议)+监听地址+转发地址(静态地址或解析地址((NAT engine, SNI DNS lookup))),静态地址也可以是域名,进程启动时会将域名解析为IP地址。
不支持http/2, 应用层协议版本会在client/server hello中协议,只要回复的不支持h2即可。对与HSTS网站需要客户端装了根证书才能正常访问(部分网站会被浏览器内置设置,或历史缓存)
https listenaddr port [nat-engine|fwdaddr port|sni port] //This mode currently suppresses WebSockets and HTTP/2
ssl listenaddr port [nat-engine|fwdaddr port|sni port] //decrypted connection content is treated as opaque stream of bytes and not modified
http listenaddr port [nat-engine|fwdaddr port]
tcp listenaddr port [nat-engine|fwdaddr port]
autossl listenaddr port [nat-engine|fwdaddr port]

启动参数

重要参数//原文内容查看man sslsplit或者官网文档
1、可以指定客户端cert -a、-b
2、可指定priv-key等证书相关信息,以提高效率,不然需要每次动态生成;-t优先-A优先-c-k; 其他-C、-g、-G、-K
3、-P可以在server失败或者找不到证书时直接透传ssl数据。
4、日志可以记录连接数据,连接内容,内容可独立文件,也可以记录一个文件; -F可以自定义路径名;命令: -F, -L and -S、-l
5、可镜像连接内容pcap到接口,相关参数 -T addr、-I、 -X, -Y and -y may be used
6、支持jaildir 安全措施;-j
7、-M logfile 可将私钥等信息记录到本地,供wireshark解密使用
8、-O 禁用所有ocsp流量
9、可指定crl\CDP,防止部分.net应用不接受不包含cdp的证书。-q crlurl
10、-r / -R可禁用启用TLS各协议版本
11、可指定-s ciphers,密码算法
12、支持进程降权,-u user、-m //dropuser生效时需要父子进程通信获取fd记录log/pcap,比较消耗性能,需要注意。且未设置-u 时会默认使用dropuser=nobody,所以可以显示设置-u=root
如果设置的不是每个connection一个文件,对性能没什么影响,只需要获取一次fd;每条连接一个日志文件时,需要每次重新获取fd,所以比较消耗性能。
13、支持指定nat引擎-E、-e
14 -Z 禁用TLS层压缩功能
15、-W gendir 、-w gendir 将证书/keys写到gendir目录下
16、-d -Ddebug模式或daemon 模式
17、-x engine指定openssl引擎
18、-f conffile //通过配置文件启动时可以不指定参数,配置文件格式可以参考源码中//load_conffile函数内容。
其他: -h-V

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值