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
|
local io = require "io"
local msrpc = require "msrpc"
local smb = require "smb"
local stdnse = require "stdnse"
local stringaux = require "stringaux"
-- -*- mode: lua -*-
-- vim: set filetype=lua :
description = [[
Detects whether a host is infected with the Stuxnet worm (http://en.wikipedia.org/wiki/Stuxnet).
An executable version of the Stuxnet infection will be downloaded if a format
for the filename is given on the command line.
]]
---
-- @usage
-- nmap --script stuxnet-detect -p 445 <host>
--
-- @args stuxnet-detect.save Path to save Stuxnet executable under, with
-- <code>%h</code> replaced by the host's IP address, and <code>%v</code>
-- replaced by the version of Stuxnet.
--
-- @output
-- PORT STATE SERVICE REASON
-- 445/tcp open microsoft-ds syn-ack
--
-- Host script results:
-- |_stuxnet-detect: INFECTED (version 4c:04:00:00:01:00:00:00)
--
-- @see smb-vuln-ms10-061.nse
author = "Mak Kolybabi"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"discovery", "intrusive"}
local STUXNET_PATHS = {"\\\\browser", "\\\\ntsvcs", "\\\\pipe\\browser", "\\\\pipe\\ntsvcs"}
local STUXNET_UUID = "\xe1\x04\x02\x00\x00\x00\x00\x00\xc0\x00\x00\x00\x00\x00\x00\x46"
local STUXNET_VERSION = 0x01
local RPC_GET_VERSION = 0x00
local RPC_GET_EXECUTABLE = 0x04
local function check_infected(host, path, save)
local file, result, session, status, version
-- Create an SMB session.
status, session = msrpc.start_smb(host, path)
if not status then
stdnse.debug1("Failed to establish session on %s.", path)
return false, nil
end
-- Bind to the Stuxnet service.
status, result = msrpc.bind(session, STUXNET_UUID, STUXNET_VERSION, nil)
if not status or result["ack_result"] ~= 0 then
stdnse.debug1("Failed to bind to Stuxnet service.")
msrpc.stop_smb(session)
return false, nil
end
-- Request version of Stuxnet infection.
status, result = msrpc.call_function(session, RPC_GET_VERSION, "")
if not status then
stdnse.debug1("Failed to retrieve Stuxnet version: %s", result)
msrpc.stop_smb(session)
return false, nil
end
version = stdnse.tohex(result.arguments, {separator = ":"})
-- Request executable of Stuxnet infection.
if save then
local file, fmt
status, result = msrpc.call_function(session, RPC_GET_EXECUTABLE, "")
if not status then
stdnse.debug1("Failed to retrieve Stuxnet executable: %s", result)
msrpc.stop_smb(session)
return true, version
end
fmt = save:gsub("%%h", host.ip)
fmt = fmt:gsub("%%v", version)
file = io.open(stringaux.filename_escape(fmt), "w")
if file then
stdnse.debug1("Wrote %d bytes to file %s.", #result.arguments, fmt)
file:write(result.arguments)
file:close()
else
stdnse.debug1("Failed to open file: %s", fmt)
end
end
-- Destroy the SMB session
msrpc.stop_smb(session)
return true, version
end
hostrule = function(host)
return (smb.get_port(host) ~= nil)
end
action = function(host, port)
local _, path, result, save, status
-- Get script arguments.
save = stdnse.get_script_args("stuxnet-detect.save")
-- Try to find Stuxnet on this host.
for _, path in pairs(STUXNET_PATHS) do
status, result = check_infected(host, path, save)
if status then
return "INFECTED (version " .. result .. ")"
end
end
end
|