mirror of
https://github.com/theopfr/somo.git
synced 2025-06-10 01:33:32 +03:00
added abuseipdb check
This commit is contained in:
@@ -1,44 +1,48 @@
|
||||
|
||||
use std::error::Error;
|
||||
use reqwest;
|
||||
use std::{error::Error, env};
|
||||
use reqwest::{self, Response};
|
||||
use serde_json::{json, Value};
|
||||
|
||||
|
||||
|
||||
|
||||
/// this doesnt work yet
|
||||
pub fn get_ip_audit() -> Result<(), Box<dyn Error>> {
|
||||
let url = "https://api.abuseipdb.com/api/v2/check";
|
||||
pub fn get_ip_audit(remote_ip: &String, verbose: bool) -> Result<Option<i64>, Box<dyn Error>> {
|
||||
|
||||
let abuseipdb_api_key: String = match env::var("ABUSEIPDB_API_KEY") {
|
||||
Ok(val) => val,
|
||||
Err(_e) => {
|
||||
if verbose {
|
||||
println!("Couldn't find AbuseIPDB API key. If you want to use this feature make sure to put the API key into the environment variable 'ABUSEIPDB_API_KEY'.");
|
||||
}
|
||||
return Ok(None);
|
||||
},
|
||||
};
|
||||
|
||||
let client = reqwest::blocking::Client::new();
|
||||
|
||||
let ips = vec!["127.0.0.1", "192.168.0.1", "10.0.0.1"];
|
||||
let params = json!({
|
||||
"ip": ips,
|
||||
"maxAgeInDays": 30,
|
||||
"verbose": true,
|
||||
});
|
||||
|
||||
let response = client.get(url)
|
||||
.header("Key", "...")
|
||||
let url = "https://api.abuseipdb.com/api/v2/check";
|
||||
let params = [
|
||||
("ipAddress", remote_ip),
|
||||
("maxAgeInDays", &("40".to_string())),
|
||||
];
|
||||
let response: reqwest::blocking::Response = client
|
||||
.get(url)
|
||||
.header("Key", abuseipdb_api_key)
|
||||
.header("Accept", "application/json")
|
||||
.query(¶ms)
|
||||
.send()?;
|
||||
|
||||
|
||||
// check if the request was successful
|
||||
if response.status().is_success() {
|
||||
let json_response: Value = response.json()?;
|
||||
println!("hdfhdfh");
|
||||
for ip_result in json_response["data"].as_array().unwrap() {
|
||||
let ip = &ip_result["ipAddress"];
|
||||
let abuse_confidence_score = &ip_result["abuseConfidenceScore"];
|
||||
let abuse_confidence_score: Option<i64> = json_response["data"]["abuseConfidenceScore"].as_i64();
|
||||
|
||||
println!("IP: {}, Score: {}", ip, abuse_confidence_score);
|
||||
}
|
||||
} else {
|
||||
println!("Request failed with status code: {}", response.status());
|
||||
return Ok(abuse_confidence_score);
|
||||
}
|
||||
else {
|
||||
println!("AbuseIPDB Request failed with status code: {}", response.status());
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +53,7 @@ pub fn check_if_known(remote_ip: &str) -> String {
|
||||
return format!("*{}*", remote_ip.to_string());
|
||||
}
|
||||
else if remote_ip == "127.0.0.1" || remote_ip == "[::1]" {
|
||||
return format!("{} *localhost*", remote_ip.to_string());
|
||||
return format!("*{} localhost*", remote_ip.to_string());
|
||||
}
|
||||
return remote_ip.to_string();
|
||||
}
|
||||
|
||||
28
src/cli.rs
28
src/cli.rs
@@ -62,8 +62,6 @@ struct Args {
|
||||
pub fn cli() -> FlagValues {
|
||||
let args = Args::parse();
|
||||
|
||||
println!("{:?}", args.kill);
|
||||
|
||||
let flag_values = FlagValues {
|
||||
check: args.check,
|
||||
kill: args.kill,
|
||||
@@ -81,22 +79,28 @@ pub fn cli() -> FlagValues {
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// kills aprocess by PID
|
||||
pub fn kill_process(pid: &String) {
|
||||
let output = process::Command::new("kill")
|
||||
.arg(pid)
|
||||
.output()
|
||||
.expect(&format!("Failed to kill process with PID {}", pid));
|
||||
|
||||
if output.status.success() {
|
||||
println!("Killed process with PID {}.", pid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// select option for choosing a process to kill
|
||||
pub fn interactve_process_kill(connections: &Vec<connections::Connection>) {
|
||||
let selection: Result<u32, InquireError> = Select::new("Which process to kill (search or type index)?", (1..=connections.len() as u32).collect()).prompt();
|
||||
|
||||
match selection {
|
||||
Ok(choice) => {
|
||||
let output = process::Command::new("kill")
|
||||
.arg(&connections[choice as usize - 1].pid)
|
||||
.output()
|
||||
.expect(&format!("Failed to kill process with PID {}", choice));
|
||||
|
||||
if output.status.success() {
|
||||
println!("Killed process with PID {}.", choice);
|
||||
}
|
||||
let pid: &String = &connections[choice as usize - 1].pid;
|
||||
kill_process(&pid);
|
||||
},
|
||||
Err(_) => println!("Couldn't find process."),
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +1,12 @@
|
||||
|
||||
use procfs;
|
||||
use procfs::process::Stat;
|
||||
use procfs::process::FDTarget;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::string_utils;
|
||||
use crate::address_checkers;
|
||||
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FilterOptions {
|
||||
pub by_proto: Option<String>,
|
||||
pub by_program: Option<String>,
|
||||
@@ -30,13 +28,12 @@ pub struct Connection {
|
||||
pub program: String,
|
||||
pub pid: String,
|
||||
pub state: String,
|
||||
pub abuse_score: Option<i64>
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// gets all running processes on the system
|
||||
fn get_processes() -> HashMap<u64, Stat> {
|
||||
/* gets all running processes on the system */
|
||||
|
||||
let all_procs = procfs::process::all_processes().unwrap();
|
||||
|
||||
let mut map: HashMap<u64, Stat> = HashMap::new();
|
||||
@@ -86,7 +83,7 @@ fn filter_out_connection(connection_details: &Connection, filter_options: &Filte
|
||||
|
||||
|
||||
/// gets all open tcp connectections
|
||||
fn get_tcp_connections(all_processes: &HashMap<u64, Stat>, filter_options: &FilterOptions) -> Vec<Connection> {
|
||||
fn get_tcp_connections(all_processes: &HashMap<u64, Stat>, filter_options: &FilterOptions, check_malicious: bool) -> Vec<Connection> {
|
||||
let mut tcp = procfs::net::tcp().unwrap();
|
||||
if !filter_options.exclude_ipv6 {
|
||||
tcp.extend(procfs::net::tcp6().unwrap());
|
||||
@@ -111,20 +108,27 @@ fn get_tcp_connections(all_processes: &HashMap<u64, Stat>, filter_options: &Filt
|
||||
pid = "-".to_string();
|
||||
}
|
||||
|
||||
let connection: Connection = Connection {
|
||||
let mut connection: Connection = Connection {
|
||||
proto: "tcp".to_string(),
|
||||
local_port: local_port,
|
||||
remote_address: remote_address,
|
||||
remote_address: (&remote_address).to_string(),
|
||||
remote_port: remote_port,
|
||||
program: program,
|
||||
pid: pid,
|
||||
state: state,
|
||||
abuse_score: None
|
||||
};
|
||||
|
||||
// check if connection should be filtered out
|
||||
let filter_connection: bool = filter_out_connection(&connection, filter_options);
|
||||
if filter_connection {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if malicious-check is activated, get an abuse score from AbuseIPDB.com
|
||||
if check_malicious {
|
||||
connection.abuse_score = address_checkers::get_ip_audit(&remote_address, false).unwrap_or(Some(-1i64));
|
||||
}
|
||||
|
||||
all_tcp_connections.push(connection);
|
||||
}
|
||||
@@ -134,7 +138,7 @@ fn get_tcp_connections(all_processes: &HashMap<u64, Stat>, filter_options: &Filt
|
||||
|
||||
|
||||
/// gets all open udp connectections
|
||||
fn get_udp_connections(all_processes: &HashMap<u64, Stat>, filter_options: &FilterOptions) -> Vec<Connection> {
|
||||
fn get_udp_connections(all_processes: &HashMap<u64, Stat>, filter_options: &FilterOptions, check_malicious: bool) -> Vec<Connection> {
|
||||
let mut udp = procfs::net::udp().unwrap();
|
||||
if !filter_options.exclude_ipv6 {
|
||||
udp.extend(procfs::net::udp6().unwrap());
|
||||
@@ -159,20 +163,27 @@ fn get_udp_connections(all_processes: &HashMap<u64, Stat>, filter_options: &Filt
|
||||
pid = "-".to_string();
|
||||
}
|
||||
|
||||
let connection: Connection = Connection {
|
||||
let mut connection: Connection = Connection {
|
||||
proto: "udp".to_string(),
|
||||
local_port: local_port,
|
||||
remote_address: remote_address,
|
||||
remote_address: (&remote_address).to_string(),
|
||||
remote_port: remote_port,
|
||||
program: program,
|
||||
pid: pid,
|
||||
state: state,
|
||||
abuse_score: None
|
||||
};
|
||||
|
||||
// check if connection should be filtered out
|
||||
let filter_connection: bool = filter_out_connection(&connection, filter_options);
|
||||
if filter_connection {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if malicious-check is activated, get an abuse score from AbuseIPDB.com
|
||||
if check_malicious {
|
||||
connection.abuse_score = address_checkers::get_ip_audit(&remote_address, false).unwrap_or(Some(-1i64));
|
||||
}
|
||||
|
||||
all_udp_connections.push(connection);
|
||||
}
|
||||
@@ -183,17 +194,17 @@ fn get_udp_connections(all_processes: &HashMap<u64, Stat>, filter_options: &Filt
|
||||
|
||||
|
||||
// gets all tcp and udp connections
|
||||
pub fn get_all_connections(filter_options: &FilterOptions) -> Vec<Connection> {
|
||||
pub fn get_all_connections(filter_options: &FilterOptions, check_malicious: bool) -> Vec<Connection> {
|
||||
let all_processes: HashMap<u64, Stat> = get_processes();
|
||||
|
||||
match &filter_options.by_proto {
|
||||
Some(filter_proto) if filter_proto == "tcp" => return get_tcp_connections(&all_processes, filter_options),
|
||||
Some(filter_proto) if filter_proto == "udp" => return get_udp_connections(&all_processes, filter_options),
|
||||
Some(filter_proto) if filter_proto == "tcp" => return get_tcp_connections(&all_processes, filter_options, check_malicious),
|
||||
Some(filter_proto) if filter_proto == "udp" => return get_udp_connections(&all_processes, filter_options, check_malicious),
|
||||
_ => { }
|
||||
}
|
||||
|
||||
let mut all_connections = get_tcp_connections(&all_processes, filter_options);
|
||||
let all_udp_connections = get_udp_connections(&all_processes, filter_options);
|
||||
let mut all_connections = get_tcp_connections(&all_processes, filter_options, check_malicious);
|
||||
let all_udp_connections = get_udp_connections(&all_processes, filter_options, check_malicious);
|
||||
all_connections.extend(all_udp_connections);
|
||||
|
||||
return all_connections;
|
||||
|
||||
26
src/main.rs
26
src/main.rs
@@ -1,23 +1,13 @@
|
||||
use termimad::crossterm::style::{Color::*};
|
||||
use termimad::*;
|
||||
use reqwest;
|
||||
|
||||
use std::error::Error;
|
||||
|
||||
mod connections;
|
||||
mod processes;
|
||||
mod address_checkers;
|
||||
mod string_utils;
|
||||
mod table;
|
||||
mod interactive;
|
||||
mod cli;
|
||||
|
||||
|
||||
fn main() {
|
||||
|
||||
|
||||
|
||||
let args: cli::FlagValues = cli::cli();
|
||||
let mut args: cli::FlagValues = cli::cli();
|
||||
|
||||
// example filter option: Some("tcp".to_string())
|
||||
let filter_options: connections::FilterOptions = connections::FilterOptions {
|
||||
@@ -31,8 +21,20 @@ fn main() {
|
||||
exclude_ipv6: args.exclude_ipv6
|
||||
};
|
||||
|
||||
if args.check {
|
||||
println!("Checking IPs using AbuseIPDB.com...");
|
||||
let abuse_result = address_checkers::get_ip_audit(&("127.0.0.1cc".to_string()), true).unwrap();
|
||||
match abuse_result {
|
||||
Some(_) => { }
|
||||
None => {
|
||||
println!("Cancelling IP abuse check.");
|
||||
args.check = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get running processes
|
||||
let all_connections: Vec<connections::Connection> = connections::get_all_connections(&filter_options);
|
||||
let all_connections: Vec<connections::Connection> = connections::get_all_connections(&filter_options, args.check);
|
||||
|
||||
table::get_connections_table(&all_connections, args.check);
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
use procfs;
|
||||
use procfs::process::FDTarget;
|
||||
use procfs::process::Stat;
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
||||
pub fn get_processes() -> HashMap<u64, Stat> {
|
||||
/* gets all running processes on the system */
|
||||
|
||||
let all_procs = procfs::process::all_processes().unwrap();
|
||||
|
||||
let mut map: HashMap<u64, Stat> = HashMap::new();
|
||||
for p in all_procs {
|
||||
let process = p.unwrap();
|
||||
if let (Ok(stat), Ok(fds)) = (process.stat(), process.fd()) {
|
||||
for fd in fds {
|
||||
if let FDTarget::Socket(inode) = fd.unwrap().target {
|
||||
map.insert(inode, stat.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
48
src/table.rs
48
src/table.rs
@@ -35,17 +35,21 @@ pub fn get_connections_table(all_connections: &Vec<connections::Connection>, che
|
||||
// check if the remote IP is a DNS server
|
||||
let remote_address = &connection.remote_address;
|
||||
let remote_address_new = &address_checkers::check_if_known(remote_address);
|
||||
|
||||
/*if check_malicious_adresses {
|
||||
// check if the remote IP is malicious using the AbuseIpDb API
|
||||
let (marked_remote_addess, checked) = address_checkers::check_if_malicious(&remote_address_new);
|
||||
remote_address_new = &marked_remote_addess;
|
||||
checked_ip_status = checked;
|
||||
}*/
|
||||
|
||||
let checked_remote_address: String;
|
||||
if connection.abuse_score >= Some(50) {
|
||||
checked_remote_address = format!("{} ~~malicious~~", &remote_address_new);
|
||||
}
|
||||
else if connection.abuse_score == Some(0) {
|
||||
checked_remote_address = format!("{} **✓**", &remote_address_new);
|
||||
}
|
||||
else {
|
||||
checked_remote_address = (&remote_address_new).to_string();
|
||||
}
|
||||
|
||||
// add row with connection information
|
||||
markdown.push_str(&format!("| *{}* | {} | {} | {} | {} | {}*/{}* | {} |\n",
|
||||
idx + 1, connection.proto, connection.local_port, &remote_address_new, connection.remote_port, connection.program, connection.pid, connection.state
|
||||
idx + 1, connection.proto, connection.local_port, &checked_remote_address, connection.remote_port, connection.program, connection.pid, connection.state
|
||||
));
|
||||
}
|
||||
|
||||
@@ -56,19 +60,21 @@ pub fn get_connections_table(all_connections: &Vec<connections::Connection>, che
|
||||
markdown.push_str(&terminal_filling_row);
|
||||
markdown.push_str(CENTER_MARKDOWN_ROW);
|
||||
|
||||
// print information about checking malicious IPs
|
||||
markdown.push_str("\n*Info:*");
|
||||
if checked_ip_status == 1 {
|
||||
markdown.push_str("\n*Successfully checked remote IPs with the AbuseIpDB API.*\n");
|
||||
}
|
||||
else if checked_ip_status == 0 {
|
||||
markdown.push_str("\n*If you want somo to automatically check for malicious IP addresses in your connections, make an account at `www.abuseipdb.com` and add your API key as an env variable: `ABUSEIPDB_API_KEY={your-api-key}`.*\n");
|
||||
}
|
||||
else if checked_ip_status == -1 {
|
||||
markdown.push_str("\n~~A~~ *Couldn't reach the AbuseIpDB API to check for malicious IP address in your connections.*\n");
|
||||
markdown.push_str("*Possible problems:*\n");
|
||||
markdown.push_str("*1. API down or new non-backward compatible changes -> check if there is a new version of somo avaialble *\n");
|
||||
markdown.push_str("*2. wrong or expired API key stored in the `ABUSEIPDB_API_KEY` env variable *\n");
|
||||
if check_malicious_adresses {
|
||||
// print information about checking malicious IPs
|
||||
markdown.push_str("\n*Info:*");
|
||||
if checked_ip_status == 1 {
|
||||
markdown.push_str("\n*Successfully checked remote IPs with the AbuseIpDB API.*\n");
|
||||
}
|
||||
else if checked_ip_status == 0 {
|
||||
markdown.push_str("\n*If you want somo to automatically check for malicious IP addresses in your connections, make an account at `www.abuseipdb.com` and add your API key as an env variable: `ABUSEIPDB_API_KEY={your-api-key}`.*\n");
|
||||
}
|
||||
else if checked_ip_status == -1 {
|
||||
markdown.push_str("\n~~A~~ *Couldn't reach the AbuseIpDB API to check for malicious IP address in your connections.*\n");
|
||||
markdown.push_str("*Possible problems:*\n");
|
||||
markdown.push_str("*1. API down or new non-backward compatible changes -> check if there is a new version of somo avaialble *\n");
|
||||
markdown.push_str("*2. wrong or expired API key stored in the `ABUSEIPDB_API_KEY` env variable *\n");
|
||||
}
|
||||
}
|
||||
|
||||
println!("{}\n", skin.term_text(&markdown));
|
||||
|
||||
Reference in New Issue
Block a user