Skip to content

Commit

Permalink
feat(ssl_sniffer): added process name to logs
Browse files Browse the repository at this point in the history
  • Loading branch information
rphang committed May 14, 2024
1 parent 69aa352 commit a888675
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 38 deletions.
Binary file added resources/ssl_sniffer_demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
70 changes: 55 additions & 15 deletions src/ssl_sniffer/README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,68 @@
# SSL Sniffer

> [!NOTE]
> Still in WIP. A `bpftrace_demo.sh` script is provided to try to sniff any encrypted messages on a provided program.
> Run the script with `sudo ./bpftrace_demo.sh <program_name/path>`.
`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.

`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.
![ssl_demo](../../resources/ssl_sniffer_demo.gif)

## Features

- ✅ Sniff many SSL/TLS libraries (OpenSSL, GnuTLS, NSS)
- ✅ On-the-fly SSL/TLS traffic sniffing (no need to install certificates & restart the application)
- ✅ Bypass SSL Pinning
- 🚧 [**Planned**] protocol parsing (HTTP2+)

It supports out-of-the-box the following applications:
- `curl`
- `wget`
- `nginx` (not all versions, some have inbuilt SSL/TLS support)
& many more...

## Usage

```bash
$ sudo ./ssl_sniffer
libssl.so probes attached to ...
```

From there, any application making uses of the system's SSL/TLS libraries will be sniffed. The output will be displayed on the terminal.

```bash
$ sudo ./ssl_sniffer
...
Press Ctrl+C to stop
[+] curl(12345), ts: 1234567890, op: SSL_OP_WRITE, len: 78 -->
GET / HTTP/1.1
Host: www.google.com
User-Agent: curl/7.81.0
Accept: */*


[+] curl(12345), ts: 1234567890, op: SSL_OP_READ, len: 1378 -->
HTTP/1.1 200 OK
Date: ...
Set-Cookie: ...
...
```

## How it works

`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:
- OpenSSL
- GnuTLS
- NSS

> [!IMPORTANT]
> 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.
>
> 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.
# Requirements
## Requirements

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

# Steps
Tested on:
- Ubuntu 22.04 LTS (kernel 5.15.0-106-generic)

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

We now know the outgoing/incoming data of any SSL/TLS connection on the machine and their destination.
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.
55 changes: 46 additions & 9 deletions src/ssl_sniffer/ebpf/loader.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <bpf/libbpf.h>

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

static void int_exit(int sig)
/**
* @brief Unload and destroy the BPF program & poller
*
* @return void
*/
void ssl_exit()
{
if (!skel)
return;
exiting = true;
sniffer_bpf__destroy(skel);
exit(0);
}

/**
Expand Down Expand Up @@ -70,9 +75,6 @@ int ssl_load()
fprintf(stderr, "Failed to load BPF skeleton\n");
return 1;
}

signal(SIGINT, int_exit);
signal(SIGTERM, int_exit);
return 0;
}

Expand All @@ -92,14 +94,49 @@ int ssl_attach_openssl(char *program_path)
return 0;
}

/**
* @brief Attach the GnuTLS probes to the specified library/program path
*
* @param program_path the path to the program/library to attach the probes to
* @return int 0 if the probes are attached successfully, 1 otherwise
*/
int ssl_attach_gnutls(char *program_path)
{
__ATTACH_UPROBE(program_path, "gnutls_record_send", probe_ssl_rw_enter, false);
__ATTACH_UPROBE(program_path, "gnutls_record_send", probe_ssl_write_return, true);
__ATTACH_UPROBE(program_path, "gnutls_record_recv", probe_ssl_rw_enter, false);
__ATTACH_UPROBE(program_path, "gnutls_record_recv", probe_ssl_read_return, true);
return 0;
}

/**
* @brief Attach the NSS probes to the specified library/program path
*
* @param program_path the path to the program/library to attach the probes to
* @return int 0 if the probes are attached successfully, 1 otherwise
*/
int ssl_attach_nss(char *program_path)
{
__ATTACH_UPROBE(program_path, "PR_Write" , probe_ssl_rw_enter, false);
__ATTACH_UPROBE(program_path, "PR_Write" , probe_ssl_write_return, true);
__ATTACH_UPROBE(program_path, "PR_Read" , probe_ssl_rw_enter, false);
__ATTACH_UPROBE(program_path, "PR_Read" , probe_ssl_read_return, true);
__ATTACH_UPROBE(program_path, "PR_Send" , probe_ssl_rw_enter, false);
__ATTACH_UPROBE(program_path, "PR_Send" , probe_ssl_write_return, true);
__ATTACH_UPROBE(program_path, "PR_Recv" , probe_ssl_rw_enter, false);
__ATTACH_UPROBE(program_path, "PR_Recv" , probe_ssl_read_return, true);
return 0;
}

static void log_event(struct data_event *event)
{
char *op = event->op == 1 ? "SSL_OP_READ" : "SSL_OP_WRITE";
printf("program: %s(%d), ts: %llu, op: %s, len: %d --> \n", event->comm, event->pid, event->ts, op, event->len);
fprintf(stdout, "[+] %s(%d), ts: %llu, op: %s, len: %d --> \n", event->comm, event->pid, event->ts, op, event->len);
for (int i = 0; i < event->len; i++)
{
printf("%c", event->data[i]);
fprintf(stdout, "%c", event->data[i]);
}
fprintf(stdout, "\n");
}

static int handle_event(void *ctx, void *data, size_t len)
Expand Down Expand Up @@ -137,7 +174,7 @@ int ssl_listen_event()
}
if (err < 0)
{
printf("Error polling ring buffer: %d\n", err);
fprintf(stderr, "Error polling ring buffer: %d\n", err);
break;
}
}
Expand Down
8 changes: 7 additions & 1 deletion src/ssl_sniffer/ebpf/main.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ static __always_inline int handle_rw_exit(struct pt_regs *ctx, int is_write)
int resp = PT_REGS_RC_CORE(ctx);
if (resp <= 0)
return 0;
if (resp > MAX_DATA_LEN)
bpf_printk("we might lose some data (%d), need some recursive read\n", resp);
u32 read_len = min((size_t)resp, (size_t)MAX_DATA_LEN);

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

//if (!is_write)
// bpf_probe_write_user((void *)*buf, "HTTP/1.1 200 OK\nContent-Length: 12\n\nHello World\n\00", 50);
// We can fake the data being sent back to user space but difference in read size will be detected

bpf_core_read_user(&msg->data, read_len, (void *)*buf);
// Sending to ring buffer
bpf_ringbuf_submit(msg, 0);
return 0;
Expand Down
4 changes: 4 additions & 0 deletions src/ssl_sniffer/include/ebpf/loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
#define BPF_ENTRY_H

int ssl_load();
void ssl_exit();
int ssl_listen_event();

int ssl_attach_openssl(char* program_path);
int ssl_attach_gnutls(char* program_path);
int ssl_attach_nss(char* program_path);

#endif
42 changes: 29 additions & 13 deletions src/ssl_sniffer/sniffer.c
Original file line number Diff line number Diff line change
@@ -1,32 +1,48 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

#include "ebpf/loader.h"
#include "utils/libresolver.h"

#define __ATTACH_SYS_LIBRARY(library_name, ebpf_fn) \
do \
{ \
char library_path[MAX_PATH_LEN] = {0}; \
if (global_search_library(library_name, library_path) == 0) \
{ \
if (ssl_attach_##ebpf_fn(library_path) != 0) \
{ \
fprintf(stderr, "Failed to attach %s probes\n", library_name); \
return 1; \
} \
fprintf(stdout, "%s probes attached to %s\n", library_name, library_path); \
} \
} while (0)

void exit_handler(int sig)
{
fprintf(stdout, "Exiting...\n");
ssl_exit();
exit(0);
}

int main()
{
if (ssl_load() != 0)
{
return 1;
}

char found_path[MAX_PATH_LEN];
char library_name[] = "libssl.so";
if (global_search_library(library_name, found_path) != 0)
{
fprintf(stderr, "Failed to find library %s\n", library_name);
return 1;
}
__ATTACH_SYS_LIBRARY("libssl.so", openssl);
__ATTACH_SYS_LIBRARY("libgnutls.so", gnutls);
__ATTACH_SYS_LIBRARY("libnspr4.so", nss);

if (ssl_attach_openssl(found_path) != 0)
{
fprintf(stderr, "Failed to attach openssl\n");
return 1;
}
signal(SIGINT, exit_handler);
signal(SIGTERM, exit_handler);

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

if (ssl_listen_event() != 0)
{
Expand Down

0 comments on commit a888675

Please sign in to comment.