rapid7 / ruby_smb

A native Ruby implementation of the SMB Protocol Family
Other
79 stars 82 forks source link

New DCERPC Structures and NDR Protocol Implementation Redesign #179

Closed cdelafuente-r7 closed 2 years ago

cdelafuente-r7 commented 2 years ago

This PR adds new structures needed for the Windows Secrets Dump module future improvements. This adds support to new DCERPC structures and implements a standalone Client. This also includes a redesign of the NDR structures.

Note that only partial specs were added to this PR. The missing specs will be added in a separate PR.

New NDR design

The file lib/ruby_smb/dcerpc/ndr.rb has been heavily updated and the NDR protocol implementation has been completely redesigned to match the complexity of the new DRSR structures. This is an attempt to match the NDR transfer syntax definition as close as possible, taking into account Microsoft additions as specified in [MS-RPCE]: Remote Procedure Call Protocol Extensions. This new implementation improves the handling of pointers and constructed types like arrays, strings and embedded structures. It also automatically handles alignment (only 32-bit NDR is supported at the moment).

New DCERPC client

The new DCERPC client (lib/ruby_smb/dcerpc/client.rb) follows the same design as the SMB Client, except it communicates with the remote server directly over TCP (NCACN_IP_TCP). It takes care of connecting to the correct endpoint based on the endpoint argument, which is one of the module classes defined in lib/ruby_smb/dcerpc/.

client = RubySMB::Dcerpc::Client.new('192.168.1.1', RubySMB::Dcerpc::Samr)
client.bind

If an already connected socket is not provided (through the tcp_socket argument), connect will ask the remote Endpoint Mapper Interface which port the service is listening from. It will then connect a new TCPSocket to this port.

The client also supports authentication (NTLM only at the moment), signing and encryption. Calling bind will take care of binding to the remote server interface endpoint and performing authentication. With NTLM, once the three-legs authentication handshake is done, the session key is stored. This key will be used to sign the subsequent messages sent with dcerpc_request. The authentication, signing and encryption processes are transparent and can be enabled by passing a username and password to the client and the appropriate auth_level and auth_type to bind (see 2.2.1.1.8 Authentication Levels and 2.2.1.1.7 Security Providers). For now, only NTLM security provider is supported with integrity and privacy authentication levels:

client = RubySMB::Dcerpc::Client.new(
  '192.168.1.1',
  RubySMB::Dcerpc::Drsr,
  username: username,
  password: password,
)
client.bind(
  auth_level: RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
  auth_type: RubySMB::Dcerpc::RPC_C_AUTHN_WINNT
)

New structures

The partial implementations of the following DCERPC structures were added:

Endpoint Mapper Interface

File: lib/ruby_smb/dcerpc/epm.rb Documentation: 2.2.1.2 Endpoint Mapper Interface Extensions Related structures: EpmEptMap

This endpoint is used by the client to retrieve the port number a service is listening from.

The Directory Replication Service (DRS) Remote Protocol

File: lib/ruby_smb/dcerpc/drsr.rb Documentation: [MS-DRSR]: Directory Replication Service (DRS) Remote Protocol Related structures: IDL_DRSBind, IDL_DRSUnbind, IDL_DRSCrackNames, IDL_DRSDomainControllerInfo, IDL_DRSGetNCChanges

This endpoint is used to retrieve information from the Domain Controller using the Active Directory replication mechanism.

Security Account Manager (SAM) Remote Protocol

File: lib/ruby_smb/dcerpc/samr.rb Documentation: [MS-SAMR]: Security Account Manager (SAM) Remote Protocol (Client-to-Server) Related structures: SamrConnect, SamrCloseHandle, SamrEnumerateUsersInDomain, SamrGetAliasMembership, SamrGetGroupsForUser, SamrLookupDomainInSamServer, SamrOpenDomain, SamrOpenUser, SamrRidToSid,

This endpoint is used to retrieve account and domain information on the remote host (e.g. enumerate users in a domain, get group membership for a specific user, etc.).

Workstation Service Remote Protocol

File: lib/ruby_smb/dcerpc/wkssvc.rb Documentation: [MS-WKST]: Workstation Service Remote Protocol Related structures: NetrWkstaGetInfo

This endpoint is used to get details about a remote computer environment, including platform-specific information, the names of the domain and local computer, and the operating system version.

Updated structures

Many existing structures were also updated to use the new NDR protocol implementation. This will potentially break existing code using the old version. In my knowledge, only the Metasploit Windows Secrets Dump and the Netlogon Weak Cryptographic Authentication modules are affected and will be fixed in a separate PR. Other external projects using RubySMB DCERPC implementation will need to be reviewed and updated.

Testing

The scripts in examples/ folder can be used to test. Specifically, the following scripts were added to make sure the new structures behave as expected.

SAMR module

ruby examples/enum_domain_users.rb 192.168.1.1 testuser mypassword MYLAB

DRSR module (this script uses the new DCERPC client)

You can get a valid SID using the examples/enum_domain_users.rb script.

WKST module

ruby examples/get_computer_info.rb 192.168.1.1 testuser mypassword

SVCCTL module

ruby examples/query_service_status.rb 192.168.1.1 testuser mypassword RemoteRegistry

Note that, when a script uses SMB as a transport layer, SMBv3 will be used by default and, therefore, the communication will be encrypted. This can be an issue if you want to capture the network packets (with Wireshark for example) for inspection. An easy way to avoid encryption is to force SMBv2. This can be done by adding 2 as the last argument of the script:

ruby examples/get_computer_info.rb 192.168.1.1 test_user mypassword 2

An alternative would be to modify each script and set the option always_encrypt to false:

RubySMB::Client.new(dispatcher, smb1: smb_versions.include?('1'), smb2: smb_versions.include?('2'), smb3: smb_versions.include?('3'), username: username, password: password, always_encrypt: false)

TODO

smcintyre-r7 commented 2 years ago

Also is this ready to be landed after it's been reviewed and tested, or is there another PR that needs to made in parallel to update MSF modules or something else along those lines?

cdelafuente-r7 commented 2 years ago

Thanks for reviewing this PR and for your feedback @smcintyre-r7. I'll make the suggested changes as soon as possible.

Absolutely, at least one MSF module (I need to look into it closely) will need to be updated with these changes. So, this PR can be landed but a new gem should not be released until MSF is updated.

cdelafuente-r7 commented 2 years ago

I believe I've fixed all the comments from the code review. I just left the comment about ArgumentError open with additional information.

cdelafuente-r7 commented 2 years ago

Thanks @smcintyre-r7 and @jmartin-r7 for the review and testing. I've pushed some fixes and improvements as suggested.

cdelafuente-r7 commented 2 years ago

I push some fixes related to issues found in the Windows Secrets Dump module:

I also removed openssl dependency in ruby_smb.gemspec and rebased to be up-to-date with master.