summaryrefslogtreecommitdiffstats
path: root/scripts/smb-double-pulsar-backdoor.nse
blob: b0b991dfed66429a53b233dd00d79b3215d0cb08 (plain)
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
local smb = require "smb"
local vulns = require "vulns"
local stdnse = require "stdnse"
local string = require "string"

description = [[
Checks if the target machine is running the Double Pulsar SMB backdoor.

Based on the python detection script by Luke Jennings of Countercept.
https://github.com/countercept/doublepulsar-detection-script
]]

---
-- @usage nmap -p 445 <target> --script=smb-double-pulsar-backdoor
--
-- @see smb-vuln-ms17-010.nse
--
-- @output
-- | smb-double-pulsar-backdoor:
-- |   VULNERABLE:
-- |   Double Pulsar SMB Backdoor
-- |     State: VULNERABLE
-- |     Risk factor: HIGH  CVSSv2: 10.0 (HIGH) (AV:N/AC:L/Au:N/C:C/I:C/A:C)
-- |       The Double Pulsar SMB backdoor was detected running on the remote machine.
-- |
-- |     Disclosure date: 2017-04-14
-- |     References:
-- |       https://isc.sans.edu/forums/diary/Detecting+SMB+Covert+Channel+Double+Pulsar/22312/
-- |       https://github.com/countercept/doublepulsar-detection-script
-- |_      https://steemit.com/shadowbrokers/@theshadowbrokers/lost-in-translation

author = "Andrew Orr"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"vuln", "safe", "malware"}

hostrule = function(host)
  return smb.get_port(host) ~= nil
end

-- stolen from smb.lua as timeout needs to be modified to get a response
local function send_transaction2(smbstate, sub_command, function_parameters, function_data, overrides)
  overrides = overrides or {}
  local header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, pid, mid
  local header, parameters, data
  local parameter_offset = 0
  local parameter_size   = 0
  local data_offset      = 0
  local data_size        = 0
  local total_word_count, total_data_count, reserved1, parameter_count, parameter_displacement, data_count, data_displacement, setup_count, reserved2
  local response = {}

  -- Header is 0x20 bytes long (not counting NetBIOS header).
  header = smb.smb_encode_header(smbstate, 0x32, overrides) -- 0x32 = SMB_COM_TRANSACTION2

  if(function_parameters) then
    parameter_offset = 0x44
    parameter_size = #function_parameters
    data_offset = #function_parameters + 33 + 32
  end

  -- Parameters are 0x20 bytes long.
  parameters = string.pack("<I2 I2 I2 I2 B B I2 I4 I2 I2 I2 I2 I2 B B I2",
    parameter_size,                  -- Total parameter count.
    data_size,                       -- Total data count.
    0x000a,                          -- Max parameter count.
    0x3984,                          -- Max data count.
    0x00,                            -- Max setup count.
    0x00,                            -- Reserved.
    0x0000,                          -- Flags (0x0000 = 2-way transaction, don't disconnect TIDs).
    10803622,                        -- Timeout
    0x0000,                          -- Reserved.
    parameter_size,                  -- Parameter bytes.
    parameter_offset,                -- Parameter offset.
    data_size,                       -- Data bytes.
    data_offset,                     -- Data offset.
    0x01,                            -- Setup Count
    0x00,                            -- Reserved
    sub_command                      -- Sub command
    )

  local data = "\0\0\0" .. (function_parameters or '')
  .. (function_data or '')

  -- Send the transaction request
  stdnse.debug2("SMB: Sending SMB_COM_TRANSACTION2")
  local result, err = smb.smb_send(smbstate, header, parameters, data, overrides)
  if(result == false) then
    return false, err
  end

  return true
end

action = function(host,port)
  local double_pulsar  = {
    title = "Double Pulsar SMB Backdoor",
--    IDS = {CVE = 'CVE-2010-2550'},
    risk_factor = "HIGH",
    scores = {
      CVSSv2 = "10.0 (HIGH) (AV:N/AC:L/Au:N/C:C/I:C/A:C)",
    },
    description = [[
The Double Pulsar SMB backdoor was detected running on the remote machine.
]],
    references = {
      'https://github.com/countercept/doublepulsar-detection-script',
      'https://isc.sans.edu/forums/diary/Detecting+SMB+Covert+Channel+Double+Pulsar/22312/',
      'https://steemit.com/shadowbrokers/@theshadowbrokers/lost-in-translation'
    },
    dates = {
      disclosure = {year = '2017', month = '04', day = '14'},
    },
    exploit_results = {},
  }

  local report = vulns.Report:new(SCRIPT_NAME, host, port)
  double_pulsar.state = vulns.STATE.NOT_VULN

  local share = "IPC$"

  local status, smbstate = smb.start_ex(host, true, true, share, nil, nil, nil)

  if not status then
    stdnse.debug1("Could not connect to IPC$ share over SMB.")
  else
    -- the multiplex ID needs to be 65
    smbstate["mid"] = 65;
    -- 12 (not 11, not 13) nulls
    local param = ("\0"):rep(12)
    -- 0x000e is SESSION_SETUP
    local status, result = send_transaction2(smbstate, 0xe, param)
    if not status then
      stdnse.debug1("Error: ", result)
    else
      local status, header, parameters, data = smb.smb_read(smbstate)
      local multiplex_id = string.unpack("<I2", header, 1 + string.packsize("BBBBB I4 B I2 I2 i8 I2 I2 I2 I2"))

      if (multiplex_id == 81) then
        double_pulsar.state = vulns.STATE.VULN
      else
        stdnse.debug1("Machine is not vulnerable")
      end
    end
  end
  return report:make_output(double_pulsar)
end