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!**