120 lines
3.3 KiB
Lua
120 lines
3.3 KiB
Lua
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
|