Post

CVE 2017 0144 Scanner

CVE 2017 0144 Scanner

While exploring CVE-2017-0144 eternalblue exploit, I found msfconsole runs an auxiliary scanner just before triggering the actual exploit. I have re-created a CVE-2017-0144 vulnerability scanner based on network capture by Wireshark during msfconsole exploit run.

Obviously this script might not be that useful in today’s day given that this vulnerability is really old, I created this Python script for my own learning experience to understand those network packets in a more practical way.

Top level summary of the network capture is,

Only 4 type of packets are enough to identify if SMB server is vulnerable or not.

  • First Packet <Negotiate Protocol Request>
  • Second Packet <Session Setup AndX Request>
  • Third Packet <Tree Connect AndX Request>
  • Fourth Packet <PeekNamedPipe Request>

I found fourth packet PeekNamedPipe Request is the only packet which is crafted by the scanner and rest all are as it is. The response to PeekNamedPipe Request packet decides whether the server is vulnerable or not. Response with an error STATUS_INSUFF_SERVER_RESOURCES means server is vulnerable to CVE-2017-0144.

Wireshark Capture


I have also created a small video explaining the packet capture and the Python script itself.

Understanding CVE-2017-0144 Vulnerability Scanner


Python Script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
from scapy.all import *
from impacket.smb import *
import sys
import struct
import os
import socket

try:
	ip = sys.argv[1]
	port = int(sys.argv[2])
except:
	print("argv[1] is IP, argv[2] is port.")
	exit(1)


def check_smb_vuln(ip, port):
	# anon login
	# negotiate smb
	try:
		smb_scan = SMB(ip,ip,sess_port=445)
		smb_scan.login("","")
	except Exception as ex:
		print("Anon Login / Dialect negotiation Failed")
		exit()

	print("SMB Dialect : ",smb_scan.getDialect())
	print("OS : ",smb_scan.get_server_os())

	
	"""
	Frame 13: Packet, 144 bytes on wire (1152 bits), 144 bytes captured (1152 bits) on interface eth1, id 0
Ethernet II, Src: PCSSystemtec_96:40:03 (08:00:27:96:40:03), Dst: PCSSystemtec_83:be:7b (08:00:27:83:be:7b)
Internet Protocol Version 4, Src: 192.168.56.102, Dst: 192.168.56.101
Transmission Control Protocol, Src Port: 34443, Dst Port: 445, Seq: 507, Ack: 536, Len: 78
NetBIOS Session Service
SMB (Server Message Block Protocol), Trans Request (0x25)
    SMB Header
    Trans Request (0x25)
        Word Count (WCT): 16
        Total Parameter Count: 0
        Total Data Count: 0
        Max Parameter Count: 65535
        Max Data Count: 65535
        Max Setup Count: 0
        Reserved: 00
        Flags: 0x0000
        Timeout: Return immediately (0)
        Reserved: 0000
        Parameter Count: 0
        Parameter Offset: 74
        Data Count: 0
        Data Offset: 74
        Setup Count: 2
        Reserved: 00
        Byte Count (BCC): 7
        Transaction Name: \\PIPE\\
SMB Pipe Protocol
    Function: PeekNamedPipe (0x0023)


	"""
	# tree connect andx request
	tid = smb_scan.tree_connect_andx("\\\\"+ip+"\\IPC$")
	#print("TID :",tid)
	# trans - peeknamedpipe
	smb_pkt = NewSMBPacket()
	smb_pkt['Pid'] = os.getpid() & 0xffff
	smb_pkt['Uid'] = smb_scan._uid
	smb_pkt['Mid'] = 1
	smb_pkt['Tid'] = tid

	trans_com = SMBCommand(SMB.SMB_COM_TRANSACTION)
	params = SMBTransaction_Parameters()
	params['MaxParameterCount'] = 0xffff
	params['MaxDataCount'] = 0xffff
	params['Flags'] = 0x0000
	FID = 0x0000
	PEEK_NAMED_PIPED = 0x0023
	name = "\\PIPE\\\x00"
	setup = struct.pack("<HH",PEEK_NAMED_PIPED,FID)
	params['Setup'] = setup
	params['ParameterOffset'] = 32+3+28+len(setup)+len(name)
	params['DataOffset'] = params['ParameterOffset']

	# found after viewing all params field which are none and are not set by SMB
	params['TotalParameterCount'] = 0
	params['TotalDataCount'] = 0
	params['MaxSetupCount'] = 0
	params['Timeout'] = 0
	params['ParameterCount'] = 0
	params['DataCount'] = 0
	#params['SetupCount'] = 2
	# params['DataCount'] = 0
	# params['DataCount'] = 0

 	
	
	
	data = SMBTransaction_Data()
	data['Name'] = name
	# found after viewing all params field which are none and are not set by SMB
	data['Trans_Parameters'] = 0
	data['Trans_Data'] = 0


	trans_com['Parameters'] = params
	trans_com['Data'] = data


	smb_pkt.addCommand(trans_com)

	#print(dir(smb_pkt))
	# for field in smb_pkt.structure:
	# 	if not field[0].startswith('_'):
	# 		val = smb_pkt.fields.get(field[0])
	# 		print(field[0],":",val)

	# for field in params.structure:
	# 	if not field[0].startswith('_'):
	# 		val = params.fields.get(field[0])
	# 		print(field[0],":",val)

	# for field in data.structure:
	# 	if not field[0].startswith('_'):
	# 		val = data.fields.get(field[0])
	# 		print(field[0],":",val)
	#exit()
	smb_scan.sendSMB(smb_pkt)
	response = smb_scan.recvSMB()

	print("Error Class : ", response['ErrorClass'],"(5 means vulnerable)")
	if response['ErrorClass'] == 0x5:
		print(ip,"is vulnerable to CVE-2017-0144")



port_open = False
#check if Port is Open using syn scan
if os.geteuid() == 0: # if root
	#print("ROOT")
	p = IP(dst=ip)/TCP(dport=port, flags='S') #Syn Packet
	ans, unans = sr(p,timeout = 0.2, verbose=0)
	for req, resp in ans:
		if not resp.haslayer(TCP):
			continue
		tcp_layer = resp.getlayer(TCP)
		if tcp_layer.flags == 0x12: #checking syn Ack
			port_open = True
			
			#resetting the half open connection
			sr(IP(dst=ip)/TCP(dport=port, flags='AR'), timeout=1, verbose=0)

else:
	#print("Not ROOT")
	s = socket.socket()
	s.settimeout(0.5)
	if s.connect_ex((ip,port)) == 0:
		port_open = True
	else:
		port_open = False



if port_open:
	print(f"IP : {ip} Port : {port} is Open.")
	# check if its SMB service and is vulnerable.
	check_smb_vuln(ip, port)


else:
	print(f"IP : {ip} Port : {port} is Closed.")

This post is licensed under CC BY 4.0 by the author.