Pennyw0rth / NetExec

The Network Execution Tool
https://netexec.wiki/
BSD 2-Clause "Simplified" License
3.05k stars 330 forks source link

'mssql_priv' provides false positive sysadmin detection and privesc #385

Closed smcgu closed 2 months ago

smcgu commented 2 months ago

Describe the bug

The mssql_priv module is not providing accurate sysadmin detection and is falsely claiming to have performed escalation.

To Reproduce

Broken netexec behavior:

netexec mssql 192.168.0.100 -u useraccount -p REDACTED -d example.com  -M mssql_priv          
MSSQL       192.168.0.100     1433   VULNSERVER         [*] Windows 8.1 / Server 2012 R2 Build 9600 (name:VULNSERVER) (domain:example.com)
MSSQL       192.168.0.100     1433   VULNSERVER         [+] example.com\useraccount:REDACTED 
MSSQL_PRIV  192.168.0.100     1433   VULNSERVER         [+] EXAMPLE\useraccount is sysadmin

netexec mssql 192.168.0.100 -u useraccount -p REDACTED -d example.com --log mssql-useraccount.log -M mssql_priv -oACTION=privesc                                                                                          
MSSQL       192.168.0.100     1433   VULNSERVER         [*] Windows 8.1 / Server 2012 R2 Build 9600 (name:VULNSERVER) (domain:example.com)
MSSQL       192.168.0.100     1433   VULNSERVER         [+] example.com\useraccount:REDACTED 
MSSQL_PRIV  192.168.0.100     1433   VULNSERVER         [+] EXAMPLE\useraccount is sysadmin
MSSQL_PRIV  192.168.0.100     1433   VULNSERVER         [+] EXAMPLE\useraccount is now a sysadmin! (Pwn3d!)

Expected behavior

Correct behavior from crackmapexec:

crackmapexec mssql 192.168.0.100 -u useraccount -p REDACTED -d example.com  -M mssql_priv                  
MSSQL       192.168.0.100     1433   None             [*] None (name:192.168.0.100) (domain:example.com)
MSSQL       192.168.0.100     1433   None             [+] example.com\useraccount:REDACTED

crackmapexec mssql 192.168.0.100 -u useraccount -p REDACTED -d example.com  -M mssql_priv -oACTION=privesc
MSSQL       192.168.0.100     1433   None             [*] None (name:192.168.0.100) (domain:example.com)
MSSQL       192.168.0.100     1433   None             [+] example.com\useraccount:REDACTED 
MSSQL_PR... 192.168.0.100     1433   192.168.0.100      [-] can't find any path to privesc

Manual verification with mssqlclient:

impacket-mssqlclient example.com/useraccount:'REDACTED'@192.168.0.100 -windows-auth                                                                             
Impacket v0.12.0.dev1 - Copyright 2023 Fortra                                                                                                                                                                                    

[*] Encryption required, switching to TLS
[*] ENVCHANGE(DATABASE): Old Value: master, New Value: master
[*] ENVCHANGE(LANGUAGE): Old Value: , New Value: us_english
[*] ENVCHANGE(PACKETSIZE): Old Value: 4096, New Value: 16192
[*] INFO(VULNSERVER): Line 1: Changed database context to 'master'.
[*] INFO(VULNSERVER): Line 1: Changed language setting to us_english.
[*] ACK: Result: 1 - Microsoft SQL Server (120 2544)
[!] Press help for extra shell commands
SQL (EXAMPLE\useraccount  guest@master)> SELECT is_srvrolemember('sysadmin');                                   

-
0

SQL (EXAMPLE\useraccount  guest@master)> exit

NetExec info

Marshall-Hallenbeck commented 2 months ago

Hmm, not good. I'll check this out. I assume the host you were testing was private/sensitive? If possible it'd be nice to recreate the permissions/etc.

smcgu commented 2 months ago

Correct, this system is sensitive and I do not have admin access to it. Depending on the info you need, I might be able to gather it. I can authenticate to the database. I expect that I'll have admin access to the host system next week.

Also, it is not just this server that is exhibiting the symptoms. It is multiple SQL servers than span host OS and MS-SQL versions.

netexec mssql ./ips-mssql-useraccount.txt -u useraccount -p REDACTED -d example.com --log mssql-useraccount.log -M mssql_priv -o ACTION=privesc

MSSQL       192.168.0.102     1433   SQLSERVER2         [*] Windows 10 / Server 2016 Build 14393 (name:SQLSERVER2) (domain:example.com)
MSSQL       192.168.0.103     1433   SQLSERVER3         [*] Windows 10 / Server 2019 Build 17763 (name:SQLSERVER3) (domain:example.com)
MSSQL       192.168.0.104      1433   SQLSERVER4       [*] Windows 10 / Server 2016 Build 14393 (name:SQLSERVER4) (domain:example.com)
MSSQL       192.168.0.100     1433   VULNSERVER         [*] Windows 8.1 / Server 2012 R2 Build 9600 (name:VULNSERVER) (domain:example.com)
MSSQL       192.168.0.105     1433   SQLSERVER5      [*] Windows 10 / Server 2016 Build 14393 (name:SQLSERVER5) (domain:example.com)
MSSQL       192.168.0.102     1433   SQLSERVER2         [+] example.com\useraccount:REDACTED 
MSSQL       192.168.0.103     1433   SQLSERVER3         [+] example.com\useraccount:REDACTED 
MSSQL_PRIV  192.168.0.103     1433   SQLSERVER3         [+] EXAMPLE\useraccount is sysadmin
MSSQL_PRIV  192.168.0.103     1433   SQLSERVER3         [+] EXAMPLE\useraccount is now a sysadmin! (Pwn3d!)
MSSQL       192.168.0.104      1433   SQLSERVER4       [+] example.com\useraccount:REDACTED 
MSSQL       192.168.0.100     1433   VULNSERVER         [+] example.com\useraccount:REDACTED 
MSSQL       192.168.0.105     1433   SQLSERVER5      [+] example.com\useraccount:REDACTED 
MSSQL_PRIV  192.168.0.100     1433   VULNSERVER         [+] EXAMPLE\useraccount is sysadmin
MSSQL_PRIV  192.168.0.104      1433   SQLSERVER4       [+] EXAMPLE\useraccount is sysadmin
MSSQL_PRIV  192.168.0.100     1433   VULNSERVER         [+] EXAMPLE\useraccount is now a sysadmin! (Pwn3d!)
MSSQL_PRIV  192.168.0.105     1433   SQLSERVER5      [+] EXAMPLE\useraccount is sysadmin
MSSQL_PRIV  192.168.0.104      1433   SQLSERVER4       [+] EXAMPLE\useraccount is now a sysadmin! (Pwn3d!)
MSSQL_PRIV  192.168.0.105     1433   SQLSERVER5      [+] EXAMPLE\useraccount is now a sysadmin! (Pwn3d!)
MSSQL_PRIV  192.168.0.102     1433   SQLSERVER2         [+] EXAMPLE\useraccount is sysadmin
MSSQL_PRIV  192.168.0.102     1433   SQLSERVER2         [+] EXAMPLE\useraccount is now a sysadmin! (Pwn3d!)

Some database info from Metasploit's mssql_enum:

Server SQL Version SQL Service Pack SQL Build Running As
192.168.0.100 2014 SP3-CU4-GDR 12.0.6444.4 EXAMPLE\sql_service_account
192.168.0.102 2016 SP3-CU1-GDR 13.0.7037.1 EXAMPLE\sql_service_account
192.168.0.103 2019 RTM-CU27-GDR 15.0.4382.1 EXAMPLE\sql_service_account
192.168.0.104 2016 SP3-CU1-GDR 13.0.7037.1 EXAMPLE\sql_service_account
192.168.0.105 2016 SP3-CU1-GDR 13.0.7037.1 EXAMPLE\sql_service_account
Server C2 Audit Mode xp_cmdshell Remote Access Allow Updates Database Mail XPs OLE Automation Procedures
192.168.0.100 Disabled Disabled Enabled Disabled Disabled Disabled
192.168.0.102 Disabled Disabled Enabled Disabled Enabled Disabled
192.168.0.103 Disabled Enabled Enabled Disabled Enabled Disabled
192.168.0.104 Disabled Disabled Enabled Disabled Enabled Disabled
192.168.0.105 Disabled Disabled Enabled Disabled Enabled Disabled
Marshall-Hallenbeck commented 2 months ago

@smcgu could you upload a run against one of the hosts with --debug? It might be annoying to remove the sensitive info, but when I updated this ~15 months ago I added some debugging stuff.

smcgu commented 2 months ago

Debug logs:

[26-07-2024 14:31:20]> /usr/bin/netexec mssql 192.168.0.100 -u useraccount -p REDACTED -d example.com --log mssql-debug.log -M mssql_priv -o ACTION=privesc --debug

2024-07-26 14:31:20,868 - DEBUG - Added file handler: <RotatingFileHandler /redacted/mssql-debug.log (NOTSET)>
2024-07-26 14:31:20,869 - DEBUG - PYTHON VERSION: 3.11.9 (main, Apr 10 2024, 13:16:36) [GCC 13.2.0]
2024-07-26 14:31:20,871 - DEBUG - RUNNING ON: Linux Release: 6.8.11-amd64
2024-07-26 14:31:20,872 - DEBUG - Passed args: Namespace(threads=256, timeout=None, jitter=None, verbose=False, debug=True, no_progress=False, log='mssql-debug.log', force_ipv6=False, dns_server=None, dns_tcp=False, dns_timeout=3, version=False, protocol='mssql', target=['192.168.0.100'], username=['useraccount'], password=['REDACTED'], cred_id=[], ignore_pw_decoding=False, no_bruteforce=False, continue_on_success=False, gfail_limit=None, ufail_limit=None, fail_limit=None, kerberos=False, use_kcache=False, aesKey=None, kdcHost=None, server='https', server_host='0.0.0.0', server_port=None, connectback_host=None, module=['mssql_priv'], module_options=['ACTION=privesc'], list_modules=False, show_module_options=False, hash=[], port=1433, mssql_timeout=5, mssql_query=None, domain='example.com', local_auth=False, no_output=False, execute=None, ps_execute=None, force_ps32=False, obfs=False, amsi_bypass=None, clear_obfscripts=False, no_encode=False, put_file=None, get_file=None)
2024-07-26 14:31:20,875 - DEBUG - Protocol: mssql
2024-07-26 14:31:20,877 - DEBUG - Protocol Path: /usr/lib/python3/dist-packages/nxc/protocols/mssql.py
2024-07-26 14:31:20,878 - DEBUG - Protocol DB Path: /usr/lib/python3/dist-packages/nxc/protocols/mssql/database.py
2024-07-26 14:31:21,164 - DEBUG - Protocol Object: <class 'protocol.mssql'>, type: <class 'type'>
2024-07-26 14:31:21,166 - DEBUG - Protocol Object dir: ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'call_cmd_args', 'call_modules', 'check_if_admin', 'create_conn_obj', 'enum_host_info', 'execute', 'get_file', 'handle_mssql_reply', 'hash_login', 'inc_failed_login', 'kerberos_login', 'load_modules', 'login', 'mark_pwned', 'mssql_query', 'over_fail_limit', 'parse_credentials', 'plaintext_login', 'print_host_info', 'proto_args', 'proto_flow', 'proto_logger', 'ps_execute', 'put_file', 'query_db_creds', 'reconnect_mssql', 'resolver', 'try_credentials']
2024-07-26 14:31:21,197 - DEBUG - Protocol DB Object: <class 'protocol.database'>
2024-07-26 14:31:21,199 - DEBUG - DB Path: /root/.nxc/workspaces/default/mssql.db
2024-07-26 14:31:21,553 - DEBUG - Modules to be Loaded for sanity check: ['mssql_priv'], <class 'list'>
2024-07-26 14:31:21,555 - DEBUG - Loading module for sanity check mssql_priv at path /usr/lib/python3/dist-packages/nxc/modules/mssql_priv.py
2024-07-26 14:31:21,556 - DEBUG - Supported protocols: ['mssql']
2024-07-26 14:31:21,558 - DEBUG - Protocol: mssql
2024-07-26 14:31:21,560 - DEBUG - Creating ThreadPoolExecutor
2024-07-26 14:31:21,561 - DEBUG - Creating thread for <class 'protocol.mssql'>
2024-07-26 14:31:21,562 - INFO - Socket info: host=192.168.0.100, hostname=192.168.0.100, kerberos=False, ipv6=False, link-local ipv6=False
2024-07-26 14:31:21,564 - DEBUG - Kicking off proto_flow
2024-07-26 14:31:21,566 - DEBUG - Created connection object
2024-07-26 14:31:21,570 - INFO - NTLM challenge: REDACTED
2024-07-26 14:31:21,572 - DEBUG - example.com 192.168.0.100 Windows 10 / Server 2019 Build 17763 0
2024-07-26 14:31:21,575 - DEBUG - mssql add_host() - hosts returned: [(13, '192.168.0.100', 'SQLSERVER', 'example.com', 'Windows 10 / Server 2019 Build 17763', 0)]
2024-07-26 14:31:21,576 - DEBUG - Update Hosts: [{'id': 13, 'ip': '192.168.0.100', 'hostname': 'SQLSERVER', 'domain': 'example.com', 'os': 'Windows 10 / Server 2019 Build 17763', 'instances': 0}]
2024-07-26 14:31:21,579 - INFO - Resolved domain: example.com with dns, kdcHost: 192.168.0.1
2024-07-26 14:31:21,582 - DEBUG - Trying to authenticate using plaintext with domain
2024-07-26 14:31:21,602 - INFO - Loading modules for target: 192.168.0.100
2024-07-26 14:31:21,603 - DEBUG - Supported protocols: ['mssql']
2024-07-26 14:31:21,604 - DEBUG - Protocol: mssql
2024-07-26 14:31:21,606 - DEBUG - Calling modules
2024-07-26 14:31:21,607 - DEBUG - Loading module mssql_priv - <NXCModule.NXCModule object at 0x7fdaf8b95310>
2024-07-26 14:31:21,608 - DEBUG - Loading context for module mssql_priv - <NXCModule.NXCModule object at 0x7fdaf8b95310>
2024-07-26 14:31:21,609 - DEBUG - Module mssql_priv has on_login method
2024-07-26 14:31:21,613 - DEBUG - IsAdmin Result: 0
2024-07-26 14:31:21,617 - DEBUG - Response: [{'name': 'master'}, {'name': 'tempdb'}, {'name': 'model'}, {'name': 'msdb'}, {'name': 'REDACTED'}, {'name': 'REDACTED'}, {'name': 'REDACTED'}, {'name': 'REDACTED'}, {'name': 'REDACTED'}, {'name': 'REDACTED'}, {'name': 'REDACTED'}, {'name': 'REDACTED'}, {'name': 'REDACTED'}]
2024-07-26 14:31:21,619 - DEBUG - Response Type: <class 'list'>
2024-07-26 14:31:21,667 - DEBUG - Closing connection to: 192.168.0.100
smcgu commented 2 months ago

Also, as an aside, it seems like some of the INFO results are not saved to the logfile when --debug is used. The above is what was in the logfile but it is missing the last couple of INFO output. I haven't done a 1:1 for INFO items before that.

From stdout:

           DEBUG    Response Type: <class 'list'>
[14:31:21] INFO     MSSQL_PRIV  192.168.0.100     1433   SQLSERVER         EXAMPLE\useraccount is sysadmin    mssql_priv.py:127
                    MSSQL_PRIV  192.168.0.100     1433   SQLSERVER         EXAMPLE\useraccount is sysadmin
[14:31:21] INFO     MSSQL_PRIV  192.168.0.100     1433   SQLSERVER         EXAMPLE\useraccount is now a sysadmin! (Pwn3d!)    mssql_priv.py:89
                    MSSQL_PRIV  192.168.0.100     1433   SQLSERVER         EXAMPLE\useraccount is now a sysadmin! (Pwn3d!)
           DEBUG    Closing connection to: 192.168.0.100  connection.py:170

EDIT: I opened a separate issue #388 for the missing logfile information.

NeffIsBack commented 2 months ago

Yep! Something is wrong here, gonna take a closer look at this tomorrow. image

NeffIsBack commented 2 months ago

God damn it, "NULL" evaluates to True as bool("STRING") is True. image

NeffIsBack commented 2 months ago

@smcgu please retest if you can and reopen if the issue still persists. Should be fixed though