It’s been almost 6 years since I wrote the article to bypass split tunneling restriction on Juniper Network Connect. It’s now time to bypass the split tunneling restriction on VPNs running on Juniper Junos Pulse (or Pulse Secure).
The workaround for Junos Pulse (or Pulse Secure) is a lot more complicated compared to Network Connect. The DLL / EXE files are also now digitally signed and are checked for valid signatures whenever you logon to the VPN. Also, my method mentioned below may not be most optimal way to bypass the split tunneling restriction on Junos Pulse, but it has been working well for me so far, for the last few days at least (I only managed to get it working 3 days back!). Okay, let’s not waste any more time and get down to business.
Tools Required
You will need the following tools for this hack.
openssl
utility – This is used to generate the root CA and subordinate certificates. I have it installed under Cygwin.- IDA Pro – IDA Pro disassembler and decompiler
- SignTool – part of Windows SDK to digitally sign DLL / EXE files
- Hex Editor – To patch the binary files, I am using PsPad
- x86 opcode reference – I use the site x86asm.net for opcode reference.
All my work was done on a PC running on a 64-bit version of Windows 7.
Files to Patch
There are two files to be patched. One is dsAccessService.exe
, the other is dsTMService.dll
. The dsAccessService.exe
is the executable used for Windows Service. The dsTMService.dll
file is used to monitor the routes. The path for the files are:
1 |
C:\Program Files (x86)\Common Files\Juniper Networks\JUNS\dsAccessService.exe |
1 |
C:\Program Files (x86)\Common Files\Juniper Networks\Tunnel Manager\dsTMService.dll |
You can verify the location of the dsAccessService.exe
file by querying service JuniperAccessService
. You will get one of the two outputs below.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
C:\> sc qc JuniperAccessService [SC] QueryServiceConfig SUCCESS SERVICE_NAME: JuniperAccessService TYPE : 10 WIN32_OWN_PROCESS START_TYPE : 2 AUTO_START ERROR_CONTROL : 1 NORMAL BINARY_PATH_NAME : "C:\Program Files (x86)\Common Files\Juniper Networks\JUNS\dsAccessService.exe" LOAD_ORDER_GROUP : TDI TAG : 0 DISPLAY_NAME : Juniper Unified Network Service DEPENDENCIES : SERVICE_START_NAME : LocalSystem |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
C:\> sc qc JuniperAccessService [SC] QueryServiceConfig SUCCESS SERVICE_NAME: JuniperAccessService TYPE : 10 WIN32_OWN_PROCESS START_TYPE : 2 AUTO_START ERROR_CONTROL : 1 NORMAL BINARY_PATH_NAME : "C:\Program Files (x86)\Common Files\Juniper Networks\JUNS\dsAccessService.exe" LOAD_ORDER_GROUP : TDI TAG : 0 DISPLAY_NAME : Pulse Secure Network Service DEPENDENCIES : SERVICE_START_NAME : LocalSystem |
The dsAccessService.exe
file has code to check the digital signature of dsTMService.dll
. So once the file dsTMService.dll
is patched to ignore route changes, the signature verification will fail, and the VPN connection will fail.
Bypassing the signature check seem too troublesome, so I decided to resign the modified dsTMService.dll
file, but it again failed at signature verification; but this check is easier to bypass.
Generate Root CA and Subordinate Certificate
This process is only done once. Once the certificates are generated, you can reuse them again till they expire. To generate a root CA certificate, run openssl
as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$ openssl genrsa -out ca.key 4096 $ openssl req -new -x509 -days 3650 -key ca.key -out ca.crt You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [SG]:SG State or Province Name (full name) [Some-State]:Singapore Locality Name (eg, city) []:Singapore Organization Name (eg, company) [Internet Widgits Pty Ltd]:Digital Internals Organizational Unit Name (eg, section) []: Common Name (e.g. server FQDN or YOUR name) []:Digital Internals Email Address []: |
Enter any value when prompted.
Now generate the subordinate certificate. This time make sure you specify Juniper Networks, Inc.
when prompted for the names.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
$ openssl genrsa -out juniper-ia.key 4096 $ openssl req -new -key juniper-ia.key -out juniper-ia.csr You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [SG]:SG State or Province Name (full name) [Some-State]:Singapore Locality Name (eg, city) []:Singapore Organization Name (eg, company) [Internet Widgits Pty Ltd]:Juniper Networks, Inc. Organizational Unit Name (eg, section) []:Juniper Networks, Inc. Common Name (e.g. server FQDN or YOUR name) []:Juniper Networks, Inc. Email Address []: Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []: |
Next, sign the subordinate CA certificate with the root CA certificate.
1 2 3 4 |
$ openssl x509 -req -days 3650 -in juniper-ia.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out juniper-ia.crt Signature ok subject=/C=SG/ST=Singapore/L=Singapore/O=Juniper Networks, Inc./OU=Juniper Networks, Inc./CN=Juniper Networks, Inc. Getting CA Private Key |
Package the keys in PKCS12 format. This format is used by the Microsoft SignTool. It is not required to enter any password when prompted.
1 2 3 |
$ openssl pkcs12 -export -out juniper-ia.p12 -inkey juniper-ia.key -in juniper-ia.crt -chain -CAfile ca.crt Enter Export Password: Verifying - Enter Export Password: |
Goto Control Panel -> Internet Options -> Content -> Certificates -> Trusted Root Certification Authorities and click on the “Import” button. Select “ca.crt” file and install the certificate. Once installed, make sure you can view the certificate.
Patch dsAccessService.exe
Start IDA Pro, open the
dsAccessService.exe
file and generate the assembly code by going to File -> Produce File -> Create ASM File.Search for the string referece “
WinVerifyTrust() failed
“.1aWinverifytru_0 db 'WinVerifyTrust() failed, 0x%08X',0The string reference is
aWinverifytru_0
.Search for the string reference in the assembly code.
12345678910jz short loc_411ED7 ; patch to jmp instructionpush eaxpush offset aWinverifytru_0 ; "WinVerifyTrust() failed, 0x%08X"push offset aDshttpverifyfi ; "dsHttpVerifyFile()"mov [ebp+14Ch+var_15C], eaxpush 0D8hjmp loc_411E0Dloc_411ED7:mov eax, [ebp+14Ch+var_178]Look for a
jz
instruction above the string reference (highlighted above). This instruction has to be patched to become ajmp
instruction. Thejz
instruction has an opcode of 0x74 with arel8
offset. The correspondingjmp
instruction with arel8
offset has an opcode of 0xEB. Note that there are multiple opcodes for jz and jmp instructions. It depends on the operand size. In our case, the operand is rel8 or 8 bits forshort loc_411ED7
. If you check the values under Hex View in IDA Pro, the instruction takes just 2 bytes, which means the operand is only 1 byte or 8 bits.Opcode
0xE9
is also forjmp
instruction, but it’s for 16/32 bit operand and it’s not applicable in our case.- Get the hexadecimal data surrounding the jz instruction using the Hex View function of IDA Pro, open the file
dsAccessService.exe
in PsPad or some other hex editor and change thejz
(0x74
) instruction to ajmp
(0xEB
) instruction. After patching, the code will become like this:12345678910jmp short loc_411ED7 ; 74 18 patched to EB 18push eaxpush offset aWinverifytru_0 ; "WinVerifyTrust() failed, 0x%08X"push offset aDshttpverifyfi ; "dsHttpVerifyFile()"mov [ebp+14Ch+var_15C], eaxpush 0D8hjmp loc_411E0Dloc_411ED7:mov eax, [ebp+14Ch+var_178]
Patch dsTMService.dll
The dsTMService.dll
has to be patched at 3 places. Let’s look at the first patch.
Start IDA Pro, open the
dsTMService.dll
file and generate the assembly code.Search for the string reference
C_RouteTableMonitor::ThreadProc(): Route table notify event signaled
.123; char aC_routetablemo[]aC_routetablemo db 'C_RouteTableMonitor::ThreadProc(): 'db 'Route table notify event signaled.',0The string reference is
aC_routetablemo
.Search for the string reference in the assembly code.
1234567891011jz short loc_1006CA02 ; patch to jmp instructionpush offset aC_routetablemo ; "C_RouteTableMonitor::ThreadProc(): Rout"...push offset aRm ; "RM"push 105h ; intpush offset a_Win32Platform ; ".\\Win32\\platform.cpp"push 4 ; intcall sub_1007D736add esp, 14hloc_1006CA02: ; hEventpush [esp+84h+overlapped.hEvent]- Look for a
jz
instruction above the string reference (highlighted above). This instruction has to be patched to become ajmp
instruction –0x74
will become0xEB
. After patching, the code will become like this:1234567891011jmp short loc_1006CA02 ; 74 1E patched to EB 1Epush offset aC_routetablemo ; "C_RouteTableMonitor::ThreadProc(): Rout"...push offset aRm ; "RM"push 105h ; intpush offset a_Win32Platform ; ".\\Win32\\platform.cpp"push 4 ; intcall sub_1007D736add esp, 14hloc_1006CA02: ; hEventpush [esp+84h+overlapped.hEvent]
Now, for the second patch.
Search for the string reference
C_RouteTableMonitor::ThreadProc(): Address table notify event signaled
.1234; char aC_routetable_0[]aC_routetable_0 db 'C_RouteTableMonitor::ThreadProc(): 'db 'Address table notify event signaled'db '.',0The string reference is
aC_routetable_0
.Search for the string reference in the assembly code.
1234567891011jz short loc_1006CA64 ; patch to jmp instructionpush offset aC_routetable_0 ; "C_RouteTableMonitor::ThreadProc(): Addr"...push offset aRm ; "RM"push 13Ch ; intpush offset a_Win32Platform ; ".\\Win32\\platform.cpp"push 4 ; intcall sub_1007D736add esp, 14hloc_1006CA64: ; hEventpush [esp+84h+notifyOverlapped.hEvent]- Look for a
jz
instruction above the string reference (highlighted above). This instruction has to be patched to become ajmp
instruction –0x74
will become0xEB
. After patching, the code will become like this:1234567891011jmp short loc_1006CA64 ; 74 1E patched to EB 1Epush offset aC_routetable_0 ; "C_RouteTableMonitor::ThreadProc(): Addr"...push offset aRm ; "RM"push 13Ch ; intpush offset a_Win32Platform ; ".\\Win32\\platform.cpp"push 4 ; intcall sub_1007D736add esp, 14hloc_1006CA64: ; hEventpush [esp+84h+notifyOverlapped.hEvent]
Now, for the last patch.
Search for the string reference
C_RoutePolicy::IsConflicted(): Start policy monitoring
.123; char aC_routepolicyI[]aC_routepolicyI db 'C_RoutePolicy::IsConflicted(): Star'db 't policy monitoring.',0The string reference is “
aC_routepolicyI
“. Note that the string is broken up into multiple short strings.Search for the string reference in the assembly code.
123456789sub_10068EB1 proc near...cmp ecx, 1jnz short loc_10068EFBpush offset aC_routepolicyI ; "C_RoutePolicy::IsConflicted(): Start po"...push offset aRm ; "RM"push 58Dh ; intSearch for the function name above the string reference. Function names start with the string
sub_
. In our case, the function name issub_10068EB1
.- Look for a call instruction to that function. Below that function call, there will be test instruction. We will need to remove the
call
instruction, and set registeral
to0
. This will make sure that the nexttest
andjz
instructions will work as expected.1234567891011121314call sub_10068EB1 ; original opcode: E8 13 65 00 00; patch to nop,nop,nop,xor al,altest al, aljz short loc_10062A0Ccall sub_1006330Emov esi, [eax]test esi, esimov [esp+54h+var_38], esijz short loc_100629B7mov eax, [esi]push esicall dword ptr [eax+4]loc_100629B7:lea eax, [esp+54h+var_38]To set register al to 0, we can use the xor instruction.
1xor al, al ; set al to 0, opcode: 32 C0The
call
instruction occupies 5 bytes and thexor
instruction occupies 2 bytes. So we need to use thenop
(opcode:90
) instruction to fill the remaining 3 bytes. After patching, the code will become like this:
1234567891011121314151617nop ; opcode: 90nop ; opcode: 90nop ; opcode: 90xor al, al ; opcode: 32 C0test al, aljz short loc_10062A0Ccall sub_1006330Emov esi, [eax]test esi, esimov [esp+54h+var_38], esijz short loc_100629B7mov eax, [esi]push esicall dword ptr [eax+4]loc_100629B7:lea eax, [esp+54h+var_38]
Sign dsTMService.dll
Sign the modified dsTMService.dll
file using Microsoft SignTool with the generated certificate files.
1 |
C:\> signtool sign /v /t http://timestamp.verisign.com/scripts/timstamp.dll /f juniper-ia.p12 /d dsTMService.dll dsTMService.dll |
Once signed, check the digital signature properties of the file and make sure that the signature is valid. If it says that the signature cannot be verified, it means that the root CA has not been installed.
Overwrite Files
Stop the Junos Windows service, the Junos Pulse client, overwrite the two files, dsAccessService.exe
and dsTMService.dll
and restart the service.
Launch the Junos Pulse VPN client and connect to VPN. You should be able to modify routes now.
While connecting on a patched Pulse Secure installation, you may get a prompt saying: “An upgrade is available for Pulse Secure.” I did not get this prompt on Junos Pulse. Just click on cancel to proceed unless you are sure that it’s a genuine upgrade.
Final Words
I have not done much testing with other users for this workaround, so expect some hiccups. I have tested this workaround on version 5.0.4.45965 for both dsAccessService.exe
and dsTMService.dll
files.
Do also note that Junos Pulse product line is now owned and supported by Pulse Secure. So, the software client will be renamed to Pulse Secure. My VPN client just got upgraded to Pulse Secure the week after releasing this workaround.
Update (22-Mar-2015): Added information on Pulse Secure.
DISCLAIMER: THE ABOVE ARTICLE IS MEANT FOR EDUCATIONAL PURPOSES ONLY. I DO NOT PROMOTE OR SUPPORT ANY KIND OF REVERSE ENGINEERING OF COPYRIGHTED MATERIAL OR CONTENTS. THIS IS JUST A PROOF OF CONCEPT AND I AM NOT RESPONSIBLE FOR ANY LEGAL ISSUES YOU ENCOUNTER IF YOU FOLLOW THE ABOVE GUIDES/INSTRUCTIONS. I AM NOT RESPONSIBLE OF ANY OUTCOME, LOSS OF DATA OR ANY DAMAGES RESULTED DUE TO THE ACT OF YOU PROCEEDING WITH THE ABOVE GUIDES/INSTRUCTIONS.