SecurityTube Linux Assembly Expert (SLAE32) – Introduction

Nota: el contenido de esta entrada estará escrito en inglés con el objetivo de cumplir los requerimientos del examen para la obtención del certificado SLAE32 de Pentester Academy.

Note: content of this post will be written in English in order to be compliant and pass the SLAE32 certification exam brought by Pentester Academy.

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: PA-26078

GitHub repository: https://github.com/tdkmp4n4/SLAE32_Exam

Introduction

SecurityTube Linux Assembly Expert (SLAE) for 32-bit or x86 architecture is the final certificate of Pentester Academy course “x86 Assembly Language and Shellcoding on Linux” which will be earned by those students who want to pass an exam consisting on several assignments. The following blog posts were written by student SLAE-XXXX (David Álvarez Robles) in order to qualify for the certification:

Auxiliary scripts

Throughout all blog posts regarding SLAE32 certification, several auxiliary scripts were used in order to automate some usual tasks. This section describes all the scripts developed by the student and serves as a reference for all of them.

install_libemu.sh

This script is used in order to install Libemu tool on a fresh Ubuntu operating system install. It is a simple bash script which can be run under elevated privileges.

# Filename: install_libemu.sh
# Author: David Alvarez Robles (km0xu95)
# Website: https://blog.asturhackers.es

# Purpose: This script was developed in order to install libemu on a fresh
# Ubuntu operating system installation

apt-get install git autoconf libtool
git clone https://github.com/buffer/libemu
cd libemu
autoreconf -v -i
./configure --prefix=/opt/libemu; make install
cd tools/sctest
make

Reverse_Opcode.py

This script is used to prepare the sequence of push statements needed to push all the opcodes needed to run a shellcode on the stack. It is very useful for encoders and decoders, as well as for knowing how the stack is used and padded.

#!/usr/bin/python

# Filename: Reverse_Opcode.py
# Author: David Alvarez Robles (km0xu95)
# Website: https://blog.asturhackers.es

# Purpose: This script was developed in order to prepare opcodes to be pushed
# to the stack were dealing with syscalls on assembly code

import sys

if(len(sys.argv) != 2):
	print "Usage: ./Reverse_Opcode.py <opcode (no x)>"
	sys.exit(0)

else:
	opcodes = sys.argv[1]
	bytes = [opcodes[i:i+2] for i in range (0, len(opcodes), 2)]

	if len(opcodes) % 2 != 0:
		print "Opcode lenght not divisible by 2. Check it out"
		sys.exit(0)
	
	count = 0
	str = ""
	pushes = []
	
	for byte in bytes:
		if (count == 3):
			str = byte + str
			push = "push 0x"+str
			pushes.insert(0,push)
			str = ""
			count=0
		else:
			str = byte + str
			count+=1
	
	while((len(str)!=8 and str!="") and len(str)<=8):
		str = "90" + str

	if str!="":
		push = "push 0x"+str
		pushes.insert(0,push)

	for push in pushes:
		print push

Get-String-To-Stack.py

This script is used to prepare the sequence of push statements needed to push a string on the stack. It is very useful for syscalls where strings are passed as arguments, as well as for knowing how the stack is used and padded.

#!/usr/bin/python

# Filename: Get-String-To-Stack.py
# Author: David Alvarez Robles (km0xu95)
# Website: https://blog.asturhackers.es

# Purpose: This script was developed in order to prepare strings to be pushed
# to the stack were dealing with syscalls on assembly code

import sys

if(len(sys.argv) != 2):
	print "Usage: ./Get-String-To-Stack.py <String>"
	sys.exit(0)

input = sys.argv[1]

print '[*] String length : ' +str(len(input))

stringList = [input[i:i+4] for i in range(0, len(input), 4)]

for item in stringList[::-1] :
	print item[::-1] + '\t push 0x' + str(item[::-1].encode('hex'))

Get-Shellcode.py

This script is used to automate all the process of compiling and linking the assembly file, the extraction of the shellcode and its testing using a C proof-of-concept. Several system commands are executed in order to obtain all the information. The source code below performs the following tasks:

  • Compilation of assembly file using nasm resulting in an object file
  • Linking of the object file using ld resulting in a binary file
  • Shellcode visualization using objdump
  • Shellcode extraction using objdump
  • Creation of custom C proof-of-concept file to test the shellcode
  • Compilation and linking of C proof-of-concept file using GCC resulting in a binary file
#!/usr/bin/python

# Filename: Get-Shellcode.py
# Author: David Alvarez Robles (km0xu95)
# Website: https://blog.asturhackers.es

# Purpose: This script was developed in order to automate all the tasks related
# to assembly code compilation and linking. Moreover, this script will compile
# a C proof-of-concept program to test the shellcode dumped from the assembled
# file

import sys,os,time

def replace_line(filename, line, text):
	lines = open(filename, "r").readlines()
	lines[line] = text
	out = open(filename,"w")
	out.writelines(lines)
	out.close()

if(len(sys.argv) != 2):
	print "Usage: ./Get-Shellcode.py <Assembly File (.nasm)>"
	sys.exit(0)
else:
	print "[*] Trying to create object file from assembly code using nasm"
	ret = os.system("nasm -f elf32 -o "+sys.argv[1].split(".")[0]+".o "+sys.argv[1]+" > /dev/null 2>&1")
	if (ret!=0):
		print "[-] Failed to create object file from assembly code using nasm"
		sys.exit(0)
	else:
		time.sleep(2)
		print "[+] Done!"



	print "\n[*] Linking file using LD to create final executable"
	ret = os.system("ld -o "+sys.argv[1].split(".")[0]+" "+sys.argv[1].split(".")[0]+".o > /dev/null 2>&1")
	if (ret!=0):
		print "[-] Failed to link file using LD"
		sys.exit(0)
	else:
		time.sleep(2)
		print "[+] Done!"



	print "\n[*] Getting shellcode using objdump utility"
	print "[*] Check objdump raw output"
	ret = os.system("objdump -D -M intel -d ./"+sys.argv[1].split(".")[0]+" 2>&1")
	if (ret!=0):
		print "[-] Failed obtaining objdump raw output"
		sys.exit(0)
	else:
		time.sleep(2)

	print "\n[*] Check objdump shellcode extraction"
	command = "objdump -D -M intel -d ./"+sys.argv[1].split(".")[0]+"|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-9 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\\\x/g'|paste -d '' -s |sed 's/^/\"/'|sed 's/$/\"/g' > shellcode.txt"
 	ret = os.system(command)
	if (ret!=0):
		print "[-] Failed obtaining raw shellcode"
		sys.exit(0)
	else:
		time.sleep(2)
		f = open("shellcode.txt","r")
		shellcode = f.read()
		print "[+] Shellcode: "+shellcode

	command = "objdump -D -M intel -d ./"+sys.argv[1].split(".")[0]+"|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-9 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'| sed 's/ //g' | paste -d '' -s |sed 's/^/\"/'|sed 's/$/\"/g' > shellcode_opcode.txt"
 	ret = os.system(command)
	if (ret!=0):
		print "[-] Failed obtaining raw shellcode (opcodes)"
		sys.exit(0)
	else:
		time.sleep(2)
		f = open("shellcode_opcode.txt","r")
		shellcode_opcode = f.read()
		print "[+] Shellcode (opcode): "+shellcode_opcode
		print "[+] Done!"



	print "\n[*] Creating shellcode testing file"
	text = 'unsigned char code[] = '+shellcode.split("\n")[0]+';\n'
	replace_line("shellcode.c",3,text)
	os.system("cat shellcode.c")
	time.sleep(2)
	print "[+] Done!"



	print "\n[*] Building shellcode testing binary file"
 	ret = os.system("gcc -fno-stack-protector -zexecstack shellcode.c -o shellcode")
	if (ret!=0):
		print "[-] Failed building shellcode testing binary file"
		sys.exit(0)
	else:
		time.sleep(2)
		print "[+] Done!"

Get-AssemblyDefineBytes.py

This script is used to prepare the sequence of bytes to define them on assembly language. It is very useful for syscalls where strings are passed as variables.

#!/usr/bin/python

# Filename: Get-AssemblyDefineBytes.py
# Author: David Alvarez Robles (km0xu95)
# Website: https://blog.asturhackers.es

# Purpose: This script was developed in order to prepare opcodes to be defined
# on assembly code

import sys

if(len(sys.argv) != 2):
	print "Usage: ./Get-AssemblyDefineBytes.py <opcode (no x)>"
	sys.exit(0)

else:
	opcodes = sys.argv[1]
	print "\nOriginal shellcode: " + opcodes
	output = ""
	for i in range(0,len(opcodes),2):
		insertion = "0x"+opcodes[i:i+2]+", "
		output+=insertion

print "\n[+] Shellcode to be inserted: " + output[:-2]

~km0xu95