Skip to content

Instantly share code, notes, and snippets.

@yarienkiva
Last active November 7, 2024 21:22
Show Gist options
  • Save yarienkiva/75c6db73c9d1da31f4c1e831ba08d9bb to your computer and use it in GitHub Desktop.
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.
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