lib/msf/core/post/windows/version.rb
# -*- coding: binary -*-
module Msf::Post::Windows::Version
include Msf::Post::Windows::Registry
class Error < RuntimeError
end
def initialize(info = {})
super(
update_info(
info,
'Compat' => {
'Meterpreter' => {
'Commands' => %w[
stdapi_railgun_api
]
}
}
)
)
end
def get_version_info
result = get_version_info_impl
if result.nil?
print_error("Couldn't retrieve the target's build number!")
raise Error, "Couldn't retrieve the target's build number!"
end
result
end
def get_version_info_fallback_impl
build_num_raw = cmd_exec('cmd.exe /c ver')
groups = build_num_raw.match(/Version\s+(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?/)
if groups.nil?
return nil
end
major, minor, build, revision = groups.captures
# Default to workstation, since it'll likely be an older OS - pre Server editions
return Msf::WindowsVersion.new(major.to_i, minor.to_i, build.to_i, 0, revision, Msf::WindowsVersion::VER_NT_WORKSTATION)
end
def get_version_info_impl
if session.type == 'meterpreter'
result = session.railgun.ntdll.RtlGetVersion(input_os_version_info_ex)
os_version_info_ex = unpack_version_info(result['VersionInformation'])
major = os_version_info_ex[1]
minor = os_version_info_ex[2]
build = os_version_info_ex[3]
service_pack = os_version_info_ex[6]
product_type = os_version_info_ex[9]
revision = 0
if (major >= 10)
revision = registry_getvaldata('HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion', 'UBR', Msf::Post::Windows::Registry::REGISTRY_VIEW_NATIVE)
end
Msf::WindowsVersion.new(major, minor, build, service_pack, revision, product_type)
else
# Command shell - we'll try reg commands, and fall back to `ver`
build_str = shell_registry_getvaldata('HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion', 'CurrentBuildNumber', Msf::Post::Windows::Registry::REGISTRY_VIEW_NATIVE)
if build_str.nil?
return get_version_info_fallback_impl
end
version_str = shell_registry_getvaldata('HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion', 'CurrentVersion', Msf::Post::Windows::Registry::REGISTRY_VIEW_NATIVE)
if version_str.nil?
return get_version_info_fallback_impl
end
build_num = build_str.to_i
version_match = version_str.match(/(\d+)\.(\d+)/)
if version_match.nil?
return get_version_info_fallback_impl
end
major, minor = version_match.captures
major = major.to_i
minor = minor.to_i
product = shell_registry_getvaldata('HKLM\SYSTEM\CurrentControlSet\Control\ProductOptions', 'ProductType', Msf::Post::Windows::Registry::REGISTRY_VIEW_NATIVE)
case product
when 'WinNT'
product_type = Msf::WindowsVersion::VER_NT_WORKSTATION
when 'LanmanNT'
product_type = Msf::WindowsVersion::VER_NT_DOMAIN_CONTROLLER
when 'ServerNT'
product_type = Msf::WindowsVersion::VER_NT_SERVER
else
product_type = Msf::WindowsVersion::VER_NT_WORKSTATION
end
if (major == 6) && (minor == 3) && (build_num > 9600) # 9600 is Windows 8.1 build number
# This is Windows 10+ - the version numbering is calculated differently
major = shell_registry_getvaldata('HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion', 'CurrentMajorVersionNumber', Msf::Post::Windows::Registry::REGISTRY_VIEW_NATIVE)
minor = shell_registry_getvaldata('HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion', 'CurrentMinorVersionNumber', Msf::Post::Windows::Registry::REGISTRY_VIEW_NATIVE)
ubr = shell_registry_getvaldata('HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion', 'UBR', Msf::Post::Windows::Registry::REGISTRY_VIEW_NATIVE)
if major.nil? || minor.nil? || ubr.nil?
return get_version_info_fallback_impl
end
Msf::WindowsVersion.new(major, minor, build_num, 0, ubr, product_type)
else
# Pre-Windows 10
service_pack_raw = shell_registry_getvaldata('HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion', 'CSDVersion', Msf::Post::Windows::Registry::REGISTRY_VIEW_NATIVE)
if service_pack_raw.nil? && (major >= 6)
# Some older versions didn't put the Service Pack value in both 32 and 64-bit versions of the registry - look there specifically
service_pack_raw = shell_registry_getvaldata('HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion', 'CSDVersion', Msf::Post::Windows::Registry::REGISTRY_VIEW_32_BIT)
end
service_pack = 0
unless service_pack_raw.nil?
match = service_pack_raw.match(/Service Pack (\d+)/)
unless match.nil?
service_pack = match[1].to_i
end
end
Msf::WindowsVersion.new(major, minor, build_num, service_pack, 0, product_type)
end
end
end
private
def empty_os_version_info_ex
[
0,
0,
0,
0,
0,
'',
0,
0,
0,
0,
0
]
end
def pack_version_info(info)
info.pack('VVVVVa256vvvCC')
end
def unpack_version_info(bytes)
bytes.unpack('VVVVVa256vvvCC')
end
def input_os_version_info_ex
input = empty_os_version_info_ex
size = pack_version_info(input).size
input[0] = size
pack_version_info(input)
end
end