Skip to content
This repository has been archived by the owner on Mar 6, 2024. It is now read-only.

Daemon GRPC interaction along with CLI app. #5

Merged
merged 12 commits into from
Jan 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
571 changes: 566 additions & 5 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
[workspace]
members = [
"hogg-daemon",
"hogg-grpc",
"hogg-cli",
"hogg-common"
]
28 changes: 20 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,51 @@
# Hogg 🐽

An experimental passive website scanner. Hogg acts as a proxy between you and your DNS server and scans every website you visit for common vulnerabilities.

## Installation & Usage
Currently you can only run the daemon. In future, you will be able to use a special CLI or GUI to interact with daemon. The daemon is responsible of sending notifications.
```shell
git clone https://github.com/yallxe/hogg
cd hogg
cargo run -p hogg-deamon # requires root on linux & macos
```
After you run the daemon, you can set you DNS server to `localhost:53`, so all the DNS requests will be sent to hogg.
## Building

0. Make sure you have Rust installed. If not, follow the instructions [here](https://www.rust-lang.org/tools/install).
1. Install dependencies for [tonic](https://github.com/hyperium/tonic) from [here](https://github.com/hyperium/tonic#dependencies).
2. Install [Nuclei](https://github.com/projectdiscovery/nuclei) and make sure it's in your `$PATH`.
3. Clone the repo and `cd` into it.
4. Run `cargo build --release` to build the binary.

## Using it

To make hogg work, you need to run the daemon, which will serve the DNS proxy and scan the websites you visit. You will get a notification when a vulnerability is found. To view the vulnerabilities, you can use the `hogg` CLI. Use `hogg-cli -h` to see the available commands.
To run the daemon, use `hogg-daemon` binary.

## Configuration

Checkout your configuration path, which is printed when you start the daemon, or use `echo $HOGG_CONFIG_DIR`

## How does it work?

1. Your browser or a desktop app resolves a domain name via DNS.
2. Hogg requests the data from your upstream DNS provider (Cloudflare by default) and sends it back to the app.
3. Hogg scans the website using [Nuclei](https://github.com/projectdiscovery/nuclei).

## How is it different?

Hogg will help you scan almost every website you visit (not limited to your browser) without causing any disruption to the app's functionality.

## Anything besides DNS?

Not yet. Stay tuned for future updates that may include other solutions (like an HTTP proxy).

## Limitations

- Hogg doesn't yet support DNS over HTTPS, DNS over TLS etc.
- Some apps may bypass your system's default DNS resolver. In this case, Hogg will not intercept the app's requests.

## Progress

- [x] Working DNS proxy and Nuclei scanner
- [x] Notifications (OS notifications for now)
- [ ] Automatic request redirection to DNS Proxy
- [ ] GUI (a tray icon)
- [ ] DNS over HTTPS

## Credits

- Inspired by [Trufflehog-Chrome-Extension](https://github.com/trufflesecurity/Trufflehog-Chrome-Extension) ❤️
1 change: 1 addition & 0 deletions hogg-cli/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target/
17 changes: 17 additions & 0 deletions hogg-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "hogg-cli"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
hogg-grpc = { path = "../hogg-grpc" }
hogg-common = { path = "../hogg-common" }
hogg-daemon = { path = "../hogg-daemon" }

anyhow = "1.0.68"
async-trait = "0.1.60"
clap = { version = "4.0.32", features = ["derive", "cargo"] }
logs = "0.7.1"
tokio = "1.23.0"
73 changes: 73 additions & 0 deletions hogg-cli/src/cmds/detections.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use std::path::Path;

use anyhow::Result;
use hogg_common::db::HoggDatabase;
use hogg_common::env;
use hogg_daemon::nuclei::NucleiJsonOutput;
use hogg_grpc::grpc;

fn get_database_dir() -> String {
Path::new(&env::get_hogg_dir())
.join(".hoggdb.json")
.to_str()
.unwrap()
.to_string()
}

pub async fn get_unviewed() -> Result<()> {
let mut db = HoggDatabase::<NucleiJsonOutput>::from_file_unconfigured(get_database_dir())?;

let detections = db.get_unviewed_detections(true)?;

println!();
for d in detections.iter() {
println!(
"Vulnerability {} - {}",
d.data.info.name, d.data.info.severity
);
println!(
" - Host: {}",
d.data.host
);
println!(
" - Matched at: {}",
d.data.matched_at.clone().unwrap_or("".to_string())
);
println!(
" - Description: {}",
d.data
.info
.description
.clone()
.unwrap_or("No description".to_string())
.trim()
);
println!(
" - References: {}",
d.data
.info
.reference
.clone()
.unwrap_or(vec!["None".to_string()])
.join(", ")
);

println!(" - Timestamp: {}", d.data.timestamp);
println!();
println!();
}
logs::info!("There were {} unviewed detections", detections.len());
Ok(())
}

pub async fn flush_detections() -> Result<()> {
logs::info!("Flushing detections from database...");
let mut db = HoggDatabase::<NucleiJsonOutput>::from_file_unconfigured(get_database_dir())?;
db.flush_detections()?;

logs::info!("Sending database forced reload to hogg-daemon...");
let mut grpc = grpc::connect_grpc_client().await?;
grpc.reload_database(grpc::ReloadDatabaseRequest {}).await?;

Ok(())
}
6 changes: 6 additions & 0 deletions hogg-cli/src/cmds/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pub mod ping;
pub use ping::run as ping_command;

pub mod detections;
pub use detections::get_unviewed as unviewed_detections_command;
pub use detections::flush_detections as flush_command;
16 changes: 16 additions & 0 deletions hogg-cli/src/cmds/ping.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use anyhow::Result;
use hogg_grpc::grpc;

pub async fn run() -> Result<()> {
let mut grpc = grpc::connect_grpc_client().await?;

let ping_request = grpc::PingRequest {
message: "Hello from hogg-cli".to_string(),
};

let ping_response = grpc.ping(ping_request).await?;

logs::info!("Received pong: {:?}", ping_response);

Ok(())
}
48 changes: 48 additions & 0 deletions hogg-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use anyhow::Result;
use clap::{Parser, Subcommand};
use logs::LevelFilter;

mod cmds;

#[derive(Parser)]
#[command(name = "hogg-cli", version = "0.1.0", author = "yallxe")]
#[command(about = "A CLI for Hogg")]
struct CliArgs {
#[command(subcommand)]
command: Option<Commands>,
}

#[derive(Subcommand)]
enum Commands {
#[command(about = "Ping the Hogg Daemon to check if it's running")]
Ping,
#[command(about = "Get all unviewed detections from hogg database")]
UnviewedDetections,
#[command(about = "Delete all the detections from hogg database")]
Flush
}

#[tokio::main]
async fn main() -> Result<()> {
let logger = logs::Logs::new().color(true);
match logger.level_from_env("HOGG_CLI_LOG") {
Ok(logger) => {
logger.init();
}
Err(_) => {
logs::Logs::new()
.color(true)
.level(LevelFilter::Info)
.init();
}
};

match CliArgs::parse().command {
Some(Commands::Ping) => cmds::ping_command().await?,
Some(Commands::UnviewedDetections) => cmds::unviewed_detections_command().await?,
Some(Commands::Flush) => cmds::flush_command().await?,
None => logs::error!("No command given"),
}

Ok(())
}
2 changes: 2 additions & 0 deletions hogg-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ edition = "2021"
anyhow = "1.0.68"
async-trait = "0.1.60"
directories = "4.0.1"
err-derive = "0.3.1"
include_dir = { version = "0.7.3", features = ["glob"] }
logs = "0.7.1"
reqwest = "0.11.13"
serde = { version = "1.0.151", features = ["derive"] }
serde_derive = "1.0.151"
serde_json = "1.0.91"
toml = "0.5.10"
12 changes: 11 additions & 1 deletion hogg-common/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ pub struct HoggConfig {
pub dnsproxy: DnsProxyConfig,
pub daemon: DaemonConfig,
pub scanner: ScannerConfig,
pub database: DatabaseConfig,

#[serde(skip)]
_file: String,
pub _file: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct DnsProxyConfig {
#[serde(default)]
pub enabled: bool,
pub bind: String,
pub upstreams: Vec<String>,
}
Expand Down Expand Up @@ -44,6 +47,13 @@ pub struct ScannerNucleiConfig {
pub using_community_templates: Vec<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct DatabaseConfig {
pub save_detections: bool,
pub detections_limiter_enabled: bool,
pub max_detections: usize,
}

impl HoggConfig {
pub fn from_file(path: &str) -> Result<Self> {
let config = std::fs::read_to_string(path)?;
Expand Down
Loading