Skip to content

Instantly share code, notes, and snippets.

@dustyfresh
Last active June 9, 2021 00:41
Show Gist options
  • Save dustyfresh/c6d81e6c77e0d4e30e844edc25cbee40 to your computer and use it in GitHub Desktop.
Save dustyfresh/c6d81e6c77e0d4e30e844edc25cbee40 to your computer and use it in GitHub Desktop.
Simple http sensor written in Rust with Rocket. Always responds 200 to every request, logs POST data, and each request has a random content-length
[package]
name = "http-sensor"
version = "0.1.0"
authors = [""]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rocket = "0.4.10"
chrono = "0.4.19"
rand = "0.8.3"
# This is a snippet of example logs from Nikto and sqlmap
[2021-06-08 23:10:12.888856713 UTC] -- 127.0.0.1 Mozilla/5.00 (Nikto/2.1.5) (Evasions:None) (Test:006481) POST /private.php
[2021-06-08 23:10:12.888927262 UTC] -- COLLECTED PAYLOAD => "my_post_key=&keywords='+or+'a'+'a&quick_search=Search+PMs&allbox=Check+All&fromfid=0&fid=4&jumpto=4&action=do_stuff"
[2021-06-08 23:10:12.888969288 UTC] -- Completed POST request for 127.0.0.1
[2021-06-09 00:40:33.976259891 UTC] -- 127.0.0.1 sqlmap/1.4.4#stable (http://sqlmap.org) GET /test.php?id=1%29%20WAITFOR%20DELAY%20%270%3A0%3A5%27%20AND%20%286362%3D6362
[2021-06-09 00:40:33.977417364 UTC] -- Completed GET request for 127.0.0.1
[2021-06-09 00:40:33.980714617 UTC] -- 127.0.0.1 sqlmap/1.4.4#stable (http://sqlmap.org) GET /test.php?id=1%20WAITFOR%20DELAY%20%270%3A0%3A5%27
[2021-06-09 00:40:33.981199446 UTC] -- Completed GET request for 127.0.0.1
[2021-06-09 00:40:33.984192726 UTC] -- 127.0.0.1 sqlmap/1.4.4#stable (http://sqlmap.org) GET /test.php?id=1%20WAITFOR%20DELAY%20%270%3A0%3A5%27--%20ruVs
[2021-06-09 00:40:33.985204211 UTC] -- Completed GET request for 127.0.0.1
#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use] extern crate rocket;
mod web {
// Rocket dependencies & types
use rocket::data;
use rocket::Data;
use rocket::{Request};
use rocket::http::Status;
use std::fs::OpenOptions;
use rocket::fairing::AdHoc;
use rocket::data::FromDataSimple;
// Non-rocket dependencies & types
use rand::{distributions::Alphanumeric, Rng};
use std::io::{Read, Write, BufWriter};
use chrono::{DateTime, Utc};
use std::path::PathBuf;
// function for logging events
fn logger(data: &String){
let timestamp: DateTime<Utc> = Utc::now();
let mut outfile = BufWriter::new(OpenOptions::new()
.write(true)
.append(true)
.create(true)
.open("http-sensor.log")
.unwrap());
writeln!(outfile, "[{}] -- {}", timestamp.to_string(), &data).expect("Error!");
}
// Generate random data to include in responses for showing varying content-length
fn random_data() -> String {
let mut rng = rand::thread_rng();
let random_num: u16 = rng.gen_range(100..=3000);
let random_str: String = rng.sample_iter(&Alphanumeric)
.take(random_num.into())
.map(char::from)
.collect();
random_str
}
// We create a struct to sign the post payload
#[derive(Debug)]
struct SignedPayload {
contents: String,
}
impl FromDataSimple for SignedPayload {
type Error = String;
fn from_data(_req: &Request, data: Data) -> data::Outcome<Self, String> {
let mut contents = String::new();
if let Err(e) = data.open().take(10000).read_to_string(&mut contents) {
return rocket::Outcome::Failure((Status::InternalServerError, format!("{:?}", e)));
}
rocket::Outcome::Success(SignedPayload { contents })
}
}
#[post("/", data = "<_payload>")]
fn honey_post(_payload: SignedPayload) -> String {
let log_line = format!("COLLECTED PAYLOAD => {:?}", _payload.contents);
logger(&log_line);
format!("{}", random_data())
}
#[post("/<_path..>", data = "<_payload>")]
fn honey_post_dynamic(_path: PathBuf, _payload: SignedPayload) -> String {
let log_line = format!("COLLECTED PAYLOAD => {:?}", _payload.contents);
logger(&log_line);
format!("{}", random_data())
}
#[get("/")]
fn honey_get() -> String {
format!("{}", random_data())
}
#[get("/<_path..>")]
fn honey_get_dynamic(_path: PathBuf) -> String {
format!("{}", random_data())
}
// Starts the server, mounts the routes and attaches adhoc request logic
pub fn server(){
rocket::ignite().mount("/", routes![
honey_get,
honey_get_dynamic,
honey_post,
honey_post_dynamic
]).attach(AdHoc::on_request("new request handler", |request, _| {
let headers = request.headers();
let user_agent = headers.get_one("User-Agent").unwrap_or("No-User-Agent");
let method = request.method().to_string();
let uri = request.uri().to_string();
let client_ip = request.client_ip().unwrap();
let log_line = format!("{} {} {} {}", client_ip, user_agent, method, uri);
logger(&log_line);
})).attach(AdHoc::on_response("set custom headers on all responses", |_request, response| {
// Overrides the default Server header with a vuln nginx version
response.set_raw_header("Server", "nginx/1.20.0");
})).attach(AdHoc::on_response("logger", |request, _response| {
let log_line = format!("Completed {} request for {}\n",
request.method(),
request.client_ip().unwrap()
);
logger(&log_line);
})).launch();
}
}
fn main() {
// Start the webserver
web::server();
}
[production]
address = "0.0.0.0"
port = 80
workers = 16
log = "off"
[development]
address = "127.0.0.1"
port = 80
workers = 16
log = "normal"
[staging]
address = "127.0.0.1"
port = 80
workers = 16
log = "normal"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment