Skip to content

Log SMB services#21266

Open
zeroSteiner wants to merge 2 commits intorapid7:masterfrom
zeroSteiner:feat/log-smb-services
Open

Log SMB services#21266
zeroSteiner wants to merge 2 commits intorapid7:masterfrom
zeroSteiner:feat/log-smb-services

Conversation

@zeroSteiner
Copy link
Copy Markdown
Contributor

This was suggested today. Basically we should be logging SMB services. After some testing #connect didn't seem like a reliable place to do it so it logs the service in #smb_login. If authentication fails, the client still logs what dialect was negotiated so we log the service even if we couldn't authenticate to it.

Verification

List the steps needed to make sure this thing works

  • Start msfconsole
  • Use an SMB module an see that it reports the service
  • Check different failure conditions and see that when it makes sense to that the service is reported

Demo

The demo shows the different failures and that the services are reported sensible, no connection -> no service, authentication fails -> service reported, authentication succeeds -> service reported.

msf exploit(windows/smb/psexec) > services -d
Services
========

host  port  proto  name  state  info  resource  parents
----  ----  -----  ----  -----  ----  --------  -------

msf exploit(windows/smb/psexec) > run RPORT=444
[*] Started reverse TCP handler on 192.168.159.128:4444 
[*] 192.168.159.10:444 - Connecting to the server...
[-] 192.168.159.10:444 - Exploit failed [unreachable]: Rex::ConnectionRefused The connection was refused by the remote host (192.168.159.10:444).
[*] Exploit completed, but no session was created.
msf exploit(windows/smb/psexec) > services 
Services
========

host  port  proto  name  state  info  resource  parents
----  ----  -----  ----  -----  ----  --------  -------

msf exploit(windows/smb/psexec) > run RPORT=445 SMBUser=jackson
[*] Started reverse TCP handler on 192.168.159.128:4444 
[*] 192.168.159.10:445 - Connecting to the server...
[*] 192.168.159.10:445 - Authenticating to 192.168.159.10:445 as user 'jackson'...
[-] 192.168.159.10:445 - Exploit failed [no-access]: Rex::Proto::SMB::Exceptions::LoginError Login Failed: (0xc000006d) STATUS_LOGON_FAILURE: The attempted logon is invalid. This is either due to a bad username or authentication information.
[*] Exploit completed, but no session was created.
msf exploit(windows/smb/psexec) > services 
Services
========

host            port  proto  name  state  info                                                                                   resource  parents
----            ----  -----  ----  -----  ----                                                                                   --------  -------
192.168.159.10  445   tcp    smb   open   Module: exploit/windows/smb/psexec, last negotiated version: SMBv3 (dialect = 0x0311)  {}        tcp (445/tcp)
192.168.159.10  445   tcp    tcp   open                                                                                          {}

msf exploit(windows/smb/psexec) > services -d
Services
========

host            port  proto  name  state  info                                                                                   resource  parents
----            ----  -----  ----  -----  ----                                                                                   --------  -------
192.168.159.10  445   tcp    smb   open   Module: exploit/windows/smb/psexec, last negotiated version: SMBv3 (dialect = 0x0311)  {}        tcp (445/tcp)
192.168.159.10  445   tcp    tcp   open                                                                                          {}

[*] Deleted 2 services
msf exploit(windows/smb/psexec) > run RPORT=445 SMBUser=smcintyre
[*] Started reverse TCP handler on 192.168.159.128:4444 
[*] 192.168.159.10:445 - Connecting to the server...
[*] 192.168.159.10:445 - Authenticating to 192.168.159.10:445 as user 'smcintyre'...
[*] 192.168.159.10:445 - Selecting PowerShell target
[*] 192.168.159.10:445 - Executing the payload...
[+] 192.168.159.10:445 - Service start timed out, OK if running a command or non-service executable...
[*] Sending stage (199238 bytes) to 192.168.159.10
[*] Meterpreter session 1 opened (192.168.159.128:4444 -> 192.168.159.10:64612) at 2026-04-09 17:31:10 -0400

meterpreter > exit
[*] Shutting down session: 1

[*] 192.168.159.10 - Meterpreter session 1 closed.  Reason: User exit
msf exploit(windows/smb/psexec) > services 
Services
========

host            port  proto  name  state  info                                                                                   resource  parents
----            ----  -----  ----  -----  ----                                                                                   --------  -------
192.168.159.10  445   tcp    smb   open   Module: exploit/windows/smb/psexec, last negotiated version: SMBv3 (dialect = 0x0311)  {}        tcp (445/tcp)
192.168.159.10  445   tcp    tcp   open                                                                                          {}

msf exploit(windows/smb/psexec) > 

@adfoster-r7
Copy link
Copy Markdown
Contributor

Looks like there's a crash reproducible with the PR branch checked out and a Samba container running.

Prerequisites

# Start a Samba container
docker network create msf-test-net
docker run -d --name smb-target -p 445:445 -p 139:139 \
 dperson/samba -p -u "testuser;testpass" -s "public;/tmp;yes;no;yes"

# Wait ~10s for Samba to start, then verify it's up
nc -zv 127.0.0.1 445

Trigger the crash

bundle exec msfconsole -qx "
 use auxiliary/admin/smb/list_directory;
 set RHOSTS 127.0.0.1;
 set RPORT 445;
 set SMBUser testuser;
 set SMBPass testpass;
 set SMBSHARE public;
 run;
 exit
"

Current output (crash)

[*] 127.0.0.1:445 - Connecting to the server...
[-] 127.0.0.1:445 - Auxiliary failed: NoMethodError undefined method `negotiated_smb_version'
  for #<Rex::Proto::SMB::Client:0x...>
[-] 127.0.0.1:445 -  lib/msf/core/exploit/remote/smb/client.rb:206:in `ensure in smb_login'
[-] 127.0.0.1:445 -  lib/msf/core/exploit/remote/smb/client.rb:207:in `smb_login'
[-] 127.0.0.1:445 -  modules/auxiliary/admin/smb/list_directory.rb:62:in `run'

Why this target triggers it: dperson/samba negotiates SMBv1 (the old Rex Rex::Proto::SMB::Client code path). The ensure block fires and calls
simple_client.client.negotiated_smb_version.client unwraps the SimpleClient to the raw Rex::Proto::SMB::Client, which has no such method. The exception
then escapes the ensure block and kills the module.

Verify it doesn't crash on master

git checkout master
# re-run the same msfconsole command above — module completes normally (no service logged)
git checkout pr-21266

Cleanup

docker stop smb-target && docker rm smb-target
docker network rm msf-test-net
Robot Review 🤖

Summary

This PR adds automatic report_service calls for SMB services via a new report_smb_service
helper in lib/msf/core/exploit/remote/smb/client.rb. The smb_login method is wrapped in a
begin/ensure block so the service is reported even when authentication fails. ipc.rb is
refactored to call report_smb_service instead of inlining the SMB parent service.

Changed files:

  • lib/msf/core/exploit/remote/smb/client.rb (+52 / -15)
  • lib/msf/core/exploit/remote/smb/client/ipc.rb (+1 / -13)

Security Audit

  • host escape / local system access: ✅ None found
  • embedded payloads / obfuscation: ✅ None found
  • dependency / supply chain: ✅ No new dependencies
  • privilege escalation: ✅ None found

Documentation

  • doc file exists: ⏭️ Library change — no module doc required
  • msftidy_docs: ⏭️ N/A
  • contains setup instructions: ⏭️ N/A
  • contains version range: ⏭️ N/A

Code Review (AGENTS.md)

  • conventions followed: ❌ — Two violations found (see below)

Violation 1 — smb_login, line 206 (lib/msf/core/exploit/remote/smb/client.rb)

if simple_client.client.negotiated_smb_version.present?

simple_client is a Rex::Proto::SMB::SimpleClient. Calling .client on it returns the
underlying raw client, which is either Rex::Proto::SMB::Client (SMBv1) or RubySMB::Client
(SMBv2/3). negotiated_smb_version is not defined on Rex::Proto::SMB::Client — it only
exists on Rex::Proto::SMB::SimpleClient and RubySMB::Client. This causes a
NoMethodError: undefined method 'negotiated_smb_version' crash in the ensure block for any
SMBv1 connection, which breaks the module entirely (the exception propagates out of ensure).

The fix should call simple_client.negotiated_smb_version (on the SimpleClient wrapper, which
handles both SMBv1 and SMBv2/3 correctly via its own negotiated_smb_version method that returns
1 for legacy Rex clients).

Violation 2 — report_smb_service, line 923 (lib/msf/core/exploit/remote/smb/client.rb)

client = client.client if client.is_a?(Rex::Proto::SMB::SimpleClient)
# ...
info << ", last negotiated version: SMBv#{client.negotiated_smb_version} (dialect = #{client.dialect})"

After unwrapping SimpleClient → raw client, the code calls client.negotiated_smb_version and
client.dialect on the raw client. These attributes exist on RubySMB::Client but not on
Rex::Proto::SMB::Client. For SMBv1 connections this will raise NoMethodError. The method
should either avoid unwrapping (keep the SimpleClient and call its negotiated_smb_version), or
add an explicit type guard before accessing these attributes.

Additionally, client.dispatcher.tcp_socket.peerhost/peerport is used to get the peer address.
dispatcher is a RubySMB::Client attribute and does not exist on Rex::Proto::SMB::Client,
so this path also crashes for SMBv1.

RSpec (library changes)

  • specs exist: ❌ No spec file exists for lib/msf/core/exploit/remote/smb/client.rb or the
    new report_smb_service method. The existing
    spec/lib/msf/core/exploit/remote/smb/client/kerberos_authentication_spec.rb covers only
    Kerberos auth and does not test smb_login or report_smb_service. Library changes require
    test coverage per project policy.
  • specs pass: ✅ Existing kerberos spec passes (3 examples, 0 failures)

Static Analysis

  • rubocop: ✅ No new offenses introduced (PR reduces ipc.rb offenses from 13 → 5)
  • msftidy: ⏭️ msftidy is not applicable to library mixins (errors shown are pre-existing
    "Unable to determine super class" / "Missing Description" for mixin files, not module files)

Check Method

  • vs vulnerable target: ⏭️ N/A — library change, no check method
  • vs patched target: ⏭️ N/A
  • vs unrelated target: ⏭️ N/A

Exploitation / Module Execution

Dynamic testing was performed using a Samba Docker container (dperson/samba) on port 445.

Test: auxiliary/admin/smb/list_directory against SMBv1 Samba target (auth success path)

[-] 127.0.0.1:445 - Auxiliary failed: NoMethodError undefined method `negotiated_smb_version'
    for #<Rex::Proto::SMB::Client:...>
[-] 127.0.0.1:445 -   lib/msf/core/exploit/remote/smb/client.rb:206:in `ensure in smb_login'
[-] 127.0.0.1:445 -   lib/msf/core/exploit/remote/smb/client.rb:207:in `smb_login'

Result: ❌ Module crashes. The ensure block in smb_login raises NoMethodError when
connecting to an SMBv1 target, causing the module to fail entirely. No service is logged.

Test: auxiliary/scanner/smb/smb_login (uses its own login scanner, not the mixin)

The smb_login scanner uses Metasploit::Framework::LoginScanner::SMB and does not call the
smb_login mixin method, so it is unaffected by this bug. Service was logged but info field
was empty (the scanner's own report_service call, not the PR's new code).

  • vs vulnerable target: ❌ Crashes with NoMethodError for SMBv1 targets
  • session stability: ⏭️ N/A (module crashes before session)
  • multi-run reliability: ❌ Consistently crashes
  • cleanup verification: ⏭️ N/A
  • vs patched target: ⏭️ N/A

Resilience

  • connection refused: ⏭️ Not tested (blocked by critical bug)
  • connection timeout: ⏭️ Not tested (blocked by critical bug)
  • flakiness: ❌ Consistently fails for SMBv1 targets

Verdict: ❌ REQUEST_CHANGES

The PR introduces a regression: any module that calls smb_login against an SMBv1 target will
crash with NoMethodError: undefined method 'negotiated_smb_version' in the ensure block.
This is because simple_client.client returns a Rex::Proto::SMB::Client for SMBv1 connections,
which does not have negotiated_smb_version. The guard on line 206 should use
simple_client.negotiated_smb_version instead. The report_smb_service method has the same
issue — it unwraps to the raw client and then calls RubySMB-only methods on it.

Required fixes:

  1. Line 206: Change simple_client.client.negotiated_smb_version.present?
    simple_client.negotiated_smb_version.present?

  2. report_smb_service: Do not unwrap SimpleClient to raw client before calling
    negotiated_smb_version/dialect. Either keep the SimpleClient reference, or add a
    client.is_a?(RubySMB::Client) guard before accessing RubySMB-specific attributes.

  3. Add RSpec coverage for report_smb_service covering both SMBv1 and SMBv2/3 paths.

Windows Testing

  • environment method: ⏭️ SKIPPED
  • reason for skip: Library change tested via Linux Samba Docker target; Windows-specific
    SMB behavior would be relevant for a follow-up but the critical bug is reproducible on Linux.

@zeroSteiner zeroSteiner force-pushed the feat/log-smb-services branch from f72bc3e to f3baacd Compare April 16, 2026 20:19
@zeroSteiner
Copy link
Copy Markdown
Contributor Author

Issue was an API descrapency in the legacy SMB client and the new RubySMB client.

Copy link
Copy Markdown
Contributor

@dwelch-r7 dwelch-r7 left a comment

Choose a reason for hiding this comment

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

Looks good, to whoever tests this let's make sure for modules like smb_version that also reports the smb_service we aren't accidentally double reporting the service

{ :use_spn => datastore['NTLM::SendSPN'], :name => simple_client.peerhost }
)
ensure
if simple_client.client.dialect.present?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

NAB: Let's consider wrappingthis detection up in a function so it's more obvious to future travellers what the intent is and so we can potentially change the underlying implementation in the future/add error handling etc.

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

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

4 participants