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 #General settings
hostname = "mein-iserv.de" hostname = "mein-iserv.de"
session_cookie = "IServSAT=mvuvFvCZSlpvuwhk0mmNwF1NKEQypM4d; IServSession=xEPqyJO1a5nsVd34HEpbPqQGMMQKapvw" cookie = "IServSAT=mvuvFvCZSlpvuwhk0mmNwF1NKEQypM4d; IServSession=xEPqyJO1a5nsVd34HEpbPqQGMMQKapvw"
# Targeted device settings # Targeted device settings
target_ip = "10.1.1.1" host_ip = "10.1.1.1"
target_room = 123 host_room = 123
target_compilation = 1 mdm_compilation = 1
[host_filter] [host_filter]

View File

@ -87,7 +87,7 @@ args_or_config! {
|optional: |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."))] #[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 { macro_rules! print_banner {
@ -115,12 +115,23 @@ fn main_loop(args: &Config) -> Result<(), Box<dyn std::error::Error>> {
.send()?; .send()?;
let status = &resp.status(); let status = &resp.status();
let plain = &resp.text()?; 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( let content = squeeze(
&plain, &plain,
"<script id=\"crud-data\" type=\"application/json\">", "<script id=\"crud-data\" type=\"application/json\">",
"</script>", "</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"] let json_part = &json["data"][0]["name"]["rendered"]
.to_string() .to_string()
.replace("\\", ""); .replace("\\", "");
@ -135,46 +146,59 @@ No new host: skipping (Retry in {}s)
std::thread::sleep(std::time::Duration::from_secs(5)); std::thread::sleep(std::time::Duration::from_secs(5));
return Ok(()); return Ok(());
} }
let host_id = squeeze(json_part, "data-host-id=\"", "\""); let host_id = squeeze(json_part, "data-host-id=\"", "\"");
let device_name = squeeze(json_part, "</span>", "</a>"); 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); 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 dest = format!("https://{}/iserv/admin/host/edit/{}", args.hostname, &host_id);
let resp = client.get(&dest).header(header.0, header.1).send()?; let resp = client.get(&dest).header(header.0, header.1).send()?;
//TODO Check for non 200 response
let status = &resp.status(); let status = &resp.status();
if !status.is_success() && *status != 300 { if !status.is_success() {
eprintln!("WARN: Found invalid Status Code: {status}"); eprintln!("{}, {}", "WARN: Found invalid Status Code: ".bright_yellow(), status);
return Ok(()) return Ok(())
} }
let plain = &resp.text()?; let plain = &resp.text()?;
std::fs::write("lol.out.html", plain)?;
let mut form_ip: &str = &args.host_ip; let mut form_ip: &str = &args.host_ip;
let form_name = squeeze_html_form(plain, "name=\"host[name]\"", "value=\"", "\""); let mut form_ip_index = 2;
let form_tags = squeeze_html_form(plain, "name=\"host[tags][]\"", "value=\"", "\""); let form_name = squeeze_html_form(plain, "name=\"host[name]\"", ">", "value=\"", "\"");
let form_mac = squeeze_html_form(plain, "name=\"host[mac]\"", "value=\"", "\""); let plain_inner_tags = squeeze(plain, "name=\"host[tags][]\"", "</select>");
let form_description = squeeze_html_form(plain, "name=\"host[description]\"", ">", "<"); let mut form_tags = Vec::new();
let form_token = squeeze_html_form(plain, "name=\"host[_token]\"", "value=\"", "\""); squeeze_loop(&mut form_tags, plain_inner_tags, "<option value=\"", "\"", " selected=\"selected\"", 3, "host[tags][]");
let mut form_data = [
("host[name]", form_name), let form_mac = squeeze_html_form(plain, "name=\"host[mac]\"", ">", "value=\"", "\"");
("host[tags][]", form_tags), let form_description = squeeze_html_form(plain, "name=\"host[description]\"", "/textarea>", ">", "<");
("host[room]", &args.host_room.to_string()), let form_token = squeeze_html_form(plain, "name=\"host[_token]\"", ">", "value=\"", "\"");
("host[ip]", &form_ip), let mut form_data = Vec::from([
("host[mac]", form_mac), ("host[name]",form_name),
("host[internet]", "1"), ].map(|(a, b)| (Cow::Borrowed(a), Cow::Borrowed(b))));
("host[proxyEnforce]", "0"),
("host[owner]", ""), // empty tag fields are not permitted
("host[controllable]", "0"), if !(form_tags.is_empty()) {
("host[inventoryNumber]", ""), form_ip_index += form_tags.len();
("host[shutdown]", ""), form_data.extend(form_tags);
("host[description]", form_description), }
("host[image][id]", ""),
("host[image][x]", ""), let host_room: &str = &args.host_room.to_string();
("host[image][y]", ""), form_data.extend([
("host[actions][submit]", ""), ("host[room]", host_room),
("host[_token]", form_token), ("host[ip]", form_ip),
]; ("host[mac]", form_mac),
print_banner!("2/8 Received current host properties\nReceived response status: {:?}", status); ("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); println!("{:#?}", form_data);
let resp = client let resp = client
@ -183,18 +207,22 @@ No new host: skipping (Retry in {}s)
.form(&form_data) .form(&form_data)
.send()?; .send()?;
let status = &resp.status(); let status = &resp.status();
if !status.is_success() {
eprintln!("{}, {}", "WARN: Found invalid Status Code: ".bright_yellow(), status);
return Ok(())
}
let plain = &resp.text()?; let plain = &resp.text()?;
if !plain.contains("<form name=\"host\"") { if !plain.contains("<form name=\"host\"") {
print_banner!("3/8 The configured ip was accepted {}\nReceived response status: {:?}", form_ip, status); 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 { } else {
// retrieve next available ip address from sent form // retrieve next available ip address from sent form
form_ip = squeeze_html_form(plain, "name=\"host[ip]\"", "value=\"", "\""); form_ip = squeeze_html_form(plain, "name=\"host[ip]\"", ">", "value=\"", "\"");
form_data[3] = ("host[ip]", form_ip); form_data[form_ip_index] = ("host[ip]".into(), form_ip.into());
print_banner!("3/8 Received recommended ip: {}\nReceived response status: {:?}", form_ip, status); print_banner!("3/8 Received recommended ip: {}\nReceived response status: {:?}", form_ip, status);
let resp = client let resp = client
.post(&dest) .post(&dest)
.header(header.0, header.1) .header(header.0, header.1)
@ -204,15 +232,18 @@ No new host: skipping (Retry in {}s)
print_banner!("4/8 Sucessfully updated host\nReceived response status: {:?}", status); 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 let resp = client
.get(format!("https://{}/iserv/admin/mdm/ios/compilation/edit/{}", args.hostname, target_compilation)) .get(format!("https://{}/iserv/admin/mdm/ios/compilation/edit/{}", args.hostname, target_compilation))
.header(header.0, header.1) .header(header.0, header.1)
.send()?; .send()?;
//TODO Check for non 200 response
let status = &resp.status(); let status = &resp.status();
if !status.is_success() {
eprintln!("{}, {}", "WARN: Found invalid Status Code: ".bright_yellow(), status);
return Ok(())
}
let plain = &resp.text()?; let plain = &resp.text()?;
print_banner!("5/8 Received MDM compilation\nReceived response status: {:?}", status); 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([ let mut form_data = Vec::from([
( (
"ioscompilation[name]", "ioscompilation[name]",
squeeze_html_form(plain, "name=\"ioscompilation[name]\"", "value=\"", "\"") squeeze_html_form(plain, "name=\"ioscompilation[name]\"", ">", "value=\"", "\"")
), ),
( (
"ioscompilation[description]", "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)))); ].map(|(a, b)| (Cow::Borrowed(a), Cow::Borrowed(b))));
// push apps // 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][]"); squeeze_loop(&mut form_data, plain_inner_applications, "<option value=\"", "\"", " selected=\"selected\"", 3, "ioscompilation[applications][]");
// push profiles // 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][]"); squeeze_loop(&mut form_data, plain_inner_profiles, "<option value=\"", "\"", " selected=\"selected\"", 3, "ioscompilation[profiles][]");
// push existing devices // push existing devices
let plain_inner_devices = squeeze_html_form(plain, "name=\"ioscompilation[devices][]\"", ">", "</select>"); let plain_inner_devices = squeeze(plain, "name=\"ioscompilation[devices][]\"", "</select>");
squeeze_loop(&mut form_data, plain_inner_devices, "<option value=\"", "\"", " selected=\"selected\"", 3, "ioscompilation[devices][]"); squeeze_loop(&mut form_data, plain_inner_devices, "<option value=\"", "\"", " selected=\"", 3, "ioscompilation[devices][]");
// push new device // push new device
let mdm_new_idevice_id = squeeze_right(plain_inner_devices, "<option value=\"", &format!("\">{}", form_name)); let mdm_new_idevice_id = squeeze_right(plain_inner_devices, "<option value=\"", &format!("\">{}", form_name));
if plain_inner_devices.contains(form_name) { if plain_inner_devices.contains(form_name) {
form_data.push(("ioscompilation[devices][]".into(), mdm_new_idevice_id.into())); form_data.push(("ioscompilation[devices][]".into(), mdm_new_idevice_id.into()));
} else { } 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(()); return Ok(());
} }
@ -253,15 +284,19 @@ No new host: skipping (Retry in {}s)
form_data.push(("ioscompilation[actions][submit]".into(), "".into())); form_data.push(("ioscompilation[actions][submit]".into(), "".into()));
//push token //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())); form_data.push(("ioscompilation[_token]".into(), form_token.into()));
let resp = client 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) .header(header.0, header.1)
.form(&form_data) .form(&form_data)
.send()?; .send()?;
let status = &resp.status(); 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); print_banner!("6/8 Added device to MDM Compilation\nReceived response status: {:?}", status);
let resp = client let resp = client
@ -270,8 +305,12 @@ No new host: skipping (Retry in {}s)
.send()?; .send()?;
let status = &resp.status(); let status = &resp.status();
if !status.is_success() {
eprintln!("{}, {}", "WARN: Found invalid Status Code: ".bright_yellow(), status);
return Ok(())
}
let plain = &resp.text()?; 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); 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) .form(&form_data)
.send()?; .send()?;
let status = &resp.status(); 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); print_banner!("8/8 Confirmed device actions\nReceived response status: {:?}", status);
Ok(()) Ok(())
} }
@ -326,29 +369,15 @@ fn squeeze_right<'inp>(input: &'inp str, before: &str, after: &str) -> &'inp str
"" ""
} }
fn squeeze_html_form<'inp>( fn squeeze_html_form<'inp>(
input: &'inp str, input: &'inp str,
first: &str, outer_before: &str,
second: &str, outer_after: &str,
last: &str, inner_before: &str,
inner_after: &str
) -> &'inp str { ) -> &'inp str {
//find first occurence (broad search) let outer_str = squeeze(input, outer_before, outer_after);
if let Some(from_first_bytes) = input.find(first) { squeeze(outer_str, inner_before, inner_after)
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];
}
}
}
""
} }
fn squeeze_loop<'inp>( fn squeeze_loop<'inp>(