Fixed tags, added checks, much refactor - such wow

- Fixed host_tags retrieval (now supports multiple tags and properly handles none)
- Added status code check
- Added check for failed authorization
- Renamed target_compilation -> mdm_compilation
- Fixed and refactored squeeze_html_form()
This commit is contained in:
Kevin Dorner 2022-03-26 20:01:39 +01:00
parent 87231e227f
commit d833c2a76a
2 changed files with 100 additions and 71 deletions

View File

@ -1,11 +1,11 @@
#General settings
hostname = "mein-iserv.de"
session_cookie = "IServSAT=mvuvFvCZSlpvuwhk0mmNwF1NKEQypM4d; IServSession=xEPqyJO1a5nsVd34HEpbPqQGMMQKapvw"
cookie = "IServSAT=mvuvFvCZSlpvuwhk0mmNwF1NKEQypM4d; IServSession=xEPqyJO1a5nsVd34HEpbPqQGMMQKapvw"
# Targeted device settings
target_ip = "10.1.1.1"
target_room = 123
target_compilation = 1
host_ip = "10.1.1.1"
host_room = 123
mdm_compilation = 1
[host_filter]

View File

@ -87,7 +87,7 @@ args_or_config! {
|optional:
#[clap(long, help("Numerical compilation id the newly added device should be assigned to. HINT: The id can be found at the end of the url when inspecting a compilation in the mdm admin section. If this is not specified, the new device will not be added to a collection."))]
target_compilation: u16
mdm_compilation: u16
}
macro_rules! print_banner {
@ -115,12 +115,23 @@ fn main_loop(args: &Config) -> Result<(), Box<dyn std::error::Error>> {
.send()?;
let status = &resp.status();
let plain = &resp.text()?;
if plain.contains("class=\"login-form\"") {
eprintln!("ERR: Failed to authorize. Server forwarded to login site. HINT: Check the provided cookie.");
std::process::exit(187);
}
if status.is_client_error() || status.is_server_error() {
eprintln!("ERR: Received error status (status code: {:?})", status);
std::process::exit(187);
}
let content = squeeze(
&plain,
"<script id=\"crud-data\" type=\"application/json\">",
"</script>",
);
let json: serde_json::Value = serde_json::from_str(content).expect("Failed to parse JSON data. This may be because the server did not return valid JSON data to parse.");
let json: serde_json::Value = serde_json::from_str(content).expect("ERR: Failed to parse JSON data. This may be because the server did not return valid JSON data to parse.");
let json_part = &json["data"][0]["name"]["rendered"]
.to_string()
.replace("\\", "");
@ -135,46 +146,59 @@ No new host: skipping (Retry in {}s)
std::thread::sleep(std::time::Duration::from_secs(5));
return Ok(());
}
let host_id = squeeze(json_part, "data-host-id=\"", "\"");
let device_name = squeeze(json_part, "</span>", "</a>");
print_banner!("1/8 Found host\nHost id: {}\nDevice name: {}\nReceived response status: {:?}", host_id, device_name, status);
let dest = format!("https://{}/iserv/admin/host/edit/{}", args.hostname, &host_id);
let resp = client.get(&dest).header(header.0, header.1).send()?;
//TODO Check for non 200 response
let status = &resp.status();
if !status.is_success() && *status != 300 {
eprintln!("WARN: Found invalid Status Code: {status}");
if !status.is_success() {
eprintln!("{}, {}", "WARN: Found invalid Status Code: ".bright_yellow(), status);
return Ok(())
}
let plain = &resp.text()?;
std::fs::write("lol.out.html", plain)?;
let mut form_ip: &str = &args.host_ip;
let form_name = squeeze_html_form(plain, "name=\"host[name]\"", "value=\"", "\"");
let form_tags = squeeze_html_form(plain, "name=\"host[tags][]\"", "value=\"", "\"");
let form_mac = squeeze_html_form(plain, "name=\"host[mac]\"", "value=\"", "\"");
let form_description = squeeze_html_form(plain, "name=\"host[description]\"", ">", "<");
let form_token = squeeze_html_form(plain, "name=\"host[_token]\"", "value=\"", "\"");
let mut form_data = [
("host[name]", form_name),
("host[tags][]", form_tags),
("host[room]", &args.host_room.to_string()),
("host[ip]", &form_ip),
("host[mac]", form_mac),
("host[internet]", "1"),
("host[proxyEnforce]", "0"),
("host[owner]", ""),
("host[controllable]", "0"),
("host[inventoryNumber]", ""),
("host[shutdown]", ""),
("host[description]", form_description),
("host[image][id]", ""),
("host[image][x]", ""),
("host[image][y]", ""),
("host[actions][submit]", ""),
("host[_token]", form_token),
];
print_banner!("2/8 Received current host properties\nReceived response status: {:?}", status);
let mut form_ip_index = 2;
let form_name = squeeze_html_form(plain, "name=\"host[name]\"", ">", "value=\"", "\"");
let plain_inner_tags = squeeze(plain, "name=\"host[tags][]\"", "</select>");
let mut form_tags = Vec::new();
squeeze_loop(&mut form_tags, plain_inner_tags, "<option value=\"", "\"", " selected=\"selected\"", 3, "host[tags][]");
let form_mac = squeeze_html_form(plain, "name=\"host[mac]\"", ">", "value=\"", "\"");
let form_description = squeeze_html_form(plain, "name=\"host[description]\"", "/textarea>", ">", "<");
let form_token = squeeze_html_form(plain, "name=\"host[_token]\"", ">", "value=\"", "\"");
let mut form_data = Vec::from([
("host[name]",form_name),
].map(|(a, b)| (Cow::Borrowed(a), Cow::Borrowed(b))));
// empty tag fields are not permitted
if !(form_tags.is_empty()) {
form_ip_index += form_tags.len();
form_data.extend(form_tags);
}
let host_room: &str = &args.host_room.to_string();
form_data.extend([
("host[room]", host_room),
("host[ip]", form_ip),
("host[mac]", form_mac),
("host[internet]", "1"),
("host[proxyEnforce]", "0"),
("host[owner]", ""),
("host[controllable]", "0"),
("host[inventoryNumber]", ""),
("host[shutdown]", ""),
("host[description]", form_description),
("host[image][id]", ""),
("host[image][x]", ""),
("host[image][y]", ""),
("host[actions][submit]", ""),
("host[_token]", form_token),
].map(|(a, b)| (Cow::Borrowed(a), Cow::Borrowed(b))));
print_banner!("2/8 Received current host properties\nReceived response status: {:?}", status);
println!("{:#?}", form_data);
let resp = client
@ -183,16 +207,20 @@ No new host: skipping (Retry in {}s)
.form(&form_data)
.send()?;
let status = &resp.status();
if !status.is_success() {
eprintln!("{}, {}", "WARN: Found invalid Status Code: ".bright_yellow(), status);
return Ok(())
}
let plain = &resp.text()?;
if !plain.contains("<form name=\"host\"") {
print_banner!("3/8 The configured ip was accepted {}\nReceived response status: {:?}", form_ip, status);
print_banner!("{}", "4/8 IP address resubmission was skipped");
print_banner!("{}", "4/8 Ip address resubmission was skipped");
} else {
// retrieve next available ip address from sent form
form_ip = squeeze_html_form(plain, "name=\"host[ip]\"", "value=\"", "\"");
form_data[3] = ("host[ip]", form_ip);
form_ip = squeeze_html_form(plain, "name=\"host[ip]\"", ">", "value=\"", "\"");
form_data[form_ip_index] = ("host[ip]".into(), form_ip.into());
print_banner!("3/8 Received recommended ip: {}\nReceived response status: {:?}", form_ip, status);
let resp = client
@ -204,15 +232,18 @@ No new host: skipping (Retry in {}s)
print_banner!("4/8 Sucessfully updated host\nReceived response status: {:?}", status);
}
let target_compilation = if let Some(t) = args.target_compilation { t } else { return Ok(()) };
let target_compilation = if let Some(t) = args.mdm_compilation { t } else { return Ok(()) };
let resp = client
.get(format!("https://{}/iserv/admin/mdm/ios/compilation/edit/{}", args.hostname, target_compilation))
.header(header.0, header.1)
.send()?;
//TODO Check for non 200 response
let status = &resp.status();
if !status.is_success() {
eprintln!("{}, {}", "WARN: Found invalid Status Code: ".bright_yellow(), status);
return Ok(())
}
let plain = &resp.text()?;
print_banner!("5/8 Received MDM compilation\nReceived response status: {:?}", status);
@ -220,32 +251,32 @@ No new host: skipping (Retry in {}s)
let mut form_data = Vec::from([
(
"ioscompilation[name]",
squeeze_html_form(plain, "name=\"ioscompilation[name]\"", "value=\"", "\"")
squeeze_html_form(plain, "name=\"ioscompilation[name]\"", ">", "value=\"", "\"")
),
(
"ioscompilation[description]",
squeeze_html_form(plain, "name=\"ioscompilation[description]\"", "value=\"", "\"")
squeeze_html_form(plain, "name=\"ioscompilation[description]\"", ">", "value=\"", "\"")
),
].map(|(a, b)| (Cow::Borrowed(a), Cow::Borrowed(b))));
// push apps
let plain_inner_applications = squeeze_html_form(plain, "name=\"ioscompilation[applications][]\"", ">", "</select>");
let plain_inner_applications = squeeze(plain, "name=\"ioscompilation[applications][]\"", "</select>");
squeeze_loop(&mut form_data, plain_inner_applications, "<option value=\"", "\"", " selected=\"selected\"", 3, "ioscompilation[applications][]");
// push profiles
let plain_inner_profiles = squeeze_html_form(plain, "name=\"ioscompilation[profiles][]\"", ">", "</select>");
let plain_inner_profiles = squeeze(plain, "name=\"ioscompilation[profiles][]\"", "</select>");
squeeze_loop(&mut form_data, plain_inner_profiles, "<option value=\"", "\"", " selected=\"selected\"", 3, "ioscompilation[profiles][]");
// push existing devices
let plain_inner_devices = squeeze_html_form(plain, "name=\"ioscompilation[devices][]\"", ">", "</select>");
squeeze_loop(&mut form_data, plain_inner_devices, "<option value=\"", "\"", " selected=\"selected\"", 3, "ioscompilation[devices][]");
let plain_inner_devices = squeeze(plain, "name=\"ioscompilation[devices][]\"", "</select>");
squeeze_loop(&mut form_data, plain_inner_devices, "<option value=\"", "\"", " selected=\"", 3, "ioscompilation[devices][]");
// push new device
let mdm_new_idevice_id = squeeze_right(plain_inner_devices, "<option value=\"", &format!("\">{}", form_name));
if plain_inner_devices.contains(form_name) {
form_data.push(("ioscompilation[devices][]".into(), mdm_new_idevice_id.into()));
} else {
eprintln!("{}", "WARN: Device not found in compilation form.\nSkipping compilation edit...".bright_red());
eprintln!("{}", "WARN: Device not found in compilation form.\nSkipping compilation edit...".bright_yellow());
return Ok(());
}
@ -253,15 +284,19 @@ No new host: skipping (Retry in {}s)
form_data.push(("ioscompilation[actions][submit]".into(), "".into()));
//push token
let form_token = squeeze_html_form(plain, "name=\"ioscompilation[_token]\"", "value=\"", "\"");
let form_token = squeeze_html_form(plain, "name=\"ioscompilation[_token]\"", ">", "value=\"", "\"");
form_data.push(("ioscompilation[_token]".into(), form_token.into()));
let resp = client
.post(format!("https://{}/iserv/admin/mdm/ios/compilation/edit/{}", args.hostname, args.target_compilation.unwrap()))
.post(format!("https://{}/iserv/admin/mdm/ios/compilation/edit/{}", args.hostname, args.mdm_compilation.unwrap()))
.header(header.0, header.1)
.form(&form_data)
.send()?;
let status = &resp.status();
if !status.is_success() {
eprintln!("{}, {}", "WARN: Found invalid Status Code: ".bright_yellow(), status);
return Ok(())
}
print_banner!("6/8 Added device to MDM Compilation\nReceived response status: {:?}", status);
let resp = client
@ -270,8 +305,12 @@ No new host: skipping (Retry in {}s)
.send()?;
let status = &resp.status();
if !status.is_success() {
eprintln!("{}, {}", "WARN: Found invalid Status Code: ".bright_yellow(), status);
return Ok(())
}
let plain = &resp.text()?;
let csrf_token = squeeze_html_form(plain, "name=\"iserv_crud_multi_select[_token]\"", "value=\"", "\"");
let csrf_token = squeeze_html_form(plain, "name=\"iserv_crud_multi_select[_token]\"", ">", "value=\"", "\"");
print_banner!("7/8 Retrieved CSRF token\nReceived response status: {:?}", status);
@ -287,6 +326,10 @@ No new host: skipping (Retry in {}s)
.form(&form_data)
.send()?;
let status = &resp.status();
if !status.is_success() {
eprintln!("{}, {}", "WARN: Found invalid Status Code: ".bright_yellow(), status);
return Ok(())
}
print_banner!("8/8 Confirmed device actions\nReceived response status: {:?}", status);
Ok(())
}
@ -326,29 +369,15 @@ fn squeeze_right<'inp>(input: &'inp str, before: &str, after: &str) -> &'inp str
""
}
fn squeeze_html_form<'inp>(
input: &'inp str,
first: &str,
second: &str,
last: &str,
outer_before: &str,
outer_after: &str,
inner_before: &str,
inner_after: &str
) -> &'inp str {
//find first occurence (broad search)
if let Some(from_first_bytes) = input.find(first) {
let after_first_bytes = from_first_bytes + first.len();
let after_first = &input[after_first_bytes..];
//find second occurence (field search)
if let Some(rel_from_second_bytes) = after_first.find(second) {
let after_second_bytes = rel_from_second_bytes + after_first_bytes + second.len();
let after_second = &input[after_second_bytes..];
//find closing last match (excluded from output)
if let Some(rel_from_last_byte) = after_second.find(last) {
let from_last_byte = rel_from_last_byte + after_second_bytes;
return &input[after_second_bytes..from_last_byte];
}
}
}
""
let outer_str = squeeze(input, outer_before, outer_after);
squeeze(outer_str, inner_before, inner_after)
}
fn squeeze_loop<'inp>(