Skip to content

Commit

Permalink
Dev (#25)
Browse files Browse the repository at this point in the history
* added check and stuff

* Working on adding a new active test and diving into schemes.

* slow progress on active tests

* refactor structure for crates.io deployment.
added `auth.rs` - unused

* removed misconfigured tests from active tests

* fixed version numbers

* fixed version numbers 2

* Update main.rs

* Update Cargo.toml

* Update Cargo.toml

* Update checks.rs

Co-authored-by: raz <raz.m@blstsecurity.com>
Co-authored-by: raz <raz@magori.online>
  • Loading branch information
3 people committed May 16, 2022
1 parent 8d8d7b3 commit 7003a0e
Show file tree
Hide file tree
Showing 17 changed files with 501 additions and 214 deletions.
340 changes: 185 additions & 155 deletions Cargo.lock

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
[package]
name = "cherrybomb"
version = "0.6.1"
authors = ["BLST Security"]
description = """
Cherrybomb is a CLI tool that helps you avoid undefined user behavior by validating your API specifications.
"""
documentation = "https://github.com/blst-security/cherrybomb"
homepage = "https://blstsecurity.com/"
repository = "https://github.com/blst-security/cherrybomb"
keywords = ["API","security","OAS","Open API Specefication", "specifications", "CLI", "OpenAPI", "OAS", "business logic", "scanning"]
categories = ["command-line-utilities","web-programming"]
license = "Apache-2.0"
edition = "2021"



[[bin]]
name = "cherrybomb"
path = "cli/src/main.rs"
test = false

[workspace]

members = [
Expand All @@ -7,6 +29,32 @@ members = [
"attacker",
"swagger",
]
[dependencies]
cli = {version="0.6",path="cli"}
mapper = {path="./mapper"}
attacker = {path="./attacker"}
decider = {path="./decider"}
swagger = {path="./swagger"}
clap = { version = "^3", features = ["derive"] }
uuid = { version = "0.8", features = ["v4","serde"] }
serde = { version = "^1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "^1.0", features = ["full"] }
futures = "0.3"
futures-util = { version = "^0.3", default-features = false, features = ["alloc"] }
colored = "2.0.0"
url = { version = "2" }
hyper = { version = "^0.14", features = ["http2"] }
hyper-rustls = { git = "https://github.com/rustls/hyper-rustls", features = ["http2"] }
httparse = "1.5.1"
dirs="^4"
serde_yaml="^0.8"
reqwest = { version = "^0.11",default_features = false, features = ["json","rustls-tls"] }
strum = "0.23"
strum_macros = "0.23"
base64 = "0.13.0"
regex = "1"
serde_with = "1.11.0"

[profile.release]
opt-level = 3
4 changes: 2 additions & 2 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cherrybomb"
version = "0.6.0"
name = "cli"
version = "0.6.1"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
172 changes: 172 additions & 0 deletions cli/src/auth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
use colored::*;
use hyper::{body, Body, Client, Method, Request};
use hyper_rustls::HttpsConnectorBuilder;
use std::fs::File;
use std::io::{Read,Write};
use std::path::Path;


const TOKEN_FILE:&str = ".cherrybomb/token.txt";
async fn sign_up(filename:&Path,dir:&Path)->bool{
/*
match set_current_dir(dirs::home_dir().unwrap()){
Ok(_)=>(),
Err(e)=>{
println!("{:?}",e);
panic!("Could not generate a CLI token, please contact BLST at support@blstsecurity.com");
}
};*/
let mut file = match File::create(filename) {
Ok(f) => f,
Err(_) => {
match std::fs::create_dir(dir){
Ok(_)=>{
match File::create(filename) {
Ok(f)=>f,
Err(e)=>{
//println!("{:?}",e);
//panic!("Could not generate a CLI token, please contact BLST at support@blstsecurity.com");
return false;
}
}
}
Err(e)=>{
//println!("{:?}",e);
//panic!("Could not generate a CLI token, please contact BLST at support@blstsecurity.com");
return false;
}
}
}
};
let res = match reqwest::get("https://cherrybomb.blstsecurity.com/token").await{
Ok(r)=>{
match r.text().await{
Ok(t)=>t,
Err(_)=>{
//panic!("Could not generate a CLI token, please contact BLST at support@blstsecurity.com");
return false;
}
}
},
Err(e)=>{
//println!("{:?}",e);
//panic!("Could not generate a CLI token, please contact BLST at support@blstsecurity.com");
return false;
}
};
let json: serde_json::Value = match serde_json::from_str(&res) {
Ok(j) => j,
Err(_) => {
//panic!("Could not generate a CLI token, please contact BLST at support@blstsecurity.com");
return false;
}
};
match file.write_all(json["client_token"].to_string().as_bytes()){
Ok(_)=>(),
Err(_)=>{
//panic!("Could not generate a CLI token, please contact BLST at support@blstsecurity.com");
return false;
}
}
true
}
async fn get_token()->String{
let mut filename = dirs::home_dir().unwrap();
filename.push(TOKEN_FILE);
let dir = dirs::home_dir().unwrap();
let mut file = match File::open(&filename) {
Ok(f) => f,
Err(_) => {
if sign_up(&filename,&dir).await{
match File::open(&filename) {
Ok(f)=>f,
Err(_)=>{
panic!("Could not validate the CLI token, please contact BLST at support@blstsecurity.com");
}
}
}else{
panic!("Could not validate the CLI token, please contact BLST at support@blstsecurity.com");
}
/*
println!(
"{}",
"file \"token.txt\" not found, make sure that you have it in this directory".red()
);
println!("{}", "to get your token go to your user details dashboard at https://www.blstsecurity.com/cherrybomb/UserDetails".purple().bold());
return false;
*/
}
};
let mut token = String::new();
match file.read_to_string(&mut token) {
Ok(_) => (),
Err(_) => {
//panic!("Could not validate the CLI token, please contact BLST at support@blstsecurity.com");
return String::new();
/*
println!(
"{}",
"could not read the data from \"token.txt\", make sure the data is valid".red()
);
println!("{}", "to get your token go to your user details dashboard at https://www.blstsecurity.com/cherrybomb/UserDetails".purple().bold());*/
}
}
token
}
pub async fn get_access(action: &str) -> bool {
let token = get_token().await;
let connector = HttpsConnectorBuilder::new()
.with_native_roots()
.https_only()
.enable_http1()
.enable_http2()
.build();
let client = Client::builder().build(connector);
let req = Request::builder()
.method(Method::POST)
.uri("https://cherrybomb.blstsecurity.com/auth")
.body(Body::from(format!(
"{{\"client_token\":{},\"action\":\"{}\"}}",
token, action
).replace("\n","")))
.unwrap();
let r = match client.request(req).await {
Ok(r) => r,
Err(_) => {
//println!("{}", "authentication request failed".red());
return false;
}
};
let txt = body::to_bytes(r.into_body()).await.unwrap();
let json: serde_json::Value = match serde_json::from_slice(&txt) {
Ok(j) => j,
Err(_) => {
//panic!("Invalid CLI token, please contact BLST at support@blstsecurity.com");
return false;
/*
println!("{}", "client_token not valid".red());
println!("{}", "to get your token go to your user details dashboard at https://www.blstsecurity.com/cherrybomb/UserDetails".purple().bold());*/
}
};
match json["opt_in"].as_bool() {
Some(b) => {
if b {
true
} else {
//panic!("Invalid CLI token, please contact BLST at support@blstsecurity.com");
//println!("{}", json["msg"].to_string().red());
false
}
}
None => {
//panic!("Invalid CLI token, please contact BLST at support@blstsecurity.com");
false
/*
println!(
"{}",
"error while parsing the response from the authenticator".red()
);
false*/
}
}
}
2 changes: 2 additions & 0 deletions cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ mod utils;
pub use utils::*;
mod config;
pub use config::*;
mod auth;
pub use auth::*;
2 changes: 1 addition & 1 deletion cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use clap::{Parser,Subcommand};
use std::str::FromStr;
use std::fmt;
use colored::*;
use cherrybomb::*;
use cli::*;
use attacker::{Authorization, Verbosity};
use mapper::digest::Header;
use futures::executor::block_on;
Expand Down
2 changes: 1 addition & 1 deletion swagger/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub struct Link {
#[serde(rename = "operationRef")]
pub operation_ref: Option<String>,
#[serde(rename = "operationId")]
pub oeration_id: Option<String>,
pub operation_id: Option<String>,
pub parameters: Option<LinkParameters>,
//Any
#[serde(rename = "requestBody")]
Expand Down
14 changes: 7 additions & 7 deletions swagger/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ async fn main() {
//let f_names = ["swagger2.json","swagger3.json","swagger4.json","swagger5.json","swagger6.json","swagger7.json"];
//for f_name in f_names{
let swagger_value: serde_json::Value =
serde_json::from_str(&std::fs::read_to_string(f_name).unwrap()).unwrap();
serde_yaml::from_str(&std::fs::read_to_string(f_name).unwrap()).unwrap();
/*
let version = swagger_value["openapi"].to_string().trim().replace("\"","");
let swagger = if version.starts_with("3.1"){
Expand All @@ -23,21 +23,21 @@ async fn main() {
serde_json::from_str::<OAS3_1>(&std::fs::read_to_string(f_name).unwrap()).unwrap();*/
// println!("{:?}",swagger.paths.unwrap().get("/users").unwrap().get.as_ref().unwrap().security.as_ref().unwrap());
//}
/*

let mut a = ActiveScan::<OAS3_1>::new(swagger_value).unwrap();
use futures::executor;
executor::block_on(a.run(ActiveScanType::Full,&Authorization::None));

a.print(0);
*/
let mut a = PassiveSwaggerScan::<OAS3_1>::new(swagger_value.clone()).unwrap();
a.run(PassiveScanType::Full);

//let mut a = PassiveSwaggerScan::<OAS3_1>::new(swagger_value.clone()).unwrap();
//a.run(PassiveScanType::Full);
//println!("{:?}",serde_json::to_string(&a).unwrap());
//a.print(1);
let t = EpTable::new::<OAS3_1>(&swagger_value);
//let t = EpTable::new::<OAS3_1>(&swagger_value);
//let t = ParamTable::new::<OAS3_1>(&swagger_value);
//println!("{:?}",serde_json::to_string(&t).unwrap());
t.print();
//t.print();
//print_checks_table(&a);
//print_alerts_table(&a);
//let _sw = swagger_str.convert_to_map(swagger_value);
Expand Down
18 changes: 9 additions & 9 deletions swagger/src/scan/active/additional_checks.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
use super::*;
use serde_json::json;

impl<T: OAS + Serialize> ActiveScan<T> {
pub async fn check_default(&self,auth:&Authorization) -> Vec<Alert> {
pub async fn check_default(&self, auth: &Authorization) -> Vec<Alert> {
let mut alerts = vec![];
let mut logs = AttackLog::default();
for (path, item) in self.oas.get_paths() {
let urls = get_path_urls(&item, self.oas.servers());
for url in urls {
/*
let req = AttackRequest::builder()
.uri(&url.1,&path)
.uri(&url.1, &path)
.method(url.0)
.headers(vec![])
.parameters(vec![])
.auth(auth.clone())
.build();
if let Ok(res) = req.send_request(true).await{
if let Ok(res) = req.send_request(true).await {
logs.requests.push(req);
logs.responses.push(res);
}else{
println!("FUCK");
}*/
alerts.push(Alert::with_certainty(Level::Low,"description","https://thingy".to_string(),Certainty::Certain));
} else {
println!("request failed");
}
alerts.push(Alert::with_certainty(Level::Low, "description", "https://thingy".to_string(), Certainty::Certain));
}
}
//println!("{:?}",logs);
alerts
}
}
}
4 changes: 2 additions & 2 deletions swagger/src/scan/active/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ impl<T: OAS + Serialize + for<'de> Deserialize<'de>> ActiveScan<T> {
pub fn print(&self, verbosity: u8) {
match verbosity {
0 => {
print_checks_table(&self.checks);
//print_checks_table(&self.checks);
print_attack_alerts_table(&self.checks);
},
1 => {
print_checks_table(&self.checks);
//print_checks_table(&self.checks);
},
2 => print_failed_checks_table(&self.checks),
_ => (),
Expand Down
7 changes: 4 additions & 3 deletions swagger/src/scan/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ impl Check for PassiveChecks {
}
}
}
/*

impl Check for ActiveChecks {
fn alerts_text(&self) -> ColoredString {
match self.inner().len() {
Expand All @@ -67,7 +67,7 @@ impl Check for ActiveChecks {
"PASSED"
}
}
}*/
}
impl_passive_checks![
//name in enum check function check name check description
(CheckServerUrl,check_server_url,"SERVER URL","Checks for server url misconfigurations"),
Expand All @@ -88,5 +88,6 @@ impl_passive_checks![
(CheckArrAttrs,check_arr_attrs,"ARRAY ATTRIBUTES","Checks for the definion of array type attributes - max_items, min_items"),
(CheckObjAttrs,check_obj_attrs,"OBJECT ATTRIBUTES","Checks for the definion of object type attributes - max_properties, properties"),
(CheckValidResponses,check_valid_responses,"VALID RESPONSES","Checks for valid responses codes"),
(CheckMethodPermissions, check_method_permissions, "METHOD PERMISSIONS", "Checks for correct permission cofiguration for GET/PUT/POST requests")
(CheckMethodPermissions, check_method_permissions, "METHOD PERMISSIONS", "Checks for correct permission configuration for GET/PUT/POST requests"),
(CheckContainsOperation, check_contains_operation, "CONTAINS OPERATION", "Checks that each path contains at least one operation")
];
4 changes: 2 additions & 2 deletions swagger/src/scan/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ macro_rules! impl_passive_checks{
}
}
}
/*

#[macro_export]
macro_rules! impl_active_checks{
( $( ($check:ident,$check_func:ident,$name:literal,$desc:literal )),* ) => {
Expand Down Expand Up @@ -92,4 +92,4 @@ macro_rules! impl_active_checks{
}
}
}
}*/
}

0 comments on commit 7003a0e

Please sign in to comment.