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.
I have also created a small video explaining the packet capture and the Python script itself.
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.")

