OpenBSD Remote Exploit
”Only two remote holes in the default install” Alfredo A. Ortega
July 26, 2007
How the bug was found
Events: 1. January 16,2007: OpenBSD Team releases a patch for the IPv6 Stack titled “OpenBSD 008: RELIABILITY FIX”. (Infinite loop on kernel mode)
How the bug was found
Events: 1. January 16,2007: OpenBSD Team releases a patch for the IPv6 Stack titled “OpenBSD 008: RELIABILITY FIX”. (Infinite loop on kernel mode) 2. A research project was started to reproduce this vulnerability.
How the bug was found
Events: 1. January 16,2007: OpenBSD Team releases a patch for the IPv6 Stack titled “OpenBSD 008: RELIABILITY FIX”. (Infinite loop on kernel mode) 2. A research project was started to reproduce this vulnerability. 3. Because of the lack of information regarding the bug, a IPv6 fuzzer system was implemented. 4. The system Manually send fragmented IPv6 Packets containing differents headers. 5. A couple of lucky packet broke all versions of OpenBSD.
Mbuf buffer overflow
Buffer overflow Researching the “OpenBSD 008: RELIABILITY FIX” a new vulnerability was found: The m dup1() function causes an overflow on the mbuf structure, used by the kernel to store network packets.
Copy direction
mbuf1
mbuf2
mbuf3
mbuf4
End of overflow
Figure:
The function m freem() crashed...
mbuf chain overflow direction
Searching for a way to gain code execution
Searching for a way to gain code execution
C code equivalent
/ s y s / mbuf . h MEXTREMOVE(m) do { \ i f (MCLISREFERENCED(m) ) { \ MCLDEREFERENCE(m) ; \ } e l s e i f ( (m)−>m f l a g s & M CLUSTER) { \ p o o l p u t (& m c l p o o l , (m)−>m e x t . e x t b u f ) ; \ } e l s e i f ( (m)−>m e x t . e x t f r e e ) { \ ( ∗ ( (m)−>m e x t . e x t f r e e ) ) ( (m)−>m e x t . e x t b u f , \ (m)−>m e x t . e x t s i z e , (m)−>m e x t . e x t a r g ) ; \ } else { \ f r e e ( (m)−>m e x t . e x t b u f , (m)−>m e x t . e x t t y p e ) ; \ } \ (m)−>m f l a g s &= ˜ (M CLUSTER |M EXT ) ; \ (m)−>m e x t . e x t s i z e = 0 ; /∗ why ? ? ? ∗/ \ } w h i l e ( /∗ CONSTCOND ∗/ 0 )
#d e f i n e
IcmpV6 packets Attack vector We use two IcmpV6 packets as the attack vector
Mbuf chain Fragment 1
Fragment 2
IPv6 Header
IPv6 Header
Hop−by−Hop Header
Fragmentation Header
Fragmentation Header
Header
mbuf 1
Icmpv6 Icmpv6 Header Trampoline
ShellCode
Header
mbuf 2
SyscallHook Payload
Header
mbuf 3
Figure:
Detail of IcmpV6 fragments
Where are we?
Code execution We really don’t know where in kernel-land we are. But ESI is pointing to our code.
Final situation
Initial situation
User process
Kernel ?
? Ring 0
?
Ring 3
Hooked syscall
?
?
?
ESI
ShellCode
ShellCode ?
iret Int 0x80
? ?
?
?
Ring 0
Kernel
Where we are?
Figure:
Initial and final situations
Now what? Hook (remember DOS TSRs?) We hook the system call (Int 0x80)
Normal System Call
Hooked System Call
User process Ring 3 INT 0x80
User process Normal syscall
INT 0x80
return
Hooked syscall
return Kernel
Kernel Ring 0
Hook
Figure:
System call hook
Note: If the OS uses SYSENTER for system calls, the operation is slightly different.
New syscall pseudo-code
1. Get curproc variable (current process)
New syscall pseudo-code
1. Get curproc variable (current process) 2. Get user Id (curproc− >userID)
New syscall pseudo-code
1. Get curproc variable (current process) 2. Get user Id (curproc− >userID) 3. If userID == 0, the process is root : 3.1 3.2 3.3 3.4 3.5
Get LDT position Extend DS and CS on the LDT (This disables WˆX!) Copy the user-mode code to the the stack of the process Modify return address for the syscall to point to our code Restore the original Int 0x80 vector (remove the hook)
New syscall pseudo-code
1. Get curproc variable (current process) 2. Get user Id (curproc− >userID) 3. If userID == 0, the process is root : 3.1 3.2 3.3 3.4 3.5
Get LDT position Extend DS and CS on the LDT (This disables WˆX!) Copy the user-mode code to the the stack of the process Modify return address for the syscall to point to our code Restore the original Int 0x80 vector (remove the hook)
New syscall pseudo-code
1. Get curproc variable (current process) 2. Get user Id (curproc− >userID) 3. If userID == 0, the process is root : 3.1 3.2 3.3 3.4 3.5
Get LDT position Extend DS and CS on the LDT (This disables WˆX!) Copy the user-mode code to the the stack of the process Modify return address for the syscall to point to our code Restore the original Int 0x80 vector (remove the hook)
4. Continue with the original syscall
OpenBSD WˆX internals
WˆX: Writable memory is never executable i386: uses CS selector to limit the execution. To disable WˆX, we extend CS from ring0.
0x00000000
0xffffffff
4 GB
.text
.so
.so
heap
stack
512 MB User Code Segment (CS) User Data Segment (DS)
Figure:
Extension stack
OpenBSD selector scheme and extension
Extension
Defeating WˆX from ring0
Our algorithm, independent of the Kernel: sldt ax ; S t o r e LDT i n d e x on EAX sub esp , b y t e 0 x 7 f ; Store global d e s c r i p t o r table sgdt [ e s p +4] mov ebx , [ e s p +6] add esp , b y t e 0 x 7 f push eax ; Save l o c a l d e s c r i p t o r t a b l e i n d e x mov edx , [ ebx+eax ] mov ecx , [ ebx+eax+0x4 ] shr edx , 1 6 ; b a s e l o w−−>e dx mov eax , e c x shl eax , 2 4 ; b a s e m i d d l e −−> e dx shr eax , 8 or edx , eax mov eax , e c x ; b a s e h i g h −−> e dx and eax , 0 x f f 0 0 0 0 0 0 or edx , eax mov ebx , edx ; l d t −−> e bx ; E x t e n d CS s e l e c t o r or dword [ ebx+0x 1 c ] , 0 x 0 0 0 f 0 0 0 0 ; E x t e n d DS s e l e c t o r or dword [ ebx+0x24 ] , 0 x 0 0 0 f 0 0 0 0
Injected code WˆX will be restored on the next context switch, so we have two choices to do safe execution from user-mode: Creating a W+X section
Turning off W^X (from usermode)
From kernel...
From kernel...
User Stack
Ring 3
1. mprotect() 2.fork() 3.Standard user−mode code
mprotect() extends CS permanently
Figure:
Ring 3
Payload injection options
User Stack
1. fork() 2.mmap() 3.copy 4.jmp to mmap 5. Standard user−mode code
Questions before going on?
Now we are executing standard user-mode code, and the system has been compromised.
Proposed protection
Limit the Kernel CS selector The same strategy than on user-space. Used on PaX (http://pax.grsecurity.net) for Linux.
0x00000000
0xffffffff
4 GB 0xD1000000 kernel
0xD0000000
CS shrink
Kernel Code Segment (CS) Kernel Data Segment (DS)
Figure:
mbuf chains, etc
OpenBSD Kernel CS selector shrink
A third remote vulnerability?
IPv6 Routing Headers Uninitialized variable on the processing of IPv6 headers. 1. DoS or Code Execution (depending who you ask!) 2. Present on CVS from January to March of 2007 (very few systems affected) RCS f i l e : / u s r /OpenBSD/ c v s / s r c / s y s / n e t i n e t 6 / r o u t e 6 . c , v r e t r i e v i n g r e v i s i o n 1.13 r e t r i e v i n g r e v i s i o n 1.14 s w i t c h ( rh−>i p 6 r t y p e ) { c a s e IPV6 RTHDR TYPE 0 : + r h l e n = ( rh−>i p 6 r l e n + 1 ) << 3 ; i f ( rh−>i p 6 r s e g l e f t == 0 ) b r e a k ; /∗ F i n a l d s t . J u s t i g n o r e t h e h e a d e r . ∗/ − r h l e n = ( rh−>i p 6 r l e n + 1 ) << 3 ;
Conclusions
In this article we presented: 1. Generic kernel execution code and strategy 2. Possible security improvement of the kernel
Conclusions
In this article we presented: 1. Generic kernel execution code and strategy 2. Possible security improvement of the kernel 3. A third bug - No software is perfect
Final Questions?
Thanks to: Gerardo Richarte: Exploit Architecture Mario Vilas and Nico Economou: Coding support