Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Guillaume Valadon committed May 19, 2022
0 parents commit c336fbd
Show file tree
Hide file tree
Showing 10 changed files with 800 additions and 0 deletions.
30 changes: 30 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# SPDX-License-Identifier: GPL-2.0+
# Guillaume Valadon <[email protected]>

FROM ubuntu:20.04

ENV TZ=Europe/Paris DEBIAN_FRONTEND=noninteractive
RUN echo 'PS1="quarkslab/peetch:\w# "' >> /root/.bashrc

# Install dependencies
RUN set -x && \
PACKAGES="bison build-essential cmake flex git \
libedit-dev libllvm11 llvm-11-dev libclang-11-dev python zlib1g-dev \
libelf-dev libfl-dev python3-distutils python3-pip linux-headers-$(uname -r) \
libssl-dev iproute2 tmux curl" &&\
apt-get update && apt-get install -y $PACKAGES && \
apt-get autoclean && apt-get --purge -y autoremove && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# Compile bcc
RUN git clone https://github.com/guedou/bcc && cd bcc/ && mkdir build && \
cd build && cmake .. && make install && cd src && make install && rm -rf /bcc/

# Install Scapy
RUN git clone https://github.com/guedou/scapy-issues && cd scapy-issues && \
git checkout pcapng-comment && pip install .[complete] && rm -rf /scapy-issues/
RUN pip install cryptography==2.8

# Install peetch
COPY . /peetch
RUN cd /peetch && pip install -r requirements.txt && pip install . && rm -rf /peetch
112 changes: 112 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# peetch

`peetch` is a collection of tools aimed at experimenting with different aspects of eBPF to bypass TLS protocol protections.

Currently, peetch includes two subcommands. The first called `dump` aims to sniff network traffic by associating information about the source process with each packet. The second called `tls` allows to identify processes using OpenSSL to extract cryptographic keys.

Combined, these two commands make it possible to decrypt TLS exchanges recorded in the PCAPng format.


# Installation

`peetch` relies on several dependencies including non-merged modifications of [bcc](https://github.com/iovisor/bcc) and [Scapy](https://github.com/secdev/scapy). A Docker image can be easily built in order to easily test `peetch` using the following command:
```
docker build -t quarkslab/peetch .
```


# Commands Walk Through

The following examples assume that you used the following command to enter the Docker image and launch examples within it:
```
docker run --privileged --network host --mount type=bind,source=/sys,target=/sys --mount type=bind,source=/proc,target=/proc --rm -it quarkslab/peetch
```


## `dump`

This sub-command gives you the ability to sniff packets using an eBPF TC classifier and to retrieve the corresponding PID and process names with:
```
peetch dump
curl/1289291 - Ether / IP / TCP 10.211.55.10:53052 > 208.97.177.124:https S / Padding
curl/1289291 - Ether / IP / TCP 208.97.177.124:https > 10.211.55.10:53052 SA / Padding
curl/1289291 - Ether / IP / TCP 10.211.55.10:53052 > 208.97.177.124:https A / Padding
curl/1289291 - Ether / IP / TCP 10.211.55.10:53052 > 208.97.177.124:https PA / Raw / Padding
curl/1289291 - Ether / IP / TCP 208.97.177.124:https > 10.211.55.10:53052 A / Padding
```

Note that for demonstration purposes, `dump` will only capture IPv4 based TCP segments.

For convenience, the captured packets can be store to PCAPng along with process information using `--write`:
```
peetch dump --write peetch.pcapng
^C
```

This PCAPng can easily be manipulated with Wireshark or Scapy:
```
scapy
>>> l = rdpcap("peetch.pcapng")
>>> l[0]
<Ether dst=00:1c:42:00:00:18 src=00:1c:42:54:f3:34 type=IPv4 |<IP version=4 ihl=5 tos=0x0 len=60 id=11088 flags=DF frag=0 ttl=64 proto=tcp chksum=0x4bb1 src=10.211.55.10 dst=208.97.177.124 |<TCP sport=53054 dport=https seq=631406526 ack=0 dataofs=10 reserved=0 flags=S window=64240 chksum=0xc3e9 urgptr=0 options=[('MSS', 1460), ('SAckOK', b''), ('Timestamp', (1272423534, 0)), ('NOP', None), ('WScale', 7)] |<Padding load='\x00\x00' |>>>>
>>> l[0].comment
b'curl/1289909'
```


## `tls`

This sub-command aims at identifying process that uses OpenSSl and makes it is to dump several things like plaintext and secrets.

By default, `peetch tls` will only display one line per process, the `--directions` argument makes it possible to display the exchanges messages:
```
peetch tls --directions
<- curl (1291078) 208.97.177.124/443 TLS1.2 ECDHE-RSA-AES128-GCM-SHA256
> curl (1291078) 208.97.177.124/443 TLS1.-1 ECDHE-RSA-AES128-GCM-SHA256
```

Displaying OpenSSL buffer content is achieved with `--content`.
```
peetch tls --content
<- curl (1290608) 208.97.177.124/443 TLS1.2 ECDHE-RSA-AES128-GCM-SHA256
0000 47 45 54 20 2F 20 48 54 54 50 2F 31 2E 31 0D 0A GET / HTTP/1.1..
0010 48 6F 73 74 3A 20 77 77 77 2E 70 65 72 64 75 2E Host: www.perdu.
0020 63 6F 6D 0D 0A 55 73 65 72 2D 41 67 65 6E 74 3A com..User-Agent:
0030 20 63 75 72 6C 2F 37 2E 36 38 2E 30 0D 0A 41 63 curl/7.68.0..Ac
-> curl (1290608) 208.97.177.124/443 TLS1.-1 ECDHE-RSA-AES128-GCM-SHA256
0000 48 54 54 50 2F 31 2E 31 20 32 30 30 20 4F 4B 0D HTTP/1.1 200 OK.
0010 0A 44 61 74 65 3A 20 54 68 75 2C 20 31 39 20 4D .Date: Thu, 19 M
0020 61 79 20 32 30 32 32 20 31 38 3A 31 36 3A 30 31 ay 2022 18:16:01
0030 20 47 4D 54 0D 0A 53 65 72 76 65 72 3A 20 41 70 GMT..Server: Ap
```


The `--secrets` arguments will display TLS Master Secrets extracted from memory. The following example leverages `--write` to write master secrets to discuss to simplify decruypting TLS messages with Scapy:

```
$ (sleep 5; curl https://www.perdu.com/?name=highly%20secret%20information --tls-max 1.2 -http1.1) &
# peetch tls --write &
curl (1293232) 208.97.177.124/443 TLS1.2 ECDHE-RSA-AES128-GCM-SHA256
# peetch dump --write traffic.pcapng
^C
# Add the master secret to a PCAPng file
$ editcap --inject-secrets tls,1293232-master_secret.log traffic.pcapng traffic-ms.pcapng
$ scapy
>>> load_layer("tls")
>>> conf.tls_session_enable = True
>>> l = rdpcap("traffic-ms.pcapng")
>>> l[13][TLS].msg
[<TLSApplicationData data='GET /?name=highly%20secret%20information HTTP/1.1\r\nHost: hello.guedou.workers.dev\r\nUser-Agent: curl/7.68.0\r\nAccept: */*\r\n\r\n' |>]
```


## Limitations

By design, peetch only supports OpenSSL and TLS 1.2. The default offsets for OpenSSL structures assume that you are using the `1.1.1f-1ubuntu2.13` on `arm64`. However, they can easily be changed using command line arguments.
1 change: 1 addition & 0 deletions bin/peetch
Empty file added peetch/__init__.py
Empty file.
9 changes: 9 additions & 0 deletions peetch/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0+
# Guillaume Valadon <[email protected]>

import sys

from peetch.main import main

main(sys.argv[1:])
101 changes: 101 additions & 0 deletions peetch/ebpf_programs/peetch_kprobes.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// SPDX-License-Identifier: GPL-2.0+
// Guillaume Valadon <[email protected]>

#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>


#include <uapi/linux/pkt_cls.h>
#include <uapi/linux/bpf.h>
#include <linux/sched.h>
#include <net/sock.h>


struct key_t {
u32 dst;
u32 src;
u64 padding;
};

struct data_t {
u32 pid;
char name[64];
};

BPF_HASH(pid_cache, struct key_t *);

BPF_PERF_OUTPUT(skb_events);

int process_frame(struct __sk_buff *skb)
{
// Data accessors
unsigned char *data = (void *)(long)skb->data;
unsigned char *data_end = (void *)(long)skb->data_end;

// Mapping data to the Ethernet and IP headers
struct ethhdr *eth = (struct ethhdr *)data;
struct iphdr *iph = (struct iphdr*) (data + sizeof(struct ethhdr));

// Simple length check
if ((data + sizeof(struct ethhdr) + sizeof(struct iphdr)) > data_end)
return TC_ACT_OK;

// Discard everything but IPv4
if (ntohs(eth->h_proto) != ETH_P_IP)
return TC_ACT_OK;

// Discard everything but TCP
if (iph->protocol != IPPROTO_TCP)
return TC_ACT_OK;

// Retrieve the PID and the process name from the IP addresses
struct key_t key = { .dst = iph->daddr, .src = iph->saddr };
struct data_t *value = (struct data_t *) pid_cache.lookup((struct key_t **)&key);
if (value == NULL) {
key.dst = iph->saddr;
key.src = iph->daddr;
value = (struct data_t *) pid_cache.lookup((struct key_t **)&key);
if (value == NULL)
return TC_ACT_OK;
}

// Check the PID
if (value->pid == 0)
return TC_ACT_OK;

skb_events.perf_submit_skb(skb, skb->len, value, sizeof(value));

return TC_ACT_OK;
}

int kprobe_security_sk_classify_flow(struct pt_regs *ctx, struct sock *sk, struct flowi *fl) {

if (sk->sk_family != AF_INET)
return 0;

// Extract IPv4 related structures
union flowi_uli uli;
struct flowi4 ip4;
bpf_probe_read(&ip4, sizeof(ip4), &fl->u.ip4);
bpf_probe_read(&uli, sizeof(uli), &ip4.uli);

// Get IP addresses and ports
struct key_t key;
struct data_t data;

bpf_probe_read(&key.src, sizeof(sk->__sk_common.skc_daddr), &sk->__sk_common.skc_daddr);
bpf_probe_read(&key.dst, sizeof(sk->__sk_common.skc_rcv_saddr), &sk->__sk_common.skc_rcv_saddr);

// Get and store the PID
u64 id = bpf_get_current_pid_tgid();
data.pid = id >> 32;

// Get and store the process name
bpf_get_current_comm(data.name, 64);

// Store data
pid_cache.update((struct key_t **) &key, (unsigned long long *) &data);

return 0;
}
Loading

0 comments on commit c336fbd

Please sign in to comment.