Tags: web nuclei 

Rating:

# [Misc] Nuclei TCP1P CTF 2023 Writeup

## Nuclei (Misc)

---

In this challenge, we were given a source code zipped in a file called `dist.zip`

Unzipping the file, we will have :

- Source code of a Flask web.
- Nuclei YAML Template

```python
#app.py
from flask import Flask, render_template, request, redirect, url_for
import subprocess
import re

app = Flask(__name__)

@app.route('/', methods=['GET'])
def index():
return render_template('index.html')

@app.route('/submit', methods=['POST'])
def submit():
url_pattern = re.compile(r'^(https?://[A-Za-z0-9\-._~:/?#\[\]@!$&\'()*+,;=]+)$')
url = request.form.get('url')

if url is None:
return "No URL provided.", 400

if not url_pattern.match(url):
return "Invalid URL format.", 400

if url:
command = ['./nuclei', '--silent', '-u', url, '-t', 'custom-templates.yaml']
try:
result = subprocess.run(command, capture_output=True, text=True)
print(result.stdout)
if 'info' in result.stdout and '/api/v2/echo' in result.stdout and 'custom-templates' in result.stdout:
return "TCP1P{fake_flag}"
else:
return "Your website isn't vulnerable"
except subprocess.CalledProcessError:
return "Error occurred while running command"
return "Invalid request"

if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
```

```yaml
#custom-templates.yaml
id: custom-templates

info:
name: Testing nuclei templates
author: daffainfo
severity: info
reference: https://daffa.info
metadata:
max-request: 2
tags: ctf,tcp1p

http:
- raw:
# Detect API Version
- |
GET /api/v1/version/ HTTP/1.1
Host: {{Hostname}}
Referer: https://daffa.info/

# XSS and Path Traversal
- |
GET /api/v2/echo/?name=<script>alert(1)</script>&file=/etc/passwd HTTP/1.1
Host: {{Hostname}}
Referer: https://daffa.info

req-condition: true
matchers-condition: and
matchers:
- type: dsl
dsl:
- compare_versions(version, '<= 10.0.5', '> 10.0.1')

- type: word
part: body_1
words:
- "\"NAME\":\"TCP1P\""
- "\"msg\":\"success\""
condition: and
case-insensitive: true

- type: dsl
dsl:
- "regex('TCP1P{[a-z]}', body_2)"
- 'contains(body_2, "<script>alert(1)</script>")'
- "status_code_2 == 200"
condition: and

- type: status
status:
- 200

extractors:
- type: regex
name: version
group: 1
internal: true
part: body_1
regex:
- "\"version\":\"([0-9.]+)\""
```

<aside>
ℹ️ Nuclei powerful open-source tool used to detect and exploit potential security vulnerabilities in web applications and websites.

</aside>

<aside>
ℹ️ At the core of Nuclei's functionality are templates, which allow users to define specific checks and rules for security testing.

</aside>

---

```python
if url:
command = ['./nuclei', '--silent', '-u', url, '-t', 'custom-templates.yaml']
try:
result = subprocess.run(command, capture_output=True, text=True)
print(result.stdout)
if 'info' in result.stdout and '/api/v2/echo' in result.stdout and 'custom-templates' in result.stdout:
return "TCP1P{fake_flag}"
else:
return "Your website isn't vulnerable"
except subprocess.CalledProcessError:
return "Error occurred while running command"
return "Invalid request"
```

By reading the `app.py` code block above, this seems like a simple website where it will receive a `url input` and run it to Nuclei with the `custom-templates.yaml`

Which then, will show us the flag **if Nuclei finds a vulnerability on the specified `url`**

---

**Well, what does the `custom-templates.yaml` checks for?**

Here’s a breakdown :

1. **API Version Detection**:
- Route: **`/api/v1/version/`**
- Reading from the Route: This route reads the response from the **`/api/v1/version/`** endpoint.
- Condition for Matching:
- It checks the response for the API version by extracting the version number using a regular expression. It looks for a pattern like **`"version":"X.X.X"`** and extracts the **`X.X.X`** part as the version number.
- Then, it compares this extracted version to ensure that it is less than or equal to 10.0.5 and greater than 10.0.1.
2. **XSS and Path Traversal Check**:
- Route: **`/api/v2/echo/`**
- Reading from the Route: This route reads the response from the **`/api/v2/echo/`** endpoint.
- Condition for Matching:
- It applies several matchers to the response:
- It checks if the response body (**`body_1`**) contains specific content, including **`"NAME":"TCP1P"`** and **`"msg":"success"`**. This check is case-insensitive.
- It performs regex matching on **`body_2`**, looking for the pattern **`TCP1P{[a-z]}`**.
- It checks if **`body_2`** contains the string **`"<script>alert(1)</script>"`**.
- It ensures that the HTTP status code is 200 (**`status_code_2 == 200`**).

## Solution

---

Now, the only way to get the flag is that Nuclei must return ********info******** after scanning (the website that we are scanning matches the rules specified in the yaml above).

I am pretty sure it’s impossible to find a website which matches the rules above, especially the `TCP1P{[a-z]}` regex part.

Well, there’s no other way…

**Let’s create and deploy our own website! (which matches the template rules)**

![https://media1.giphy.com/media/AWj4W9BwVJdHXdckUw/giphy.gif?cid=7941fdc6q45k8sc1cu5aywwedol6pr9tjjrhz74v48qemfqg&ep=v1_gifs_search&rid=giphy.gif&ct=g](https://media1.giphy.com/media/AWj4W9BwVJdHXdckUw/giphy.gif?cid=7941fdc6q45k8sc1cu5aywwedol6pr9tjjrhz74v48qemfqg&ep=v1_gifs_search&rid=giphy.gif&ct=g)

### But dont worry! It’s not gonna be difficult.

You don’t really need to code a whole working vulnerable website.

You just have to create a website that returns **exactly** what the template yaml rules looks for.

For this challenge, I decided to use **`FastAPI`** because it’s much easier to code and setup quickly.

```python
from fastapi import FastAPI, Request

app = FastAPI()

@app.get('/api/v1/version/')
def api_version():
response = {
"version": "10.0.3",
"NAME": "TCP1P",
"msg": "success"
}
return response

@app.get('/api/v2/echo/')
async def api_echo(request: Request):
return "TCP1P{a} <script>alert(1)</script>"

if __name__ == '__main__':
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
```

Now, **deploy the website code above to a cloud instance or your own portforwarded server.**

<aside>
ℹ️ To run the code above:
`uvicorn <filenamewithoutdotpy>:app --host 0.0.0.0 --port 8000`

</aside>

<aside>
? Tried to use Ngrok for hosting but it doesn’t work for me. Nuclei request is blocked by the Ngrok warning, not sure if there’s any ‘quick’ workaround.

</aside>

### Final Step:

Pass in your deployed website url and click on **submit,** and we will get the flag!

---

**Thanks for reading my write-up and have a nice day!**

Original writeup (https://vicevirus.github.io/posts/tcp1p-ctf-2023/).