diff --git a/README.md b/README.md index 8ef38f0..2696733 100644 --- a/README.md +++ b/README.md @@ -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 @@ -30,20 +30,21 @@ Author: Girorme 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 @@ -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) diff --git a/lib/binoculo.ex b/lib/binoculo.ex index 735ea43..3b859d7 100644 --- a/lib/binoculo.ex +++ b/lib/binoculo.ex @@ -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) diff --git a/lib/binoculo/args.ex b/lib/binoculo/args.ex index f2b57f5..c02a453 100644 --- a/lib/binoculo/args.ex +++ b/lib/binoculo/args.ex @@ -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 -> @@ -56,7 +55,7 @@ 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} @@ -64,6 +63,18 @@ defmodule Binoculo.Args do {: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" ] ] ) diff --git a/lib/binoculo/config.ex b/lib/binoculo/config.ex index 98555cd..52e825a 100644 --- a/lib/binoculo/config.ex +++ b/lib/binoculo/config.ex @@ -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 diff --git a/lib/binoculo/cross_saver.ex b/lib/binoculo/cross_saver.ex index 2118b3c..93e6600 100644 --- a/lib/binoculo/cross_saver.ex +++ b/lib/binoculo/cross_saver.ex @@ -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 @@ -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 diff --git a/lib/binoculo/worker.ex b/lib/binoculo/worker.ex index fac727a..1143958 100644 --- a/lib/binoculo/worker.ex +++ b/lib/binoculo/worker.ex @@ -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() @@ -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