Skip to content

Commit

Permalink
feat: add if condition for HTTP target
Browse files Browse the repository at this point in the history
  • Loading branch information
irvingoujAtDevolution committed Oct 6, 2023
1 parent 949d360 commit 1fd8f76
Show file tree
Hide file tree
Showing 5 changed files with 418 additions and 43 deletions.
8 changes: 6 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 30 additions & 13 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,8 @@ description = "A Rust implementation of the Security Support Provider Interface
keywords = ["ntlm", "auth", "sspi", "windows", "kerberos"]

[workspace]
members = [
"ffi",
"ffi/symbol-rename-macro",
]
exclude = [
"tools/sspi-ffi-attacker",
"tools/wasm-testcompile",
]
members = ["ffi", "ffi/symbol-rename-macro"]
exclude = ["tools/sspi-ffi-attacker", "tools/wasm-testcompile"]

[features]
default = []
Expand Down Expand Up @@ -47,7 +41,11 @@ lazy_static = "1.4"
serde = "1"
serde_derive = "1"
url = "2.4"
reqwest = { version = "0.11", features = ["blocking", "rustls-tls", "rustls-tls-native-roots"], optional = true, default-features = false }
reqwest = { version = "0.11", features = [
"blocking",
"rustls-tls",
"rustls-tls-native-roots",
], optional = true, default-features = false }

picky = { version = "7.0.0-rc.8", default-features = false }
picky-krb = "0.8"
Expand All @@ -61,17 +59,32 @@ trust-dns-resolver = { version = "0.23", optional = true }
portpicker = { version = "0.1", optional = true }
num-bigint-dig = "0.8"
tracing = "0.1"
rustls = { version = "0.21", features = ["dangerous_configuration"], optional = true }
rustls = { version = "0.21", features = [
"dangerous_configuration",
], optional = true }
zeroize = { version = "1.6", features = ["zeroize_derive"] }
tokio = { version = "1.32", features = ["time", "rt"], optional = true }
pcsc = { version = "2.8", optional = true }
async-recursion = "1.0.5"

[target.'cfg(windows)'.dependencies]
winreg = "0.51"
winapi = { version = "0.3", features = ["std", "sspi", "rpcdce", "impl-default", "timezoneapi", "wincrypt"] }
windows = { version = "0.51", features = [ "Win32_Foundation", "Win32_NetworkManagement_Dns"] }
windows-sys = { version = "0.48", features = ["Win32_Security_Cryptography", "Win32_Foundation"] }
winapi = { version = "0.3", features = [
"std",
"sspi",
"rpcdce",
"impl-default",
"timezoneapi",
"wincrypt",
] }
windows = { version = "0.51", features = [
"Win32_Foundation",
"Win32_NetworkManagement_Dns",
] }
windows-sys = { version = "0.48", features = [
"Win32_Security_Cryptography",
"Win32_Foundation",
] }

[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
async-dnssd = "0.5"
Expand All @@ -82,3 +95,7 @@ tokio = { version = "1.32", features = ["time", "rt"] }
static_assertions = "1"
whoami = "1.4"
picky = { version = "7.0.0-rc.8", features = ["x509"] }
base64 = "0.21.4"
hyper = "0.14.27"
tracing-log = "0.1.3"
tracing-subscriber = "0.3.17"
141 changes: 141 additions & 0 deletions examples/kerberos.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
use base64::Engine;
use hyper::header::{
ACCEPT, ACCEPT_ENCODING, ACCEPT_LANGUAGE, AUTHORIZATION, CONNECTION, CONTENT_LENGTH, HOST, USER_AGENT,
};
use hyper::{Body, Request, Response, StatusCode, Uri};
use sspi::builders::EmptyInitializeSecurityContext;
use sspi::generator::GeneratorState;
use sspi::{
AcquireCredentialsHandleResult, AuthIdentity, ClientRequestFlags, CredentialsBuffers, DataRepresentation,
InitializeSecurityContextResult, KerberosConfig, Negotiate, NegotiateConfig, SecurityBuffer, SecurityBufferType,
SecurityStatus, Sspi,
};
use sspi::{Kerberos, SspiImpl};
use std::error::Error;
use tracing::debug;
use tracing_log::log::info;
const target_name: &'static str = "HTTP/IT-HELP-DC.ad.it-help.ninja";

fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::DEBUG) // Adjust level as needed
.init();
let kdc_url = "IT-HELP-DC.ad.it-help.ninja:88".to_string();
let client_hostname = whoami::hostname();
let kerberos_config = KerberosConfig::new(&kdc_url, client_hostname.clone());
let mut kerberos = Kerberos::new_client_from_config(kerberos_config).unwrap();

let mut acq_creds_handle_result = get_cred_handle(&mut kerberos);

let mut input = vec![SecurityBuffer::new(Vec::new(), SecurityBufferType::Token)];
let mut output = vec![SecurityBuffer::new(Vec::new(), SecurityBufferType::Token)];

loop {
match step(
&mut kerberos,
&mut acq_creds_handle_result.credentials_handle,
&mut input,
&mut output,
) {
Err(e) => panic!("error steping {:?}", e),
Ok(result) => {
let status_code = process_authentication(&mut input, &mut output)?;
if status_code == StatusCode::OK {
info!("connection authenticated");
break Ok(());
}

if result.status == SecurityStatus::Ok && status_code != StatusCode::OK {
panic!("connection authentication failed");
}
}
}
}
}

fn get_cred_handle(kerberos: &mut Kerberos) -> AcquireCredentialsHandleResult<Option<CredentialsBuffers>> {
let username = "Administrator@ad.it-help.ninja".to_string();
let password = "DevoLabs123!".to_string();
let identity = sspi::AuthIdentity {
username,
password: password.into(),
domain: None,
};
let mut acq_creds_handle_result = kerberos
.acquire_credentials_handle()
.with_credential_use(sspi::CredentialUse::Outbound)
.with_auth_data(&identity.into())
.execute()
.expect("AcquireCredentialsHandle resulted in error");
acq_creds_handle_result
}

fn process_authentication(
input: &mut Vec<SecurityBuffer>,
output: &mut Vec<SecurityBuffer>,
) -> Result<StatusCode, Box<dyn std::error::Error + Send + Sync>> {
let output_token_in_binary = &output[0].buffer;
let base_64_token = base64::engine::general_purpose::STANDARD.encode(output_token_in_binary);
let server_result = send_http(base_64_token)?;
debug!("server responde = {:?}", server_result);
let www_authenticate = server_result
.headers()
.get("www-authenticate")
.ok_or("www-authentication header not found")?;
let server_token = www_authenticate.to_str().unwrap().replace("Negotiate ", "");
if server_token.len() <= 5 {
panic!("server token not found");
}
let decoded_new_token = base64::engine::general_purpose::STANDARD.decode(server_token).unwrap();
clear(input);
clear(output);
input[0].buffer = decoded_new_token;

Ok(server_result.status())
}

fn clear(buf: &mut Vec<SecurityBuffer>) {
buf[0].buffer.clear();
}

fn send_http(negotiate_token: String) -> Result<reqwest::blocking::Response, Box<dyn Error + Send + Sync>> {
let client = reqwest::blocking::Client::new();
let resp = client
.post("http://it-help-dc.ad.it-help.ninja:5985/wsman?PSVersion=5.1.22621.963")
.header(AUTHORIZATION, format!("Negotiate {}", negotiate_token))
.header(HOST, "it-help-dc.ad.it-help.ninja:5985")
.header(CONNECTION, "keep-alive")
.header(CONTENT_LENGTH, "0")
.header(
USER_AGENT,
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/118.0",
)
.header(ACCEPT, "*/*")
.header(ACCEPT_ENCODING, "gzip, deflate")
.header(ACCEPT_LANGUAGE, "en-US,en;q=0.9")
.send()?;

Ok(resp)
}

fn step(
kerberos: &mut Kerberos,
credential_handle: &mut <Kerberos as SspiImpl>::CredentialsHandle,
input_buffer: &mut Vec<sspi::SecurityBuffer>,
output_buffer: &mut Vec<sspi::SecurityBuffer>,
) -> Result<InitializeSecurityContextResult, Box<dyn std::error::Error>> {
output_buffer[0].buffer.clear();
let mut builder = EmptyInitializeSecurityContext::<<Negotiate as SspiImpl>::CredentialsHandle>::new()
.with_credentials_handle(credential_handle)
.with_context_requirements(ClientRequestFlags::MUTUAL_AUTH)
.with_target_data_representation(DataRepresentation::Native)
.with_target_name(target_name)
.with_input(input_buffer)
.with_output(output_buffer);

let result = kerberos
.initialize_security_context_impl(&mut builder)
.resolve_with_default_network_client()?;

Ok(result)
}
27 changes: 27 additions & 0 deletions src/kerberos/client/generators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,33 @@ pub fn generate_neg_ap_req(ap_req: ApReq, mech_id: oid::ObjectIdentifier) -> Res
}))
}

pub fn generate_neg_ap_req_plain(
ap_req: ApReq,
mech_id: oid::ObjectIdentifier,
) -> Result<ApplicationTag0<GssApiNegInit>> {
let krb_blob: ApplicationTag<_, 0> = ApplicationTag(KrbMessage {
krb5_oid: ObjectIdentifierAsn1::from(mech_id),
krb5_token_id: AP_REQ_TOKEN_ID,
krb_msg: ap_req,
});

let data = ExplicitContextTag0::from(NegTokenInit {
mech_types: Optional::from(Some(ExplicitContextTag0::from(get_mech_list()))),
req_flags: Optional::from(None),
mech_token: Optional::from(Some(ExplicitContextTag2::from(OctetStringAsn1::from(
picky_asn1_der::to_vec(&krb_blob)?,
)))),
mech_list_mic: Optional::from(None),
});

let gss_req = GssApiNegInit {
oid: ObjectIdentifierAsn1::from(oids::spnego()),
neg_token_init: data,
};

Ok(ApplicationTag0(gss_req))
}

pub fn generate_final_neg_token_targ(mech_list_mic: Option<Vec<u8>>) -> NegTokenTarg1 {
NegTokenTarg1::from(NegTokenTarg {
neg_result: Optional::from(Some(ExplicitContextTag0::from(Asn1RawDer(ACCEPT_COMPLETE.to_vec())))),
Expand Down
Loading

0 comments on commit 1fd8f76

Please sign in to comment.