Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

netapi32 enhancements and new post module, add_user #680

Closed
wants to merge 3 commits into from

3 participants

Chris Lennert bperry-r7 James Lee
Chris Lennert

Adds additional netapi32.dll functions to def_netapi32.rb. This improves the coverage of functions offered by this DLL.

Adds new post module, post/windows/manage/add_user, to complement the existing delete_user module. With this addition, Windows local user accounts can be both created and deleted.

calennert added some commits
Chris Lennert calennert Added additional netapi32.dll functions. e27369c
Chris Lennert calennert Added add_user method to accounts module.
This complements the existing delete_user method in the same
module, thereby permitting both the creation and deletion of
local user accounts in Windows.
b3137db
Chris Lennert calennert Added post module for adding local user accounts.
This complements the existing post/windows/manage/delete_user module.
22afd08
James Lee jlee-r7 commented on the diff
lib/msf/core/post/windows/accounts.rb
@@ -177,6 +268,35 @@ def lookup_SID_NAME_USE(enum_value)
:integrity_label
][enum_value - 1]
end
+
+ ##
+ # Writes a ruby string as null-terminated Unicode string to host's memory.
+ # Returns:
+ # The affected memory address, if all goes well
+ # Or 0 if value argument is nil
+ # Or nil if an error occurs
+ ##
+ def set_value(value)
James Lee Collaborator
jlee-r7 added a note

Poorly named method. Should be something more descriptive, like alloc_and_write_str. Also has nothing to do with Windows accounts and therefore belongs in a different mixin, probably Windows::Railgun

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
James Lee jlee-r7 commented on the diff
lib/msf/core/post/windows/accounts.rb
((18 lines not shown))
+ # One of the following:
+ # :success - Account was created successfully
+ # :access_denied - You do not have permission to add the account
+ # :user_exists - A user account with +username+ already exists
+ # :group_exists - Group exists (unclear why NetUserAdd would return this, but it does)
+ # :invalid_server - The server name provided was invalid
+ # :not_on_primary - Operation allowed only on domain controller
+ # :invalid_password - Password violates password policy somehow (complexity, length, etc.)
+ #
+ # OR nil if there was an exceptional windows error (example: ran out of memory)
+ #
+ # Caveats:
+ # nil is returned if there is an *exceptional* windows error. That error is printed.
+ # Everything other than ':success' signifies failure
+ ##
+ def add_user(username, password = nil, comment = nil, dont_expire_pwd = false, server_name = nil)
James Lee Collaborator
jlee-r7 added a note

Should check for existence of railgun before attempting to use it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
bperry-r7

Closed due to inactivity and being unable to be merged cleanly. If you would like to resubmit at a later date, please do.

bperry-r7 bperry-r7 closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Aug 9, 2012
  1. Chris Lennert
  2. Chris Lennert

    Added add_user method to accounts module.

    calennert authored
    This complements the existing delete_user method in the same
    module, thereby permitting both the creation and deletion of
    local user accounts in Windows.
  3. Chris Lennert

    Added post module for adding local user accounts.

    calennert authored
    This complements the existing post/windows/manage/delete_user module.
This page is out of date. Refresh to see the latest.
120 lib/msf/core/post/windows/accounts.rb
View
@@ -58,6 +58,97 @@ def delete_user(username, server_name = nil)
return nil
end
+ ##
+ # add_user(username, password = nil, comment = nil, dont_expire_pwd = false, server_name = nil)
+ #
+ # Summary:
+ # Adds a user account to the given server (or local, if none specified)
+ #
+ # Parameters
+ # username - The username of the account to add (not-qualified, e.g. BOB)
+ # password - The password to be assigned to the new user account
+ # comment - A comment or description of the user account
+ # dont_expire_pwd - toggles the "Password never expires" flag on the account
+ # server_name - DNS or NetBIOS name of remote server on which to add user
+ #
+ # Returns:
+ # One of the following:
+ # :success - Account was created successfully
+ # :access_denied - You do not have permission to add the account
+ # :user_exists - A user account with +username+ already exists
+ # :group_exists - Group exists (unclear why NetUserAdd would return this, but it does)
+ # :invalid_server - The server name provided was invalid
+ # :not_on_primary - Operation allowed only on domain controller
+ # :invalid_password - Password violates password policy somehow (complexity, length, etc.)
+ #
+ # OR nil if there was an exceptional windows error (example: ran out of memory)
+ #
+ # Caveats:
+ # nil is returned if there is an *exceptional* windows error. That error is printed.
+ # Everything other than ':success' signifies failure
+ ##
+ def add_user(username, password = nil, comment = nil, dont_expire_pwd = false, server_name = nil)
James Lee Collaborator
jlee-r7 added a note

Should check for existence of railgun before attempting to use it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ addr_username = set_value(username)
+ addr_password = set_value(password)
+ addr_comment = set_value(comment)
+
+ if addr_username.nil? || addr_password.nil? || addr_comment.nil?
+ return nil
+ end
+
+ acct_flags = 'UF_SCRIPT | UF_NORMAL_ACCOUNT'
+ if dont_expire_pwd
+ acct_flags << ' | UF_DONT_EXPIRE_PASSWD'
+ end
+
+ usri1 = [ # struct USER_INFO_1
+ addr_username, # usri1_name
+ addr_password, # usri1_password
+ 0, # usri1_password_age
+ 1, # usri1_priv = USER_PRIV_USER
+ 0, # usri1_home_dir
+ addr_comment, # usri1_comment
+ client.railgun.const(acct_flags), # usri1_flags
+ 0 # usri1_script_path
+ ].pack("VVVVVVVV")
+
+ result = client.railgun.netapi32.NetUserAdd(server_name, 1, usri1, 4)
+
+ client.railgun.multi([
+ ["kernel32", "VirtualFree", [addr_username, 0, MEM_RELEASE]],
+ ["kernel32", "VirtualFree", [addr_password, 0, MEM_RELEASE]],
+ ["kernel32", "VirtualFree", [addr_comment, 0, MEM_RELEASE]]
+ ])
+
+ case result['return']
+ when 0
+ return :success
+ when client.railgun.const('ERROR_ACCESS_DENIED')
+ return :access_denied
+ when 2224 # NERR_UserExists
+ return :user_exists
+ when 2223 # NERR_GroupExists
+ return :group_exists
+ when 2351 # NERR_InvalidComputer
+ return :invalid_server
+ when 2226 # NERR_NotPrimary
+ return :not_on_primary
+ when 2245 # NERR_PasswordTooShort
+ return :invalid_password
+ else
+ error = result['GetLastError']
+ if error != 0
+ print_error "Unexpected Windows System Error #{error}"
+ else
+ # Uh... we shouldn't be here
+ print_error "add_user unexpectedly returned #{result['return']}"
+ end
+ end
+
+ # If we got here, then something above failed
+ return nil
+ end
+
##
# resolve_sid(sid, system_name = nil)
@@ -177,6 +268,35 @@ def lookup_SID_NAME_USE(enum_value)
:integrity_label
][enum_value - 1]
end
+
+ ##
+ # Writes a ruby string as null-terminated Unicode string to host's memory.
+ # Returns:
+ # The affected memory address, if all goes well
+ # Or 0 if value argument is nil
+ # Or nil if an error occurs
+ ##
+ def set_value(value)
James Lee Collaborator
jlee-r7 added a note

Poorly named method. Should be something more descriptive, like alloc_and_write_str. Also has nothing to do with Windows accounts and therefore belongs in a different mixin, probably Windows::Railgun

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ if value == nil
+ return 0
+ else
+ data = client.railgun.util.str_to_uni_z(value)
+ result = client.railgun.kernel32.VirtualAlloc(nil, data.length, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)
+
+ if result['return'].nil?
+ print_error "Failed to allocate memory on the host."
+ return nil
+ end
+
+ addr = result['return']
+ if client.railgun.memwrite(addr, data, data.length)
+ return addr
+ else
+ print_error "Failed to write to memory on the host."
+ return nil
+ end
+ end
+ end
end # Accounts
end # Windows
end # Post
156 lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_netapi32.rb
View
@@ -9,24 +9,102 @@ module Def
class Def_netapi32
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa371109.aspx
+ USER_INFO_1 = [
+ [:usri1_name, :LPWSTR],
+ [:usri1_password, :LPWSTR],
+ [:usri1_password_age, :DWORD],
+ [:usri1_priv, :DWORD],
+ [:usri1_home_dir, :LPWSTR],
+ [:usri1_comment, :LPWSTR],
+ [:usri1_flags, :DWORD],
+ [:usri1_script_path, :LPWSTR]
+ ]
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370897.aspx
+ SERVER_INFO_100 = [
+ [:sv100_platform_id, :DWORD],
+ [:sv100_name, :LPWSTR]
+ ]
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370903.aspx
+ SERVER_INFO_101 = [
+ [:sv101_platform_id, :DWORD],
+ [:sv101_name, :LPWSTR],
+ [:sv101_version_major, :DWORD],
+ [:sv101_version_minor, :DWORD],
+ [:sv101_type, :DWORD],
+ [:sv101_comment, :LPWSTR]
+ ]
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370247.aspx
+ AT_ENUM = [
+ [:JobId, :DWORD],
+ [:JobTime, :PDWORD_PTR],
+ [:DaysOfMonth, :DWORD],
+ [:DaysOfWeek, :UCHAR],
+ [:Flags, :UCHAR],
+ [:Command, :LPWSTR]
+ ]
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370248.aspx
+ AT_INFO = [
+ [:JobTime, :PDWORD_PTR],
+ [:DaysOfMonth, :DWORD],
+ [:DaysOfWeek, :UCHAR],
+ [:Flags, :UCHAR],
+ [:Command, :LPWSTR]
+ ]
+
def self.create_dll(dll_path = 'netapi32')
dll = DLL.new(dll_path, ApiConstants.manager)
- dll.add_function('NetUserDel', 'DWORD',[
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370649.aspx
+ dll.add_function('NetUserAdd', 'DWORD',[
["PWCHAR","servername","in"],
+ ["DWORD","level","in"], # 1, 2, 3, 4
+ ["PBLOB","buf","in"], # ptr to USER_INFO_x structure where x = +level+
+ ["PDWORD","parm_err","out"]
+ ])
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370650.aspx
+ dll.add_function('NetUserChangePassword', 'DWORD',[
+ ["PWCHAR","domainname","in"],
["PWCHAR","username","in"],
+ ["PWCHAR","oldpassword","in"],
+ ["PWCHAR","newpassword","in"]
+ ])
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370651.aspx
+ dll.add_function('NetUserDel', 'DWORD',[
+ ["PWCHAR","servername","in"],
+ ["PWCHAR","username","in"]
])
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370652.aspx
+ dll.add_function('NetUserEnum', 'DWORD',[
+ ["PWCHAR","servername","in"],
+ ["DWORD","level","in"], # 0, 1, 2, 3, 4, 10, 11, 20, 23
+ ["DWORD","filter","in"],
+ ["PDWORD","bufptr","out"], # ptr to array of USER_INFO_x structures where x = +level+
+ ["DWORD","prefmaxlen","in"],
+ ["PDWORD","entriesread","out"],
+ ["PDWORD","totalentries","out"],
+ ["DWORD","resume_handle","inout"]
+ ])
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370423.aspx
dll.add_function('NetGetJoinInformation', 'DWORD',[
["PWCHAR","lpServer","in"],
["PDWORD","lpNameBuffer","out"],
["PDWORD","BufferType","out"]
])
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370623.aspx
dll.add_function('NetServerEnum', 'DWORD',[
["PWCHAR","servername","in"],
- ["DWORD","level","in"],
- ["PDWORD","bufptr","out"],
+ ["DWORD","level","in"], # 100, 101
+ ["PDWORD","bufptr","out"], # ptr to array of SERVER_INFO_x structures where x = +level+
["DWORD","prefmaxlen","in"],
["PDWORD","entriesread","out"],
["PDWORD","totalentries","out"],
@@ -35,11 +113,79 @@ def self.create_dll(dll_path = 'netapi32')
["DWORD","resume_handle","inout"]
])
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370304.aspx
+ dll.add_function('NetApiBufferFree', 'DWORD',[
+ ["LPVOID","Buffer","in"]
+ ])
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370426.aspx
+ dll.add_function('NetGroupDel', 'DWORD',[
+ ["PWCHAR","servername","in"],
+ ["PWCHAR","groupname","in"]
+ ])
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370428.aspx
+ dll.add_function('NetGroupEnum', 'DWORD',[
+ ["PWCHAR","servername","in"],
+ ["DWORD","level","in"], # 0, 1, 2, 3
+ ["PDWORD","bufptr","out"], # ptr to array of GROUP_INFO_x structures where x = +level+
+ ["DWORD","prefmaxlen","in"],
+ ["PDWORD","entriesread","out"],
+ ["PDWORD","totalentries","out"],
+ ["DWORD","resume_handle","inout"]
+ ])
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370437.aspx
+ dll.add_function('NetLocalGroupDel', 'DWORD',[
+ ["PWCHAR","servername","in"],
+ ["PWCHAR","groupname","in"]
+ ])
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370440.aspx
+ dll.add_function('NetLocalGroupEnum', 'DWORD',[
+ ["PWCHAR","servername","in"],
+ ["DWORD","level","in"], # 0, 1
+ ["PDWORD","bufptr","out"], # ptr to array of LOCALGROUP_INFO_x structures where x = +level+
+ ["DWORD","prefmaxlen","in"],
+ ["PDWORD","entriesread","out"],
+ ["PDWORD","totalentries","out"],
+ ["DWORD","resumehandle","inout"]
+ ])
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370614.aspx
+ dll.add_function('NetScheduleJobAdd', 'DWORD',[
+ ["PWCHAR","Servername","in"],
+ ["PBLOB","Buffer","in"], # ptr to AT_INFO structure
+ ["PDWORD","JobId","out"]
+ ])
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370615.aspx
+ dll.add_function('NetScheduleJobDel', 'DWORD',[
+ ["PWCHAR","Servername","in"],
+ ["DWORD","MinJobId","in"],
+ ["DWORD","MaxJobId","in"]
+ ])
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370616.aspx
+ dll.add_function('NetScheduleJobEnum', 'DWORD',[
+ ["PWCHAR","Servername","in"],
+ ["PDWORD","PointerToBuffer","out"], # ptr to array of AT_ENUM structures
+ ["DWORD","PreferredMaximumLength","in"],
+ ["PDWORD","EntriesRead","out"],
+ ["PDWORD","TotalEntries","out"],
+ ["DWORD","ResumeHandle","inout"]
+ ])
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370617.aspx
+ dll.add_function('NetScheduleJobGetInfo', 'DWORD',[
+ ["PWCHAR","Servername","in"],
+ ["DWORD","JobId","in"],
+ ["PBLOB","PointerToBuffer","out"] # ptr to AT_INFO structure
+ ])
+
return dll
end
end
end; end; end; end; end; end; end
-
-
68 modules/post/windows/manage/add_user.rb
View
@@ -0,0 +1,68 @@
+##
+# This file is part of the Metasploit Framework and may be subject to
+# redistribution and commercial restrictions. Please see the Metasploit
+# web site for more information on licensing and terms of use.
+# http://metasploit.com/
+##
+
+require 'msf/core'
+require 'msf/core/post/windows/accounts'
+
+class Metasploit3 < Msf::Post
+
+ include Msf::Post::Windows::Accounts
+
+ def initialize(info={})
+ super( update_info( info,
+ 'Name' => 'Windows Manage Local User Account Addition',
+ 'Description' => %q{
+ This module adds a local user account to the specified server,
+ or the local machine if no server is given.
+ },
+ 'License' => MSF_LICENSE,
+ 'Author' => 'Chris Lennert',
+ 'Platform' => [ 'windows' ],
+ 'SessionTypes' => [ 'meterpreter' ]
+ ))
+
+ register_options(
+ [
+ OptString.new('USERNAME', [ true, 'The username of the user to add (not-qualified, e.g. BOB)' ]),
+ OptString.new('SERVER_NAME', [ false, 'DNS or NetBIOS name of remote server on which to add user' ]),
+ OptString.new('PASSWORD', [ false, 'The password of the user account to be created' ]),
+ OptBool.new( 'DONT_EXPIRE_PWD', [ false, 'Set to true to toggle the "Password never expires" flag on account', false ]),
+ OptString.new('COMMENT', [ false, 'The comment/description to apply to the new account' ]),
+ ], self.class)
+ end
+
+ def run
+ result = add_user(
+ datastore['USERNAME'],
+ datastore['PASSWORD'],
+ datastore['COMMENT'],
+ datastore['DONT_EXPIRE_PWD'],
+ datastore['SERVER_NAME']
+ )
+
+ case result
+ when :success
+ print_status 'User was added!'
+ when :user_exists
+ print_error 'User already exists.'
+ when :group_exists
+ print_error 'Group already exists.'
+ when :access_denied
+ print_error 'Sorry, you do not have permission to add that user.'
+ when :invalid_server
+ print_error 'The server you specified was invalid.'
+ when :not_on_primary
+ print_error 'You must be on the primary domain controller to do that.'
+ when :invalid_password
+ print_error 'The password does not appear to be valid (too short, too long, too recent, etc.).'
+ when nil
+ print_error 'Something horrible just happened. Sorry.'
+ else
+ print_error 'This module is out of date.'
+ end
+ end
+end
Something went wrong with that request. Please try again.