Commit d496d5ce authored by Adrian Lopez's avatar Adrian Lopez

Tool to sniff data contents before encrypted with OpenSSL

Add tool as talked in iovisor-dev 'BCC: bpf_probe_read read function arguments'
parent 6f89445a
...@@ -110,6 +110,7 @@ Examples: ...@@ -110,6 +110,7 @@ Examples:
- tools/[runqlat](tools/runqlat.py): Run queue (scheduler) latency as a histogram. [Examples](tools/runqlat_example.txt). - tools/[runqlat](tools/runqlat.py): Run queue (scheduler) latency as a histogram. [Examples](tools/runqlat_example.txt).
- tools/[softirqs](tools/softirqs.py): Measure soft IRQ (soft interrupt) event time. [Examples](tools/softirqs_example.txt). - tools/[softirqs](tools/softirqs.py): Measure soft IRQ (soft interrupt) event time. [Examples](tools/softirqs_example.txt).
- tools/[solisten](tools/solisten.py): Trace TCP socket listen. [Examples](tools/solisten_example.txt). - tools/[solisten](tools/solisten.py): Trace TCP socket listen. [Examples](tools/solisten_example.txt).
- tools/[sslsniff](tools/sslsniff.py): Sniff OpenSSL written and readed data. [Examples](tools/sslsniff_example.txt).
- tools/[stackcount](tools/stackcount.py): Count kernel function calls and their stack traces. [Examples](tools/stackcount_example.txt). - tools/[stackcount](tools/stackcount.py): Count kernel function calls and their stack traces. [Examples](tools/stackcount_example.txt).
- tools/[stacksnoop](tools/stacksnoop.py): Trace a kernel function and print all kernel stack traces. [Examples](tools/stacksnoop_example.txt). - tools/[stacksnoop](tools/stacksnoop.py): Trace a kernel function and print all kernel stack traces. [Examples](tools/stacksnoop_example.txt).
- tools/[statsnoop](tools/statsnoop.py): Trace stat() syscalls. [Examples](tools/statsnoop_example.txt). - tools/[statsnoop](tools/statsnoop.py): Trace stat() syscalls. [Examples](tools/statsnoop_example.txt).
......
.TH sslsniff 8 "2016-08-16" "USER COMMANDS"
.SH NAME
sslsniff \- Print data passed to OpenSSL. Uses Linux eBPF/bcc.
.SH SYNOPSIS
.B sslsniff
.SH DESCRIPTION
sslsniff prints data sent to SSL_write and SSL_read OpenSSL functions, allowing
us to read plain text content before encryption (when writing) and after
decryption (when reading).
This works reading the second parameter of both functions (*buf).
Since this uses BPF, only the root user can use this tool.
.SH REQUIREMENTS
CONFIG_BPF and bcc.
.SH EXAMPLES
.TP
Print all calls to SSL_write and SSL_read system-wide:
#
.B sslsniff
.SH FIELDS
.TP
FUNC
Which function is being called (SSL_write or SSL_read)
.TP
TIME
Time of the command, in seconds.
.TP
COMM
Entered command.
.TP
PID
Process ID calling OpenSSL.
.TP
LEN
Bytes written or read by OpenSSL functions.
.SH SOURCE
This is from bcc.
.IP
https://github.com/iovisor/bcc
.PP
Also look in the bcc distribution for a companion _examples.txt file containing
example usage, output, and commentary for this tool.
.SH OS
Linux
.SH STABILITY
Unstable - in development.
.SH AUTHORS
Adrian Lopez and Mark Drayton
.SH SEE ALSO
trace(8)
#!/usr/bin/python
#
# sslsniff Captures data on SSL_READ or SSL_WRITE functions of OpenSSL
# For Linux, uses BCC, eBPF.
#
# Licensed under the Apache License, Version 2.0 (the "License")
#
# 12-Aug-2016 Adrian Lopez Created this.
# 13-Aug-2016 Mark Drayton Fix SSL_Read
from __future__ import print_function
import ctypes as ct
from bcc import BPF
prog = """
#include <linux/ptrace.h>
#include <linux/sched.h> /* For TASK_COMM_LEN */
struct probe_SSL_data_t {
u64 timestamp_ns;
u32 pid;
char comm[TASK_COMM_LEN];
char v0[472];
u32 len;
};
BPF_PERF_OUTPUT(perf_SSL_write);
int probe_SSL_write(struct pt_regs *ctx, void *ssl, void *buf, int num) {
struct probe_SSL_data_t __data = {0};
__data.timestamp_ns = bpf_ktime_get_ns();
__data.pid = bpf_get_current_pid_tgid();
__data.len = num;
bpf_get_current_comm(&__data.comm, sizeof(__data.comm));
if ( buf != 0) {
bpf_probe_read(&__data.v0, sizeof(__data.v0), buf);
}
perf_SSL_write.perf_submit(ctx, &__data, sizeof(__data));
return 0;
}
BPF_PERF_OUTPUT(perf_SSL_read);
BPF_HASH(bufs, u32, u64);
int probe_SSL_read_enter(struct pt_regs *ctx, void *ssl, void *buf, int num) {
u32 pid = bpf_get_current_pid_tgid();
bufs.update(&pid, (u64*)&buf);
return 0;
}
int probe_SSL_read_exit(struct pt_regs *ctx, void *ssl, void *buf, int num) {
u32 pid = bpf_get_current_pid_tgid();
u64 *bufp = bufs.lookup(&pid);
if (bufp == 0) {
return 0;
}
struct probe_SSL_data_t __data = {0};
__data.timestamp_ns = bpf_ktime_get_ns();
__data.pid = pid;
__data.len = PT_REGS_RC(ctx);
bpf_get_current_comm(&__data.comm, sizeof(__data.comm));
if (bufp != 0) {
bpf_probe_read(&__data.v0, sizeof(__data.v0), (char *)*bufp);
}
bufs.delete(&pid);
perf_SSL_read.perf_submit(ctx, &__data, sizeof(__data));
return 0;
}
"""
b = BPF(text=prog)
# Join to ssl_write
b.attach_uprobe(name="ssl", sym="SSL_write", fn_name="probe_SSL_write")
# Join to ssl_read
# It looks like SSL_read's arguments aren't available in a return probe so you
# need to stash the buffer address in a map on the function entry and read it
# on its exit (Mark Drayton)
b.attach_uprobe(name="ssl", sym="SSL_read", fn_name="probe_SSL_read_enter")
b.attach_uretprobe(name="ssl", sym="SSL_read", fn_name="probe_SSL_read_exit")
# define output data structure in Python
TASK_COMM_LEN = 16 # linux/sched.h
MAX_BUF_SIZE = 472 # Limited by the BPF stack
# Max size of the whole struct: 512 bytes
class Data(ct.Structure):
_fields_ = [
("timestamp_ns", ct.c_ulonglong),
("pid", ct.c_uint),
("comm", ct.c_char * TASK_COMM_LEN),
("v0", ct.c_char * MAX_BUF_SIZE),
("len", ct.c_uint)
]
# header
print("%-12s %-18s %-16s %-6s %-6s" % ("FUNC", "TIME(s)", "COMM", "PID",
"LEN"))
# process event
start = 0
def print_event_write(cpu, data, size):
print_event(cpu, data, size, "SSL_WRITE")
def print_event_read(cpu, data, size):
print_event(cpu, data, size, "SSL_READ")
def print_event(cpu, data, size, rw):
global start
event = ct.cast(data, ct.POINTER(Data)).contents
if start == 0:
start = event.timestamp_ns
time_s = (float(event.timestamp_ns - start)) / 1000000000
s_mark = "-" * 5 + " DATA " + "-" * 5
e_mark = "-" * 5 + " END DATA " + "-" * 5
truncated_bytes = event.len - MAX_BUF_SIZE
if truncated_bytes > 0 :
e_mark = "-" * 5 + " END DATA (TRUNCATED, " + str(truncated_bytes) + \
" bytes lost) " + "-" * 5
print("%-12s %-18.9f %-16s %-6d %-6d\n%s\n%s\n%s\n" % (rw, time_s,
event.comm,
event.pid,
event.len,
s_mark, event.v0,
e_mark))
b["perf_SSL_write"].open_perf_buffer(print_event_write)
b["perf_SSL_read"].open_perf_buffer(print_event_read)
while 1:
b.kprobe_poll()
Demonstrations of sslsniff.py
This tool traces the OpenSSL functions SSL_READ and SSL_WRITE.
Data passed to this functions is printed as plain text.
Useful, for example, to sniff HTTP before encrypted with SSL.
Output of tool executing in other shell "curl https://example.com"
% sudo python sslsniff.py
FUNC TIME(s) COMM PID LEN
SSL_WRITE 0.000000000 curl 12915 75
----- DATA -----
GET / HTTP/1.1
Host: example.com
User-Agent: curl/7.50.1
Accept: */*
----- END DATA -----
SSL_READ 0.127144585 curl 12915 333
----- DATA -----
HTTP/1.1 200 OK
Cache-Control: max-age=604800
Content-Type: text/html
Date: Tue, 16 Aug 2016 15:42:12 GMT
Etag: "359670651+gzip+ident"
Expires: Tue, 23 Aug 2016 15:42:12 GMT
Last-Modified: Fri, 09 Aug 2013 23:54:35 GMT
Server: ECS (iad/18CB)
Vary: Accept-Encoding
X-Cache: HIT
x-ec-custom-error: 1
Content-Length: 1270
----- END DATA -----
SSL_READ 0.129967972 curl 12915 1270
----- DATA -----
<!doctype html>
<html>
<head>
<title>Example Domain</title>
<meta charset="utf-8" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
body {
background-color: #f0f0f2;
margin: 0;
padding: 0;
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
}
div {
w
----- END DATA (TRUNCATED, 798 bytes lost) -----
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment