Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/pnscan flags #23

Merged
merged 2 commits into from
Jan 2, 2024
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
28 changes: 19 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ Fast Network Scanning
Search engines Integration
> Seamlessly integrate Binoculo with Meilisearch (current), enabling lightning-fast search capabilities over your scan results. Index and query your collected data with Meilisearch's powerful search engine, enabling efficient retrieval of network service information.

WIP - Specific Banner Searches
Specific Banner Searches
> Perform targeted searches for specific service banners. Refine your queries to focus on precise service types or versions, streamlining your network reconnaissance efforts.

WIP - HTTP Write (pnscan inspired)
HTTP Write (pnscan inspired)
> Send commands over HTTP to communicate with services and perform actions, enhancing your network exploration capabilities

## Commands
Expand All @@ -30,20 +30,21 @@ Author: Girorme <g1r0rm3@gmail.com>
A banner grabbing tool

USAGE:
Binoculo [-v] --range host_notation --port port(s) [--output output]
Binoculo [-v] --range host_notation --port port(s) [--output output] [--write write] [--read read]
Binoculo --version
Binoculo --help

FLAGS:

-v Verbosity level
-v Verbosity level

OPTIONS:

-r, --range CIDR or IP range: 192.168.1.0/24 or
192.168.1.0..192.168.1.255
-p, --port Port(s) to scan: 80,443,8080 or 80-8080 or 21,80-8080
-o, --output Output file
--range CIDR or IP range: 192.168.1.0/24 or 192.168.1.0..192.168.1.255
-p, --port Port(s) to scan: 80,443,8080 or 80-8080 or 21,80-8080
-o, --output Output file
-w, --write Write cutom payload to socket, e.g: GET / HTTP/1.1
-r, --read Save only responses that match with this string, e.g: Apache
```

## Usage
Expand All @@ -61,10 +62,19 @@ $ ./binoculo -r 192.168.101.1/24 -p 21,22 --output my_result.txt
Finishing the scan you can get the results via `output/my_result.txt` generated by the `--output` switch

## More features
- Write custom payload to socket (inspired by pnscan :bowtie:)
```
$ ./binoculo --range 192.168.101.1/24 -p 80 --output result.txt -w "GET / HTTP/1.1"
```

- Save only matching criteria (inspired by pnscan :bowtie:)
```
$ ./binoculo --range 192.168.101.1/24 -p 80 --output result.txt -w "HEAD / HTTP/1.1" -r "Apache"
```

- You can get/filter the results via a dashboard (wip) using the `--dashboard` flag:
```
$ ./binoculo -r 192.168.101.1/24 -p 21,22 --output my_result.txt --dashboard
$ ./binoculo --range 192.168.101.1/24 -p 21,22 --output my_result.txt --dashboard
```

Finishing the scan above visit `localhost:3000` to see a page with the results (collected from meilisearch)
Expand Down
5 changes: 5 additions & 0 deletions lib/binoculo.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ defmodule Binoculo do
ports = get_in(parsed_args, [Access.key!(:options), Access.key!(:ports)])
output = get_in(parsed_args, [Access.key!(:options), Access.key!(:output)])
port_count = Enum.count(ports)
write_payload = get_in(parsed_args, [Access.key!(:options), Access.key!(:write)])
read_payload = get_in(parsed_args, [Access.key!(:options), Access.key!(:read)])

# TODO: improve config set to pipe configs above
Config.set_output_file(output)
Config.set_write_payload(write_payload)
Config.set_read_payload(read_payload)

{:ok, qty_to_run} = Maestro.start_get_banner_workers(host_notation, ports)

Expand Down
15 changes: 13 additions & 2 deletions lib/binoculo/args.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ defmodule Binoculo.Args do
options: [
host_notation: [
value_name: "host_notation",
short: "-r",
long: "--range",
help: "CIDR or IP range: 192.168.1.0/24 or 192.168.1.0..192.168.1.255",
parser: fn notation ->
Expand Down Expand Up @@ -56,14 +55,26 @@ defmodule Binoculo.Args do
long: "--output",
help: "Output file",
parser: fn output ->
case File.open("output/#{output}", [:write]) do
case File.open("output/#{output}", [:append]) do
{:ok, _file} ->
{:ok, output}

{:error, _} ->
{:error, "invalid output file"}
end
end
],
write: [
value_name: "write",
short: "-w",
long: "--write",
help: "Write cutom payload to socket, e.g: GET / HTTP/1.1"
],
read: [
value_name: "read",
short: "-r",
long: "--read",
help: "Save only responses that match with this string, e.g: Apache"
]
]
)
Expand Down
32 changes: 32 additions & 0 deletions lib/binoculo/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,43 @@ defmodule Binoculo.Config do
GenServer.call(__MODULE__, :get_output_file)
end

def set_write_payload(payload) do
GenServer.cast(__MODULE__, {:set_write_payload, payload})
end

def get_write_payload() do
GenServer.call(__MODULE__, :get_write_payload)
end

def set_read_payload(payload) do
GenServer.cast(__MODULE__, {:set_read_payload, payload})
end

def get_read_payload() do
GenServer.call(__MODULE__, :get_read_payload)
end

def handle_cast({:set_output_file, file_name}, state) do
{:noreply, Map.put(state, :output_file, file_name)}
end

def handle_cast({:set_write_payload, payload}, state) do
{:noreply, Map.put(state, :write_payload, payload)}
end

def handle_cast({:set_read_payload, payload}, state) do
{:noreply, Map.put(state, :read_payload, payload)}
end

def handle_call(:get_output_file, _from, state) do
{:reply, state[:output_file], state}
end

def handle_call(:get_write_payload, _from, state) do
{:reply, state[:write_payload], state}
end

def handle_call(:get_read_payload, _from, state) do
{:reply, state[:read_payload], state}
end
end
18 changes: 17 additions & 1 deletion lib/binoculo/cross_saver.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ defmodule Binoculo.CrossSaver do

alias Binoculo.{Config, Results, Util}

# TODO: Add save to msearch

def save_results() do
check_and_save_to_file()
end
Expand All @@ -26,9 +28,23 @@ defmodule Binoculo.CrossSaver do
defp save_to_file(true) do
results =
Results.get_finished()
|> Enum.map(&Util.host_info_to_text_template/1)
|> check_and_filter_read_payload()

results = Enum.map(results, &Util.host_info_to_text_template/1)

Config.get_output_file()
|> File.write(results, [:append])
end

defp check_and_filter_read_payload(results) do
case Config.get_read_payload() do
nil ->
results

read_payload ->
Enum.filter(results, fn %{response: response} ->
Regex.match?(~r/#{read_payload}/i, response)
end)
end
end
end
31 changes: 27 additions & 4 deletions lib/binoculo/worker.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule Binoculo.Worker do
Main Worker
"""

alias Binoculo.Util
alias Binoculo.{Config, Util}

@type host() :: String.t()
@type host_port() :: integer()
Expand Down Expand Up @@ -50,9 +50,32 @@ defmodule Binoculo.Worker do
:tcp -> ""
end

case :gen_tcp.send(socket, payload) do
:ok -> {:ok, socket}
{:error, reason} -> {:error, reason}
# User write payload
payload =
if write_payload = Config.get_write_payload() do
write_payload
else
payload
end

# TODO: improve code readbility
if write_payload do
case :gen_tcp.send(socket, payload) do
:ok ->
# Send extra payload when user wants to write to socket
case :gen_tcp.send(socket, "\r\n\r\n") do
:ok -> {:ok, socket}
{:error, reason} -> {:error, reason}
end

{:error, reason} ->
{:error, reason}
end
else
case :gen_tcp.send(socket, payload) do
:ok -> {:ok, socket}
{:error, reason} -> {:error, reason}
end
end
end

Expand Down
Loading