diff --git a/Gemfile b/Gemfile index 98f1b86..f8378d2 100644 --- a/Gemfile +++ b/Gemfile @@ -46,9 +46,9 @@ gem 'jwt', '2.8.1' gem 'libusb', '0.7.1' gem 'luhn', '1.0.2' gem 'mail', '2.8.1' +gem 'meshtastic', '0.0.1' gem 'metasm', '1.0.5' -gem 'mqtt', '0.6.0' -# gem 'mongo', '2.19.3' +gem 'mongo', '2.20.0' gem 'msfrpc-client', '1.1.2' gem 'netaddr', '2.0.6' gem 'net-ldap', '0.19.0' @@ -84,7 +84,7 @@ gem 'ruby-nmap', '1.0.3' gem 'ruby-saml', '1.16.0' gem 'rvm', '1.11.3.9' gem 'savon', '2.15.0' -gem 'selenium-devtools', '0.123.0' +gem 'selenium-devtools', '0.124.0' gem 'serialport', '1.3.2' # gem 'sinatra', '4.0.0' gem 'slack-ruby-client', '2.3.0' diff --git a/README.md b/README.md index ada7297..ae558e9 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ $ cd /opt/pwn $ ./install.sh $ ./install.sh ruby-gem $ pwn -pwn[v0.5.84]:001 >>> PWN.help +pwn[v0.5.87]:001 >>> PWN.help ``` [![Installing the pwn Security Automation Framework](https://raw.githubusercontent.com/0dayInc/pwn/master/documentation/pwn_install.png)](https://youtu.be/G7iLUY4FzsI) @@ -52,7 +52,7 @@ $ rvm use ruby-3.3.0@pwn $ gem uninstall --all --executables pwn $ gem install --verbose pwn $ pwn -pwn[v0.5.84]:001 >>> PWN.help +pwn[v0.5.87]:001 >>> PWN.help ``` If you're using a multi-user install of RVM do: @@ -62,7 +62,7 @@ $ rvm use ruby-3.3.0@pwn $ rvmsudo gem uninstall --all --executables pwn $ rvmsudo gem install --verbose pwn $ pwn -pwn[v0.5.84]:001 >>> PWN.help +pwn[v0.5.87]:001 >>> PWN.help ``` PWN periodically upgrades to the latest version of Ruby which is reflected in `/opt/pwn/.ruby-version`. The easiest way to upgrade to the latest version of Ruby from a previous PWN installation is to run the following script: diff --git a/lib/pwn.rb b/lib/pwn.rb index 79a8709..b7598d9 100644 --- a/lib/pwn.rb +++ b/lib/pwn.rb @@ -17,7 +17,7 @@ module PWN autoload :SAST, 'pwn/sast' autoload :WWW, 'pwn/www' - # Display Usage for the PWN Framework ~ + # Display a List of Every PWN Module public_class_method def self.help constants.sort diff --git a/lib/pwn/aws.rb b/lib/pwn/aws.rb index 8985791..067f919 100644 --- a/lib/pwn/aws.rb +++ b/lib/pwn/aws.rb @@ -96,7 +96,7 @@ module AWS autoload :Workspaces, 'pwn/aws/workspaces' autoload :XRay, 'pwn/aws/x_ray' - # Display a List of Every PWN Plugin + # Display a List of Every PWN::AWS Module public_class_method def self.help constants.sort diff --git a/lib/pwn/ffi.rb b/lib/pwn/ffi.rb index add6961..cf21c7f 100644 --- a/lib/pwn/ffi.rb +++ b/lib/pwn/ffi.rb @@ -5,9 +5,9 @@ module PWN # into memory only when they're needed. For more information, see: # http://www.rubyinside.com/ruby-techniques-revealed-autoload-1652.html module FFI - # autoload :Sock, 'pwn/ffi/sock' + autoload :Stdio, 'pwn/ffi/stdio' - # Display a List of Every PWN Report + # Display a List of Every PWN::FFI Module public_class_method def self.help constants.sort diff --git a/lib/pwn/ffi/stdio.rb b/lib/pwn/ffi/stdio.rb new file mode 100644 index 0000000..cdf148d --- /dev/null +++ b/lib/pwn/ffi/stdio.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require 'ffi' + +PubFFI = FFI +module PWN + module FFI + # This plugin is a wrapper for the standard I/O functions in libc. + module Stdio + extend PubFFI::Library + + ffi_lib PubFFI::Library::LIBC + + attach_function(:puts, [:string], :int) + attach_function(:printf, %i[string varargs], :int, convention: :default) + attach_function(:scanf, %i[string varargs], :int) + + # Author(s):: 0day Inc. + + public_class_method def self.authors + "AUTHOR(S): + 0day Inc. + " + end + + # Display Usage for this Module + + public_class_method def self.help + puts "USAGE: + #{self}.puts string + #{self}.printf(\"format string\", str, int, etc) + + scanf_buffer = FFI::MemoryPointer.new(:char, 100) + #{self}.scanf(\"format string\", scanf_buffer) + + #{self}.authors + " + end + end + end +end diff --git a/lib/pwn/plugins.rb b/lib/pwn/plugins.rb index f6cd57d..566003e 100644 --- a/lib/pwn/plugins.rb +++ b/lib/pwn/plugins.rb @@ -37,7 +37,6 @@ module Plugins autoload :JSONPathify, 'pwn/plugins/json_pathify' autoload :Log, 'pwn/plugins/log' autoload :MailAgent, 'pwn/plugins/mail_agent' - autoload :Meshtastic, 'pwn/plugins/meshtastic' autoload :Metasploit, 'pwn/plugins/metasploit' autoload :MonkeyPatch, 'pwn/plugins/monkey_patch' autoload :MSR206, 'pwn/plugins/msr206' @@ -75,7 +74,7 @@ module Plugins autoload :Vsphere, 'pwn/plugins/vsphere' autoload :XXD, 'pwn/plugins/xxd' - # Display a List of Every PWN Plugin + # Display a List of Every PWN::Plugins Module public_class_method def self.help constants.sort diff --git a/lib/pwn/plugins/meshtastic.rb b/lib/pwn/plugins/meshtastic.rb deleted file mode 100644 index 9585de7..0000000 --- a/lib/pwn/plugins/meshtastic.rb +++ /dev/null @@ -1,265 +0,0 @@ -# frozen_string_literal: true - -require 'base64' -require 'geocoder' -require 'json' -require 'mqtt' -require 'openssl' -require 'securerandom' - -module PWN - module Plugins - # Plugin used to interact with Meshtastic nodes - module Meshtastic - # Supported Method Parameters:: - # mqtt_obj = PWN::Plugins::Meshtastic.connect( - # host: 'optional - mqtt host (default: mqtt.meshtastic.org)', - # port: 'optional - mqtt port (defaults: 1883)', - # username: 'optional - mqtt username (default: meshdev)', - # password: 'optional - (default: large4cats)' - # ) - - public_class_method def self.connect(opts = {}) - # Publicly available MQTT server / credentials by default - host = opts[:host] ||= 'mqtt.meshtastic.org' - port = opts[:port] ||= 1883 - username = opts[:username] ||= 'meshdev' - password = opts[:password] ||= 'large4cats' - - mqtt_obj = MQTT::Client.connect( - host: host, - port: port, - username: username, - password: password - ) - - mqtt_obj.client_id = SecureRandom.random_bytes(8).unpack1('H*') - - mqtt_obj - rescue StandardError => e - raise e - end - - # Supported Method Parameters:: - # PWN::Plugins::Meshtastic.subscribe( - # mqtt_obj: 'required - mqtt_obj returned from #connect method' - # region: 'optional - region (default: US)', - # channel: 'optional - channel name (default: LongFast)', - # psk: 'optional - channel pre-shared key (default: AQ==)', - # qos: 'optional - quality of service (default: 0)', - # json: 'optional - JSON output (default: false)' - # ) - - public_class_method def self.subscribe(opts = {}) - mqtt_obj = opts[:mqtt_obj] - region = opts[:region] ||= 'US' - channel = opts[:channel] ||= 'LongFast' - psk = opts[:psk] ||= 'AQ==' - qos = opts[:qos] ||= 0 - json = opts[:json] ||= false - - # TODO: Find JSON URI for this - root_topic = "msh/#{region}/2/json" if json - # root_topic = "msh/#{region}/2/e" unless json - root_topic = "msh/#{region}/2/c" unless json - mqtt_obj.subscribe("#{root_topic}/#{channel}/#", qos) - - # Decrypt the message - # Our AES key is 128 or 256 bits, shared as part of the 'Channel' specification. - - # Actual pre-shared key for LongFast channel - psk = '1PG7OiApB1nwvP+rz05pAQ==' if channel == 'LongFast' - padded_psk = psk.ljust(psk.length + ((4 - (psk.length % 4)) % 4), '=') - replaced_psk = padded_psk.gsub('-', '+').gsub('_', '/') - psk = replaced_psk - dec_psk = Base64.strict_decode64(psk) - - # cipher = OpenSSL::Cipher.new('AES-256-CTR') - cipher = OpenSSL::Cipher.new('AES-128-CTR') - - if json - mqtt_obj.get_packet do |json_packet| - puts '-' * 80 - packet = JSON.parse(json_packet.payload, symbolize_names: true) - puts JSON.pretty_generate(packet) - puts '-' * 80 - puts "\n\n\n" - end - else - mqtt_obj.get_packet do |packet| - puts '-' * 80 - - payload = packet.payload.to_s - - # Convert raw packet to hex-escaped bytes - # puts "PSK: #{psk.inspect} | Length: #{psk.length}" - # puts "Dec PSK: #{dec_psk.inspect} | Length: #{dec_psk.length}" - packet_from_backwards = payload[3..6] - if packet_from_backwards - packet_from_str = packet_from_backwards.reverse - packet_from_hex = packet_from_str.bytes.map { |byte| byte.to_s(16).rjust(2, '0') }.join - packet_from = packet_from_hex.to_i(16) - puts "\nFrom: #{packet_from_str.inspect} >> #{packet_from_hex} >> #{packet_from}" - end - - packet_to_backwards = payload[8..11] - if packet_to_backwards - packet_to_str = packet_to_backwards.reverse - packet_to_hex = packet_to_str.bytes.map { |byte| byte.to_s(16).rjust(2, '0') }.join - packet_to = packet_to_hex.to_i(16) - puts "To: #{packet_to_str.inspect} >> #{packet_to_hex} >> #{packet_to}" - end - - mystery_byte = payload[12] - if mystery_byte - mystery_hex = mystery_byte.bytes.map { |byte| byte.to_s(16).rjust(2, '0') }.join - mystery = mystery_hex.to_i(16) - puts "Mystery 1: #{mystery_byte.inspect} >> #{mystery_hex} >> #{mystery}" - end - - msg_len = 0 - msg_len_byte = payload[13] - if msg_len_byte - msg_len_hex = msg_len_byte.bytes.map { |byte| byte.to_s(16).rjust(2, '0') }.join - msg_len = msg_len_hex.to_i(16) - end - puts "Message Length: #{msg_len_byte.inspect} >> #{msg_len}" - - channel_byte = payload[14] - if channel_byte - channel_hex = channel_byte.bytes.map { |byte| byte.to_s(16).rjust(2, '0') }.join - channel = channel_hex.to_i(16) - puts "Channel: #{channel_byte.inspect} >> #{channel_hex} >> #{channel}" - end - - mystery_byte = payload[15] - if mystery_byte - mystery_hex = mystery_byte.bytes.map { |byte| byte.to_s(16).rjust(2, '0') }.join - mystery = mystery_hex.to_i(16) - puts "Mystery 2: #{mystery_byte.inspect} >> #{mystery_hex} >> #{mystery}" - end - - pid_id_backwards = payload.b[-34..-31] - if pid_id_backwards - pid_str = pid_id_backwards.reverse - pid_hex = pid_str.bytes.map { |byte| byte.to_s(16).rjust(2, '0') }.join - packet_id = pid_hex.to_i(16) - puts "ID: #{pid_str.inspect} >> #{pid_hex} >> #{packet_id}" - end - - topic = packet.topic - puts "\nTopic: #{topic}" - - if msg_len.positive? - begin - puts "Payload: #{payload.inspect}" - puts "Payload Length: #{payload.length}" - - nonce_packet_id = [packet_id].pack('V').ljust(8, "\x00") - nonce_from_node = [packet_from].pack('V').ljust(8, "\x00") - # puts "Nonce from Node: #{nonce_from_node.inspect} | Length: #{nonce_from_node.length}" - nonce = "#{nonce_packet_id}#{nonce_from_node}".b - puts "Nonce: #{nonce.inspect} | Length: #{nonce.length}" - - # Decrypt the message - # Key must be 32 bytes - # IV mustr be 16 bytes - cipher.decrypt - cipher.key = dec_psk - cipher.iv = nonce - first_byte = 16 - last_byte = first_byte + msg_len - 1 - encrypted_payload = payload[first_byte..last_byte] - puts "\nEncrypted Payload:\n#{encrypted_payload.inspect}" - puts "Length: #{encrypted_payload.length}" if encrypted_payload - - decrypted = cipher.update(encrypted_payload) + cipher.final - puts "\nDecrypted Payload:\n#{decrypted.inspect}" - puts "Length: #{decrypted.length}" if decrypted - rescue StandardError => e - puts "Error decrypting message: #{e}" - end - end - raw_packet = packet.to_s.b - puts "\nRaw Packet: #{raw_packet.inspect}" - puts "Length: #{packet.to_s.length}" - puts '-' * 80 - puts "\n\n\n" - end - end - rescue Interrupt - puts "\nCTRL+C detected. Exiting..." - rescue StandardError => e - raise e - ensure - mqtt_obj.disconnect if mqtt_obj - end - - # Supported Method Parameters:: - # mqtt_obj = PWN::Plugins::Meshtastic.gps_search( - # lat: 'required - latitude float (e.g. 37.7749)', - # lon: 'required - longitude float (e.g. -122.4194)', - # ) - public_class_method def self.gps_search(opts = {}) - lat = opts[:lat] - lon = opts[:lon] - - raise 'ERROR: Latitude and Longitude are required' unless lat && lon - - gps_arr = [lat.to_f, lon.to_f] - - Geocoder.search(gps_arr) - rescue StandardError => e - raise e - end - - # Supported Method Parameters:: - # mqtt_obj = PWN::Plugins::Meshtastic.disconnect( - # mqtt_obj: 'required - mqtt_obj returned from #connect method' - # ) - public_class_method def self.disconnect(opts = {}) - mqtt_obj = opts[:mqtt_obj] - - mqtt_obj.disconnect if mqtt_obj - nil - rescue StandardError => e - raise e - end - - # Author(s):: 0day Inc. - - public_class_method def self.authors - "AUTHOR(S): - 0day Inc. - " - end - - # Display Usage for this Module - - public_class_method def self.help - puts "USAGE: - mqtt_obj = #{self}.connect( - host: 'optional - mqtt host (default: mqtt.meshtastic.org)', - port: 'optional - mqtt port (defaults: 1883)', - username: 'optional - mqtt username (default: meshdev)', - password: 'optional - (default: large4cats)' - ) - - #{self}.subscribe( - mqtt_obj: 'required - mqtt_obj object returned from #connect method', - region: 'optional - region (default: US)', - channel: 'optional - channel name (default: LongFast)', - psk: 'optional - channel pre-shared key (default: AQ==)', - qos: 'optional - quality of service (default: 0)' - ) - - mqtt_obj = #{self}.disconnect( - mqtt_obj: 'required - mqtt_obj object returned from #connect method' - ) - - #{self}.authors - " - end - end - end -end diff --git a/lib/pwn/reports.rb b/lib/pwn/reports.rb index c55a2a5..421c2fd 100644 --- a/lib/pwn/reports.rb +++ b/lib/pwn/reports.rb @@ -14,7 +14,7 @@ module Reports autoload :URIBuster, 'pwn/reports/uri_buster' # autoload :XML, 'pwn/reports/xml' - # Display a List of Every PWN Report + # Display a List of Every PWN::Reports Module public_class_method def self.help constants.sort diff --git a/lib/pwn/sast.rb b/lib/pwn/sast.rb index 543a5cc..9b83d5f 100644 --- a/lib/pwn/sast.rb +++ b/lib/pwn/sast.rb @@ -50,7 +50,7 @@ module SAST autoload :Version, 'pwn/sast/version' autoload :WindowLocationHash, 'pwn/sast/window_location_hash' - # Display a List of Each Static Code Anti-Pattern Matching Module + # Display a List of Every PWN::SAST Module public_class_method def self.help constants.sort diff --git a/lib/pwn/version.rb b/lib/pwn/version.rb index 9e97e8e..65ffe2b 100644 --- a/lib/pwn/version.rb +++ b/lib/pwn/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module PWN - VERSION = '0.5.84' + VERSION = '0.5.87' end diff --git a/lib/pwn/www.rb b/lib/pwn/www.rb index baa42ce..8c14fee 100644 --- a/lib/pwn/www.rb +++ b/lib/pwn/www.rb @@ -26,7 +26,7 @@ module WWW autoload :Upwork, 'pwn/www/upwork' autoload :Youtube, 'pwn/www/youtube' - # Display a List of Every PWN WWW module + # Display a List of Every PWN::WWW Module public_class_method def self.help constants.sort diff --git a/spec/lib/pwn/plugins/meshtastic_spec.rb b/spec/lib/pwn/ffi/stdio_spec.rb similarity index 68% rename from spec/lib/pwn/plugins/meshtastic_spec.rb rename to spec/lib/pwn/ffi/stdio_spec.rb index 2f02594..814ea99 100644 --- a/spec/lib/pwn/plugins/meshtastic_spec.rb +++ b/spec/lib/pwn/ffi/stdio_spec.rb @@ -2,14 +2,14 @@ require 'spec_helper' -describe PWN::Plugins::Meshtastic do +describe PWN::FFI::Stdio do it 'should display information for authors' do - authors_response = PWN::Plugins::Meshtastic + authors_response = PWN::FFI::Stdio expect(authors_response).to respond_to :authors end it 'should display information for existing help method' do - help_response = PWN::Plugins::Meshtastic + help_response = PWN::FFI::Stdio expect(help_response).to respond_to :help end end