Skip to content

Commit 7793682

Browse files
committed
* Add Documentation
1 parent 375e83e commit 7793682

File tree

13 files changed

+245
-1
lines changed

13 files changed

+245
-1
lines changed

documentation/README.md

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
# OnlyLeveling Documentation
2+
3+
## Table of Contents
4+
5+
1. [Introduction](#1-introduction)
6+
2. [Architecture Overview](#2-architecture-overview)
7+
3. [Installation](#3-installation)
8+
- [3.1. Clone the Repository](#31-clone-the-repository)
9+
- [3.2. Running the Service](#32-running-the-service)
10+
- [3.3. Running the Checker](#33-running-the-checker)
11+
4. [Usage](#4-usage)
12+
5. [Exploits and Fixes](#5-exploits-and-fixes)
13+
- [5.1. Directory Traversal in File Access](#51-directory-traversal-in-file-access)
14+
- [5.2. Collision-Prone Seed Hash for Item IDs](#52-collisionprone-seed-hash-for-item-ids)
15+
- [5.3. Insecure JWT SECRET_KEY Generation](#53-insecure-jwt-secret_key-generation)
16+
6. [File Structure](#6-file-structure)
17+
- [6.1. Checker](#61-checker)
18+
- [6.2. Service](#62-service)
19+
20+
---
21+
22+
## 1. Introduction
23+
24+
**OnlyLeveling** is a multiplayer RPG-style web application where players progress by opening lootboxes, creating/playing dungeons, battling others, and leveling up. The system includes a frontend, backend, and a seed generator component used for item ID derivation.
25+
26+
**Main functionalities:**
27+
- Register & Login
28+
- Open Lootboxes to obtain Items
29+
- Create & Play Dungeons
30+
- Fight other players
31+
- Gain XP and level up
32+
33+
---
34+
35+
## 2. Architecture Overview
36+
37+
OnlyLeveling consists of several components:
38+
39+
- **Frontend (Port 2627):** Player-facing UI for authentication, lootboxes, dungeons, PvP, and inventory management.
40+
- **Backend (Port 2626):** Core API for auth, item management, dungeon logic, battles, and file serving (e.g., `/images/{filename}`).
41+
- **SeedGen (Port 2628):** Item seed/hash helper used to derive ItemIDs. Note: Since the main functionality for this service is written in OPAL I used Python to handle Connections to this service.
42+
- **Checker (Port 12627):** External process that interacts with the service to validate behavior and flag availability.
43+
44+
---
45+
46+
## 3. Installation
47+
48+
### 3.1. Clone the Repository
49+
50+
```bash
51+
git clone https://github.com/enowars/enowars9-service-only-leveling.git
52+
cd enowars9-service-only-leveling
53+
```
54+
55+
### 3.2. Running the Service
56+
57+
From the project root (see the `service/` folder), start the stack (frontend, backend, seedgen).
58+
59+
```bash
60+
cd Service
61+
docker compose up --build -d
62+
```
63+
64+
- **Frontend:** http://localhost:2627
65+
- **Backend:** http://localhost:2626
66+
- **SeedGen:** http://localhost:2628
67+
68+
### 3.3. Running the Checker
69+
70+
From the `checker/` directory:
71+
72+
```bash
73+
cd checker
74+
docker compose up --build -d
75+
```
76+
77+
- **Checker:** reachable on port **12627**
78+
79+
---
80+
81+
## 4. Usage
82+
83+
Once running:
84+
### Registration and Login
85+
#### Open the **Frontend** at `http://localhost:2627/` to register/login.
86+
![Register Screen](./assets/registerpage.png)
87+
![Login Screen](./assets/loginpage.png)
88+
#### After Logging in you will see the **Dashboard** with your current Level /XP
89+
![Dashboard Screen](./assets/dashboard.png)
90+
### Items Page - Opening a Lootbox and Saving notes
91+
#### Switch to the "Items"-Page. Use the **"Open Lootbox!"** Button clicking on it to obtain items (max. 2).
92+
![Items Screen](./assets/itemspage_unnopened.png)
93+
#### After Opening a Lootbox you will see your obtained Items here
94+
![Items Screen](./assets/itemspage_opened.png)
95+
#### With a click on the "Edit"-icon you will be able to type in and save a note
96+
![Items Screen](./assets/itemspage_notesaved.png)
97+
### Create Dungeon
98+
#### Switch to the **Create Dungeon Page**. Before creating your own dungeon you also can upload a own image for it
99+
![Create Dungeon Screen](./assets/createdungeonpage.png)
100+
#### After that you can just fill in the required Dungeon information and create you own dungeon
101+
### Play Dungeons
102+
#### Switch to the **"Dungeons" Page**. here you will see already existing Dungeons and Dungeons you created your own. To Play a Dungeon simply click on **"Enter Dungeon"**. You will gain XP for successful entered Dungeons.
103+
![Play Dungeon Screen](./assets/dungeonpage.png)
104+
### Fight other Players
105+
#### Switch to the **Fight Page**. Here you can simply select your opponent in the Drop-Down-List and click **"Fight!"** to fight them
106+
![Fight Players Screen](./assets/fightpage.png)
107+
#### After the Fight you will see if you won the fight, the items used by each player and the amount of XP you've earned.
108+
#### Having more power than the opponent will lead to an Win and having less will lead to an Loose
109+
![Fight Players Screen](./assets/fightpage_afterfight.png)
110+
### Leaderboard
111+
#### There is also a Leaderboard where you can see the Top players (Name, Level & Power)
112+
![Leaderboard Screen](./assets/leaderboard.png)
113+
#### Clicking on **"view"** will result on a detailed vie of the respective user.
114+
![Leaderboard Screen](./assets/dashboard.png)
115+
116+
---
117+
118+
## 5. Exploits and Fixes
119+
This service contains 2 Flagstores with 3 possible exploit Pathes.
120+
121+
### 5.1 Directory Traversal in File Access
122+
123+
**Issue**
124+
The route `/images/{filename}` does not adequately validate the `filename` parameter since the method uses **":path"**:
125+
```bash
126+
@app.get("/images/{filename:path}")
127+
```
128+
Inputs containing path traversal tokens can break out of the intended directory.
129+
130+
**Flag location**
131+
A **JPG** in another user’s upload directory contains the flag (steganographically embedded with `steghide`).
132+
133+
**Exploit steps:**
134+
1. Upload image to have your own directory created
135+
2. Send crafted request to `/images/%2e%2e/[USERNAME]/stegano.jpg` using directory traversal.
136+
2. Save `stegano.jpg`.
137+
3. Extract flag:
138+
```bash
139+
steghide extract -sf [image_path] -xf [flagfile_path] -p
140+
```
141+
142+
**Fix:** Remove ":path" from method, restrict filenames, normalize and check paths or serve files by ID.
143+
144+
**Example-Fix:**
145+
```bash
146+
#service/backend/main.py
147+
def is_safe_filename(filename):
148+
if ".." in filename or "/" in filename or "\\" in filename or "\x00" in filename:
149+
return False
150+
if not re.match(r'^[\w\-. ]+$', filename):
151+
return False
152+
return True
153+
154+
@app.get("/images/{filename}")
155+
async def get_private_dungeon_image(
156+
filename: str,
157+
current_user: schemas.User = Depends(get_current_active_user)
158+
):
159+
if not is_safe_filename(filename):
160+
raise HTTPException(status_code=400, detail="Invalid filename.")
161+
162+
user_dir = os.path.abspath(os.path.join(UPLOADS_DIR, current_user.username))
163+
abs_file_path = os.path.abspath(os.path.join(user_dir, filename))
164+
real_file_path = os.path.realpath(abs_file_path)
165+
166+
if os.path.commonpath([real_file_path, user_dir]) != user_dir:
167+
raise HTTPException(status_code=404, detail="Image not found or you do not have permission to access it.")
168+
169+
if not os.path.isfile(real_file_path):
170+
raise HTTPException(status_code=404, detail="Image not found or you do not have permission to access it.")
171+
172+
return FileResponse(
173+
real_file_path
174+
)
175+
```
176+
---
177+
178+
### 5.2 Collision-Prone Seed Hash for Item IDs
179+
180+
**Issue**
181+
Weak arithmetic with small modulo causes collisions. Attackers can choose usernames mapping to same ItemID as target.
182+
183+
**Flag location**
184+
Flag in **Item Note** of item with colliding ID.
185+
186+
**Exploit steps:**
187+
1. Reverse seed function.
188+
2. Pick username with matching ItemID.
189+
3. Login and open Lootbox with respective user
190+
3. Read flagged Item Note.
191+
192+
**Fix:** Use a secure UUID/random generator
193+
194+
**Example-Fix:**
195+
```bash
196+
#service/backend/main.py
197+
random.getrandbits(14)
198+
```
199+
or use "**Random**"-Structure of the Opal lib
200+
201+
---
202+
203+
### 5.3 Insecure JWT SECRET_KEY Generation
204+
205+
**Issue**
206+
`SECRET_KEY` is derived from time and truncated to 2 chars. Predictable => JWT forgery.
207+
208+
**Flag location**
209+
With forged JWT, attacker logs in as target account to access images/items.
210+
211+
**Exploit steps:**
212+
1. Reconstruct `SECRET_KEY`.
213+
2. Forge JWT for target.
214+
3. Download secret image / read Item Note.
215+
216+
**Fix:** Use strong random secret, rotate regularly, harden token validation.
217+
**Example-Fix:**
218+
```bash
219+
#service/backend/generate_secret.sh
220+
SECRET=$(openssl rand -hex 32)
221+
```
222+
---
223+
224+
## 6. File Structure
225+
226+
### 6.1 Checker
227+
228+
```
229+
checker
230+
├── docker-compose.yaml # Docker compose file
231+
└── src # Checker source code
232+
├── checker.py # Main checker script
233+
```
234+
235+
### 6.2 Service
236+
237+
```
238+
Service
239+
├── backend/ # Core API
240+
├── main.py # Contains Routes and Logic
241+
├── cleanup/ # Maintenance Service (Cleanup DB)
242+
├── frontend/ # Web UI for user interaction
243+
└── seed/ # SeedGen for item ID and JWT Secret derivation
244+
```
73.8 KB
Loading

documentation/assets/dashboard.png

116 KB
Loading
623 KB
Loading

documentation/assets/fightpage.png

38.9 KB
Loading
107 KB
Loading
49.4 KB
Loading
71 KB
Loading
35.4 KB
Loading
37.7 KB
Loading

0 commit comments

Comments
 (0)