首页
社区
课程
招聘
[原创]从ctf到realworld - CVE-2021-3156 分析复现
发表于: 2021-5-18 11:50 15788

[原创]从ctf到realworld - CVE-2021-3156 分析复现

2021-5-18 11:50
15788

Ubuntu 18.04

sudo-1.8.21p2

glibc-2.27

以该参数为例:

set args -s ‘\’ “AAAAAAAAAAAAAAAAAAA”

当form[0] == ‘\’时,from++ 当前字符串的结束符 ,to++ = from++ ,form指向下个字符串的起始位置(此时为A),while 循环内,将第二个字符串又写入to中,实际写入长度会超过堆块大小。

可利用点:

作者说有三种利用手段,我选择了可能最好复现的手段。

将堆块溢出到service_user 字段的name

nss_load_library 会使用该结构体来载入一个新的动态链接库。据说nss_load_librray在有堆溢出的情况下,常用于提权。之后可以继续深入研究该函数的调用过程和用处,

这个函数我们需要去命中

观察判断我们的目标在if (ni->library->lib_handle == NULL)内部,但是由于 ASLR 的存在,并且没有堆地址的泄露,所以我们没有办法确保ni->library->lib_handle 一定为空,但是在if (ni->library == NULL)的初始化情况,会新建一个service_user ,此时的ni->library->lib_handle 为空。

还有一个问题就是在载入过程中会遍历service_user 的next指针,所以不能随意将该字段写垃圾指针,应将其置为NULL。由于heap溢出可能会覆盖目标service_user结构体之前的service_user 导致遍历时出错。所以尽量覆盖到第一个service_user 来确保成功利用。

所以堆溢出覆盖service_user的数据为

在文章他们用名称为systemd的service_user ,从复现的角度来看我们也选择该名称的service_user ,当然也应该明白为什么,将这一步留到之后的分析中。
图片描述

在文章中作者,在内存空间搜索了“systemd ” 和 “mymachine”来定位待溢出的目标,但是我们通过搜索字符串我们发现只能找到systemd的堆块。

我们知道service_user 是个单链表,这里通过next指针将这条service_user 链表还原。
图片描述

0x563f627d6c58该地址并非一个堆块地址,所以猜测是一个存放service_user *的堆空间那么这个链有两个结构。

而name == “systemd “的第二个service_user链表,链上仅有一个结构体。

但是调试exp时发现,实际使用的溢出的目标堆块地址均不在这两条链上,可能在后期研究nss_load_library 时,会得到解答。也可以通过next指针回溯当时堆块所在的service_user 链。
图片描述

由于name=”x/x”

按照拼接规则
图片描述

实际会加载libnss_x/s.so.2 的动态链接库, 动态链接库代码:

本次复现还残留了不少问题

本次复现算是踏上了从ctf到真实环境漏洞的第一步, 希望锲而不舍。

https://www.kalmarunionen.dk/writeups/sudo/
https://blog.qualys.com/vulnerabilities-research/2021/01/26/cve-2021-3156-heap-based-buffer-overflow-in-sudo-baron-samedit
https://github.com/Rvn0xsy/CVE-2021-3156-plus

 
 
static int
set_cmnd(void){
    .....
    //获取所有命令行参数的长度
    for (size = 0, av = NewArgv + 1; *av; av++)
    size += strlen(*av) + 1;
    if (size == 0 || (user_args = malloc(size)) == NULL) {
    sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
    debug_return_int(-1);
    }
    //将命令行参数复制到user_args 。
    for (to = user_args, av = NewArgv + 1; (from = *av); av++) {
        while (*from) {
        //漏洞点,当以反斜杠结尾时造成heap overflow
        if (from[0] == '\\' && !isspace((unsigned char)from[1]))
            from++;
        *to++ = *from++;
        }
        *to++ = ' ';
    }
    *--to = '\0';
static int
set_cmnd(void){
    .....
    //获取所有命令行参数的长度
    for (size = 0, av = NewArgv + 1; *av; av++)
    size += strlen(*av) + 1;
    if (size == 0 || (user_args = malloc(size)) == NULL) {
    sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
    debug_return_int(-1);
    }
    //将命令行参数复制到user_args 。
    for (to = user_args, av = NewArgv + 1; (from = *av); av++) {
        while (*from) {
        //漏洞点,当以反斜杠结尾时造成heap overflow
        if (from[0] == '\\' && !isspace((unsigned char)from[1]))
            from++;
        *to++ = *from++;
        }
        *to++ = ' ';
    }
    *--to = '\0';
 
 
 
 
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 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;
static int
nss_load_library (service_user *ni)
{
  if (ni->library == NULL)
    {
      static name_database default_table;
      ni->library = nss_new_service (service_table ?: &default_table,
                     ni->name);
      if (ni->library == NULL)
    return -1;
    }
 
  if (ni->library->lib_handle == NULL)
    {
      /* Load the shared library.  */
      size_t shlen = (7 + strlen (ni->name) + 3
              + strlen (__nss_shlib_revision) + 1);
      int saved_errno = errno;
      char shlib_name[shlen];
 
      //构建动态库的名称 libnss_*.so
      __stpcpy (__stpcpy (__stpcpy (__stpcpy (shlib_name,
                          "libnss_"),
                    ni->name),
              ".so"),
        __nss_shlib_revision);
 
      ni->library->lib_handle = __libc_dlopen (shlib_name);
      if (ni->library->lib_handle == NULL)
    {
      /* Failed to load the library. Try a fallback.  */
      int n = __snprintf(shlib_name, shlen, "libnss_%s.so.%d.%d",
               ni->library->name, __GLIBC__, __GLIBC_MINOR__);
      if (n >= shlen)
        ni->library->lib_handle = NULL;
      else
        ni->library->lib_handle = __libc_dlopen (shlib_name);
 
      if (ni->library->lib_handle == NULL)
        {
          /* Ok, really fail now.  */
          ni->library->lib_handle = (void *) -1l;
          __set_errno (saved_errno);
        }
    }
static int
nss_load_library (service_user *ni)
{
  if (ni->library == NULL)
    {
      static name_database default_table;
      ni->library = nss_new_service (service_table ?: &default_table,
                     ni->name);
      if (ni->library == NULL)
    return -1;
    }
 
  if (ni->library->lib_handle == NULL)
    {
      /* Load the shared library.  */
      size_t shlen = (7 + strlen (ni->name) + 3
              + strlen (__nss_shlib_revision) + 1);
      int saved_errno = errno;
      char shlib_name[shlen];
 
      //构建动态库的名称 libnss_*.so

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 5
支持
分享
最新回复 (2)
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
为啥搜索service_user结构用search  systemd或者是 mymachine额
2021-12-12 16:27
0
雪    币: 463
活跃值: (2696)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
3
mb_oexecrwb 为啥搜索service_user结构用search systemd或者是 mymachine额
库会提供这些东西。
2021-12-20 16:48
0
游客
登录 | 注册 方可回帖
返回
//