Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new feature: capture TLS 1.3 master secret #143

Merged
merged 12 commits into from
Jul 27, 2022
18 changes: 8 additions & 10 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,19 @@
**Capture TLS master_key ,save to file. Support openssl `1.1.1.X` . `TLS 1.2` .**

Quick Guide:
- use `ecapture` to capture TLS master_key, will save master secret to `ecapture_masterkey_[pid].log`.
- use `tcpdump` to capture and save packets to `xxx.pcapng` file.
- open `xxx.pcapng` file with `wireshark`.
- Setting : `Wireshark` --> `Preferences` --> `Protocols` --> `TLS` --> `(Pre)-Master-Secret log filename`, select `ecapture_masterkey_[pid].log`.
- Using : right click packet item, select `follow` -> `HTTP Stream` / `HTTP/2 Stream`
- use `ecapture` to capture TLS master_key, will save master secret to `ecapture_masterkey_[pid].log`.
- use `tcpdump` to capture and save packets to `xxx.pcapng` file.
- open `xxx.pcapng` file with `wireshark`.
- Setting : `Wireshark` --> `Preferences` --> `Protocols` --> `TLS` --> `(Pre)-Master-Secret log filename`, select `ecapture_masterkey_[pid].log`.
- Using : right click packet item, select `follow` -> `HTTP Stream` / `HTTP/2 Stream`

## What's Changed


## New Contributors
* all : refactor event_processor EventType. by @cfc4n in https://github.com/ehids/ecapture/pull/134
* fixed #138 : You have an error in your yaml syntax on line 79 by @cfc4n in https://github.com/ehids/ecapture/pull/139
* New feature: capture openssl masterkey #27 by @cfc4n in https://github.com/ehids/ecapture/pull/140

**Full Changelog**: https://github.com/ehids/ecapture/compare/v0.2.2...v0.3.0



<hr>

# v0.2.2 (2022-07-09)
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ require (
github.com/stretchr/testify v1.7.0 // indirect
github.com/vishvananda/netlink v1.1.0 // indirect
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect
golang.org/x/net v0.0.0-20210525063256-abc453219eb5 // indirect
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7Zo
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
Expand All @@ -101,6 +103,8 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5 h1:wjuX4b5yYQnEQHzd+CBcrcC6OVR2J1CN6mUy0oSxIPo=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand Down
244 changes: 216 additions & 28 deletions kern/masterkey_kern.h
Original file line number Diff line number Diff line change
@@ -1,29 +1,96 @@
#include "ecapture.h"


// https://wiki.openssl.org/index.php/TLS1.3
// 仅openssl 1.1.1 后才支持 TLS 1.3 协议

// openssl 1.1.1.X 版本相关的常量
#define SSL3_RANDOM_SIZE 32
#define MASTER_SECRET_MAX_LEN 48
#define EVP_MAX_MD_SIZE 64

// openssl 1.1.1.X 版本相关的偏移量
/*
* openssl 1.1.1.X 版本相关的常量
* 参考:https://wiki.openssl.org/index.php/TLS1.3
*/

////////// TLS 1.2 or older /////////
// ssl->session 在 ssl_st 结构中的偏移量
#define SSL_SESSION_OFFSET 0x510
#define MASTER_SECRET_OFFSET 80

// session->master_key 在 SSL_SESSION 中的偏移量
#define MASTER_KEY_OFFSET 80

// ssl->s3 在 ssl_st中的偏移量
#define SSL_S3_OFFSET 0xA8

// s3->client_random 在 ssl3_state_st 中的偏移量
#define SSL_S3_CLIENT_RANDOM_OFFSET 0xD8

struct masterkey_t {
// session->cipher 在 SSL_SESSION 中的偏移量
#define SESSION_CIPHER_OFFSET 496 // 已计算 char *psk_identity_hint; + char *psk_identity;

// session->cipher->id 在 ssl_cipher_st 中的偏移量
#define SESSION_CIPHER_ID_OFFSET 24



////////// TLS 1.3 /////////

/*
// openssl 1.1.1J repo: https://github.com/openssl/openssl/tree/OpenSSL_1_1_1j
// ssl/ssl_local.h line 1143
* The TLS1.3 secrets.
unsigned char early_secret[EVP_MAX_MD_SIZE];
unsigned char handshake_secret[EVP_MAX_MD_SIZE]; // 【NEED】
unsigned char master_secret[EVP_MAX_MD_SIZE]; // 【NEED】
unsigned char resumption_master_secret[EVP_MAX_MD_SIZE];
unsigned char client_finished_secret[EVP_MAX_MD_SIZE];
unsigned char server_finished_secret[EVP_MAX_MD_SIZE];
unsigned char server_finished_hash[EVP_MAX_MD_SIZE]; //【NEED】
unsigned char handshake_traffic_hash[EVP_MAX_MD_SIZE]; //【NEED】
unsigned char client_app_traffic_secret[EVP_MAX_MD_SIZE];
unsigned char server_app_traffic_secret[EVP_MAX_MD_SIZE];
unsigned char exporter_master_secret[EVP_MAX_MD_SIZE]; //【NEED】
unsigned char early_exporter_master_secret[EVP_MAX_MD_SIZE];
*/

// session->cipher 在 SSL_SESSION 中的偏移量
#define CIPHER_OFFSET 0xEC

// ssl_cipher_st-> id 在 ssl_cipher_st 中的偏移量
#define CIPHER_ID_OFFSET 0x18

// ssl->handshake_secret 在 ssl_st 中的偏移量
#define HANDSHAKE_SECRET_OFFSET 0x13C // 316

// ssl->master_secret 在 ssl_st 中的偏移量
#define MASTER_SECRET_OFFSET 0x17C //380

// ssl->server_finished_hash 在 ssl_st 中的偏移量
#define SERVER_FINISHED_HASH_OFFSET 0x27C // 636

// ssl->handshake_traffic_hash 在 ssl_st 中的偏移量
#define HANDSHAKE_TRAFFIC_HASH_OFFSET 0x2BC // 700

// ssl->exporter_master_secret 在 ssl_st 中的偏移量
#define EXPORTER_MASTER_SECRET_OFFSET 0x37C // 892


struct mastersecret_t {
// TLS 1.2 or older
s32 version;
u8 client_random[SSL3_RANDOM_SIZE];
u8 master_key[MASTER_SECRET_MAX_LEN];
};

// bpf map
struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
// __uint(type, BPF_MAP_TYPE_HASH);
// __type(key, u32);
// __type(value, struct masterkey_t);
// __uint(max_entries, 1024);
} masterkey_events SEC(".maps");
// TLS 1.3
u32 cipher_id;
u8 handshake_secret[EVP_MAX_MD_SIZE];
u8 master_secret[EVP_MAX_MD_SIZE];
u8 server_finished_hash[EVP_MAX_MD_SIZE];
u8 handshake_traffic_hash[EVP_MAX_MD_SIZE];
u8 exporter_master_secret[EVP_MAX_MD_SIZE];
};

// ssl/ssl_local.h 1556行
struct ssl3_state_st {
Expand All @@ -36,6 +103,43 @@ struct ssl3_state_st {
unsigned char client_random[SSL3_RANDOM_SIZE];
};

#define TLS1_3_VERSION 0x0304

/////////////////////////BPF MAPS ////////////////////////////////

// bpf map
struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
} mastersecret_events SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__type(key, u64);
__type(value, struct mastersecret_t);
__uint(max_entries, 2048);
} bpf_context SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, u32);
__type(value, struct mastersecret_t);
__uint(max_entries, 1);
} bpf_context_gen SEC(".maps");

/////////////////////////COMMON FUNCTIONS ////////////////////////////////
//这个函数用来规避512字节栈空间限制,通过在堆上创建内存的方式,避开限制
static __always_inline struct mastersecret_t *make_event() {
u32 key_gen = 0;
struct mastersecret_t *bpf_ctx = bpf_map_lookup_elem(&bpf_context_gen, &key_gen);
if (!bpf_ctx)
return 0;
u64 id = bpf_get_current_pid_tgid();
bpf_map_update_elem(&bpf_context, &id, bpf_ctx, BPF_ANY);
return bpf_map_lookup_elem(&bpf_context, &id);
}


/////////////////////////BPF FUNCTIONS ////////////////////////////////
SEC("uprobe/SSL_write_key")
int probe_ssl_master_key(struct pt_regs *ctx) {
u64 current_pid_tgid = bpf_get_current_pid_tgid();
Expand All @@ -54,36 +158,53 @@ int probe_ssl_master_key(struct pt_regs *ctx) {
#endif
debug_bpf_printk("openssl uprobe/SSL_write masterKey PID :%d\n", pid);

// masterkey_t sent to userspace
struct masterkey_t masterkey;
__builtin_memset(&masterkey, 0, sizeof(masterkey));
// mastersecret_t sent to userspace
struct mastersecret_t *mastersecret = make_event();
// Get a ssl_st pointer
void *ssl_st_ptr = (void *)PT_REGS_PARM1(ctx);

if (!mastersecret) {
debug_bpf_printk("mastersecret is null\n");
return 0;
}
// Get a ssl_session_st pointer
u64 *ssl_session_st_ptr = (u64 *)(ssl_st_ptr + SSL_SESSION_OFFSET);
u64 *ssl_s3_st_ptr = (u64 *)(ssl_st_ptr + SSL_S3_OFFSET);

int version;
int ret = bpf_probe_read_user(&version, sizeof(version), (void *)ssl_st_ptr);
if (ret) {
debug_bpf_printk(
"bpf_probe_read tls_version failed, ret :%d\n", ret);
return 0;
}
mastersecret->version = version;
debug_bpf_printk("tls_version: %d\n", mastersecret->version);


// Get SSL_SESSION_t pointer
u64 address;
int ret =
ret =
bpf_probe_read_user(&address, sizeof(address), ssl_session_st_ptr);
if (ret) {
debug_bpf_printk(
"bpf_probe_read ssl_session_st_ptr failed, ret :%d\n", ret);
return 0;
}

// Access the TLS 1.2 master secret
void *ms_ptr = (void *)(address + MASTER_SECRET_OFFSET);
ret = bpf_probe_read_user(&masterkey.master_key,
sizeof(masterkey.master_key), ms_ptr);
// Get SSL_SESSION->cipher pointer
u64 *ssl_cipher_st_ptr = (u64 *)(address + SESSION_CIPHER_OFFSET);

///////////////////////// get TLS 1.2 master secret ////////////////////
void *ms_ptr = (void *)(address + MASTER_KEY_OFFSET);
ret = bpf_probe_read_user(&mastersecret->master_key,
sizeof(mastersecret->master_key), ms_ptr);
if (ret) {
debug_bpf_printk(
"bpf_probe_read MASTER_SECRET_OFFSET failed, ret :%d\n", ret);
"bpf_probe_read MASTER_KEY_OFFSET failed, ret :%d\n", ret);
return 0;
}

debug_bpf_printk("master_key: %x %x %x\n",masterkey.master_key[0],masterkey.master_key[1],masterkey.master_key[2]);
debug_bpf_printk("master_key: %x %x %x\n",mastersecret->master_key[0],mastersecret->master_key[1],mastersecret->master_key[2]);


// Get ssl3_state_st pointer
Expand All @@ -102,17 +223,84 @@ int probe_ssl_master_key(struct pt_regs *ctx) {
return 0;
}
debug_bpf_printk("client_random: %x %x %x\n",ssl3_stat.client_random[0],ssl3_stat.client_random[1],ssl3_stat.client_random[2]);
ret = bpf_probe_read_kernel(&masterkey.client_random,
sizeof(masterkey.client_random),
ret = bpf_probe_read_kernel(&mastersecret->client_random,
sizeof(mastersecret->client_random),
(void *)&ssl3_stat.client_random);
if (ret) {
debug_bpf_printk(
"bpf_probe_read_kernel ssl3_stat.client_random failed, ret :%d\n", ret);
return 0;
}
debug_bpf_printk("copy : %x %x %x\n",masterkey.client_random[0],masterkey.client_random[1],masterkey.client_random[2]);

bpf_perf_event_output(ctx, &masterkey_events, BPF_F_CURRENT_CPU, &masterkey,
sizeof(struct masterkey_t));
//////////////////// check tls version eq to TLS 1.3 ////////////////////////
if (version != TLS1_3_VERSION) {
bpf_perf_event_output(ctx, &mastersecret_events, BPF_F_CURRENT_CPU, mastersecret,
sizeof(struct mastersecret_t));
return 0;
}

// get cipher_suite_st pointer
debug_bpf_printk("cipher_suite_st pointer: %x\n", ssl_cipher_st_ptr);
ret = bpf_probe_read_user(&address, sizeof(address), ssl_cipher_st_ptr);
if (ret) {
debug_bpf_printk(
"bpf_probe_read ssl_cipher_st_ptr failed, ret :%d\n", ret);
return 0;
}

void *cipher_id_ptr = (void *)(address + SESSION_CIPHER_ID_OFFSET);
ret = bpf_probe_read_user(&mastersecret->cipher_id,
sizeof(mastersecret->cipher_id), cipher_id_ptr);
if (ret) {
debug_bpf_printk(
"bpf_probe_read SESSION_CIPHER_ID_OFFSET failed, ret :%d\n", ret);
return 0;
}
debug_bpf_printk("cipher_id: %d\n", mastersecret->cipher_id);

//////////////////// TLS 1.3 master secret ////////////////////////

void *hs_ptr_tls13 = (void *)(ssl_st_ptr + HANDSHAKE_SECRET_OFFSET);
ret = bpf_probe_read_user(&mastersecret->handshake_secret, sizeof(mastersecret->handshake_secret), (void *)hs_ptr_tls13);
if (ret) {
debug_bpf_printk(
"bpf_probe_read HANDSHAKE_SECRET_OFFSET failed, ret :%d\n", ret);
return 0;
}

void *ms_ptr_tls13 = (void *)(ssl_st_ptr + MASTER_SECRET_OFFSET);
ret = bpf_probe_read_user(&mastersecret->master_secret, sizeof(mastersecret->master_secret), (void *)ms_ptr_tls13);
if (ret) {
debug_bpf_printk(
"bpf_probe_read MASTER_SECRET_OFFSET failed, ret :%d\n", ret);
return 0;
}

void *sf_ptr_tls13 = (void *)(ssl_st_ptr + SERVER_FINISHED_HASH_OFFSET);
ret = bpf_probe_read_user(&mastersecret->server_finished_hash, sizeof(mastersecret->server_finished_hash), (void *)sf_ptr_tls13);
if (ret) {
debug_bpf_printk(
"bpf_probe_read SERVER_FINISHED_HASH_OFFSET failed, ret :%d\n", ret);
return 0;
}

void *hth_ptr_tls13 = (void *)(ssl_st_ptr + HANDSHAKE_TRAFFIC_HASH_OFFSET);
ret = bpf_probe_read_user(&mastersecret->handshake_traffic_hash, sizeof(mastersecret->handshake_traffic_hash), (void *)hth_ptr_tls13);
if (ret) {
debug_bpf_printk(
"bpf_probe_read HANDSHAKE_TRAFFIC_HASH_OFFSET failed, ret :%d\n", ret);
return 0;
}

void *ems_ptr_tls13 = (void *)(ssl_st_ptr + EXPORTER_MASTER_SECRET_OFFSET);
ret = bpf_probe_read_user(&mastersecret->exporter_master_secret, sizeof(mastersecret->exporter_master_secret), (void *)ems_ptr_tls13);
if (ret) {
debug_bpf_printk(
"bpf_probe_read EXPORTER_MASTER_SECRET_OFFSET failed, ret :%d\n", ret);
return 0;
}
debug_bpf_printk("*****master_secret*****: %x %x %x\n",mastersecret->master_secret[0],mastersecret->master_secret[1],mastersecret->master_secret[2]);
bpf_perf_event_output(ctx, &mastersecret_events, BPF_F_CURRENT_CPU, mastersecret,
sizeof(struct mastersecret_t));
return 0;
}
Loading