Last active
November 7, 2024 21:22
-
-
Save yarienkiva/75c6db73c9d1da31f4c1e831ba08d9bb to your computer and use it in GitHub Desktop.
Recover the plaintext by extracting the internal state (a C array) of a RC4 algorithm in memory.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import subprocess | |
import re | |
PID = int(subprocess.check_output(["pgrep", "-f", ...])) | |
CIPHERTEXT = ... | |
class RC4: | |
def __init__(self, key: bytes) -> None: | |
self.S = list(range(256)) | |
j = 0 | |
for i in range(256): | |
j = (j + self.S[i] + key[i % len(key)]) % 256 | |
self.S[i], self.S[j] = self.S[j], self.S[i] | |
def PRGA(self) -> int: | |
i = 0 | |
j = 0 | |
while True: | |
i = (i + 1) % 256 | |
j = (j + self.S[i]) % 256 | |
self.S[i], self.S[j] = self.S[j], self.S[i] | |
K = self.S[(self.S[i] + self.S[j]) % 256] | |
yield K | |
def encrypt(self, plaintext: bytes) -> bytes: | |
keystream = self.PRGA() | |
return bytes(bytearray([c ^ next(keystream) for c in plaintext])) | |
def false_positive(b: list) -> bool: | |
x = 0 | |
for i in range(1, len(b)): | |
if b[i - 1] > b[i]: | |
x += 1 | |
return x in [0, 1, 254, 255] | |
def find_state(s: bytes) -> list: | |
start = 0 | |
char_map = {} | |
internal_states = [] | |
for end in range(len(s)): | |
if s[end] in char_map: | |
start = max(start, char_map[s[end]] + 1) | |
char_map[s[end]] = end | |
if end - start + 1 == 256 and not false_positive(s[start : end + 1]): | |
print(f"Key at {hex(start)}:", list(s[start : end + 1])) | |
internal_states.append(s[start : end + 1]) | |
return internal_states | |
def recover_plaintext(ct: bytes, output_state: list) -> None: | |
for y in range(256): | |
i, j = len(ct), y | |
state = [*output_state] | |
while i != 0: | |
state[i], state[j] = state[j], state[i] | |
j = (j - state[i]) % 256 | |
i = (i - 1) % 256 | |
if j != 0: | |
continue | |
rc4 = RC4(b"dummykey") | |
rc4.S = [*state] | |
flag = rc4.encrypt(CIPHERTEXT) | |
if flag.isascii(): | |
print("possible plaintext:", flag) | |
print(f"Searching process={PID}") | |
with open(f"/proc/{PID}/maps", "r") as f: | |
maps = f.read().splitlines() | |
mems = open(f"/proc/{PID}/mem", "rb", 0) | |
for line in maps: | |
try: | |
m = re.match(r"([0-9A-Fa-f]+)-([0-9A-Fa-f]+) ([-r])", line) | |
if not (m.group(3) == "r"): | |
continue | |
start, end = int(m.group(1), 16), int(m.group(2), 16) | |
path = "<anon>" if len(line.strip().split()) == 5 else line.strip().split()[5] | |
print(f"Parsing: '{path}', size:{hex(end - start)}") | |
mems.seek(start) | |
chunk = mems.read(end - start) | |
for state in find_state(chunk): | |
recover_plaintext(CIPHERTEXT, state) | |
except Exception as e: | |
print("Error :", e) | |
mems.close() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment