"""d09-d17: Lateral movement + privilege escalation."""
from diag import *


def d09():
    fig, ax = canvas("DCOM lateral movement: anatomy", w=13.0, h=5.5)
    boundary(ax, 4, 14, 32, 30, "ATTACKER HOST")
    ps = box(ax, 20, 33, 26, 9, "PowerShell / impacket", fs=9.5)
    boundary(ax, 58, 10, 68, 38, "TARGET HOST")
    rpc = box(ax, 66, 33, 14, 8, "RPCSS", fs=9.5, weight="bold")
    mmc = box(ax, 99, 33, 34, 9, "mmc.exe — hosts\nMMC20.Application", fs=9)
    cmd = box(ax, 99, 16, 22, 8, "cmd.exe child", fs=9.5)
    arrow(ax, ps.side("r"), rpc.side("l"), kind="attack",
          label="DCOM activation\nKerberos/NTLM · TCP 135\n+ dynamic high ports",
          ly=1.8, fs=8.5, mono=True, va="bottom")
    arrow(ax, rpc.side("r"), mmc.side("l"), kind="attack",
          label="start/attach\nmmc.exe", ly=1.5, fs=8.5, va="bottom")
    arrow(ax, mmc.side("b"), cmd.side("t"), kind="attack",
          label="Document.ActiveView.\nExecuteShellCommand(...)", lx=8.5,
          fs=8.5, mono=True, ha="left")
    note(ax, 24, 5, "Sysmon 1: mmc.exe → cmd.exe lineage", kind="defense",
         fs=8.5)
    note(ax, 67, 5, "Sysmon 3: inbound 135 + high ports", kind="defense",
         fs=8.5)
    note(ax, 108, 5, "no service install · no SMB file drop", kind="defense",
         fs=8.5)
    return save(fig, "d09-dcom-lateral.png")


def d10():
    fig, ax = canvas("MMC20.Application kill chain", w=13.0, h=4.8,
                     caption="fires a visible MMC window unless executed headless — OpSec tradeoff")
    b1 = box(ax, 20, 31, 36, 13,
             "GetTypeFromCLSID(\n{49B2791A-B1AE-4C90-9B8E-E860BA07F889},\n\"target\")",
             fs=8.5, mono=True)
    b2 = box(ax, 62, 31, 26, 13, "ActivateAs /\nCreateInstance\n(admin creds)",
             fs=8.5, mono=True)
    b3 = box(ax, 104, 31, 30, 13, "MMC snap-in\nobject model", fs=9)
    b4 = box(ax, 53, 10, 52, 11,
             "Document.ActiveView.ExecuteShellCommand(\n\"cmd.exe\", ...)",
             fs=8.5, mono=True)
    b5 = box(ax, 109, 10, 30, 11, "payload runs\non target", fs=9)
    conn(ax, b1, "r", b2, "l", label="remote\nobject", ly=1.4, fs=8.5,
         va="bottom")
    conn(ax, b2, "r", b3, "l", label="drives\nOM", ly=1.4, fs=8.5, va="bottom")
    elbow(ax, [b3.side("b"), (104, 21), (79, 21), b4.side("r", 0.75)],
          kind="normal", label="invoke", li=1, ly=1.3, fs=8.5, va="bottom")
    conn(ax, b4, "r", b5, "l", label="spawns", ly=1.3, fs=8.5, va="bottom")
    for i, b in enumerate([b1, b2, b3, b4, b5], 1):
        step_circle(ax, b.l, b.t, i)
    return save(fig, "d10-mmc20-chain.png")


def d11():
    fig, ax = canvas("ShellWindows / ShellBrowserWindow", w=11.0, h=4.4,
                     caption="needs an interactive session on target; runs as the logged-on user")
    atk = box(ax, 13, 30, 18, 9, "Attacker", fs=10)
    exp = box(ax, 56, 30, 38, 12,
              "explorer.exe on target hosts\nShellWindows\n{9BA05972-F6A8-11CF-A442-00A0C90A8F39}",
              fs=8.5, mono=True)
    cmd = box(ax, 99, 30, 20, 9, "cmd.exe\n(child of explorer)", fs=8.5)
    arrow(ax, atk.side("r"), exp.side("l"), kind="attack",
          label="DCOM activation\n(Kerberos/NTLM · 135+high)", ly=1.6, fs=8.5,
          va="bottom")
    arrow(ax, exp.side("r"), cmd.side("l"), kind="attack",
          label="Item().Document.Application.\nShellExecute(\"cmd.exe\")",
          t=0.25, ly=1.6, fs=8.5, mono=True, va="bottom")
    return save(fig, "d11-shellwindows-chain.png")


def d12():
    fig, ax = canvas("Bug class #1: server forgets to impersonate", w=11.0,
                     h=6.0,
                     caption="hunt: privileged file/registry/process ops reachable from low IL without impersonation")
    label(ax, 9, 50.5, "SAFE", fs=10, weight="bold", color=EDGE)
    c1 = box(ax, 15, 44, 16, 8, "client", fs=9.5)
    s1 = box(ax, 50, 44, 24, 9, "server:\nCoImpersonateClient", fs=9, mono=True)
    r1 = box(ax, 91, 44, 30, 9, "file op runs as caller\n(caller's token)", fs=9)
    conn(ax, c1, "r", s1, "l", label="call (path)", ly=1.4, fs=8.5, va="bottom")
    conn(ax, s1, "r", r1, "l", label="impersonated op", ly=1.4, fs=8.5,
         va="bottom")
    ax.plot([4, 106], [32, 32], color=RULE, lw=1.0, zorder=1)
    label(ax, 15.5, 26.5, "VULNERABLE", fs=10, weight="bold", color=ATTACK)
    c2 = box(ax, 15, 20, 16, 8, "client", fs=9.5)
    s2 = box(ax, 50, 20, 24, 9, "server (SYSTEM):\nno impersonation", fs=9)
    r2 = box(ax, 91, 20, 30, 9, "arbitrary file write\nas SYSTEM", fs=9,
             edge=ATTACK, lw=2.2, tc=ATTACK)
    conn(ax, c2, "r", s2, "l", kind="attack",
         label="attacker-controlled path", ly=1.6, fs=8.5, lc=ATTACK,
         va="bottom")
    conn(ax, s2, "r", r2, "l", kind="attack", label="write with own\nSYSTEM token",
         ly=1.6, fs=8.5, lc=ATTACK, va="bottom")
    return save(fig, "d12-missing-impersonation.png")


def d13():
    fig, ax = canvas("DiagHub: arbitrary file write → SYSTEM (Forshaw, 2018)",
                     w=13.0, h=5.0,
                     caption="patched; exploitation mitigations added in 19H1 (ProcessImageLoadPolicy)")
    b1 = box(ax, 22, 32, 38, 12,
             "activate DiagHub collector\n{42CBFAA7-A4A7-47BB-B422-BD10E9D02700}\nvia DCOM",
             fs=8.5, mono=True)
    b2 = box(ax, 65, 32, 28, 12, "start collector\nsession", fs=9)
    b3 = box(ax, 108, 32, 34, 12,
             "abuse session control:\nwrite attacker-chosen\nfile as SYSTEM",
             fs=8.5)
    b4 = box(ax, 108, 10, 36, 11, "redirect via junction/\nsymlink to DLL path",
             fs=9)
    b5 = box(ax, 65, 10, 30, 11, "trigger load into\nSYSTEM process", fs=9)
    b6 = box(ax, 22, 10, 32, 11, "code execution\nas SYSTEM", fs=9,
             edge=ATTACK, lw=2.2, tc=ATTACK)
    conn(ax, b1, "r", b2, "l", kind="attack", label="session", ly=1.3, fs=8.5,
         va="bottom")
    conn(ax, b2, "r", b3, "l", kind="attack", label="control", ly=1.3, fs=8.5,
         va="bottom")
    conn(ax, b3, "b", b4, "t", kind="attack", label="redirect", lx=7.5,
         fs=8.5, lc=ATTACK)
    conn(ax, b4, "l", b5, "r", kind="attack", label="plant DLL", ly=1.3,
         fs=8.5, va="bottom")
    conn(ax, b5, "l", b6, "r", kind="attack", label="load", ly=1.3, fs=8.5,
         va="bottom")
    for i, b in enumerate([b1, b2, b3, b4, b5, b6], 1):
        step_circle(ax, b.l, b.t, i)
    return save(fig, "d13-diaghub-chain.png")


def d14():
    fig, ax = canvas("RottenPotato: NTLM reflection over DCOM", w=13.0, h=5.0)
    svc = box(ax, 15, 29, 24, 11, "service account\nw/ SeImpersonatePrivilege",
              fs=9)
    com = box(ax, 50, 29, 20, 9, "COM activation", fs=9.5)
    ntlm = box(ax, 80, 29, 24, 9, "NTLM type 1/2/3\nrelay loop", fs=9)
    sh = box(ax, 116, 29, 20, 9, "SYSTEM shell", fs=9.5, edge=ATTACK,
             lw=2.2, tc=ATTACK)
    arrow(ax, svc.side("r"), com.side("l"), kind="attack",
          label="CoGetObject(IStorage)\nBITS {4991D34B-80A1-4291-83B6-3328366B9097}",
          ly=6.2, fs=8.5, mono=True, lc=ATTACK, va="bottom")
    step_circle(ax, 29.5, 29, 1)
    arrow(ax, com.side("r"), ntlm.side("l"), kind="attack",
          label="local NTLM reflection\nto TCP socket", ly=5.6, fs=8.5,
          lc=ATTACK, va="bottom")
    step_circle(ax, 62.5, 29, 2)
    arrow(ax, ntlm.side("r"), sh.side("l"), kind="attack",
          label="SYSTEM token → DuplicateTokenEx\n→ CreateProcessWithTokenW",
          ly=5.6, fs=8.5, mono=True, lc=ATTACK, va="bottom")
    step_circle(ax, 94.5, 29, 3)
    label(ax, 65, 12, "the service's own NTLM auth is reflected back and impersonated — no credentials needed",
          fs=9, color=SUB)
    return save(fig, "d14-rottenpotato.png")


def d15():
    fig, ax = canvas("RoguePotato: fake OXID resolver + named pipe", w=14.0,
                     h=5.0)
    b1 = box(ax, 23, 28, 40, 16,
             "trigger SYSTEM auth:\nIStorage trigger /\nRpcRemoteFindFirstPrinterChangeNotificationEx",
             fs=8.5, mono=True)
    b2 = box(ax, 65, 28, 24, 16,
             "victim's OXID\nresolution redirected\nto attacker's fake\nOXID resolver",
             fs=8.5)
    b3 = box(ax, 97, 28, 24, 16, "NTLM auth relayed\nto local named pipe\n(127.0.0.1)",
             fs=8.5)
    b4 = box(ax, 126, 28, 26, 16,
             "ImpersonateNamedPipeClient\n→ SYSTEM token",
             fs=8.5, mono=True, edge=ATTACK, lw=2.2, tc=ATTACK)
    conn(ax, b1, "r", b2, "l", kind="attack",
         label="ResolveOxid2\nto attacker", ly=1.6, fs=8.5,
         lc=ATTACK, va="bottom")
    conn(ax, b2, "r", b3, "l", kind="attack", label="NTLM\nrelay", ly=1.6,
         fs=8.5, lc=ATTACK, va="bottom")
    conn(ax, b3, "r", b4, "l", kind="attack", label="pipe", ly=1.6,
         fs=8.5, lc=ATTACK, va="bottom")
    for i, b in enumerate([b1, b2, b3, b4], 1):
        step_circle(ax, b.l, b.t, i)
    label(ax, 70, 9, "port-135 restrictions (1809+) force the victim's OXID resolution through the attacker's fake resolver",
          fs=9, color=SUB)
    return save(fig, "d15-roguepotato.png")


def d16():
    fig, ax = canvas("The *Potato lineage", w=13.5, h=5.5,
                     caption="SeImpersonatePrivilege + a coercion trigger is the common thread")
    y0 = 27
    arrow(ax, (5, y0), (131, y0), kind="normal", ms=14)
    nodes = [
        ("2016 · RottenPotato", "BITS + local NTLM reflection",
         "works: Win7–10 early\nkilled by: reflection fixes"),
        ("2018 · JuicyPotato", "CLSID bruteforce (BITS)",
         "works: ≤ Win10 1803\nkilled by: 1809 OXID hardening"),
        ("2020 · PrintSpoofer", "named pipe — no DCOM",
         "works: Win10 / Srv 2019\nneeds SeImpersonate"),
        ("2020 · RoguePotato", "fake OXID resolver",
         "works: ≥ 1809\nneeds port-135 redirect"),
        ("2021 · SweetPotato", "combo + EFSRPC trigger",
         "works: incl. Srv 2019/2022\nall-in-one tool"),
        ("2022 · JuicyPotatoNG", "PrintNotify trigger",
         "works: 1809+, Win11 /\nSrv 2022"),
        ("2023 · LocalPotato", "CVE-2023-21746",
         "NTLM type-1 swap\nkilled by: Feb 2023 patch"),
    ]
    xs = [14, 32, 50, 68, 86, 104, 122]
    for i, ((name, tech, note_), x) in enumerate(zip(nodes, xs)):
        above = (i % 2 == 0)
        cy = 41 if above else 13
        w = 25
        b = box(ax, x, cy, w, 13)
        label(ax, x, cy + 4.0, name, fs=9, weight="bold")
        label(ax, x, cy + 0.6, tech, fs=8.5, mono=True)
        label(ax, x, cy - 3.4, note_, fs=8.5, color=SUB)
        edge_y = b.b if above else b.t
        ax.plot([x, x], [y0, edge_y], color=EDGE, lw=1.0, zorder=2)
        ax.plot([x - 1.2, x + 1.2], [y0, y0], color=EDGE, lw=1.6, zorder=3)
    return save(fig, "d16-potato-timeline.png")


def d17():
    fig, ax = canvas("Trapped COM objects: type confusion via IDispatch (2025)",
                     w=12.0, h=5.5,
                     caption="bug class, not one CVE — audit every place a proxy definition can be influenced")
    b1 = box(ax, 20, 33, 32, 11, "Attacker registers/swaps\ntype library or proxy definition",
             fs=9)
    b2 = box(ax, 59, 33, 24, 11, "privileged client\nqueries interface", fs=9)
    b3 = box(ax, 96, 33, 32, 11, "object \"trapped\":\nvtable/struct layout mismatch",
             fs=9)
    b4 = box(ax, 96, 12, 36, 11, "type confusion → controlled\nmethod call in privileged context",
             fs=9, edge=ATTACK, lw=2.2, tc=ATTACK)
    conn(ax, b1, "r", b2, "l", kind="attack",
         label="influenced\nproxy definition", ly=6.5, fs=8.5, lc=ATTACK,
         va="bottom")
    conn(ax, b2, "r", b3, "l", kind="attack",
         label="QueryInterface /\nIDispatch bind", ly=6.5, fs=8.5, mono=True,
         lc=ATTACK, va="bottom")
    conn(ax, b3, "b", b4, "t", kind="attack", label="dispatch", lx=9.5,
         fs=8.5, lc=ATTACK)
    return save(fig, "d17-trapped-objects.png")


ALL = [d09, d10, d11, d12, d13, d14, d15, d16, d17]
