[Update 13 Jan 2018]

ExploitDB has published the 3 shellcodes written in this blog post.

Linux/x86-64 – Execute /bin/sh Shellcode (24 bytes)
Linux/x86-64 – Add Map ( google.lk) In /etc/hosts Shellcode (96 bytes)
Linux/x86-64 – execve(“/sbin/iptables”, [“/sbin/iptables”, “-F”], NULL) Shellcode (43 bytes)


Looking at the smallest x64 shellcodes (section Linux / Intel x86-64) in shell-storm‘s website, we find the following:

  • Linux/x86-64 – reboot(POWER_OFF) – 19 bytes by zbt
  • Linux/x86-64 – Execute /bin/sh – 27 bytes by Dad`
  • Linux/x86-64 – execve(/bin/sh); – 30 bytes by zbt
  • Linux/x86-64 – execve(/bin/sh, [/bin/sh], NULL) – 33 bytes by hophet
  • Linux/x86-64 – sethostname() & killall – 33 bytes by zbt

Due to similar functionalities of some, which makes it pointless to alter their bigger versions, I reduced the list to:

Because I didn’t want a painful debug/test process (as in a reboot after every shellcode execution), I decided to only keep the Linux/x86-64 – Execute /bin/sh – 27 bytes , while choosing other more interesting shellcodes:

There were a couple other shellcodes I noticed I not only had altered in the past but also shortened. The password protected reverse shell for example, I’d done a blog post on it already.

One note before starting to execute third party shellcode: only do this after fully understanding the code you’re compiling/executing, otherwise do it on a disposable VM/snapshot.


The original Execute /bin/sh – 27 bytes by Dad`

The original code:

Figure 1 – original smallest execve on shell-storm (Linux x86_64)

To call execve, one needs to invoke it and send the following parameters:

Figure 2 – execve(…) parameters at “$ man 2 execve”

I’ve already explained the x64 Linux calling convention on my first shellcode post, so suffice to say that:

  • rax = 59
  • rdi = “/bin/sh”
  • rsi = [“/bin/sh”, NULL byte]
  • rdx = 0

Notice that the 3rd parameter in the function call (envp) – stored in RDX register – can actually be ignored as per execve manual’s page ($man 2 execve):

Note, however, that the use of a third argument to the main function is not specified in POSIX.1; according to POSIX.1, the environment should be accessed via the external variable environ(7).

So a null byte is placed there (set to zero) instead of actually worrying about setting it to the environment variables address in memory.

One of the problems with setting a register with the value “/bin/sh” is that this string is exactly 7 bytes long. This means that, when the code is compiled into binary it will fill that missing byte with an understandable 0x00. This breaks one of the rules of shellcode development: no null bytes.

But there are a few tricks to overcome this problem. One of them is to use “//bin/sh” or “/bin//sh” which are equivalent to “/bin/sh”. Another trick, and the one used in the original code (Figure 1) is to set the register with the negative equivalent of the value it actually wants. And then negate it with the “neg” operator.

Note that this is not the same operation as the “not”. As the negative equivalent (neg) of 0 is still 0, while the bit flip (not) would be 0xffffffffffffffff.

You can see the negation occurring here:

Figure 3

The value in RBX after the negation is 0x68732f6e69622f which is “/bin/sh”:

Figure 4

The altered (and shorter) execve – 24 bytes

The code:

Figure 5 – altered and shorter execve
Figure 6 – shellcode executing


  • The original code uses RAX to store a zero value (Line 4 in Figure 1), which it ends up not using for the intended purposes (Line 12 in Figure 1), but instead using RDX. So basically the code is setting RAX with zero, and then setting it to 59 (0x3b). These two steps (adding up to 4 bytes in opcode) can be summarised into lines 4 and 5 in Figure 5 (3 bytes in opcode).
  • The original code also uses the “neg” operator, which consumes 3 bytes. The fact that Figure 5’s code is using a different technique (to avoid the null byte in the shellcode), makes it so we don’t have to use the “neg” operator, but instead a “push rdx” to have that “//bin/sh” string null terminated as required by the specification in execve man page. This push is only one byte long, saving another 2 bytes from the original code.


The original Linux/x86-64 – Add map in /etc/hosts file – 110 bytes

The original code:

Figure 7 – original shellcode that adds ‘ google.lk’ to the end of /etc/hosts file


The altered (and shorter) Add map in /etc/hosts with 96 bytes

The code:

Figure 8 – altered and shorter add map shellcode

State of /etc/hosts file before executing the shellcode:

Figure 9 – prior to executing the shellcode

And after:

Figure 10 – after executing the shellcode


The original execve(/sbin/iptables, [/sbin/iptables, -F], NULL) – 49 bytes

The original code:

Figure 11 – original shellcode that flushes the iptables firewall

The altered (and shorter) execve (iptables -F) with 43 bytes

The code:

Figure 12 – altered and shorter iptables flush shellcode

Iptables list before and after the shellcode execution:

Figure 13 – prior to shellcode execution, the firewall was blocking some traffic
Figure 14 – after execution, no more blocking rules exist



You can find all the files on my gitlab account.

Thanks to Vivek Ramachandran and the Pentester Academy team, as I have enjoyed every second of this course since I’ve learned so many interesting things, and all this inspired me to learn even more. Appreciate your work!

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:


Student ID: PA-2109