Skip to content

Commit 72faefc

Browse files
committed
feat(ssl_sniffer): added process name to logs
1 parent e4ddc42 commit 72faefc

File tree

6 files changed

+141
-38
lines changed

6 files changed

+141
-38
lines changed

resources/ssl_sniffer_demo.gif

1.67 MB
Loading

src/ssl_sniffer/README.md

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,68 @@
11
# SSL Sniffer
22

3-
> [!NOTE]
4-
> Still in WIP. A `bpftrace_demo.sh` script is provided to try to sniff any encrypted messages on a provided program.
5-
> Run the script with `sudo ./bpftrace_demo.sh <program_name/path>`.
3+
`ssl_sniffer` is a simple tool to sniff on-going SSL/TLS traffic on the machine without installing, trusting, or modifying any certificate. It will **intercept the raw decrypted SSL/TLS traffic**, and display it on the fly.
64

7-
`ssl_sniffer` is a simple tool to sniff on-going SSL/TLS traffic on the machine without installing, trusting, or modifying any certificate. It will **intercept the SSL/TLS traffic** and **decrypt it** on the fly at the system SSL libraries level.
5+
![ssl_demo](../../resources/ssl_sniffer_demo.gif)
6+
7+
## Features
8+
9+
- ✅ Sniff many SSL/TLS libraries (OpenSSL, GnuTLS, NSS)
10+
- ✅ On-the-fly SSL/TLS traffic sniffing (no need to install certificates & restart the application)
11+
- ✅ Bypass SSL Pinning
12+
- 🚧 [**Planned**] protocol parsing (HTTP2+)
13+
14+
It supports out-of-the-box the following applications:
15+
- `curl`
16+
- `wget`
17+
- `nginx` (not all versions, some have inbuilt SSL/TLS support)
18+
& many more...
19+
20+
## Usage
21+
22+
```bash
23+
$ sudo ./ssl_sniffer
24+
libssl.so probes attached to ...
25+
```
26+
27+
From there, any application making uses of the system's SSL/TLS libraries will be sniffed. The output will be displayed on the terminal.
28+
29+
```bash
30+
$ sudo ./ssl_sniffer
31+
...
32+
Press Ctrl+C to stop
33+
[+] curl(12345), ts: 1234567890, op: SSL_OP_WRITE, len: 78 -->
34+
GET / HTTP/1.1
35+
Host: www.google.com
36+
User-Agent: curl/7.81.0
37+
Accept: */*
38+
39+
40+
[+] curl(12345), ts: 1234567890, op: SSL_OP_READ, len: 1378 -->
41+
HTTP/1.1 200 OK
42+
Date: ...
43+
Set-Cookie: ...
44+
...
45+
```
46+
47+
## How it works
48+
49+
`ssl_sniffer` is an eBPF program that will be attached to various uprobe and kprobe events to intercept the SSL/TLS traffic. It supports the following libraries:
50+
- OpenSSL
51+
- GnuTLS
52+
- NSS
853

954
> [!IMPORTANT]
1055
> Despite trying to sniff most of the SSL/TLS traffic, some applications might not be appearing in the traffic. This is because the application might be **using a different non supported SSL/TLS library**, **bringing their own library** in a directory which is not being sniffed or have **inbuilt SSL/TLS support**. It would still be possible to hijack them if we know their paths and trying to guess func. by their DWARF info.
1156
>
1257
> The hardest case is when the statically compiled application is using **non-standard SSL/TLS libraries** and have **no DWARF info**. In this case, it's better to give up unless we know the functions locations.
1358
14-
# Requirements
59+
## Requirements
1560

16-
- Kernel version >= 5.8 (could be lower but that requires to ditch ring buffer's and replace them with perf buffers)
61+
- Kernel version **>= 5.8** (could be lower but that requires to ditch ring buffer's and replace them with perf buffers)
1762

18-
# Steps
63+
Tested on:
64+
- Ubuntu 22.04 LTS (kernel 5.15.0-106-generic)
1965

20-
- for every running KNOWN process to sniff, we will:
21-
- Listen for the `connect` syscall and store the file descriptor IF its a remote connection (may think about local connections later)
22-
- Linkage of the SSL/TLS library to the TCP file descriptor:
23-
- (OpenSSL) Listen for the `SSL_set_fd` function call and store the SSL context linked to the file descriptor
24-
- (GnuTLS) Listen for the `gnutls_transport_set_ptr` function call and store the SSL context linked to the file descriptor
25-
- (NSS) Listen for the `PRFileDesc` structure and store the SSL context linked to the file descriptor
26-
- Listen for the equivalent `SSL_read` and `SSL_write` function calls and decrypt the data linked to the SSL context we stored earlier.
66+
## Extra
2767

28-
We now know the outgoing/incoming data of any SSL/TLS connection on the machine and their destination.
68+
A `bpftrace_demo.sh` script is provided to try to sniff any encrypted SSL/TLS traffic on specified programs. It will use `bpftrace` to compile and load the eBPF program. It's a simplier version of the `ssl_sniffer` tool with truncated output.

src/ssl_sniffer/ebpf/loader.c

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include <stdio.h>
22
#include <stdlib.h>
3-
#include <signal.h>
43
#include <bpf/libbpf.h>
54

65
#include "ebpf/loader.h"
@@ -31,11 +30,17 @@ static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va
3130
return vfprintf(stderr, format, args);
3231
}
3332

34-
static void int_exit(int sig)
33+
/**
34+
* @brief Unload and destroy the BPF program & poller
35+
*
36+
* @return void
37+
*/
38+
void ssl_exit()
3539
{
40+
if (!skel)
41+
return;
3642
exiting = true;
3743
sniffer_bpf__destroy(skel);
38-
exit(0);
3944
}
4045

4146
/**
@@ -70,9 +75,6 @@ int ssl_load()
7075
fprintf(stderr, "Failed to load BPF skeleton\n");
7176
return 1;
7277
}
73-
74-
signal(SIGINT, int_exit);
75-
signal(SIGTERM, int_exit);
7678
return 0;
7779
}
7880

@@ -92,14 +94,49 @@ int ssl_attach_openssl(char *program_path)
9294
return 0;
9395
}
9496

97+
/**
98+
* @brief Attach the GnuTLS probes to the specified library/program path
99+
*
100+
* @param program_path the path to the program/library to attach the probes to
101+
* @return int 0 if the probes are attached successfully, 1 otherwise
102+
*/
103+
int ssl_attach_gnutls(char *program_path)
104+
{
105+
__ATTACH_UPROBE(program_path, "gnutls_record_send", probe_ssl_rw_enter, false);
106+
__ATTACH_UPROBE(program_path, "gnutls_record_send", probe_ssl_write_return, true);
107+
__ATTACH_UPROBE(program_path, "gnutls_record_recv", probe_ssl_rw_enter, false);
108+
__ATTACH_UPROBE(program_path, "gnutls_record_recv", probe_ssl_read_return, true);
109+
return 0;
110+
}
111+
112+
/**
113+
* @brief Attach the NSS probes to the specified library/program path
114+
*
115+
* @param program_path the path to the program/library to attach the probes to
116+
* @return int 0 if the probes are attached successfully, 1 otherwise
117+
*/
118+
int ssl_attach_nss(char *program_path)
119+
{
120+
__ATTACH_UPROBE(program_path, "PR_Write" , probe_ssl_rw_enter, false);
121+
__ATTACH_UPROBE(program_path, "PR_Write" , probe_ssl_write_return, true);
122+
__ATTACH_UPROBE(program_path, "PR_Read" , probe_ssl_rw_enter, false);
123+
__ATTACH_UPROBE(program_path, "PR_Read" , probe_ssl_read_return, true);
124+
__ATTACH_UPROBE(program_path, "PR_Send" , probe_ssl_rw_enter, false);
125+
__ATTACH_UPROBE(program_path, "PR_Send" , probe_ssl_write_return, true);
126+
__ATTACH_UPROBE(program_path, "PR_Recv" , probe_ssl_rw_enter, false);
127+
__ATTACH_UPROBE(program_path, "PR_Recv" , probe_ssl_read_return, true);
128+
return 0;
129+
}
130+
95131
static void log_event(struct data_event *event)
96132
{
97133
char *op = event->op == 1 ? "SSL_OP_READ" : "SSL_OP_WRITE";
98-
printf("program: %s(%d), ts: %llu, op: %s, len: %d --> \n", event->comm, event->pid, event->ts, op, event->len);
134+
fprintf(stdout, "[+] %s(%d), ts: %llu, op: %s, len: %d --> \n", event->comm, event->pid, event->ts, op, event->len);
99135
for (int i = 0; i < event->len; i++)
100136
{
101-
printf("%c", event->data[i]);
137+
fprintf(stdout, "%c", event->data[i]);
102138
}
139+
fprintf(stdout, "\n");
103140
}
104141

105142
static int handle_event(void *ctx, void *data, size_t len)
@@ -137,7 +174,7 @@ int ssl_listen_event()
137174
}
138175
if (err < 0)
139176
{
140-
printf("Error polling ring buffer: %d\n", err);
177+
fprintf(stderr, "Error polling ring buffer: %d\n", err);
141178
break;
142179
}
143180
}

src/ssl_sniffer/ebpf/main.bpf.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ static __always_inline int handle_rw_exit(struct pt_regs *ctx, int is_write)
5050
int resp = PT_REGS_RC_CORE(ctx);
5151
if (resp <= 0)
5252
return 0;
53+
if (resp > MAX_DATA_LEN)
54+
bpf_printk("we might lose some data (%d), need some recursive read\n", resp);
5355
u32 read_len = min((size_t)resp, (size_t)MAX_DATA_LEN);
5456

5557
// Prepare to send to user space (ring buffer)
@@ -65,8 +67,12 @@ static __always_inline int handle_rw_exit(struct pt_regs *ctx, int is_write)
6567
bpf_core_read(&msg->ts, sizeof(msg->ts), &ts);
6668
msg->op = is_write ? SSL_OP_WRITE : SSL_OP_READ;
6769
msg->len = resp;
68-
bpf_core_read_user(&msg->data, read_len, (void *)*buf);
6970

71+
//if (!is_write)
72+
// bpf_probe_write_user((void *)*buf, "HTTP/1.1 200 OK\nContent-Length: 12\n\nHello World\n\00", 50);
73+
// We can fake the data being sent back to user space but difference in read size will be detected
74+
75+
bpf_core_read_user(&msg->data, read_len, (void *)*buf);
7076
// Sending to ring buffer
7177
bpf_ringbuf_submit(msg, 0);
7278
return 0;

src/ssl_sniffer/include/ebpf/loader.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
#define BPF_ENTRY_H
33

44
int ssl_load();
5+
void ssl_exit();
56
int ssl_listen_event();
7+
68
int ssl_attach_openssl(char* program_path);
9+
int ssl_attach_gnutls(char* program_path);
10+
int ssl_attach_nss(char* program_path);
711

812
#endif

src/ssl_sniffer/sniffer.c

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,48 @@
11
#include <stdio.h>
22
#include <stdlib.h>
33
#include <unistd.h>
4+
#include <signal.h>
45

56
#include "ebpf/loader.h"
67
#include "utils/libresolver.h"
78

9+
#define __ATTACH_SYS_LIBRARY(library_name, ebpf_fn) \
10+
do \
11+
{ \
12+
char library_path[MAX_PATH_LEN] = {0}; \
13+
if (global_search_library(library_name, library_path) == 0) \
14+
{ \
15+
if (ssl_attach_##ebpf_fn(library_path) != 0) \
16+
{ \
17+
fprintf(stderr, "Failed to attach %s probes\n", library_name); \
18+
return 1; \
19+
} \
20+
fprintf(stdout, "%s probes attached to %s\n", library_name, library_path); \
21+
} \
22+
} while (0)
23+
24+
void exit_handler(int sig)
25+
{
26+
fprintf(stdout, "Exiting...\n");
27+
ssl_exit();
28+
exit(0);
29+
}
30+
831
int main()
932
{
1033
if (ssl_load() != 0)
1134
{
1235
return 1;
1336
}
1437

15-
char found_path[MAX_PATH_LEN];
16-
char library_name[] = "libssl.so";
17-
if (global_search_library(library_name, found_path) != 0)
18-
{
19-
fprintf(stderr, "Failed to find library %s\n", library_name);
20-
return 1;
21-
}
38+
__ATTACH_SYS_LIBRARY("libssl.so", openssl);
39+
__ATTACH_SYS_LIBRARY("libgnutls.so", gnutls);
40+
__ATTACH_SYS_LIBRARY("libnspr4.so", nss);
2241

23-
if (ssl_attach_openssl(found_path) != 0)
24-
{
25-
fprintf(stderr, "Failed to attach openssl\n");
26-
return 1;
27-
}
42+
signal(SIGINT, exit_handler);
43+
signal(SIGTERM, exit_handler);
2844

29-
printf("Press Ctrl+C to stop\n");
45+
fprintf(stdout, "Press Ctrl+C to stop\n");
3046

3147
if (ssl_listen_event() != 0)
3248
{

0 commit comments

Comments
 (0)