├── LICENSE ├── cve_testers ├── README.md ├── cve_2019_12258.py └── requirements.txt └── nse_scripts ├── README.md └── SMBv3Compression ├── README.md ├── smb2-capabilities_patched.nse └── smb2_patched.lua /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /cve_testers/README.md: -------------------------------------------------------------------------------- 1 | # CVE Testers 2 | 3 | Scripts used to check whether OT devices are vulnerable to published vulnerabilities. 4 | 5 | ## Supported CVEs 6 | 7 | * CVE-2019-12258: One of 11 vulnerabilities in VxWorks (also known as URGENT/11), made public in July 2019. 8 | CVE-2019-12258 is a DoS vulnerability which allows to drop existing TCP connections. 9 | The vulnerability affects VxWorks v6.5 and above, and affected devices are often vulnerable to other URGENT/11 vulnerabilities. 10 | For more details, see [Claroty's blog post](https://blog.claroty.com/mitigating-the-impact-of-urgent11-on-ics/ot-networks) 11 | 12 | ## Requirements 13 | 14 | * Python 3.6 or above 15 | * Required python libraries are listed in requirements.txt 16 | 17 | ## Usage 18 | * run `python3 cve_testers/cve_2019_12258.py -h` for detailed help 19 | 20 | 21 | ## License 22 | Apache License 2.0. See the parent directory. 23 | 24 | 25 | ## Disclaimer 26 | There is no warranty, expressed or implied, associated with this product. 27 | -------------------------------------------------------------------------------- /cve_testers/cve_2019_12258.py: -------------------------------------------------------------------------------- 1 | from scapy.all import * 2 | from netaddr import * 3 | import socket 4 | import sys 5 | import argparse 6 | import netifaces 7 | import os 8 | 9 | BUF_SIZE = 1024 10 | TCP_RST_FLAG = 4 11 | 12 | VERBOSE_NONE = 0 13 | VERBOSE_NORMAL = 1 14 | VERBOSE_HIGH = 2 15 | 16 | DEFAULT_PORTS = [80, 443, 502, 44818] 17 | 18 | ERR_ASSET_VULNERABLE = 1 19 | ERR_CON_REFUSED = 2 20 | ERR_CON_TIMED_OUT = 3 21 | 22 | """ 23 | This script is used to check whether a machine is vulnerable to CVE-2019-12258, one of the urgent11 vulnerabilities 24 | published in August 2019 (https://nvd.nist.gov/vuln/detail/CVE-2019-12258). 25 | CVE-2019-12258 is a relatively simple vulnerability, which allows DoS attacks on an existing tcp session without 26 | prior knowledge of the session sequence numbers. 27 | The check implemented here works as follows: 28 | 1. a tcp session is established using the given port 29 | 2. a packet with malformed TCP options is sent, with the same 4-tuple 30 | 3. the script checks whether an RST packet was sent by the server- which shows the attack worked 31 | """ 32 | 33 | 34 | # converting windows GUID to interface name 35 | def convert_windows_guid_to_interface(guid): 36 | for interface in get_windows_if_list(): 37 | if interface['guid'] == guid: 38 | return interface['name'] 39 | return None 40 | 41 | 42 | # get interface name for the given socket 43 | def get_iface(sock): 44 | source_ip = sock.getsockname()[0] 45 | for inter in netifaces.interfaces(): 46 | inet = netifaces.ifaddresses(inter).get(netifaces.AF_INET, []) 47 | if any(a['addr'] == source_ip for a in inet): 48 | if os.name == 'nt': # inter is a guid- we need to convert it to an actual name 49 | return convert_windows_guid_to_interface(inter) 50 | return inter 51 | return None 52 | 53 | 54 | class CveTester(object): 55 | def __init__(self, ip, ports, verbose=VERBOSE_NORMAL, ip_end=None): 56 | self.verbose = verbose 57 | self.ip = ip 58 | self.tcp_ports = ports 59 | self.ip_end = ip_end 60 | 61 | # initiate a TCP socket 62 | def open_socket(self, dst_ip, dst_port): 63 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 64 | s.settimeout(3) 65 | try: 66 | s.connect((dst_ip, dst_port)) 67 | except socket.timeout: 68 | if self.verbose > VERBOSE_NORMAL: 69 | print('Log: Unable to establish a connection to host {} port {}'.format(dst_ip, dst_port)) 70 | return ERR_CON_TIMED_OUT 71 | except ConnectionRefusedError: 72 | if self.verbose >= VERBOSE_NORMAL: 73 | print("Log: The host {} has actively refused a connection to port {}".format(dst_ip, dst_port)) 74 | return ERR_CON_REFUSED 75 | return s 76 | 77 | # try to execute DoS using CVE-12258 78 | def try_dos(self, sock, interface): 79 | src_ip, src_port = sock.getsockname() 80 | dst_ip, dst_port = sock.getpeername() 81 | tcp_pkt = (Ether() / IP(dst=dst_ip, src=src_ip) / TCP(dport=dst_port, sport=src_port)) 82 | tcp_pkt['TCP'].options = [('MSS', '\x00')] 83 | if self.verbose == VERBOSE_HIGH: 84 | return srp(tcp_pkt, iface=interface, timeout=2) 85 | return srp(tcp_pkt, iface=interface, timeout=2, verbose=0) 86 | 87 | def is_ip_vulnerable(self, ip, tcp_port, interface=None): 88 | s = self.open_socket(ip, tcp_port) 89 | if s in [ERR_CON_REFUSED, ERR_CON_TIMED_OUT]: 90 | return s 91 | 92 | if not interface: 93 | interface = get_iface(s) 94 | if not interface: # failed to get an interface 95 | print('Error: Failed to get the correct interface for the host {}'.format(ip)) 96 | return False 97 | 98 | out = self.try_dos(s, interface) 99 | try: 100 | answers = out[0] # get the answers 101 | res = answers[0] # results list from the answers 102 | res_packet = res[1] # the packet we want to check 103 | tcp = res_packet[TCP] # tcp layer 104 | if tcp.flags & TCP_RST_FLAG == TCP_RST_FLAG: # check whether TCP RST flag is on 105 | return True 106 | except (IndexError, TypeError): # returned packet is not what we expected 107 | pass 108 | s.close() 109 | return False 110 | 111 | def is_ip_vulnerable_wrapper(self, ip, interface): 112 | if self.verbose >= VERBOSE_NORMAL: 113 | print('Checking ip {}...'.format(ip)) 114 | asset_found = False 115 | for tcp_port in self.tcp_ports: 116 | retval = self.is_ip_vulnerable(ip, tcp_port, interface) 117 | if retval == ERR_ASSET_VULNERABLE: 118 | print('The host {} is vulnerable to CVE-2019-12258'.format(ip)) 119 | return 120 | elif retval != ERR_CON_TIMED_OUT: 121 | asset_found = True 122 | if self.verbose > VERBOSE_NONE and asset_found: 123 | print('The host {} is not vulnerable to CVE-2019-12258'.format(ip)) 124 | elif self.verbose > VERBOSE_NONE: 125 | print('Could not establish a connection to the host {}'.format(ip)) 126 | 127 | def is_ip_vulnerable_ip_range(self, interface): 128 | for ip in iter_iprange(self.ip, self.ip_end): 129 | self.is_ip_vulnerable_wrapper(str(ip), interface) 130 | 131 | def test_for_cve(self, interface): 132 | if self.ip_end: 133 | self.is_ip_vulnerable_ip_range(interface) 134 | else: 135 | self.is_ip_vulnerable_wrapper(self.ip, interface) 136 | 137 | 138 | def main(): 139 | if sys.version_info[0] < 3: 140 | raise Exception("Python 3 or a more recent version is required.") 141 | parser = argparse.ArgumentParser(description="Script for testing whether PLCs are vulnerable to CVE-2019-12258") 142 | parser.add_argument('-ip', '--ip', help='IP to test, or start of ip range', required=True) 143 | port_group = parser.add_mutually_exclusive_group(required=True) 144 | port_group.add_argument('-p', '--port', help='port to use.', type=int) 145 | port_group.add_argument('-d', '--default_ports', help='check all the default ports: {}'.format(', '.join([str(port) for port in DEFAULT_PORTS])), action='store_true') 146 | parser.add_argument('-end_ip', '--end_ip', help='end of the ip range to test', required=False) 147 | parser.add_argument('-i', '--iface', help='name of the network interface to use', required=False) 148 | parser.add_argument('-v', '--verbose', type=int, help='verbose level: 0 to print only vulnerable devices,' 149 | ' 1 to also print connection status, 2 to print scapy' 150 | ' messages as well. default is 0', required=False, 151 | default=VERBOSE_NORMAL) 152 | arguments = parser.parse_args() 153 | ip = arguments.ip 154 | tcp_port = arguments.port 155 | use_default = arguments.default_ports 156 | if use_default: 157 | tcp_ports = DEFAULT_PORTS 158 | else: 159 | tcp_ports = [tcp_port] 160 | interface = arguments.iface 161 | end_ip = arguments.end_ip 162 | verbose = arguments.verbose 163 | if verbose is None: 164 | verbose = 1 165 | cve_tester = CveTester(ip, tcp_ports, verbose=verbose, ip_end=end_ip) 166 | cve_tester.test_for_cve(interface) 167 | 168 | 169 | if __name__ == "__main__": 170 | main() 171 | -------------------------------------------------------------------------------- /cve_testers/requirements.txt: -------------------------------------------------------------------------------- 1 | netaddr==0.7.19 2 | netifaces==0.10.9 3 | scapy==2.4.3 4 | -------------------------------------------------------------------------------- /nse_scripts/README.md: -------------------------------------------------------------------------------- 1 | # NSE Scripts 2 | 3 | NSE Scripts used to check whether OT devices are vulnerable to published vulnerabilities. 4 | 5 | 6 | ## License 7 | Apache License 2.0. See the parent directory. 8 | 9 | 10 | ## Disclaimer 11 | There is no warranty, expressed or implied, associated with this product. 12 | -------------------------------------------------------------------------------- /nse_scripts/SMBv3Compression/README.md: -------------------------------------------------------------------------------- 1 | 2 | # SMBv3 Compression Tester 3 | NSE script to check if a Windows machine has SMBv3 protocol enabled with the compression feature. 4 | 5 | ## Notes 6 | Our NSE script is based on `smb2-capabilities.nse` which we expanded to detect SMBv3 compression as well. Currently it's a standalone NSE script with a patched lua file but we will PR the nmap repository with those changes. 7 | 8 | ## Example 9 | 10 | Starting Nmap 7.80SVN ( https://nmap.org ) at 2020-03-11 18:17 IST 11 | Nmap scan report for 1.2.3.4 12 | Host is up (0.00050s latency). 13 | 14 | PORT STATE SERVICE 15 | 445/tcp open microsoft-ds 16 | 17 | Host script results: 18 | | smb2-capabilities_patched: 19 | | 2.02: 20 | | Distributed File System 21 | | 2.10: 22 | | Distributed File System 23 | | Leasing 24 | | Multi-credit operations 25 | | 3.00: 26 | | Distributed File System 27 | | Leasing 28 | | Multi-credit operations 29 | | 3.02: 30 | | Distributed File System 31 | | Leasing 32 | | Multi-credit operations 33 | | 3.11: 34 | | Distributed File System 35 | | Leasing 36 | | Multi-credit operations 37 | |_ SMBv3 Compression LZTN1 (Negotiation Context) <---------- 38 | 39 | ## Supported CVEs 40 | 41 | * No CVE has been published yet. 42 | 43 | ## Requirements 44 | 45 | * nmap 46 | 47 | ## Usage 48 | `cd` into run `SMBv3Compression` (your cwd must be the same as the files) and run: 49 | 50 | nmap -p445 --script ./smb2-capabilities_patched.nse IP_ADDR 51 | 52 | Search for `SMBv3 Compression LZTN1 (Negotiation Context)`. 53 | 54 | 55 | ## Disable SMBv3 compression 56 | You can disable SMBv3 compression with the PowerShell command below: 57 | 58 | Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" DisableCompression -Type DWORD -Value 1 -Force 59 | 60 | ## License 61 | Apache License 2.0. See the parent directory. 62 | 63 | 64 | ## Disclaimer 65 | There is no warranty, expressed or implied, associated with this product. 66 | -------------------------------------------------------------------------------- /nse_scripts/SMBv3Compression/smb2-capabilities_patched.nse: -------------------------------------------------------------------------------- 1 | local smb = require "smb" 2 | local smb2 = require "smb2_patched" 3 | local stdnse = require "stdnse" 4 | local table = require "table" 5 | local nmap = require "nmap" 6 | 7 | description = [[ 8 | Attempts to list the supported capabilities in a SMBv2 server for each 9 | enabled dialect. 10 | 11 | The script sends a SMB2_COM_NEGOTIATE command and parses the response 12 | using the SMB dialects: 13 | * 2.02 14 | * 2.10 15 | * 3.00 16 | * 3.02 17 | * 3.11 18 | 19 | References: 20 | * https://msdn.microsoft.com/en-us/library/cc246561.aspx 21 | ]] 22 | 23 | --- 24 | -- @usage nmap -p 445 --script smb2-capabilities 25 | -- @usage nmap -p 139 --script smb2-capabilities 26 | -- 27 | -- @output 28 | -- | smb2-capabilities: 29 | -- | 2.02: 30 | -- | Distributed File System 31 | -- | 2.10: 32 | -- | Distributed File System 33 | -- | Leasing 34 | -- | Multi-credit operations 35 | -- 36 | -- @xmloutput 37 | -- 38 | -- Distributed File System 39 | --
40 | -- 41 | -- Distributed File System 42 | -- Leasing 43 | -- Multi-credit operations 44 | --
45 | --- 46 | 47 | author = "Paulino Calderon" 48 | license = "Same as Nmap--See https://nmap.org/book/man-legal.html" 49 | categories = {"safe", "discovery"} 50 | 51 | hostrule = function(host) 52 | return smb.get_port(host) ~= nil 53 | end 54 | 55 | action = function(host,port) 56 | local status, smbstate, overrides 57 | local output = stdnse.output_table() 58 | overrides = {} 59 | 60 | local smb2_dialects = {0x0202, 0x0210, 0x0300, 0x0302, 0x0311} 61 | 62 | for i, dialect in pairs(smb2_dialects) do 63 | -- we need a clean connection for each negotiate request 64 | status, smbstate = smb.start(host) 65 | if(status == false) then 66 | stdnse.debug1("Could not establish a connection.") 67 | return nil 68 | end 69 | -- We set our overrides Dialects table with the dialect we are testing 70 | overrides['Dialects'] = {dialect} 71 | status = smb2.negotiate_v2(smbstate, overrides) 72 | if status then 73 | local capabilities = {} 74 | stdnse.debug2("SMB2: Server capabilities: '%s'", smbstate['capabilities']) 75 | 76 | -- We check the capabilities flags. Not all of them are supported by 77 | -- every dialect but we dumb check anyway. 78 | if smbstate['capabilities'] & 0x01 == 0x01 then 79 | table.insert(capabilities, "Distributed File System") 80 | end 81 | if smbstate['capabilities'] & 0x02 == 0x02 then 82 | table.insert(capabilities, "Leasing") 83 | end 84 | if smbstate['capabilities'] & 0x04 == 0x04 then 85 | table.insert(capabilities, "Multi-credit operations") 86 | end 87 | if smbstate['capabilities'] & 0x08 == 0x08 then 88 | table.insert(capabilities, "Multiple Channel support") 89 | end 90 | if smbstate['capabilities'] & 0x10 == 0x10 then 91 | table.insert(capabilities, "Persistent handles") 92 | end 93 | if smbstate['capabilities'] & 0x20 == 0x20 then 94 | table.insert(capabilities, "Directory Leasing") 95 | end 96 | if smbstate['capabilities'] & 0x40 == 0x40 then 97 | table.insert(capabilities, "Encryption") 98 | end 99 | if dialect == 0x0311 and smbstate['is_support_compresson_lznt1'] then 100 | table.insert(capabilities, "SMBv3 Compression LZTN1 (Negotiation Context)") 101 | end 102 | 103 | if #capabilities<1 then 104 | table.insert(capabilities, "All capabilities are disabled") 105 | end 106 | output[stdnse.tohex(dialect, {separator = ".", group = 2})] = capabilities 107 | end 108 | smb.stop(smbstate) 109 | status = false 110 | end 111 | 112 | if #output>0 then 113 | return output 114 | else 115 | stdnse.debug1("No dialects were accepted.") 116 | if nmap.verbosity()>1 then 117 | return "Couldn't establish a SMBv2 connection." 118 | end 119 | end 120 | end 121 | -------------------------------------------------------------------------------- /nse_scripts/SMBv3Compression/smb2_patched.lua: -------------------------------------------------------------------------------- 1 | --- 2 | -- Implements the Server Message Block (SMB) protocol version 2 and 3. 3 | -- 4 | -- The implementation extends smb.lua to support SMB dialects 2.02, 2.10, 3.0, 5 | -- 3.02 and 3.11. This is a work in progress and not all commands are 6 | -- implemented yet. Features/functionality will be added as the scripts 7 | -- get updated. I tried to be consistent with the current implementation of 8 | -- smb.lua but some fields may have changed name or don't exist anymore. 9 | -- 10 | -- @author Paulino Calderon 11 | -- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html 12 | --- 13 | 14 | local datetime = require "datetime" 15 | local string = require "string" 16 | local stdnse = require "stdnse" 17 | local table = require "table" 18 | local tableaux = require "tableaux" 19 | local match = require "match" 20 | 21 | _ENV = stdnse.module("smb2", stdnse.seeall) 22 | 23 | local TIMEOUT = 10000 24 | local command_names = {} 25 | local command_codes = 26 | { 27 | SMB2_COM_NEGOTIATE = 0x0000, 28 | SMB2_COM_SESSION_SETUP = 0x0001, 29 | SMB2_COM_LOGOFF = 0x0002, 30 | SMB2_COM_TREE_CONNECT = 0x0003, 31 | SMB2_COM_TREE_DISCONNECT = 0x0004, 32 | SMB2_COM_CREATE = 0x0005, 33 | SMB2_COM_CLOSE = 0x0006, 34 | SMB2_COM_FLUSH = 0x0007, 35 | SMB2_COM_READ = 0x0008, 36 | SMB2_COM_WRITE = 0x0009, 37 | SMB2_COM_LOCK = 0x000A, 38 | SMB2_COM_IOCTL = 0x000B, 39 | SMB2_COM_CANCEL = 0x000C, 40 | SMB2_COM_ECHO = 0x000D, 41 | SMB2_COM_QUERY_DIRECTORY = 0x000E, 42 | SMB2_COM_CHANGE_NOTIFY = 0x000F, 43 | SMB2_COM_QUERY_INFO = 0x0010, 44 | SMB2_COM_SET_INFO = 0x0011, 45 | SMB2_COM_OPLOCK_BREAK = 0x0012 46 | } 47 | local smb2_values_codes = {} 48 | local smb2_values = { 49 | -- Security Mode 50 | SMB2_NEGOTIATE_SIGNING_ENABLED = 0x0001, 51 | SMB2_NEGOTIATE_SIGNING_REQUIRED = 0x0002, 52 | -- Capabilities 53 | SMB2_GLOBAL_CAP_DFS = 0x00000001, 54 | SMB2_GLOBAL_CAP_LEASING = 0x00000002, 55 | SMB2_GLOBAL_CAP_LARGE_MTU = 0x00000004, 56 | SMB2_GLOBAL_CAP_MULTI_CHANNEL = 0x00000008, 57 | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES = 0x00000010, 58 | SMB2_GLOBAL_CAP_DIRECTORY_LEASING = 0x00000020, 59 | SMB2_GLOBAL_CAP_ENCRYPTION = 0x00000040, 60 | -- Context Types 61 | SMB2_PREAUTH_INTEGRITY_CAPABILITIES = 0x0001, 62 | SMB2_ENCRYPTION_CAPABILITIES = 0x0002, 63 | SMB2_COMPRESSION_CAPABILITIES = 0x0003 64 | } 65 | 66 | for i, v in pairs(command_codes) do 67 | command_names[v] = i 68 | end 69 | for i, v in pairs(smb2_values) do 70 | smb2_values_codes[v] = i 71 | end 72 | 73 | --- 74 | -- Creates a SMB2 SYNC header packet. 75 | -- 76 | -- SMB2 Packet Header - SYNC: 77 | -- * https://msdn.microsoft.com/en-us/library/cc246529.aspx 78 | -- 79 | -- @param smb The SMB object associated with the connection. 80 | -- @param command The SMB2 command to execute. 81 | -- @param overrides Overrides table. 82 | -- @return header The encoded SMB2 SYNC header. 83 | --- 84 | function smb2_encode_header_sync(smb, command, overrides) 85 | overrides = overrides or {} 86 | 87 | local sig = "\xFESMB" -- SMB2 packet 88 | local structureSize = 64 -- SYNC header structure size 89 | local flags = 0 -- TODO: Set flags that will work for all dialects 90 | 91 | -- Increase the message id 92 | if smb['MessageId'] then 93 | smb['MessageId'] = smb['MessageId'] + 1 94 | end 95 | 96 | -- Header structure 97 | local header = string.pack("smb_encode_sync_header. 120 | -- @param data The data. 121 | -- @param overrides Overrides table. 122 | -- @return Boolean Status. 123 | -- @return An error message if status is false. 124 | --- 125 | function smb2_send(smb, header, data, overrides) 126 | overrides = overrides or {} 127 | local body = header .. data 128 | local attempts = 5 129 | local status, err 130 | 131 | local out = string.pack(">s4", body) 132 | repeat 133 | attempts = attempts - 1 134 | stdnse.debug3("SMB: Sending SMB packet (len: %d, attempts remaining: %d)", #out, attempts) 135 | status, err = smb['socket']:send(out) 136 | until(status or (attempts == 0)) 137 | 138 | if(attempts == 0) then 139 | stdnse.debug1("SMB: Sending packet failed after 5 tries! Giving up.") 140 | end 141 | 142 | return status, err 143 | end 144 | 145 | --- 146 | -- Reads the next SMB2 packet from the socket, and parses it into the header and data. 147 | -- Netbios handling based taken from smb.lua. 148 | -- 149 | -- @param smb The SMB object associated with the connection 150 | -- @param read_data [optional] Return data section. Set to false if you only need the header. Default: true 151 | -- @return (status, header, data) If status is true, the header, 152 | -- and data are all the raw arrays of bytes. 153 | -- If status is false, header contains an error message and data is undefined. 154 | --- 155 | function smb2_read(smb, read_data) 156 | stdnse.debug3("SMB2: Receiving SMB2 packet") 157 | 158 | -- Receive the response -- we make sure to receive at least 4 bytes, the length of the NetBIOS length 159 | smb['socket']:set_timeout(TIMEOUT) 160 | 161 | -- attempt to read the Netbios header 162 | local status, netbios_data = smb['socket']:receive_buf(match.numbytes(4), true); 163 | 164 | -- Make sure the connection is still alive 165 | if not status then 166 | return false, "SMB2: Failed to receive bytes: " .. netbios_data 167 | end 168 | 169 | -- The length of the packet is 4 bytes of big endian (for our purposes). 170 | -- The NetBIOS header is 24 bits, big endian 171 | local netbios_length, pos = string.unpack(">I4", netbios_data) 172 | -- Make the length 24 bits 173 | netbios_length = netbios_length & 0x00FFFFFF 174 | 175 | -- The total length is the netbios_length, plus 4 (for the length itself) 176 | local length = netbios_length + 4 177 | 178 | local status, smb_data = smb['socket']:receive_buf(match.numbytes(netbios_length), true) 179 | 180 | -- Make sure the connection is still alive 181 | if not status then 182 | return false, "SMB2: Failed to receive bytes after 5 attempts: " .. smb_data 183 | end 184 | 185 | local result = netbios_data .. smb_data 186 | if(#result ~= length) then 187 | stdnse.debug1("SMB2: ERROR: Received wrong number of bytes, there will likely be issues (received %d, expected %d)", #result, length) 188 | return false, string.format("SMB2: ERROR: Didn't receive the expected number of bytes; received %d, expected %d. This will almost certainly cause some errors.", #result, length) 189 | end 190 | 191 | -- The header is 64 bytes. 192 | if (pos + 64 > #result) then 193 | stdnse.debug2("SMB2: SMB2 packet too small. Size needed to be at least '%d' but we got '%d' bytes", pos+64, #result) 194 | return false, "SMB2: ERROR: Header packet too small." 195 | end 196 | local header, pos = string.unpack(" 3.11 233 | local total_data = 0 -- Data counter 234 | local padding_data = "" -- Padding string to align contexts 235 | local context_data -- Holds Context data 236 | local is_0311 = false -- Flag for SMB 3.11 237 | local status, err 238 | 239 | if not( overrides['Dialects'] ) then -- Set 2.02 as default dialect if user didn't select one 240 | overrides['Dialects'] = {0x0202} 241 | end 242 | 243 | local header = smb2_encode_header_sync(smb, command_codes['SMB2_COM_NEGOTIATE'], overrides) 244 | 245 | -- We construct the first block that works for dialects 2.02 up to 3.11. 246 | local data = string.pack(" 3.x 252 | GUID -- 16 bytes: ClientGuid 253 | ) 254 | 255 | -- The next block gets interpreted in different ways depending on the dialect 256 | if tableaux.contains(overrides['Dialects'], 0x0311) then 257 | is_0311 = true 258 | end 259 | 260 | -- If we are dealing with 3.11 we need to set the following fields: 261 | -- NegotiateContextOffset, NegotiateContextCount, and Reserved2 262 | if is_0311 then 263 | total_data = #header + #data + (DialectCount*2) 264 | padding_data = string.rep("\0", (8 - total_data % 8) % 8) 265 | total_data = total_data + #padding_data 266 | data = data .. string.pack("