lib/msf/core/auxiliary/vyos.rb
# -*- coding: binary -*-
module Msf
###
#
# This module provides methods for working with VyOS equipment
#
###
module Auxiliary::VYOS
include Msf::Auxiliary::Report
def vyos_config_eater(thost, tport, config, store = true)
credential_data = {
address: thost,
port: tport,
protocol: 'tcp',
workspace_id: myworkspace_id,
origin_type: :service,
private_type: :nonreplayable_hash,
# jtr_format: 'sha512,crypt', # default on the devices 11.4.0+
service_name: '',
module_fullname: fullname,
status: Metasploit::Model::Login::Status::UNTRIED
}
# Default SNMP to UDP
if tport == 161
credential_data[:protocol] = 'udp'
end
if store && !config.include?('such file or directory') && !config.include?('ermission denied')
l = store_loot('vyos.config', 'text/plain', thost, config.strip, 'config.txt', 'VyOS Configuration')
vprint_good("#{thost}:#{tport} Config saved to: #{l}")
end
host_info = {
host: thost,
os_name: 'VyOS'
}
report_host(host_info)
# generated by: cat /config/config.boot
# https://github.com/rapid7/metasploit-framework/issues/14124
# login {
# user jsmith {
# authentication {
# encrypted-password $6$ELBrDuW7c/8$nN7MwUST8s8O0R6HMNu/iPoTQ1s..y8HTnXraJ7Hh4bHefRmjt/2U08ZckEw4FU034wbWaeCaB5hq7mC6fNXl/
# plaintext-password ""
# }
# full-name "John Smith"
# level operator
# }
# user vyos {
# authentication {
# encrypted-password $1$5HsQse2v$VQLh5eeEp4ZzGmCG/PRBA1
# plaintext-password ""
# }
# level admin
# }
# }
# sometimes the hash is masked
# login {
# user vyos {
# authentication {
# encrypted-password ****************
# plaintext-password ""
# }
# level admin
# }
# }
# plaintext-password can also be missing: https://github.com/rapid7/metasploit-framework/pull/14161#discussion_r492884039
# in >= 1.3 'level' is no longer included and defaults to admin.
r = 'user ([^ ]+) {\s*authentication {\s*'
r << 'encrypted-password (\$?[\w$\./\*]*)\s*' # leading $ is optional in case the password is all stars
r << '(?:plaintext-password "([^"]*)")?\s*' # optional
r << '}'
r << '(?:\s*full-name "([^"]*)")?\s*' # optional
r << '(?:level (operator|admin))?' # 1.3+ seems to have removed operator
config.scan(/#{Regexp.new(r)}/mi).each do |result|
username = result[0].strip
hash = result[1].strip
# full-name is an optional field
# we label it, but dont actually use it. Maybe future expansion?
unless result[3].nil?
name = result[3].strip
end
if result[4].nil?
level = 'admin'
else
level = result[4].strip
end
cred = credential_data.dup
cred[:username] = username
unless hash.start_with?('********') # if not in config mode these are masked
cred[:jtr_format] = Metasploit::Framework::Hashes.identify_hash(hash)
cred[:private_data] = hash
print_hash = " with hash #{hash}"
end
cred[:access_level] = level
create_credential_and_login(cred) if framework.db.active
unless result[2].to_s.strip.empty?
plaintext = result[2].strip
cred[:jtr_format] = ''
cred[:private_type] = :password
cred[:private_data] = plaintext
create_credential_and_login(cred) if framework.db.active
print_hash = "with password #{plaintext}"
end
print_good("#{thost}:#{tport} Username '#{username}' with level '#{level}'#{print_hash}")
end
# generated by: cat /config/config.boot
# service {
# snmp {
# community ro {
# authorization ro
# }
# community write {
# authorization rw
# }
# }
# }
config.scan(/community (\w+) {\n\s+authorization (ro|rw)/).each do |result|
cred = credential_data.dup
cred[:port] = 161
cred[:protocol] = 'udp'
cred[:service_name] = 'snmp'
cred[:jtr_format] = ''
cred[:private_data] = result[0].strip
cred[:private_type] = :password
cred[:access_level] = result[1].strip
create_credential_and_login(cred) if framework.db.active
print_good("#{thost}:#{tport} SNMP Community '#{result[0].strip}' with #{result[1].strip} access")
end
# generated by: cat /config/config
# host-name vyos
# interfaces {
# ethernet eth0 {
# duplex auto
# hw-id 00:0c:29:c7:af:bc
# smp_affinity auto
# speed auto
# }
# ethernet eth0 {
# address 1.1.1.1/8
# hw-id 00:0c:29:c7:af:cc
# }
# loopback lo {
# }
# }
# /* Release version: VyOS 1.1.8 */
# // Release version: VyOS 1.3-rolling-202008270118
if /host-name (.+)\n/ =~ config
print_good("#{thost}:#{tport} Hostname: #{$1.strip}")
host_info[:name] = $1.strip
report_host(host_info) if framework.db.active
end
if %r{^/[/\*]\s?Release version: ([\w \.-]+)} =~ config
print_good("#{thost}:#{tport} OS Version: #{$1.strip}")
host_info[:os_flavor] = $1.strip
report_host(host_info) if framework.db.active
end
#config.scan(%r{ethernet (eth\d{1,3}) {[\w\s":-]+(?:address ([\d\.]{6,16}/\d{1,2})[\w\s:-]+)?(?:description "?([\w\.\_\s]+)"?[\w\s:-]+)?hw-id (\w{2}:\w{2}:\w{2}:\w{2}:\w{2}:\w{2})[\w\s:-]+}}).each do |result|
r = 'ethernet (eth\d{1,3}) {[\w\s":-]+'
r << '(?:address ([\d\.]{6,16}/\d{1,2})[\w\s:-]+)?'
r << '(?:description ["\']?([\w\.\_\s]+)["\']?[\w\s:-]+)?'
r << 'hw-id (\w{2}:\w{2}:\w{2}:\w{2}:\w{2}:\w{2})[\w\s:-]+'
r << '}'
config.scan(/#{Regexp.new(r)}/i).each do |result|
name = result[0].strip
mac = result[3].strip
host_info[:mac] = mac
output = "#{thost}:#{tport} Interface #{name} (#{mac})"
# static IP address
unless result[1].nil?
ip = result[1].split('/')[0].strip
host_info[:host] = ip
output << " - #{ip}"
end
# description
unless result[2].nil?
output << " with description: #{result[2].strip}"
end
report_host(host_info) if framework.db.active
print_good(output)
end
# https://docs.vyos.io/en/crux/interfaces/wireless.html
# server has type 'access-point', client is 'station'
# interfaces {
# wireless wlan0 {
# address 192.168.2.1/24
# channel 1
# mode n
# security {
# wpa {
# cipher CCMP
# mode wpa2
# passphrase "12345678"
# }
# }
# ssid "TEST"
# type access-point
# }
#}
config.scan(/wireless (wlan\d{1,3}) {\s+.+passphrase "([^\n"]+)"\s+.+ssid ["']?([^\n"]+)["']?\s+type (access-point|station)/mi).each do |result|
device = result[0].strip
password = result[1].strip
ssid = result[2].strip
type = result[3].strip
cred = credential_data.dup
cred[:port] = 1
cred[:protocol] = 'tcp'
type == 'access-point' ? cred[:service_name] ='wireless AP' : cred[:service_name] ='wireless'
cred[:jtr_format] = ''
cred[:private_data] = password
cred[:username] = ssid
cred[:private_type] = :password
create_credential_and_login(cred) if framework.db.active
print_good("#{thost}:#{tport} Wireless #{type} '#{ssid}' with password: #{password}")
end
# wireless (server) with radius
# interfaces {
# wireless wlan0 {
# address 192.168.2.1/24
# channel 1
# mode n
# security {
# wpa {
# cipher CCMP
# mode wpa2
# radius {
# server 192.168.3.10 {
# key 'VyOSPassword'
# port 1812
# }
# }
# }
# }
# ssid "Enterprise-TEST"
# type access-point
# }
# }
r = 'wireless (wlan\d{1,3}) {\s*'
r << '.+radius {\s+'
r << 'server ([^\s]+) {\s*'
r << 'key [\'"]?([^\n"]+)[\'"]?\s*'
r << 'port (\d{1,5})\s*'
r << '.+ssid [\'"]?([^\n"\']+)[\'"]?\s*'
r << 'type (access-point|station)'
#config.scan(/#{Regexp.new(r)}/mi).each do |result|
config.scan(/wireless (wlan\d{1,3}) {\s*.+radius {\s+server ([^\s]+) {\s*key ['"]?([^\n"']+)['"]?\s*port (\d{1,5})\s*.+ssid ['"]?([^\n"']+)['"]?\s*type (access-point|station)/mi).each do |result|
device = result[0].strip
server = result[1].strip
password = result[2].strip
server_port = result[3].strip
ssid = result[4].strip
type = result[5].strip
cred = credential_data.dup
cred[:port] = 1
cred[:protocol] = 'tcp'
type == 'access-point' ? cred[:service_name] ='wireless AP' : cred[:service_name] ='wireless'
cred[:jtr_format] = ''
cred[:private_data] = password
cred[:username] = ssid
cred[:private_type] = :password
create_credential_and_login(cred) if framework.db.active
print_good("#{thost}:#{tport} Wireless #{type} '#{ssid}' with radius password: #{password} to #{server}#{server_port}")
end
# https://docs.vyos.io/en/crux/services/ipoe-server.html#radius-setup
# https://docs.vyos.io/en/crux/services/webproxy.html#authentication
# https://docs.vyos.io/en/crux/vpn/pptp.html#server-example
# https://docs.vyos.io/en/crux/interfaces/l2tpv3.html#l2tpv3-over-ipsec-l2-vpn-bridge
# https://docs.vyos.io/en/crux/interfaces/pppoe.html#pppoe
# /config/auth/ldap-auth.config
end
end
end