summaryrefslogtreecommitdiffstats
path: root/scripts/smb-vuln-conficker.nse
blob: 976a92617d5db679092f210d2c1001d85f8c2418 (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
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
local msrpc = require "msrpc"
local nmap = require "nmap"
local smb = require "smb"
local string = require "string"
local vulns = require "vulns"

description = [[
Detects Microsoft Windows systems infected by the Conficker worm. This check is dangerous and
it may crash systems.

Based loosely on the Simple Conficker Scanner, found here:
-- http://iv.cs.uni-bonn.de/wg/cs/applications/containing-conficker/

This check was previously part of smb-check-vulns.
]]
---
--@usage
-- nmap --script smb-vuln-conficker.nse -p445 <host>
-- nmap -sU --script smb-vuln-conficker.nse -p T:139 <host>
--
--@output
--| smb-vuln-conficker:
--|   VULNERABLE:
--|   Microsoft Windows system infected by Conficker
--|     State: VULNERABLE
--|     IDs:  CVE:2008-4250
--|       This system shows signs of being infected by a variant of the worm Conficker.
--|     References:
--|       https://technet.microsoft.com/en-us/library/security/ms08-067.aspx
--|       http://www.microsoft.com/security/portal/threat/encyclopedia/entry.aspx?Name=Win32%2fConficker
--|_      https://cve.mitre.org/cgi-bin/cvename.cgi?name=2008-4250
---

author = {"Ron Bowes", "Jiayi Ye", "Paulino Calderon <calderon()websec.mx>"}
copyright = "Ron Bowes"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"intrusive","exploit","dos","vuln"}
-- run after all smb-* scripts (so if it DOES crash something, it doesn't kill
-- other scans have had a chance to run)
dependencies = {
  "smb-brute", "smb-enum-sessions", "smb-security-mode",
  "smb-enum-shares", "smb-server-stats",
  "smb-enum-domains", "smb-enum-users", "smb-system-info",
  "smb-enum-groups", "smb-os-discovery", "smb-enum-processes",
  "smb-psexec",
};

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

local INFECTED   = 5
local INFECTED2  = 6
local CLEAN      = 7

-- Help messages for the more common errors seen by the Conficker check.
CONFICKER_ERROR_HELP = {
  ["NT_STATUS_BAD_NETWORK_NAME"] =
  [[UNKNOWN; Network name not found (required service has crashed). (Error NT_STATUS_BAD_NETWORK_NAME)]],
  -- http://seclists.org/nmap-dev/2009/q1/0918.html "non-Windows boxes (Samba on Linux/OS X, or a printer)"
  -- http://www.skullsecurity.org/blog/?p=209#comment-156
  --   "That means either it isn’t a Windows machine, or the service is
  --    either crashed or not running. That may indicate a failed (or
  --    successful) exploit attempt, or just a locked down system.
  --    NT_STATUS_OBJECT_NAME_NOT_FOUND can be returned if the browser
  --    service is disabled. There are at least two ways that can happen:
  --    1) The service itself is disabled in the services list.
  --    2) The registry key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Browser\Parameters\MaintainServerList
  --       is set to Off/False/No rather than Auto or yes.
  --    On these systems, if you reenable the browser service, then the
  --    test will complete."
  ["NT_STATUS_OBJECT_NAME_NOT_FOUND"] =
  [[UNKNOWN; not Windows, or Windows with disabled browser service (CLEAN); or Windows with crashed browser service (possibly INFECTED).
|  If you know the remote system is Windows, try rebooting it and scanning
|_ again. (Error NT_STATUS_OBJECT_NAME_NOT_FOUND)]],
  -- http://www.skullsecurity.org/blog/?p=209#comment-100
  --   "That likely means that the server has been locked down, so we
  --    don’t have access to the necessary pipe. Fortunately, that means
  --    that neither does Conficker — NT_STATUS_ACCESS_DENIED probably
  --    means you’re ok."
  ["NT_STATUS_ACCESS_DENIED"] =
  [[Likely CLEAN; access was denied.
|  If you have a login, try using --script-args=smbuser=xxx,smbpass=yyy
|  (replace xxx and yyy with your username and password). Also try
|_ smbdomain=zzz if you know the domain. (Error NT_STATUS_ACCESS_DENIED)]],
  -- The cause of these two is still unknown.
  -- ["NT_STATUS_NOT_SUPPORTED"] =
  -- [[]]
  -- http://thatsbroken.com/?cat=5 (doesn't seem common)
  -- ["NT_STATUS_REQUEST_NOT_ACCEPTED"] =
  -- [[]]
}

---Check if the server is infected with Conficker. This can be detected by a modified MS08-067 patch,
-- which rejects a different illegal string than the official patch rejects.
--
-- Based loosely on the Simple Conficker Scanner, found here:
-- http://iv.cs.uni-bonn.de/wg/cs/applications/containing-conficker/
--
-- If there's a licensing issue, please let me (Ron Bowes) know so I can fix it
--
--@param host The host object.
--@return (status, result) If status is false, result is an error code; otherwise, result is either
--        <code>INFECTED</code> or <code>INFECTED2</code> for infected or <code>CLEAN</code> for not infected.
function check_conficker(host)
  local status, smbstate
  local bind_result, netpathcompare_result

  -- Create the SMB session
  status, smbstate = msrpc.start_smb(host, "\\\\BROWSER", true)
  if(status == false) then
    return false, smbstate
  end

  -- Bind to SRVSVC service
  status, bind_result = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil)
  if(status == false) then
    msrpc.stop_smb(smbstate)
    return false, bind_result
  end

  -- Try checking a valid string to find Conficker.D
  local netpathcanonicalize_result, error_result
  status, netpathcanonicalize_result, error_result = msrpc.srvsvc_netpathcanonicalize(smbstate, host.ip, "\\")
  if(status == true and netpathcanonicalize_result['can_path'] == 0x5c45005c) then
    msrpc.stop_smb(smbstate)
    return true, INFECTED2
  end

  -- Try checking an illegal string ("\..\") to find Conficker.C and earlier
  status, netpathcanonicalize_result, error_result = msrpc.srvsvc_netpathcanonicalize(smbstate, host.ip, "\\..\\")

  if(status == false) then
    if(string.find(netpathcanonicalize_result, "INVALID_NAME")) then
      msrpc.stop_smb(smbstate)
      return true, CLEAN
    elseif(string.find(netpathcanonicalize_result, "WERR_INVALID_PARAMETER") ~= nil) then
      msrpc.stop_smb(smbstate)
      return true, INFECTED
    else
      msrpc.stop_smb(smbstate)
      return false, netpathcanonicalize_result
    end
  end

  -- Stop the SMB session
  msrpc.stop_smb(smbstate)

  return true, CLEAN
end

action = function(host)

  local status, result, message
  local response = {}
  local vuln_report = vulns.Report:new(SCRIPT_NAME, host)
  local vuln_table = {
    title = 'Microsoft Windows system infected by Conficker',
    IDS = {CVE = '2008-4250'},
    description = [[
This system shows signs of being infected by a variant of the worm Conficker.]],
    state = vulns.STATE.NOT_VULN,
    references = {
      'http://www.microsoft.com/security/portal/threat/encyclopedia/entry.aspx?Name=Win32%2fConficker',
      'https://technet.microsoft.com/en-us/library/security/ms08-067.aspx',
    }
  }

  -- Check for Conficker
  status, result = check_conficker(host)
  if(status == false) then
    vuln_table.extra_info = CONFICKER_ERROR_HELP[result] or "UNKNOWN; got error " .. result
    vuln_table.state = vulns.STATE.NOT_VULN
  else
    if(result == CLEAN) then
      vuln_table.state = vulns.STATE.NOT_VULN
    elseif(result == INFECTED) then
      vuln_table.extra_info = "Likely infected by Conficker.C or lower"
      vuln_table.state = vulns.STATE.LIKELY_VULN
    elseif(result == INFECTED2) then
      vuln_table.extra_info = "Likely infected by Conficker.D or higher"
      vuln_table.state = vulns.STATE.LIKELY_VULN
    else
      vuln_table.state = vulns.STATE.NOT_VULN
    end
  end
  return vuln_report:make_output(vuln_table)
end