Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/msf/core/auxiliary/mikrotik.rb
Views: 11784
# -*- coding: binary -*-12module Msf3###4#5# This module provides methods for working with Mikrotik equipment6#7###8module Auxiliary::Mikrotik9include Msf::Auxiliary::Report1011# this handles `export` (default), `export compact`, `export terse` and `export verbose`12# the format is a header line: `/ tree navigation`13# followed by commands: `set thing value`14def export_to_hash(config)15return {} unless config.is_a? String1617config = config.gsub(/^\s{2,4}/, '') # replace code indents18config = config.gsub(/\\\s*\n/, '') # replace verbose multiline items as single lines, similar to terse19output = {}20header = ''21config.each_line do |line|22line = line.strip23# # jul/16/2020 14:26:57 by RouterOS 6.45.924# typically the first line in the config25if %r{^# \w{3}/\d{2}/\d{4} \d{2}:\d{2}:\d{2} by (?<os>\w+) (?<version>[\d\.]+)$} =~ line26output['OS'] = ["#{os} #{version}"]2728# terse format format is more 'cisco'-ish where header and setting is on one line29# /interface ovpn-client add connect-to=10.99.99.98 mac-address=FE:45:B0:31:4A:34 name=ovpn-out1 password=password user=user30# /interface ovpn-client add connect-to=10.99.99.98 mac-address=FE:45:B0:31:4A:34 name=ovpn-out2 password=password user=user31elsif %r{^(?<section>/[\w -]+)} =~ line && (line.include?(' add ') || line.include?(' set '))32[' add ', ' set '].each do |div|33next unless line.include?(div)3435line = line.split(div)36if output[line[0].strip]37output[line[0].strip] << "#{div}#{line[1]}".strip38next39end40output[line[0].strip] = ["#{div}#{line[1]}".strip]41end4243# /interface ovpn-client44# these are the section headers45elsif %r{^(?<section>/[\w -]+)$} =~ line46header = section.strip47output[header] = [] # initialize4849# take any line that isn't commented out50elsif !line.starts_with?('#') && !header.empty?51output[header] << line.strip52end53end54output55end5657# this takes a string of config like 'add connect-to=10.99.99.99 name=l2tp-hm password=123 user=l2tp-hm'58# and converts it to a hash of keys and values for easier processing59def values_to_hash(line)60return {} unless line.is_a? String6162hash = {}63array = line.split(' ')64array.each do |setting|65key_value = setting.split('=')66unless key_value.length == 267next # skip things like 'add'68end69# verbose mode gives empty fields, however our processing makes them "" instead of empty70# so we check if its empty and skip it to prevent a double quote or escaped double quote71# field from being loaded72next if key_value[1].strip == '""' || key_value[1].strip == '\\"\\"'7374hash[key_value[0].strip] = key_value[1].strip75end76hash77end7879def mikrotik_swos_config_eater(thost, tport, config)80if framework.db.active81credential_data = {82address: thost,83port: tport,84protocol: 'tcp',85workspace_id: myworkspace_id,86origin_type: :service,87private_type: :password,88service_name: '',89module_fullname: fullname,90status: Metasploit::Model::Login::Status::UNTRIED91}92end9394# Default SNMP to UDP95if tport == 16196credential_data[:protocol] = 'udp'97end9899store_loot('mikrotik.config', 'text/plain', thost, config.strip, 'config.txt', 'MikroTik Configuration')100101host_info = {102host: thost,103os_name: 'SwOS'104}105report_host(host_info)106107# Unfortunately SWoS doesn't have a very easily parsable format. Config is exported in one big string108# they follow a key:value format followed by a comma, but since values can be arrays,109# or hashes the actual parsing of such would be pretty difficult. Since there isn't a ton of value110# in this file, we simply run some regexes against it111112# ,sys.b:{id:'4d696b726f54696b2d637373333236',wdt:0x01,dsc:0x01,ivl:0x00,alla:0x00,allm:0x00,allp:0x03ffffff,avln:0x00,prio:0x8000,cost:0x00,igmp:0x00,ip:0x0158a8c0,iptp:0x02,dtrp:0x03ffffff,ainf:0x01}113# part we care about: ,ip:0x0158a8c0114if /,ip:0x(?<ip>[\da-f]+)/ =~ config115ip = ip.scan(/../).map { |x| x.hex.to_i }.reverse.join('.')116print_status("#{thost}:#{tport} IP Address: #{ip}")117end118119# ,sys.b:{id:'4d696b726f54696b2d637373333236'120if /,sys.b:{id:'(?<host>[a-f\d]*)'/ =~ config121host = Array(host).pack('H*')122host_info[:name] = host123report_host(host_info)124print_good("#{thost}:#{tport} Hostname: #{host}")125end126127# pull the switch password .pwd.b:{pwd:'61646d696e'} -> admin128# pull the switch password .pwd.b:{pwd:''} -> (blank, which is default)129if /,\.pwd\.b:{pwd:'(?<password>[a-f\d]*)'}/ =~ config130password = Array(password).pack('H*')131print_good("#{thost}:#{tport} Admin login password: #{password}")132if framework.db.active133cred = credential_data.dup134cred[:port] = 80135cred[:protocol] = 'tcp'136cred[:service_name] = 'www'137cred[:username] = 'admin' # hardcoded138cred[:private_data] = password.to_s139create_credential_and_login(cred)140end141end142143# ,snmp.b:{en:0x01,com:'7075626c6963',ci:'636f6e74616374696e666f',loc:'6c6f636174696f6e'}144# ,snmp.b:{en:0x01,com:'7075626c6963',ci:'',loc:''}145if /,snmp\.b:{en:0x01,com:'(?<community>[a-f\d]*)',ci:'(?<contact>[a-f\d]*)',loc:'(?<location>[a-f\d]*)'}/ =~ config146community = Array(community).pack('H*')147contact = Array(contact).pack('H*')148location = Array(location).pack('H*')149print_good("#{thost}:#{tport} SNMP Community: #{community}, contact: #{contact}, location: #{location}")150if framework.db.active151cred = credential_data.dup152cred[:port] = 161153cred[:protocol] = 'udp'154cred[:service_name] = 'snmp'155cred[:private_data] = community.to_s156create_credential_and_login(cred)157end158end159160# ,nm:['506f727431','506f727432','506f727433','506f727434','506f727435','506f727436','506f727437','506f727438','506f727439','506f72743130','506f72743131','506f72743132','506f72743133','506f72743134','506f72743135','506f72743136','506f72743137','506f72743138','506f72743139','506f72743230','506f72743231','506f72743232','506f72743233','75706c696e6b','53465031','53465032']}161if /,nm:\[(?<interfaces>[a-f\d',]*)\]/ =~ config162interfaces = interfaces.split(',')163interfaces.each_with_index do |iface, i|164iface = iface.gsub("'", '')165iface = Array(iface).pack('H*')166next if iface == "Port#{i + 1}" || /SFP\d/ =~ iface # skip defaults167168print_status("#{thost}:#{tport} Port #{i + 1} Named: #{iface}")169end170end171172end173174def mikrotik_routeros_config_eater(thost, tport, config)175if framework.db.active176credential_data = {177address: thost,178port: tport,179protocol: 'tcp',180workspace_id: myworkspace_id,181origin_type: :service,182private_type: :password,183service_name: '',184module_fullname: fullname,185status: Metasploit::Model::Login::Status::UNTRIED186}187end188189# Default SNMP to UDP190if tport == 161191credential_data[:protocol] = 'udp'192end193194store_loot('mikrotik.config', 'text/plain', thost, config.strip, 'config.txt', 'MikroTik Configuration')195196host_info = {197host: thost,198os_name: 'Mikrotik'199}200report_host(host_info)201202if config.is_a? String203config = export_to_hash(config)204end205config.each do |header, values|206case header207#208# Cover OS details209#210when 'OS'211values.each do |value|212print_good("#{thost}:#{tport} OS: #{value}")213v = value.split(' ')214host_info[:os_name] = v[0]215host_info[:os_flavor] = v[1]216report_host(host_info)217end218219#220# OpenVPN client details221#222when '/interface ovpn-client'223# https://wiki.mikrotik.com/wiki/Manual:Interface/OVPN#Client_Config224# add connect-to=10.99.99.98 mac-address=FE:45:B0:31:4A:34 name=ovpn-out1 password=password user=user225# add connect-to=10.99.99.98 disabled=yes mac-address=FE:45:B0:31:4A:34 name=ovpn-out3 password=password user=user226# no such thing as disabled=no, the value is just not there227values.each do |value|228next unless value.starts_with?('add ')229230value = values_to_hash(value)231print_good("#{thost}:#{tport} #{value['disabled'] ? 'disabled' : ''} Open VPN Client to #{value['connect-to']} on mac #{value['mac-address']} named #{value['name']} with username #{value['user']} and password #{value['password']}")232next unless framework.db.active233234cred = credential_data.dup235cred[:port] = 1194236cred[:service_name] = 'openvpn'237cred[:username] = value['user']238cred[:private_data] = value['password']239create_credential_and_login(cred)240end241242#243# PPPoE client details244#245when '/interface pppoe-client'246# https://wiki.mikrotik.com/wiki/Manual:Interface/PPPoE#PPPoE_Client247# add disabled=no interface=ether2 name=pppoe-user password=password service-name=internet user=user248values.each do |value|249next unless value.starts_with?('add ')250251value = values_to_hash(value)252print_good("#{thost}:#{tport} #{value['disabled'] ? '' : 'disabled'} PPPoE Client on #{value['interface']} named #{value['name']} and service name #{value['service-name']} with username #{value['user']} and password #{value['password']}")253next unless framework.db.active254255cred = credential_data.dup256cred[:username] = value['user']257cred[:service_name] = 'pppoe'258cred[:private_data] = value['password']259create_credential_and_login(cred)260end261262#263# L2TP client details264#265when '/interface l2tp-client'266# https://wiki.mikrotik.com/wiki/Manual:Interface/L2TP#L2TP_Client267# add connect-to=10.99.99.99 name=l2tp-hm password=123 user=l2tp-hm268values.each do |value|269next unless value.starts_with?('add ')270271value = values_to_hash(value)272print_good("#{thost}:#{tport} #{value['disabled'] ? '' : 'disabled'} L2TP Client to #{value['connect-to']} named #{value['name']} with username #{value['user']} and password #{value['password']}")273next unless framework.db.active274275cred = credential_data.dup276cred[:port] = 1701277cred[:service_name] = 'l2tp'278cred[:username] = value['user']279cred[:private_data] = value['password']280create_credential_and_login(cred)281end282#283# PPTP client details284#285when '/interface pptp-client'286# https://wiki.mikrotik.com/wiki/Manual:Interface/PPTP#PPTP_Client287# add connect-to=10.99.99.99 disabled=no name=pptp-hm password=123 user=pptp-hm288values.each do |value|289next unless value.starts_with?('add ')290291value = values_to_hash(value)292print_good("#{thost}:#{tport} #{value['disabled'] ? '' : 'disabled'} PPTP Client to #{value['connect-to']} named #{value['name']} with username #{value['user']} and password #{value['password']}")293next unless framework.db.active294295cred = credential_data.dup296cred[:service_name] = 'pptp'297cred[:port] = 1723298cred[:username] = value['user']299cred[:private_data] = value['password']300create_credential_and_login(cred)301end302#303# SNMP details304#305when '/snmp community'306# https://wiki.mikrotik.com/wiki/Manual:SNMP307# add addresses=::/0 authentication-password=write name=write write-access=yes308values.each do |value|309next unless value.starts_with?('add ')310311value = values_to_hash(value)312if value['encryption-password'] # v3313print_good("#{thost}:#{tport} SNMP community #{value['name']} with password #{value['authentication-password']}(#{value['authentication-protocol']}), encryption password #{value['encryption-password']}(#{value['encryption-protocol']}) and #{value['write-access'] ? 'write access' : 'read only'}")314else315print_good("#{thost}:#{tport} SNMP community #{value['name']} with password #{value['authentication-password']} and #{value['write-access'] ? 'write access' : 'read only'}")316end317318next unless framework.db.active319320cred = credential_data.dup321if value['write-access'] == 'yes'322cred[:access_level] = 'RW'323else324cred[:access_level] = 'RO'325end326cred[:protocol] = 'udp'327cred[:port] = 161328cred[:service_name] = 'snmp'329cred[:private_data] = value['name']330create_credential_and_login(cred)331end332#333# PPP tunnel bridging secret details334#335when '/ppp secret'336# https://wiki.mikrotik.com/wiki/Manual:BCP_bridging_(PPP_tunnel_bridging)#Office_1_configuration337# add name=ppp1 password=password profile=ppp_bridge338values.each do |value|339next unless value.starts_with?('add ')340341value = values_to_hash(value)342print_good("#{thost}:#{tport} #{value['disabled'] ? 'disabled' : ''} PPP tunnel bridging named #{value['name']} with profile name #{value['profile']} and password #{value['password']}")343next unless framework.db.active344345cred = credential_data.dup346cred[:username] = ''347cred[:private_data] = value['password']348create_credential_and_login(cred)349end350#351# SMB users details352#353when '/ip smb users'354# https://wiki.mikrotik.com/wiki/Manual:IP/SMB#User_setup355# add name=mtuser password=mtpasswd read-only=no356# add disabled=yes name=disableduser password=disabledpasswd357values.each do |value|358next unless value.starts_with?('add ')359360value = values_to_hash(value)361print_good("#{thost}:#{tport} #{value['disabled'] == 'yes' ? 'disabled' : ''} SMB Username #{value['name']} and password #{value['password']}#{' with RO only access' if value['read-only'] == 'yes' || !value['read-only']}")362next unless framework.db.active363364cred = credential_data.dup365if value['read-only'] == 'yes' || !value['read-only']366cred[:access_level] = 'RO'367end368cred[:service_name] = 'smb'369cred[:username] = value['name']370cred[:private_data] = value['password']371create_credential_and_login(cred)372end373#374# SMTP user details375#376when '/tool e-mail'377# https://wiki.mikrotik.com/wiki/Manual:Tools/email#Properties378# set address=1.1.1.1 [email protected] password=smtppassword user=smtpuser379values.each do |value|380next unless value.starts_with?('set ')381382value = values_to_hash(value)383print_good("#{thost}:#{tport} SMTP Username #{value['user']} and password #{value['password']} for #{value['address']}:#{value['port'] || '25'}")384next unless framework.db.active385386cred = credential_data.dup387cred[:service_name] = 'smtp'388cred[:port] = value['port'] ? value['port'].to_i : 25389cred[:address] = value['address']390cred[:protocol] = 'tcp'391cred[:username] = value['user']392cred[:private_data] = value['password']393create_credential_and_login(cred)394end395#396# Wireless networks details397#398when '/interface wireless security-profiles'399# https://wiki.mikrotik.com/wiki/Manual:Interface/Wireless#Security_Profiles400# add name=openwifi supplicant-identity=MikroTik401# add authentication-types=wpa-psk mode=dynamic-keys name=wpawifi supplicant-identity=MikroTik wpa-pre-shared-key=presharedkey402# add authentication-types=wpa2-psk mode=dynamic-keys name=wpa2wifi supplicant-identity=MikroTik wpa2-pre-shared-key=presharedkey403# add authentication-types=wpa2-eap mode=dynamic-keys mschapv2-password=password mschapv2-username=username name=wpaeapwifi supplicant-identity=MikroTik404# add mode=static-keys-required name=wepwifi static-key-0=0123456789 static-key-1=0987654321 static-key-2=1234509876 static-key-3=0192837645 supplicant-identity=MikroTik405values.each do |value|406next unless value.starts_with?('add ')407408value = values_to_hash(value)409output = "#{thost}:#{tport} Wireless AP #{value['name']}"410411if !value['authentication-types'] && (!value['mode'] || value['mode'] == 'none') # open wifi412output << ' with no encryption (open wifi)'413vprint_good(output)414next415end416417if framework.db.active418cred = credential_data.dup419else420cred = {}421end422423# The following section is a little complicated due to the way mikrotik exports values424# in compact/terse/default mode, it will skip printing default values, so we have to check425# that keys are present.426# In verbose mode, they will print but be empty427if value['wpa-pre-shared-key'] && !value['wpa-pre-shared-key'].empty?428output << " with WPA password #{value['wpa-pre-shared-key']}"429if framework.db.active430cred[:private_data] = value['wpa-pre-shared-key']431create_credential_and_login(cred)432end433elsif value['wpa2-pre-shared-key'] && !value['wpa2-pre-shared-key'].empty?434output << " with WPA2 password #{value['wpa2-pre-shared-key']}"435if framework.db.active436cred[:private_data] = value['wpa2-pre-shared-key']437create_credential_and_login(cred)438end439elsif value['authentication-types'] == 'wpa2-eap'440output << " with WPA2-EAP username #{value['mschapv2-username']} password #{value['mschapv2-password']}"441if framework.db.active442cred[:username] = value['mschapv2-username']443cred[:private_data] = value['mschapv2-password']444create_credential_and_login(cred)445end446elsif value['static-key-0'] || value['static-key-1'] || value['static-key-2'] || value['static-key-3']447(0..3).each do |i|448key = "static-key-#{i}"449next unless value[key]450451output << " with WEP password #{value[key]}"452if framework.db.active453cred[:private_data] = value[key]454create_credential_and_login(cred) # run for each key we find455end456end457end458459print_good(output)460end461462#463# hostname details464#465when '/system identity'466# https://wiki.mikrotik.com/wiki/Manual:System/identity#Configuration467# set name=mikrotik_hostname468values.each do |value|469next unless value.starts_with?('set ')470471value = values_to_hash(value)472host_info[:name] = value['name']473report_host(host_info)474end475end476end477end478end479end480481482