reverse engineering mac os x.pdf

54
Reverse Engineering Mac OS X Reverse Engineering and Security for fun and pleasure! Skip to content About Archives Books Crackmes Gdbinit Github Links Papers Patches Tags Tools Search for: Gatekeerper – A kernel extension to mitigate Gatekeeper bypasses November 9, 2015Security, Toolstrustedbsd Last month Patrick Wardle presented “Exposing Gatekeeper” at VB2015 Prague. The core of the presentation deals with Gatekeeper bypasses originating in the fact that Gatekeeper only verifies the code signatures of the main binary and not of any linked libraries/frameworks/bundles. This means it is possible to run unsigned code using dynamic library hijacking techniques also presented by Patrick in code that should be protected by Gatekeeper. His exploit uses an Apple code signed application that is vulnerable to dylib hijacking and his modified to run unsigned code when downloaded from the Internet. In this scenario Gatekeeper enters into action and should verify if the download is code signed (assuming the default OS X scenario where it is enabled). But in this case Gatekeeper will fail to verify the linked code and effectively is bypassed. The core of the problem is that Gatekeeper only deals with the main binary code, and never verifies any linked code. This is obviously a flaw and hopefully a fix by Apple should be out sooner or later. Meanwhile we can try to build ourselves a fix using the TrustedBSD framework. For this I created Gatekeerper, a proof of concept kernel extension for Yosemite 10.10.5 (can be easily adapted to work with El Capitan, but I don’t want to release that code).

Upload: uploaderboy

Post on 19-Feb-2016

117 views

Category:

Documents


8 download

TRANSCRIPT

Page 1: Reverse Engineering Mac OS X.pdf

Reverse Engineering Mac OS X Reverse Engineering and Security for fun and pleasure!

Skip to content

About Archives Books Crackmes Gdbinit Github Links Papers Patches Tags Tools

Search for:

Gatekeerper – A kernel extension to mitigate Gatekeeper bypasses November 9, 2015Security, Toolstrustedbsd

Last month Patrick Wardle presented “Exposing Gatekeeper” at VB2015 Prague.

The core of the presentation deals with Gatekeeper bypasses originating in the fact that Gatekeeper

only verifies the code signatures of the main binary and not of any linked

libraries/frameworks/bundles.

This means it is possible to run unsigned code using dynamic library hijacking techniques also

presented by Patrick in code that should be protected by Gatekeeper. His exploit uses an Apple code

signed application that is vulnerable to dylib hijacking and his modified to run unsigned code when

downloaded from the Internet. In this scenario Gatekeeper enters into action and should verify if the

download is code signed (assuming the default OS X scenario where it is enabled). But in this case

Gatekeeper will fail to verify the linked code and effectively is bypassed.

The core of the problem is that Gatekeeper only deals with the main binary code, and never verifies

any linked code. This is obviously a flaw and hopefully a fix by Apple should be out sooner or later.

Meanwhile we can try to build ourselves a fix using the TrustedBSD framework. For this I created

Gatekeerper, a proof of concept kernel extension for Yosemite 10.10.5 (can be easily adapted to

work with El Capitan, but I don’t want to release that code).

Page 2: Reverse Engineering Mac OS X.pdf

What it does is to verify all executable code that is being mapped into the main process, and if it’s not

code signed the application will not run. This only happens if the main binary is signed, since if it was

unsigned there is really no purpose in verifying signed linked code – the main binary can already be

modified at will and/or it wouldn’t bypass Gatekeeper (assuming no other Gatekeeper bypass exploit).

It can also be easily modified to not allow any kind of unsigned code to run.

From my perspective this is what Apple fix should do, verify the code signature of all executable code

being loaded into a binary protected by Gatekeeper, and kill process if unsigned code is loaded. This

is not exactly the same as refusing to run any unsigned code as in iOS. We are just talking about

Gatekeeper feature – the protection of (Internet) downloaded applications.

Apple could go the extra mile and implement a configuration option to refuse to run any unsigned

code as it happens in iOS. Such implementation would be less secure than iOS counterpart mainly

because there’s no trusted boot chain, kernel extensions can be loaded, and so on. Like rootless,

such feature would be one kernel exploit away from being disabled. By default this feature could be

off and advanced users could enable it if they wish to do so. Rootless introduced new paradigms for

Apple and I guess this type of option is acceptable in that “vision”.

How is Gatekeerper implemented?

My PoC is based on the fact that dyld (the linker) will be responsible for mmap’ing the linked code –

libraries, frameworks, bundles. If we browse dyld source code we can see the following code

responsible for mapping executable segments (there are different code paths due to cached libraries

and so on):

void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat,

uint64_t fileLen, const LinkContext& context)

{

// find address range for image

intptr_t slide = this->assignSegmentAddresses(context);

if ( context.verboseMapping )

dyld::log("dyld: Mapping %s\n", this->getPath());

// map in all segments

Page 3: Reverse Engineering Mac OS X.pdf

for(unsigned int i=0, e=segmentCount(); i < e; ++i) { (...) // wholly zero-

fill segments have nothing to mmap() in if ( size > 0 ) {

if ( (fileOffset+size) > fileLen ) {

dyld::throwf("truncated mach-o error: segment %s extends

to %llu which is past end of file %llu",

segName(i),

(uint64_t)(fileOffset+size), fileLen);

}

void* loadAddress = mmap((void*)requestedLoadAddress, size,

protection, MAP_FIXED | MAP_PRIVATE, fd, fileOffset);

if ( loadAddress == ((void*)(-1)) ) {

dyld::throwf("mmap() error %d at address=0x%08lX,

size=0x%08lX segment=%s in Segment::map() mapping %s",

errno, requestedLoadAddress, (uintptr_t)size,

segName(i), getPath());

}

}

(...)

}

// update slide to reflect load location

this->setSlide(slide);

}

The TrustedBSD framework contains a useful mmap hook:

Page 4: Reverse Engineering Mac OS X.pdf

/**

@brief Access control check for mapping a file

@param cred Subject credential

@param fg fileglob representing file to map

@param label Policy label associated with vp

@param prot mmap protections; see mmap(2)

@param flags Type of mapped object; see mmap(2)

@param maxprot Maximum rights

Determine whether the subject identified by the credential should be

allowed to map the file represented by fg with the protections specified

in prot. The maxprot field holds the maximum permissions on the new

mapping, a combination of VM_PROT_READ, VM_PROT_WRITE, and VM_PROT_EXECUTE.

To avoid overriding prior access control checks, a policy should only

remove flags from maxprot.

@return Return 0 if access is granted, otherwise an appropriate value for

errno should be returned. Suggested failure: EACCES for label mismatch or

EPERM for lack of privilege.

*/

typedef int mpo_file_check_mmap_t(

kauth_cred_t cred,

Page 5: Reverse Engineering Mac OS X.pdf

struct fileglob *fg,

struct label *label,

int prot,

int flags,

int *maxprot

);

Essentially this hook allows a kernel extension to control what is mmap’ed into a process, which is

perfect to control what dyld tries to load into a process.

The TrustedBSD hook in kernel’s mmap implementation can be see here:

#if CONFIG_MACF

error = mac_file_check_mmap(vfs_context_ucred(ctx),

fp->f_fglob, prot, flags, &maxprot);

if (error) {

(void)vnode_put(vp);

goto bad;

}

#endif /* MAC */

This is precisely one of the tasks of the infamous AMFI – it implements a hook here

called file_check_mmap. You can disassembleAppleMobileFileIntegrity kernel extension and see

this and other AMFI hooks. The AMFI El Capitan version is more complete than the Yosemite

version. For example there is new code that allows the linker (dyld) to reject any code that is not

signed by the same Team ID if the main binary has a special codesign flag CS_REQUIRE_LV. An

example of this flag implementation can be found in XCode 7.x. If you try to inject a dynamic library

into XCode 7.x the process will be killed. Recent Pangu Team presentation at Ruxcon 2015 also

discusses this new feature. I guess this new code originated from iOS code base due to frequent

code injection abuse by iOS jailbreaks.

Page 6: Reverse Engineering Mac OS X.pdf

The AMFI code contains two interesting code signing related

functions, csfg_get_teamid and csfg_get_platform_binary. They allow to retrieve team id and code

signing information from the a file located in the filesystem. The problem is that they aren’t adequate

to my purposes – what I want to know is if the executable code is signed or not. In a bit I’ll explain

why those functions fail in some cases.

Unfortunately for us the code signing features available inside XNU kernel are very poor and most

aren’t even available in KPIs (El Capitan adds more functions but still not enough). This is definitely

an area I would love Apple to improve, to create a robust set of code signing KPIs so developers

could use them to develop their own security solutions instead of requiring all kinds of potentially

unstable tricks and hooks.

Apple, let’s finally assume security as a priority and that (some) developers are interested in

developing security solutions. It doesn’t signal your security is bad, everyone already knows that it is

so the step forward is to improve it (which you already are but more is required). Pretty

please?

To understand my solution for code signature detection let me show

you csfg_get_platform_binary source code from XNU kernel:

/*

* Function: csfg_get_platform_binary

*

* Description: This function returns the

* platform binary field for the

* fileglob fg

*/

int

csfg_get_platform_binary(struct fileglob *fg)

{

Page 7: Reverse Engineering Mac OS X.pdf

int platform_binary = 0;

struct ubc_info *uip;

vnode_t vp;

if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE)

return 0;

vp = (struct vnode *)fg->fg_data;

if (vp == NULL)

return 0;

vnode_lock(vp);

if (!UBCINFOEXISTS(vp))

goto out;

uip = vp->v_ubcinfo;

if (uip == NULL)

goto out;

if (uip->cs_blobs == NULL)

goto out;

Page 8: Reverse Engineering Mac OS X.pdf

/* It is OK to extract the teamid from the first blob

because all blobs of a vnode must have the same teamid */

platform_binary = uip->cs_blobs->csb_platform_binary;

out:

vnode_unlock(vp);

return platform_binary;

}

The return value of this function is zero if target binary is not a platform binary and one if it is. A binary

can only be (potentially) considered a platform binary if it is code signed, otherwise it will never be.

This is part of what we want – to know if something is code signed or not. The problem is that a

binary can be code signed and not be a platform binary.

What is considered a platform binary?

It is a binary signed by Apple that is located in certain system paths:

“/private/var/db/dyld/dyld_shared_cache_”

“/usr/lib/”

“/usr/libexec/”

“/System/Library/”

“/usr/bin/”

“/bin/”

“/sbin/”

“/usr/sbin/”

This decision also happens inside AMFI in the vnode_check_signature hook. The hook is triggered

from kernel function ubc_cs_blob_add:

/*

* Let policy module check whether the blob's signature is accepted.

*/

#if CONFIG_MACF

Page 9: Reverse Engineering Mac OS X.pdf

error = mac_vnode_check_signature(vp, base_offset, blob->csb_sha1, (const

void*)cd, size, &is_platform_binary);

if (error) {

if (cs_debug)

printf("check_signature[pid: %d], error = %d\n",

current_proc()->p_pid, error);

goto out;

}

#endif

So our problem using this function is that we can’t really distinguish between unsigned and signed

code – non platform binary code signed applications will return zero on this function. But this function

is perfect if we modify it to something like:

if (uip->cs_blobs == NULL)

goto out;

else

platform_binary = 1;

out:

vnode_unlock(vp);

return platform_binary;

This small modification guarantees that it will always return zero if the code is not signed, and one if

the code is signed – cs_blobs will never be NULL in this case.

My solution is to clone this function, modify it, and then use it in our own hook to verify if target code

is signed or not.

Let’s observe the disassembly output of this function to understand the necessary modifications:

Page 10: Reverse Engineering Mac OS X.pdf

_text:FFFFFF80007A7AA0 public

_csfg_get_platform_binary

__text:FFFFFF80007A7AA0 _csfg_get_platform_binary proc near

__text:FFFFFF80007A7AA0 55 push rbp

__text:FFFFFF80007A7AA1 48 89 E5 mov rbp, rsp

__text:FFFFFF80007A7AA4 41 56 push r14

__text:FFFFFF80007A7AA6 53 push rbx

__text:FFFFFF80007A7AA7 48 8B 47 28 mov rax,

[rdi+28h]

__text:FFFFFF80007A7AAB 31 DB xor ebx, ebx

__text:FFFFFF80007A7AAD 83 38 01 cmp dword ptr

[rax], 1

__text:FFFFFF80007A7AB0 75 3A jnz short

loc_FFFFFF80007A7AEC

__text:FFFFFF80007A7AB2 4C 8B 77 38 mov r14,

[rdi+38h]

__text:FFFFFF80007A7AB6 4D 85 F6 test r14, r14

__text:FFFFFF80007A7AB9 74 31 jz short

loc_FFFFFF80007A7AEC

__text:FFFFFF80007A7ABB 4C 89 F7 mov rdi, r14

__text:FFFFFF80007A7ABE E8 2D 2D C6 FF call _lck_mtx_lock

; - fix relocation

__text:FFFFFF80007A7AC3 31 DB xor ebx, ebx

__text:FFFFFF80007A7AC5 41 0F B7 46 68 movzx eax, word ptr

[r14+68h]

Page 11: Reverse Engineering Mac OS X.pdf

__text:FFFFFF80007A7ACA 83 F8 01 cmp eax, 1

__text:FFFFFF80007A7ACD 75 15 jnz short

loc_FFFFFF80007A7AE4

__text:FFFFFF80007A7ACF 49 8B 46 70 mov rax,

[r14+70h]

__text:FFFFFF80007A7AD3 48 85 C0 test rax, rax

__text:FFFFFF80007A7AD6 74 0C jz short

loc_FFFFFF80007A7AE4

__text:FFFFFF80007A7AD8 48 8B 40 50 mov rax,

[rax+50h]

__text:FFFFFF80007A7ADC 48 85 C0 test rax, rax

__text:FFFFFF80007A7ADF 74 03 jz short

loc_FFFFFF80007A7AE4

__text:FFFFFF80007A7AE1 8B 58 68 mov ebx,

[rax+68h] ; - modify here

__text:FFFFFF80007A7AE4

__text:FFFFFF80007A7AE4 loc_FFFFFF80007A7AE4:

; CODE XREF: _csfg_get_platform_binary+2D j

__text:FFFFFF80007A7AE4

; _csfg_get_platform_binary+36 j ...

__text:FFFFFF80007A7AE4 4C 89 F7 mov rdi, r14

__text:FFFFFF80007A7AE7 E8 04 33 C6 FF call

_lck_mtx_unlock ; - fix relocation

__text:FFFFFF80007A7AEC

__text:FFFFFF80007A7AEC loc_FFFFFF80007A7AEC:

; CODE XREF: _csfg_get_platform_binary+10 j

Page 12: Reverse Engineering Mac OS X.pdf

__text:FFFFFF80007A7AEC

; _csfg_get_platform_binary+19 j

__text:FFFFFF80007A7AEC 89 D8 mov eax, ebx

__text:FFFFFF80007A7AEE 5B pop rbx

__text:FFFFFF80007A7AEF 41 5E pop r14

__text:FFFFFF80007A7AF1 5D pop rbp

__text:FFFFFF80007A7AF2 C3 retn

__text:FFFFFF80007A7AF2 _csfg_get_platform_binary endp

The first thing we need is to modify the return value. The code at 0xFFFFFF80007A7AE1 is

responsible for moving the value of csb_platform_binaryinto platform_binary variable

(platform_binary = uip->cs_blobs->csb_platform_binary;). Instead of this we can move one

(platform_binary = 1;), meaning that cs_blobs isn’t NULL and code is signed. The value in EBX is the

value of platform_binary variable so the necessary code modification is just a “inc ebx” to replace

the original mov (original instruction is three bytes, new one is two so remaining byte is patched into a

NOP).

The next step is to fix the two function calls to the locks (lck_mtx_lock and lck_mtx_unlock), since

they are RIP relative and our copy is located in a different memory address. We simply need to

compute the distance from our copy to those kernel functions and update the offset values in both

calls. And voilá, with three simple patches we have a perfect working cloned function that does what

we need.

The modification to csproc_get_teamid is necessary to return if current process (aka main binary) is

code signed or not. The original function returns a pointer but I modified the clone function to return

an int value – zero if main binary is not signed, one otherwise. This function is used to decide if we

want to verify the linked libraries or not – if main binary is not code signed there is no interested in

verifying any linked code. I use this function because it contains everything I need and is easy to

modify for my purposes.

After we have working cloned functions that allow us to retrieve the code signing status of the main

process and any code that dyld wants to mmap, the last step is to replace the original AMFI hook with

my version.

What my version does is to verify if current process is code signed. If that is true then it will verify if all

executable code is code signed or not. If something is not code signed that it will refuse to mmap and

Page 13: Reverse Engineering Mac OS X.pdf

process will crash (an alternative is to kill the process right there). After this the control is passed back

to the original AMFI hook.

This means that I am replacing the original AMFI hook with my own copy. I can’t really remember the

reason why I did this (I used this solution for something else so I just reused it here). I guess in this

particular case we could just create a new hook after AMFI and verify there, instead of playing some

pointers exchange games with AMFI. Maybe you should try to implement this that way.

To make everything easier I hardcoded some space for cloned functions inside the Gatekeerper

kernel extension. This makes everything easier because it avoids intermediate jump islands in case

the allocated memory doesn’t fit in a 32 bit offset (check my SyScan rootkits presentation/codefrom

this year). Because kernel extensions are guaranteed to always be at a 32 bit offset distance from the

kernel it’s very easy to fix the kernel symbols used in the cloned functions.

The PoC code only works with Yosemite 10.10.5 build 14F27. The reason is that the offsets for the

cloned functions are hardcoded and not dynamically discovered. This could be done with a

disassembler, but this is just a PoC and I’m not working for free ;-). To make it work with El Capitan is

just a bit of extra work away.

You can find the code at Gatekeerper Github repo.

I can’t remember why I named it Gatekeerper. JP says it’s the natural evolution from the worm image

I frequently use. Sounds like robust logic!

Have fun,

fG!

https://reverse.put.as/2015/11/09/gatekeerper-a-kernel-extension-to-mitigate-gatekeeper-bypasses/#more-2491

Rootfool – a small tool to dynamically disable and enable SIP in El Capitan October 12, 2015Tools

El Capitan is finally released and System Integrity Protection aka SIP aka rootless is finally a reality

we must face. Let me briefly describe SIP (technical details maybe in another post, now that El

Capitan is final and out of NDAs). This post by Rich Trouton contains a very good description of its

userland implementation and configuration.

What is SIP anyway?

Page 14: Reverse Engineering Mac OS X.pdf

The description that I like to use is that SIP is a giant system-wide sandbox, that controls access to

what Apple considers critical files and folders. One of the reasons for this is that most of kernel side

SIP implementation exists into the Sandbox.kext, the same TrustedBSD kernel extensions that

implements OS X sandbox mechanism.

For example, if we try to write to /System folder we get the following result:

sh-3.2# touch /System/test

touch: /System/test: Operation not permitted

And in system logs:

12/10/15 17:27:20,650 sandboxd[120]: ([424]) touch(424) System Policy: deny file-write-

create /System/test

In practice it means that even with root access we are unable to modify those critical files and folders.

But, how are updates possible?

Apple created a few new private entitlements (only available to binaries signed with Apple certificates)

that allow binaries that have them to modify the files and folders. This is one of the possible attack

points against SIP – find a vulnerability in the right entitled binary and you can do whatever you want

to the system.

Can SIP be disabled?

Yes, there is an util called csrutil that can be used to update its settings. It must be run in Recovery or

Install mode. Once again refer to Rich Trouton’spost for more details.

Are there any other ways to disable it?

Yes. If we are able to run kernel code we can disable SIP. For example, we can mess with the

TrustedBSD hooks and remove them out of the way. Or attack the hook decision engine and always

authorize, and so on. Once you are able to run code at the same privilege lowest level that exists in

the machine there’s very few that can protect you, if anything (reference, PatchGuard kernel

implementation by Microsoft, bypassed a few times and which is starting to move to hypervisor level).

This doesn’t mean that SIP is necessarily a bad idea. Apple’s goal is to increase the amount of work

required by an attacker and reduce attack surface. The problem with this strategy are too many

issues in OS X kernel, but that’s another discussion.

Page 15: Reverse Engineering Mac OS X.pdf

Hacking is mostly about posing questions that break out someone’s else assumptions. So the other

day I was thinking if there was an easier way to disable SIP other than attacking the hooks and so on

(I did that before on beta releases). Because there is an option that is read from NVRAM about

disabling SIP, this gives us a signal that there is a kernel decision somewhere about

enabling/disabling SIP (because EFI has nothing to do on this decision). Essentially we are chasing

either a variable or a classical cracking je/jne decision.

How is csrutil implemented?

If we disassemble csrutil we will see the following flow:

csrutil -> csr_get_active_config() -> _csrctl() -> syscall 0x1E3 -> csrctl()

So essentially there’s a new syscall 0x1E3 that implements a new function csrctl in the kernel, that

csrutil uses to retrieve the active SIP configuration. If there’s info about active configuration there’s

information somewhere about its status. And this info can be found at address

0xFFFFFF8000ACFF48 (XNU 10.11.0 build 15A284). There are a few references to this address, all

from functions with interesting and very descriptive names. If you disassemble them you will

understand that this variable will be the jackpot we were looking for. It will enable or disable SIP.

To make something runtime we just need to find the address of this variable and modify it as we wish

to disable or enable SIP. To find its location we can disassemble the simplest function that references

it and find its value, or just hardcode it.

Is there an easy way?

Of course there is, else this question would be meaningless ;-).

Look at the function names…

__text:FFFFFF80007835C0 public _csr_set_allow_all

__text:FFFFFF80007835C0 _csr_set_allow_all proc near

__text:FFFFFF80007835C0 push rbp

__text:FFFFFF80007835C1 mov rbp, rsp

__text:FFFFFF80007835C4 test edi, edi

__text:FFFFFF80007835C6 setnz al

__text:FFFFFF80007835C9 movzx eax, al

__text:FFFFFF80007835CC mov cs:dword_FFFFFF8000ACFF48, eax

__text:FFFFFF80007835D2 pop rbp

Page 16: Reverse Engineering Mac OS X.pdf

__text:FFFFFF80007835D3 retn

__text:FFFFFF80007835D3 _csr_set_allow_all endp

This function does exactly what we are looking for. It will modify the SIP status based on the value

that we pass on its argument. To disable SIP call it with 1, to enable with 0. The function has zero

references inside the kernel, meaning that either no other function is calling it or a function pointer is

used. It can be found in the Private.kext kernel KPI.

To use this function in a kext, we either solve the symbol ourselves (the solution I used), or just link

against the Private KPI (I’m not sure if this would make kextd reject loading the kext for some reason

– haven’t poked around the new kextd yet).

A fully working kext and small GUI app to control it can be found here.

First you need to manually load the kernel extension and then you can start the GUI and connect to

the kernel extension. Then you can disable and enable SIP as you wish.

While this function is not a security vulnerability (once again we don’t really need it to disable SIP, it

just makes everything easier and cleaner) it’s not clear that Apple would allow a binary distribution of

this kernel extension. This means that you will need yourself a kernel code signing certificate to sign

your own version, if you wish to use this. Some games could be played to make sure this was

moderately secure against abuse by malware. Still it’s not good enough to risk company certificate on

this (maybe worth it to risk $99 and give it a try on a personal certificate).

Anyway, it’s purpose is more to developers and expert users that know what they are doing and don’t

want to reboot their machines for testing stuff.

I forgot one interesting detail in the original post. Disabling SIP this way doesn’t kill its log feature,

meaning that you can still find in the logs who tried to write to those unauthorized folders.

12/10/15 22:14:38,304 sandboxd[120]: ([466]) touch(466) System Policy: allow file-write-create /System/test

My SentinelOne colleague Julien-Pierre created a menu item that will load the kext and

enable/disable it. I’ll add it to the project soon.

Many thanks to SentinelOne’s Assaf for the name suggestion, better than my original rootful.

Greetings to Apple security related teams boys and girls :-).

And say thank you to SentinelOne for my time.

Enjoy,

fG!

Page 17: Reverse Engineering Mac OS X.pdf

P.S.: The blog is a bit messed up. WordPress f*cked up somewhere and things are a bit shaky. Will

try to fix or evaluate alternatives :-(.

P.S.2: It’s an happy day today, fuck off the right wing parties in Portugal. Hopefully it’s a bye bye you

fucking morons. </political rant>

https://reverse.put.as/2015/10/12/rootfool-a-small-tool-to-dynamically-disable-and-enable-sip-in-el-capitan/#more-

2469

Writing Bad @$$ Lamware for OS X August 7, 2015Securitymalware, vulnerability

The following is a guest post by noar (@noarfromspace), a long time friend.

It shows some simple attacks against BlockBlock, a software developed by Patrick Wardle that

monitors OS X common persistence locations for potential malware. The other day noar was telling

me about a few bypasses he had found so I invited him to write a guest post.

The title is obviously playing with one of Patrick’s presentations. I met Patrick at Shakacon last year

and this is not an attempt to shame him (that is reserved mostly for Apple ;-)). It just illustrates the

problems of building defensive tools and how much trust you put on them. By personal experience I

can tell you that building a security product is a much harder task than what you might think initially.

Disclaimer: we both work for an endpoint software company. Together with another colleague I

wrote an OS X version from scratch. I know very well the insane amount of problems that need to be

solved, and they never end. When you build something you are always at mercy of potential security

problems, we are no exception. Humans make mistakes. Offense is way easier ;-).

Anyway, enjoy it and hopefully learn something new!

Thank you noar!

fG!

I remember those days when there were only 3 or 4 security software editors for OS X. As the threat

counts increased, the market grew up too. Many products are now selling you a feeling of being

secure: most of them are post-mortem detection tools, and none is re-inventing the security

paradigm.

This dinosaur fight left some room for an altruistic new hype: free – but not open source – security

tools. Should we trust them blindly?

Page 18: Reverse Engineering Mac OS X.pdf

I am dedicating this post to HGDB, a former colleague and friend. Your sudden departure is leaving

us in an infinite sadness. May you rest in peace.

BlockBlock

New utilities are emerging to free the user from major companies subscription fees, like the recently

acquired Adware Medic or Objective-See toolsKnockKnock and BlockBlock. So I had interest in

reversing Patrick Wardle’s BlockBlock, a self-proclaimed continual runtime protection.

BlockBlock has a weird design: the main binary is running both as a daemon and as an agent. The

daemon waits for persistency related filesystem events and notifies the agent. The agent displays an

alert and reports a user action to the daemon.

https://reverse.put.as/2015/08/07/writing-bad-lamware-for-os-x/

Reversing Prince Harming’s kiss of death July 1, 2015Mac Reversing, SecurityEFI, patches, rootkit, vulnerability

The suspend/resume vulnerability disclosed a few weeks ago (named Prince Harming by Katie Moussouris)

turned out to be a zero day. While (I believe) its real world impact is small, it is nonetheless a critical

vulnerability and (another) spectacular failure from Apple. It must be noticed that firmware issues are not Apple

exclusive. For example, Gigabyte ships their UEFI with the flash always unlocked and other vendors also suffer

from all kinds of firmware vulnerabilities.

As I wrote in the original post, I found the vulnerability a couple of months ago while researching different

ways to reset a Mac firmware password. At the time, I did not research the source of the bug due to other higher

priority tasks. One of the reasons for its full disclosure was the assumption that Apple knew about this problem

since newer machines were not vulnerable. So the main question after the media storm was if my assumption

was wrong or not and what was really happening inside Apple’s EFI.

The bug is definitely not related to a hardware failure and can be fixed with a (simple) firmware update. The

initial assumptions pointing to some kind of S3 boot script failure were correct.

Apparently, Apple did not follow Intel’s recommendation and failed to lock the flash protections (and also

SMRR registers) after the S3 suspend cycle. The necessary information is not saved, so the locks will not be

restored when the machine wakes up from sleep.

This also allows finding which Mac models are vulnerable to this bug.

All machines based on Ivy Bridge, Sandy Bridge (and maybe older) platforms are vulnerable. This includes the

newest Mac Pro since its Xeon E5 CPU is still based on Ivy Bridge platform. All machines based on Haswell or

newer platforms are not vulnerable.

Now let’s jump to the technical part and understand why the bug occurs. I am also going to show you how to

build a temporary fix.

Page 19: Reverse Engineering Mac OS X.pdf

0 – The ACPI S3 sleep feature

The ACPI (Advanced Configuration and Power Interface) specification defines a few system power states and

transitions. One of those is the S3 state, which is a power saving feature that suspends to memory, meaning that

the current operating system state is held in memory. On this mode there is still power usage but it’s vastly

reduced. It is mostly used in notebook computers (closed lid) and its performance is important since users

expect their computer to wake up from sleep in a short period of time. If it took the same time as a normal boot,

there would be no advantages in using it (battery power consumption for example). In practice, this means that

the resume path is a special boot path that differs from the normal boot path.

The key difference is a performance optimization materialized in a boot script. It can contain the following

chipset and processor information:

I/O

PCI

Memory

System Management Bus (SMBus)

Other specific operations or routines that are necessary to restore the chipset and processor configuration

Instead of reinitializing everything again, the boot script will restore the machine to the same configuration

without executing again the whole DXE phase, which is time consuming. Script execution will be faster than

restarting and reinitializing all the necessary drivers, as it happens in a regular boot path. Sample contents of a

boot script will be shown later.

The following picture from the EFI documentation describes the differences between the normal and S3 boot

phases.

A description of each phase – SEC (Security), PEI (Pre-EFI Initialization), DXE (Driver Execution

Environment), BDS (Boot Device Selection) – can be found here. The DXE phase is where most of the

initialization work is performed (there are some 150 DXE drivers in EFI firmware), so this is the main reason

why the boot script is important for a fast resume from sleep cycle (EFI documents from 2003 describe that

Microsoft requires a maximum of 0.5 seconds for the S3 resume). It is on the DXE phase that the boot script is

created on normal boot path, meaning that the script is created once on a normal boot. The script is stored in

physical memory and this is the main reason why the Dark Jedi attack described at CCC 2014 is possible.

Page 20: Reverse Engineering Mac OS X.pdf

1 – Relevant documentation

EFI (Extensible Firmware Interface) specification was published by Intel. It was followed by the UEFI (Unified

Extensible Firmware Interface) managed by the Unified EFI Forum (www.uefi.org). Apple forked from EFI

v1.10 (Dec 2002) and introduced some changes, for example support for fat EFI binaries (support for both 32-

bit and 64-bit systems). Recent OS X versions are 64-bit only so this feature is not used anymore.

The most relevant EFI documents for this post are:

– EFI Boot Script Specification v0.91

– EFI S3 Resume Boot Path Specification v0.9

– EFI Driver Execution Environment Core Interface Spec v0.91

– EFI Firmware File System Specification v0.9

– EFI Firmware Volume Specification v0.9

– EFI Pre-EFI Initialization Core Interface Specification v0.91

– EFI Capsule Specification v0.9

– Extensible Firmware Interface Specification v1.10

Reference websites:

– Phoenix Bios Wiki

– EDK

– EDK2 Source code

– UEFI documentation

– LegbaCore (great BIOS/EFI/UEFI research papers)

Relevant papers, presentations and blog posts:

– Exploiting UEFI boot script table vulnerability

– Attacks on UEFI security, inspired by Darth Venamis’s misery and Speed Racer

– Thunderstrike

Tools and utilities:

– UEFITool and UEFIExtract

– IDA EFI Utils

– UEFI firmware parser

– CHIPSEC

2 – What’s inside the flash?

The EFI contents are stored in a flash memory chip that also contains Intel Management Engine (ME) and other

data. The two most used chips on newer Macs are the Micron N25Q064A and Macronix MX25L6405. They are

easy to identify on Mac motherboards, while in some models they are easily accessible (such as MacBook Pro

Retina) and others they require extensive disassembly (such as MacBook Pro 8,2 and MacBook Air). The

following picture shows the location on a MacBook Pro Retina 10,1(original image from iFixit.com):

Page 21: Reverse Engineering Mac OS X.pdf

If extensive disassembly is required, the iFixIt.com teardown guides are a great reference. From what I see in

teardown pictures of the newest MacBook (the gold, silver model), it appears that those flash chips are no

longer used. I do not own this model, so no clue where the EFI image is stored.

Both chips use SPI, meaning that a SPI reader/writer such as the one introduced by Trammell Hudson can be

used to read and write its contents.

This is the best and safest way to do it and you should definitely get or build one if you plan to do EFI research.

Page 22: Reverse Engineering Mac OS X.pdf

Screenshot of UEFITool processing one of my flash dumps:

Partial output of my own tool with some extra information regarding each firmware volume:

---[ EFI Root Firmware Volumes Distribution ]---

#01 @ offset 0x190000 - 7A9354D9-0468-444A-81CE-0BF617D890DF

#02 @ offset 0x330000 - 7A9354D9-0468-444A-81CE-0BF617D890DF

#03 @ offset 0x360000 - 7A9354D9-0468-444A-81CE-0BF617D890DF

#04 @ offset 0x600000 - E3B980A9-5FE3-48E5-9B92-2798385A9027

#05 @ offset 0x610000 - 7A9354D9-0468-444A-81CE-0BF617D890DF

#06 @ offset 0x620000 - 7A9354D9-0468-444A-81CE-0BF617D890DF

#07 @ offset 0x630000 - 153D2197-29BD-44DC-AC59-887F70E41A6B - Microcode

#08 @ offset 0x650000 - 153D2197-29BD-44DC-AC59-887F70E41A6B - Microcode

#09 @ offset 0x670000 - FFF12B8D-7696-4C8B-A985-2747075B4F50 - NVRAM

#10 @ offset 0x6a0000 - 7A9354D9-0468-444A-81CE-0BF617D890DF

#11 @ offset 0x740000 - 7A9354D9-0468-444A-81CE-0BF617D890DF

#12 @ offset 0x7e0000 - 04ADEEAD-61FF-4D31-B6BA-64F8BF901F5A - Boot Volume

#13 @ offset 0x7f0000 - 04ADEEAD-61FF-4D31-B6BA-64F8BF901F5A - Boot Volume

Page 23: Reverse Engineering Mac OS X.pdf

The flash contents are organized into firmware volumes. Some volumes contain the binaries for the different

SEC, PEI, DXE phases, while others CPU microcode. One of the volumes is dedicated to the NVRAM and this

is where you will find boot settings, crash logs, wifi password, etc.

It is also possible to use the scap files available on EFI firmware updates published by Apple. UEFITool is able

to process and extract the files. You can find firmware updates for newer machines on Yosemite updates.

It is important to notice that everything is identified by a GUID (globally unique identifier). These are 128-bit

values that identify everything instead of filenames.

The EFI and UEFI specifications define many GUIDs, for example to identify firmware volumes, but there are

also many vendor-only GUIDs not published anywhere. Apple firmware is not an exception and contains a few

proprietary GUIDs that require reversing to understand their purpose. In this case, Google is your ally, but also

EDK/EDK2 sources, and EFI/UEFI documentation. Good GUID compilations can be found here and here.

The contents of a file can be compressed as shown below. Apple uses LZMA algorithm.

Each file contains sections. In the above example, we have a PEI dependency section (basically information

about the load order and dependencies of each binary) and a compressed section containing a TE binary (Terse

Executable).

Page 24: Reverse Engineering Mac OS X.pdf

A file can also encapsulate another firmware volume as the next screenshot shows:

The conclusion is that the contents of a firmware volume are very flexible and can encapsulate a lot of different

information. Lucky for us, UEFITool is quite good at dealing with all this and makes it very easy to modify the

contents of each volume.

3 – Executable file types

Two types of executable file types can be found: PE32/PE32+ and TE. PE32 is Portable Executable, the same

type used in Windows, while TE is Terse Executable, which is nothing more than a stripped version of PE32.

The reason is to reduce the overhead of PE headers. The TE format is used by SEC/PEI phase binaries, while

DXE phase binaries are PE32/PE32+.

IDA is able to load TE binaries but it contains critical bugs that make the disassembly output mostly unusable.

Snare described how he built his own IDAPython TE loader for older IDA versions. It doesn’t work with newer

TE binaries without some fixes. Since I’m not a Python fan, I coded my own loader in C that also tries to locate

and name known GUIDs. For now I do not intent to release it so you should go and nag Hex-Rays for support.

Also important to point is that most of the strings are in Unicode format (2 bytes) so keep this in mind while

searching for strings. A few binaries contain useful strings but don’t expect to find many. The EDK/EDK2

sources are quite useful as reference.

Another extremely useful resource is the AMI Bios code leak. For obvious reasons, I can’t link to the leaked

archive but it’s not very hard to find around the web. It contains closed source code that the EDK/EDK2

releases don’t and it’s extremely useful to speed up the reversing process.

4 – EFI binaries reversing tips and tricks

In EFI/UEFI world there are no standard libraries that export commonly used functions. Instead, function

pointers stored in service tables are used. All service tables have the same structure, EFI_TABLE_HEADER:

Page 25: Reverse Engineering Mac OS X.pdf

typedef struct {

UINT64 Signature;

UINT32 Revision;

UINT32 HeaderSize;

UINT32 CRC32;

UINT32 Reserved;

} EFI_TABLE_HEADER;

The header is followed by the services (functions) available in that table.

The following is the structure for EFI_RUNTIME_SERVICES. These are the EFI services available after the

operating system takes control.

typedef struct {

EFI_TABLE_HEADER Hdr;

EFI_GET_TIME GetTime;

EFI_SET_TIME SetTime;

EFI_GET_WAKEUP_TIME GetWakeupTime;

EFI_SET_WAKEUP_TIME SetWakeupTime;

EFI_SET_VIRTUAL_ADDRESS_MAP SetVirtualAddressMap;

EFI_CONVERT_POINTER ConvertPointer;

EFI_GET_VARIABLE GetVariable;

EFI_GET_NEXT_VARIABLE_NAME GetNextVariableName;

EFI_SET_VARIABLE SetVariable;

EFI_GET_NEXT_HIGH_MONO_COUNT GetNextHighMonotonicCount;

EFI_RESET_SYSTEM ResetSystem;

EFI_UPDATE_CAPSULE UpdateCapsule;

EFI_QUERY_CAPSULE_CAPABILITIES QueryCapsuleCapabilities;

EFI_QUERY_VARIABLE_INFO QueryVariableInfo;

} EFI_RUNTIME_SERVICES;

For example, GetTime “Returns the current time and date, and the time-keeping capabilities of the platform.”

How are these service tables found by EFI binaries?

Let’s use the DXE phase binaries as an example. The entrypoint prototype for a DXE is described in the EFI

Driver Execution Environment Core Interface Spec document:

Page 26: Reverse Engineering Mac OS X.pdf

typedef

EFI_STATUS

(*EFI_IMAGE_ENTRY_POINT)(

IN EFI_HANDLE ImageHandle,

IN EFI_SYSTEM_TABLE *SystemTable

);

The EFI_SYSTEM_TABLE is another structure where the Boot Services and RunTime Services table pointers

can be found. Let’s translate all this into a real DXE binary to see things go.

.text:0000000000000579 public start

.text:0000000000000579 start proc near

.text:0000000000000579

.text:0000000000000579 var_20 = qword ptr -20h

.text:0000000000000579 var_18 = byte ptr -18h

.text:0000000000000579

.text:0000000000000579 push rbp

.text:000000000000057A mov rbp, rsp

.text:000000000000057D push rsi

.text:000000000000057E push rdi

.text:000000000000057F sub rsp, 30h

.text:0000000000000583 mov rsi, rdx

.text:0000000000000586 mov rdi, rcx

.text:0000000000000589 mov rcx, rdi

.text:000000000000058C mov rdx, rsi

.text:000000000000058F call GetSystemTables

(...)

This is the entrypoint function for a random DXE binary. The first call is very common in these binaries and

usually looks like this:

.text:0000000000000240 GetSystemTables proc near ; CODE XREF: start+16 p

Page 27: Reverse Engineering Mac OS X.pdf

.text:0000000000000240 mov cs:SystemTable, rdx

.text:0000000000000247 mov rax, [rdx+60h]

.text:000000000000024B mov cs:BootServices, rax

.text:0000000000000252 mov rax, [rdx+58h]

.text:0000000000000256 mov cs:RunTimeServices, rax

.text:000000000000025D xor eax, eax

.text:000000000000025F retn

.text:000000000000025F GetSystemTables endp

This function retrieves the pointers to Boot and Runtime Services tables and stores them for future usage. Some

binaries retrieve the same tables in different functions (weird compiler optimizations?) while others access them

directly at the entrypoint function. You can easily recognize these two tables by the 0x68 and 0x58 offsets.

Next is a sample function using the Boot Services function LocateProtocol to locate a specific protocol:

.text:00000000000002BD locate_bootscript_save_protocol proc near ; CODE XREF:

start+21 p

.text:00000000000002BD push rbp

.text:00000000000002BE mov rbp, rsp

.text:00000000000002C1 sub rsp, 20h

.text:00000000000002C5 mov rax, [rdx+60h]

.text:00000000000002C9 lea rcx, gEfiBootScriptSaveProtocolGuid

.text:00000000000002D0 lea r8, Boot_Script_Save_Interface

.text:00000000000002D7 xor edx, edx

.text:00000000000002D9 call qword ptr [rax+140h] ; BootServices-

>LocateProtocol()

.text:00000000000002DF test rax, rax

.text:00000000000002E2 jns short loc_2FE

.text:00000000000002E4 mov rcx, 8000000000000014h

.text:00000000000002EE cmp rax, rcx

.text:00000000000002F1 jz short loc_2FE

.text:00000000000002F3 mov cs:Boot_Script_Save_Interface, 0

Page 28: Reverse Engineering Mac OS X.pdf

.text:00000000000002FE

.text:00000000000002FE loc_2FE: ; CODE XREF:

locate_bootscript_save_protocol+25 j

.text:00000000000002FE ; locate_bootscript_save_protocol+34 j

.text:00000000000002FE xor eax, eax

.text:0000000000000300 add rsp, 20h

.text:0000000000000304 pop rbp

.text:0000000000000305 retn

.text:0000000000000305 locate_bootscript_save_protocol endp

PEI binaries use a different table, EFI_PEI_SERVICES implemented by the PEI Foundation. The concept is the

same, locate the table and access services via function pointers. The previously linked Snare’s EFI utils are

helpful for reversing, it tries to locate the tables, translate the function pointers to meaningful names and also

name known GUIDs. The scripts are not perfect but can be helpful. Binary grep is also very helpful to mass

locate GUIDs since there are some 200+ binaries on each dump.

Very important are the calling conventions used.

For 32-bit binaries the calling convention is the standard C calling convention (arguments passed on the stack).

For 64-bit binaries Microsoft’s x64 calling convention is used (arguments passed via RCX, RDX, R8, R9,

stack). The stack must be aligned in a 16-byte boundary, and a 32-byte shadow space must be reserved on the

stack above the return address. This means that we will see the first stack argument starting at offset 0x20.

SEC and PEI phase binaries are 32-bit (16-bit code also found for CPU initialization) and DXE binaries are 64-

bit.

5 – Protocols and PPIs

The EDK reference manual defines protocol as:

“A protocol is a data structure that associates:

• a GUID (naming it in an unique manner);

• possibly an interface, that is a collection of function pointers and/or public data.

So a protocol may be seen as the public definitions (or a sub-part of the public definitions) of a C++ class, or

as a Java interface. A data structure can be associated to the GUID, it generally made of one or more function

pointers (the protocol’s functions), and/or public data fields.”

A driver installs one or more protocols that can then be located and used by any other driver. Remember that

there is no standard library to link against, so protocols (and PPIs) do this work. The following is an example of

a protocol used in S3 sleep feature:

#define EFI_ACPI_S3_SAVE_GUID { 0x125f2de1, 0xfb85, 0x440c, 0xa5, 0x4c, 0x4d, 0x99, 0x35,

Page 29: Reverse Engineering Mac OS X.pdf

0x8a, 0x8d, 0x38 }

typedef

EFI_STATUS

(EFIAPI *EFI_ACPI_S3_SAVE)(

IN EFI_ACPI_S3_SAVE_PROTOCOL * This,

IN VOID * LegacyMemoryAddress

);

typedef

EFI_STATUS

(EFIAPI *EFI_ACPI_GET_LEGACY_MEMORY_SIZE)(

IN EFI_ACPI_S3_SAVE_PROTOCOL * This,

OUT UINTN * Size

);

typedef struct _EFI_ACPI_S3_SAVE_PROTOCOL {

EFI_ACPI_GET_LEGACY_MEMORY_SIZE GetLegacyMemorySize;

EFI_ACPI_S3_SAVE S3Save;

} EFI_ACPI_S3_SAVE_PROTOCOL;

This protocol provides two functions, GetLegacyMemorySize and S3Save. The latter is the function that

prepares all the information that is needed in the S3 resume boot path. It will internally use another protocol

EFI_BOOT_SCRIPT_SAVE_PROTOCOL, calling CloseTable() that is responsible for storing the boot script

in NVS (non-volatile storage).

If we want to locate the driver that publishes this protocol, we can binary grep for this protocol GUID and find

all the drivers that install and use it. In this case, it is expected that only a single driver calls S3Save.

The best way to find protocol definitions are the EFI/UEFI documentation and EDK/EDK2 source code.

In the PEI phase we can find PPIs (PEIM-to-PEIM Interface). For sake of simplicity, lets assume that they are

equivalent to protocols in terms of functionality. From the same EDK Reference Manual:

“PEIM-to-PEIM interfaces are a mechanism, similar to the protocols, that is used to facilitate the

communication between two PEIMs. The principle is roughly the same:

• PPIs are “named” with a GUID.

• PPIs have an optional data structure containing function pointers and data”

Sample PEI phase code from EDK2 using LocatePpi service:

Page 30: Reverse Engineering Mac OS X.pdf

EFI_STATUS

EFIAPI

CapsulePpiNotifyCallback (

IN EFI_PEI_SERVICES **PeiServices,

IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,

IN VOID *Ppi

)

{

EFI_STATUS Status;

EFI_BOOT_MODE BootMode;

PEI_CAPSULE_PPI *Capsule;

Status = (*PeiServices)->GetBootMode((const EFI_PEI_SERVICES **)PeiServices,

&BootMode);

ASSERT_EFI_ERROR (Status);

if (BootMode == BOOT_ON_S3_RESUME) {

//

// Determine if we're in capsule update mode

//

Status = (*PeiServices)->LocatePpi ((const EFI_PEI_SERVICES **)PeiServices,

&gPeiCapsulePpiGuid,

0,

NULL,

(VOID **)&Capsule

);

Page 31: Reverse Engineering Mac OS X.pdf

if (Status == EFI_SUCCESS) {

if (Capsule->CheckCapsuleUpdate ((EFI_PEI_SERVICES**)PeiServices) ==

EFI_SUCCESS) {

BootMode = BOOT_ON_FLASH_UPDATE;

Status = (*PeiServices)->SetBootMode((const EFI_PEI_SERVICES **)PeiServices,

BootMode);

ASSERT_EFI_ERROR (Status);

}

}

}

return Status;

}

6 – Building and executing the S3 boot script

Most, if not all, boot script content is added by different DXE drivers. The related protocol is

EFI_BOOT_SCRIPT_SAVE_PROTOCOL. It publishes two functions, Write and CloseTable.

The Write function is used to write the information to a boot script table. The specification allows multiple boot

tables but for now only one table is used, EFI_ACPI_S3_RESUME_SCRIPT_TABLE (0x0).

One of the disassembly listings above, locate_bootscript_save_protocol, is used to locate this specific protocol.

It can be found in all the DXE drivers that use this protocol.

The Write function supports a few different opcodes. Each opcode supports different operations and data. The

following is an incomplete definition of a few opcodes:

#define EFI_BOOT_SCRIPT_IO_WRITE_OPCODE 0x00

#define EFI_BOOT_SCRIPT_IO_READ_WRITE_OPCODE 0x01

#define EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE 0x02

#define EFI_BOOT_SCRIPT_MEM_READ_WRITE_OPCODE 0x03

#define EFI_BOOT_SCRIPT_PCI_CONFIG_WRITE_OPCODE 0x04

#define EFI_BOOT_SCRIPT_PCI_CONFIG_READ_WRITE_OPCODE 0x05

#define EFI_BOOT_SCRIPT_SMBUS_EXECUTE_OPCODE 0x06

#define EFI_BOOT_SCRIPT_STALL_OPCODE 0x07

#define EFI_BOOT_SCRIPT_DISPATCH_OPCODE 0x08

Page 32: Reverse Engineering Mac OS X.pdf

EDK2 supports a few more and Apple also implements some custom opcodes. The opcodes can be vendor

specific, so they require reversing to understand their purpose. The functions that deal with each opcode are

easy to find. For example, the following listing is a common function found in DXE drivers that implements

the EFI_BOOT_SCRIPT_IO_WRITE_OPCODE:

.text:0000000000000289 save_script_io_write_opcode proc near

.text:0000000000000289

.text:0000000000000289 var_20 = qword ptr -20h

.text:0000000000000289 var_18 = qword ptr -18h

.text:0000000000000289 var_10 = qword ptr -10h

.text:0000000000000289 arg_20 = qword ptr 30h

.text:0000000000000289

.text:0000000000000289 push rbp

.text:000000000000028A mov rbp, rsp

.text:000000000000028D sub rsp, 40h

.text:0000000000000291 mov eax, edx

.text:0000000000000293 mov rdx, 800000000000000Eh

.text:000000000000029D mov r10, cs:Boot_Script_Save_Interface

.text:00000000000002A4 test r10, r10

.text:00000000000002A7 jz short loc_2CD

.text:00000000000002A9 mov rdx, [rbp+arg_20]

.text:00000000000002AD mov [rsp+30h], rdx ; *Buffer

.text:00000000000002B2 mov [rsp+28h], r9 ; Count

.text:00000000000002B7 mov [rsp+20h], r8 ; Address

.text:00000000000002BC movzx edx, cx ; TableName (always 0)

.text:00000000000002BF mov rcx, r10 ; *This

.text:00000000000002C2 xor r8d, r8d ;

EFI_BOOT_SCRIPT_IO_WRITE_OPCODE

.text:00000000000002C5 mov r9d, eax ; Width

Page 33: Reverse Engineering Mac OS X.pdf

.text:00000000000002C8 call qword ptr [r10] ;

EFI_BOOT_SCRIPT_SAVE_PROTOCOL.Write()

.text:00000000000002CB xor edx, edx

.text:00000000000002CD

.text:00000000000002CD loc_2CD: ; CODE XREF:

save_script_io_write_opcode+1E

.text:00000000000002CD mov rax, rdx

.text:00000000000002D0 add rsp, 40h

.text:00000000000002D4 pop rbp

.text:00000000000002D5 retn

.text:00000000000002D5 save_script_io_write_opcode endp

All the opcode functions are be easily identified by the reference to EFI_BOOT_SCRIPT_SAVE_PROTOCOL

and the value on R8 register before the call to Write.

As previously described, there is a DXE driver that locates the EFI_ACPI_S3_SAVE_PROTOCOL and

executes S3Save to make the boot script ready for S3 resume. This function calls CloseTable that allocates a

new memory pool to save the script, returning the physical address for the boot script. As previously described,

the script is dynamically built on a normal boot path.

How about execution of the boot script?

The S3 boot script execution is initialized by the DXE IPL (Initial Program Load). This is the last binary

executed in the PEI phase and if a S3 boot script exists it will follow that special boot path instead of the normal

path.

This is done by locating EFI_PEI_S3_RESUME_PPI:

#define EFI_PEI_S3_RESUME_PPI_GUID { 0x4426CCB2, 0xE684, 0x4a8a, 0xAE, 0x40, 0x20, 0xD4,

0xB0, 0x25, 0xB7, 0x10}

typedef struct _EFI_PEI_S3_RESUME_PPI {

EFI_PEI_S3_RESUME_PPI_RESTORE_CONFIG S3RestoreConfig;

} EFI_PEI_S3_RESUME_PPI;

Parameters

S3RestoreConfig

Restores the platform to its preboot configuration for an S3 resume and jumps to the OS

Page 34: Reverse Engineering Mac OS X.pdf

waking vector.

The S3RestoreConfig function is responsible for locating the S3 boot script, other necessary information and

trigger the boot script execution via another PPI, EFI_PEI_BOOT_SCRIPT_EXECUTE_PPI:

#define EFI_PEI_BOOT_SCRIPT_EXECUTER_PPI_GUID { 0xabd42895, 0x78cf, 0x4872, 0x84, 0x44,

0x1b, 0x5c, 0x18, 0x0b, 0xfb, 0xff }

typedef

EFI_STATUS

(EFIAPI *EFI_PEI_BOOT_SCRIPT_EXECUTE)(

IN EFI_PEI_SERVICES **PeiServices,

IN EFI_PEI_BOOT_SCRIPT_EXECUTER_PPI *This,

IN EFI_PHYSICAL_ADDRESS Address,

IN EFI_GUID *FvFile OPTIONAL

);

typedef struct _EFI_PEI_BOOT_SCRIPT_EXECUTER_PPI {

EFI_PEI_BOOT_SCRIPT_EXECUTE Execute;

} EFI_PEI_BOOT_SCRIPT_EXECUTER_PPI;

For a real world implementation and reversing check cr4sh’s blog post.

After the boot script is executed with EFI_PEI_BOOT_SCRIPT_EXECUTER_PPI.Execute(), control is

transfered to the OS waking vector and execution resumes at the operating system level.

The Dark Jedi vulnerability exploits this phase. The vulnerability exists because the boot script is saved into

unprotected physical memory. This memory can be found and modified from a kernel extension. When the

machine comes back from sleep the modified boot script is executed and malicious code can be run at firmware

level. The flash memory can be written because the lock protections are never restored.

It should be noticed that all Macs are still vulnerable to this specific attack (to be presented by Trammel

Hudson, Xeno Kovah, and Corey Kallenberg at next BlackHat/Defcon).

7 – Finding the vulnerability

The PPI and Protocol GUIDs allow us to track all the binaries involved in the S3 boot script. I disassembled

every single binary that uses the EFI_BOOT_SCRIPT_SAVE_PROTOCOL. The interesting DXE driver where

the vulnerability occurs is the DXE identified with the GUID DE23ACEE-CF55-4FB6-AA77-984AB53DE823.

If you lookup this GUID on the web, you will find a very interesting name, PchInitDxe.efi. What is so special

about this one? The flash protections are implemented by the PCH (Platform Controller Hub) controller. This

controller provides multiple IO functions and different protections for the flash chip. For example, the Flash

Configuration Lock-Down (FLOCKDN) is described on PCH documentation as:

“When set to 1, those Flash Program Registers that are locked down by this FLOCKDN bit cannot be written.

Page 35: Reverse Engineering Mac OS X.pdf

Once set to 1, this bit can only be cleared by a hardware reset due to a global reset or host partition reset in an

Intel® ME enabled system.”

The Protected Range registers (PRX) can be configured to protect memory ranges of the flash chip itself (not

machine RAM). We already saw that there is a writable NVRAM memory region in the flash chip. In practice

the protected range registers will write-protect all flash memory except the NVRAM region. There are many

other protections available. Please refer to Legbacore presentations for complete descriptions of available

protections.

The suspend/resume vulnerability makes it possible to write the previously protected flash memory regions

because the FLOCKDN bit is set to zero after the suspend/resume cycle. While this could be caused by some

hardware failure, the reality is much simpler. What is missing is the boot script information that restores this

register configuration. When the machine is put to sleep, the CPU context is lost so the flash memory is

unlocked (this is another reason why Dark Jedi attack is possible) until the S3 boot script is executed. If there is

no such information saved then the flash will be left unlocked, making it vulnerable to writes from the operating

system level.

How does it look like the code to save the information of this FLOCKDN register?

//

// SPI Flash Programming Guide Section 5.5.1 Flash Configuration Lockdown

// It is strongly recommended that BIOS sets the Host and GbE Flash Configuration Lock-

Down (FLOCKDN)

// bits (located at SPIBAR + 04h and MBAR + 04h respectively) to 1 on production

platforms

//

MmioOr16 ((UINTN) (RootComplexBar + R_PCH_SPI_HSFS), (UINT16) (B_PCH_SPI_HSFS_FLOCKDN));

SCRIPT_MEM_WRITE (

EFI_ACPI_S3_RESUME_SCRIPT_TABLE,

EfiBootScriptWidthUint16,

(UINTN) (RootComplexBar + R_PCH_SPI_HSFS),

1,

(VOID *) (UINTN) (RootComplexBar + R_PCH_SPI_HSFS)

);

R_PCH_SPI_HSFS value is 0x3804. This makes it easier to track down the location where this code (or

something like it) might be implemented.

We know that newer machines are not vulnerable so let’s start by trying to locate this code on those models.

The following disassembly listing is the equivalent to above’s code found on MacBook Pro Retina 11,1

firmware:

Page 36: Reverse Engineering Mac OS X.pdf

.text:000000000000519F lea edi, [rbx+3804h] ; R_PCH_SPI_HSFS

.text:00000000000051A5 mov [rbp-0BCh], ebx

.text:00000000000051AB mov rcx, rdi ; Address

.text:00000000000051AE mov edx, 8000h ; B_PCH_SPI_HSFS_FLOCKDN

.text:00000000000051B3

.text:00000000000051B3 loc_51B3: ; CODE XREF: PchInitBeforeBoot+7BF

.text:00000000000051B3 call MmioOr16 ; MmioOr16 ((UINTN) (RootComplexBar

+ R_PCH_SPI_HSFS), (UINT16) (B_PCH_SPI_HSFS_FLOCKDN));

.text:00000000000051B8

.text:00000000000051B8 loc_51B8: ; CODE XREF: PchInitBeforeBoot+75E

.text:00000000000051B8 mov [rbp-0D8h], r15d

.text:00000000000051BF mov [rsp+20h], rdi ; buffer

.text:00000000000051C4 xor ecx, ecx ; tableName

.text:00000000000051C6 mov edx, 1 ; width = EfiBootScriptWidthUint16

.text:00000000000051CB mov r8, rdi ; address

.text:00000000000051CE mov r9d, 1 ; count

.text:00000000000051D4 call save_mem_write_opcode

This explains why the newer machines are not vulnerable. The FLOCKDN information is being saved to the

boot script. The same code (or variant) can’t be found on vulnerable machines firmware.

From this we can conclude that the vulnerability is definitely due to flawed boot script information. Apple failed

to implement Intel’s recommendation regarding the flash protections and fails to save it to the boot script on

vulnerable machines.

Trammell was kind enough to send me the boot script output between a 10,1 and 11,2 Retina Macs (latest

CHIPSEC added support for this but still has some problems with Macs).

Retina 10,1:

MEM_WRITE: Width=2 Count=1 Address=00000000fed1f8c0 Data=00000007

MEM_WRITE: Width=2 Count=1 Address=00000000fed1f890 Data=f94000c0

MEM_WRITE: Width=2 Count=1 Address=00000000fed1f894 Data=3c6c0606

MEM_WRITE: Width=2 Count=1 Address=00000000fed1f898 Data=0103029f

Page 37: Reverse Engineering Mac OS X.pdf

MEM_WRITE: Width=2 Count=1 Address=00000000fed1f89c Data=ffd82005

MEM_WRITE: Width=2 Count=1 Address=00000000fed1f8c8 Data=00002005

MEM_WRITE: Width=2 Count=1 Address=00000000fed1f8c4 Data=00800000

Retina 11,2:

MEM_WRITE: Width=2 Count=1 Address=00000000fed1f890 Data=f94204c0

MEM_WRITE: Width=2 Count=1 Address=00000000fed1f894 Data=3c6c0606

MEM_WRITE: Width=2 Count=1 Address=00000000fed1f898 Data=0103029f

MEM_WRITE: Width=2 Count=1 Address=00000000fed1f89c Data=ffd82005

MEM_WRITE: Width=2 Count=1 Address=00000000fed1f8c4 Data=80002045

MEM_WRITE: Width=2 Count=1 Address=00000000fed1f8c8 Data=00000000

MEM_WRITE: Width=2 Count=1 Address=00000000fed1f8c0 Data=0000000f

MEM_WRITE: Width=2 Count=1 Address=00000000fed1f874 Data=80010000

MEM_WRITE: Width=2 Count=1 Address=00000000fed1f878 Data=860f0190

MEM_WRITE: Width=2 Count=1 Address=00000000fed1f87c Data=9fff0632

MEM_WRITE: Width=1 Count=1 Address=00000000fed1f804 Data=e008

These values translate to:

Retina 10,1:

fed1f890 f94000c0 SSFS/SSFC

fed1f894 3c6c0606 PREOP/OPTYPE

fed1f898 0103029f OPMENU

fed1f89c ffd82005 OPMENU+4

fed1f8c8 00002005 UVSCC

fed1f8c4 00800000 LVSCC

Retina 11,2:

Page 38: Reverse Engineering Mac OS X.pdf

fed1f890 f94204c0 SSFS/SSFC

fed1f894 3c6c0606 PREOP/OPTYPE

fed1f898 0103029f OPMENU

fed1f89c ffd82005 OPMENU+4

fed1f8c4 80002045 LVSCC

fed1f8c8 00000000 UVSCC

fed1f8c0 0000000f ?

fed1f874 80010000 PR0

fed1f878 860f0190 PR1

fed1f87c 9fff0632 PR2

fed1f804 e008 HSFS FLOCKDN

The boot script output allows us to reach the same conclusion: older machines don’t have the FLOCKDN and

Protected Range registers boot script information and there lies the source of the vulnerability.

To dump this information you can follow CHIPSEC code. Essentially it’s a matter of locating the ACPI variable

and the physical memory address where the boot script is located. To read physical memory the DirectHW.kext

can be used.

An interesting question is why the difference between Haswell and older platforms?

The AMI bios leak can provide us with an hint. Its reference code for Ivy Bridge and Sandy Bridge platforms

are codenamed PantherPoint and CougarPoint. Apple’s Haswell DXE drivers have string references about

LynxPoint.

From what I could gather, Haswell platform introduced a more complicated power management mode. Apple

probably followed Intel’s reference code and this time they were capable to correctly copy and paste the flash

locking code.

The other possible theory is that Apple knew about this bug, fixed it in Haswell firmware and opted for not

patching older systems, a decision that isn’t exactly rare.

8 – Fixing ourselves the vulnerability

Now let’s go for the really interesting and fun part of this post. Once again, can we fix the

vulnerability ourselves instead of waiting for Apple?

The answer is yes, of course we can. It’s all bits and bytes after all and Thunderstrike has shown us there are no

hardware protections involved!

Most of the necessary code for the fix is described on the last disassembly listing. We need to first call

MmioOr16 and then save_mem_write_opcode.

To build the fix we need to adapt that code to the target firmware and find space where to insert it.

Page 39: Reverse Engineering Mac OS X.pdf

Because there isn’t enough unused space we can use to inject the new code, I have opted for replacing an

“unused” function. More on the target function later on. An alternative could have been to add a new section or

expand a section, but that would require another read of PE format (I haven’t messed with PE for more than a

decade or so) and I didn’t knew the impacts on UEFITool and EFI itself. Replacing code was the faster

alternative.

This means that we need to jump to the new code, execute it, execute the original bytes we stole for the jump

and resume execution back at the original function where we jumped from. The first jump is placed near a

similar area to the non-vulnerable versions.

The new code for the latest MacBook Pro Retina 10,1 firmware available is the following:

.text:0000000000003BC4 lea esi, [r15+3804h] ; RootComplexBar + R_PCH_SPI_HSFS

.text:0000000000003BCB mov rcx, rsi

.text:0000000000003BCE mov edx, 8000h ; B_PCH_SPI_HSFS_FLOCKDN

.text:0000000000003BD3 call sub_1388 ; call MmioOr16

.text:0000000000003BD8 mov [rsp+20h], rsi

.text:0000000000003BDD xor ecx, ecx

.text:0000000000003BDF mov edx, 1

.text:0000000000003BE4 mov r8, rsi

.text:0000000000003BE7 mov r9d, 1

.text:0000000000003BED call sub_2D6 ; call save_mem_write_opcode

.text:0000000000003BF2 mov rax, [rbp+var_50] ; original stolen instruction

.text:0000000000003BF6 mov rcx, [rax+8] ; original stolen instruction

.text:0000000000003BFA jmp loc_1FF4 ; resume execution on original

function

As you can see it’s rather simple. From the original function code we know that RootComplexBar is stored in

R15 and that B_PCH_SPI_HSFS_FLOCKDN value is 0x8000.

The next call is to save_mem_write_opcode() function that will add the boot script entry. This is also a matter

of reversing code that calls this function and setting the right parameters in registers and stack.

The last two MOV instructions are the original ones that were stolen so we could make space for the jump. We

resume execution at the next instruction.

The original code is:

.text:0000000000001FE7 E8 3A E3 FF FF call save_script_mem_read_write_opcode

.text:0000000000001FEC 48 8B 45 B0 mov rax, [rbp+var_50] ; jump to fix

here

.text:0000000000001FF0 48 8B 48 08 mov rcx, [rax+8]

Page 40: Reverse Engineering Mac OS X.pdf

.text:0000000000001FF4 F6 01 01 test byte ptr [rcx], 1

.text:0000000000001FF7 0F 84 97 01 00 00 jz loc_2194

The modified version:

.text:0000000000001FE7 E8 3A E3 FF FF call sub_326

.text:0000000000001FEC E9 D3 1B 00 00 jmp loc_3BC4

.text:0000000000001FF1 90 90 90 align 4

.text:0000000000001FF4

.text:0000000000001FF4 loc_1FF4: ; CODE XREF: sub_1DC8+1E32 j

.text:0000000000001FF4 F6 01 01 test byte ptr [rcx], 1

.text:0000000000001FF7 0F 84 97 01 00 00 jz loc_2194

The original target function to hold the fix:

.text:0000000000003BC4 sub_3BC4 proc near ; CODE XREF: sub_5429+F7

.text:0000000000003BC4

.text:0000000000003BC4 var_20 = qword ptr -20h

.text:0000000000003BC4 var_11 = byte ptr -11h

.text:0000000000003BC4 var_10 = qword ptr -10h

.text:0000000000003BC4

.text:0000000000003BC4 push rbp

.text:0000000000003BC5 mov rbp, rsp

.text:0000000000003BC8 push rsi

.text:0000000000003BC9 sub rsp, 38h

.text:0000000000003BCD mov rsi, rcx

.text:0000000000003BD0 mov [rbp+var_11], 0

.text:0000000000003BD4 mov [rbp+var_10], 1

.text:0000000000003BDC mov rax, cs:RunTimeServices

.text:0000000000003BE3 lea rcx, [rbp+var_11]

Page 41: Reverse Engineering Mac OS X.pdf

.text:0000000000003BE7 mov [rsp+40h+var_20], rcx

.text:0000000000003BEC lea rcx, aDisabledeepsx ; "DisableDeepSX"

.text:0000000000003BF3 lea rdx, dword_9D70

.text:0000000000003BFA lea r9, [rbp+var_10]

.text:0000000000003BFE xor r8d, r8d

.text:0000000000003C01 call qword ptr [rax+48h] ; GetVariable

.text:0000000000003C04 test rax, rax

.text:0000000000003C07 js short loc_3C13

.text:0000000000003C09 mov cl, [rbp+var_11]

.text:0000000000003C0C cmp cl, 1

.text:0000000000003C0F jnz short loc_3C13

.text:0000000000003C11 mov [rsi], cl

.text:0000000000003C13

.text:0000000000003C13 loc_3C13: ; CODE XREF: sub_3BC4+43 j

.text:0000000000003C13 ; sub_3BC4+4B j

.text:0000000000003C13 add rsp, 38h

.text:0000000000003C17 pop rsi

.text:0000000000003C18 pop rbp

.text:0000000000003C19 retn

.text:0000000000003C19 sub_3BC4 endp

This function is called once and references a string called DisableDeepSX which is an EFI variable. It doesn’t

appear to be doing anything special so it was a good candidate. We are hacking and experimenting so why not?

Failure is part of the process!

The last thing we need to do is to remove the call to the original function since that function now contains the

fix code. I have also opted for moving 1 into var_35 instead of the original 0 (just because it appears to be a

better option).

.text:0000000000005518 C6 45 CB 01 mov [rbp+var_35], 1 ; modified to move 1

into the variable instead of original 0

.text:000000000000551C 48 8D 4D CB lea rcx, [rbp+var_35]

Page 42: Reverse Engineering Mac OS X.pdf

.text:0000000000005520 90 nop ; NOP the original call

.text:0000000000005521 90 nop

.text:0000000000005522 90 nop

.text:0000000000005523 90 nop

.text:0000000000005524 90 nop

.text:0000000000005525 80 7D CB 00 cmp [rbp+var_35], 0

Everything is now set to test the patch and check if it works.

9 – Reflashing and a temporarily bricked Retina

The next step is to replace the original DXE binary with the patched version. UEFITool is great for this because

it will take care of everything. We just need to select the right GUID, go to the compressed binary and replace it

with our patched version (use replace body option *inside* the compressed section for this GUID).

UEFITool will (re)organize the firmware volume and update all the necessary CRCs.

After the new image is ready we can replace it via SPI or use the bug itself together with flashrom.

A few minutes later the result is… a black screen. The fans temporarily spin and then shut down. This means

that the new bios image has a problem and is unable to boot.

The bad image needs to be replaced with the original dump. Always have a working dump before replacing

anything!

What is the reason for failure?

The first task is to be sure that UEFITool packed everything correctly and the CRCs were valid. Yes, everything

is ok here.

Next step is to assume there is some other kind of checksum or check somewhere else. I made a few tests and

for example the boot firmware volume can be modified without bricking the machine (if you noticed there are

two boot firmware volumes, one has invalid CRC on a working dump, maybe it’s a backup volume?). This

means that if there is another check it should exist only in the DXE phase.

Read again Thunderstrike presentation notes…

An old unanswered question pops up: what are the last four bytes of the ZeroVector used for?

Trammell says the last eight bytes change between volumes and firmware versions. We know that the first four

are the CRC32 but there is nothing about the remaining four. UEFITool doesn’t change them so it also knows

nothing about them.

What do we do? Let’s try to make sense of its value. Could it be a reference to free space or something? Yes,

bingo at the first attempt (not kidding!). It’s not the free space but the amount of space used by all the files on

each volume. My hint on this was that I previously tested modifying only its value and it also bricked the

machine. This means that the value is indeed relevant for something.

If you make the difference between the total size of the firmware volume (FVLength field from

EFI_FIRMWARE_VOLUME_HEADER) and the volume free space computed by UEFITool we get the value

located in the last four bytes of ZeroVector.

Page 43: Reverse Engineering Mac OS X.pdf

On this volume we have a full size of 0x30000 bytes and a ZeroVector value of 0x102B0 bytes.

Page 44: Reverse Engineering Mac OS X.pdf

And its free space is 0x1FD50 bytes. The difference between volume full size and free space is 0x30000-

0x1FD50 = 0x102B0, the same value found on ZeroVector.

Since the repacking of the patched binary will slightly modify the free space by a few bytes, that value is wrong

and will invalidate the bios image. Compute the new value, fix the header, and recompute the header checksum

(CRC16) to the new valid value. Reflash the new image and now it boots!

The issue has been reported to UEFITool author and it will be hopefully fixed in the next few weeks. For now

you need to manually update the value and header CRC16.

10 – Does the fix work?

The last step of this adventure is to verify if our boot script modification works or not.

And the answer is positive, FLOCKDN is now always set to 1 instead of the vulnerable 0. Repeat the

suspend/resume cycle a few times to make sure it works and it’s stable. It really works and machine is stable.

Game over! (well sort of…)

We were able to track down the source of the bug and produce a binary patch for the same bug. It’s not a perfect

patch – doesn’t take care of SMRR registers (SMM Range Registers) and misses other flash security features) –

but the main goal was to show it is possible and easy to implement. Apple has no excuses to avoid releasing

firmware updates for all the vulnerable models.

An interesting project would be to develop a fix for the Dark Jedi issue. A good starting point is Intel’s

document “A Tour Beyond BIOS Implementing S3 Resume with EDKII“. It is a paper published a few months

before Dark Jedi presentation that describes the issue and proposes a fix. It is rather amusing to read the paper

after Dark Jedi was presented since you clearly see Dark Jedi issue there. Hindsight is always 20/20.

11 – Conclusion

This was a long technical post and hopefully a good overall introduction to the EFI/UEFI world. You should

read the documentation. It is long but it is overally good and you get a good understanding of the EFI

architecture (honestly I like it after you understand how everything fits together).

While I believe the impact to average users is small, it remains a critical issue that can be exploited remotely.

I also do not regret the full disclosure and 0day release since it seems the only way to pressure vendors to fix

firmware issues. There is indeed some risk associated with firmware updates but those risks are much lower

than the security risks posed by vulnerabilities at firmware level. Hopefully this attitude will finally change.

Dark Jedi can also be remotely exploited, so fixing it is also necessary and can be bundled together with the fix

for this vulnerability.

Big thanks to SentinelOne – this research was part of Sentinel’s OS X Research group (the bug itself wasn’t) –

and Trammell, Julien-Pierre, Gualter for their feedback and proof-reading.

12 – Update

Apple just released an update for this bug and Dark Jedi right before this post went public.

I am very happy to see that Apple moved fast enough to fix both bugs and must congratulate them. It was a bit

Page 45: Reverse Engineering Mac OS X.pdf

unexpected! Maybe full disclosure and bad publicity work after all ;-).

Because it’s just so fresh I have yet to reverse Apple’s fix. I have some knowledge that they adopted a different

strategy mostly because Dark Jedi. This post will be updated sooner or later with information about Apple’s fix.

Have fun,

fG!

Post navigation

← The Empire Strikes Back Apple – how your Mac firmware security is completely brokenBSides Lisbon and

SECUINSIDE 2015 presentations →

16 thoughts on “Reversing Prince Harming’s kiss of death”

1. Timothy

July 2, 2015 at 9:24 pm

What about even older machines like, gasp, C2D?!

Yosemite supports lots of older hardware that did not get any EFI fixes.

Reply

1. fG!

September 1, 2015 at 3:09 pm

Those don’t have any flash protections at all on the SouthBridge so they are useless

machines

Reply

Page 46: Reverse Engineering Mac OS X.pdf

2. Timothy

July 3, 2015 at 7:47 pm

I think Apple stopped at 2011 machines for the Mac EFI Security Update 2015-001 patches because

things get messy prior to that and they didn’t want to deal with Wolfdale, Clarkdale, Lynnfield,

Arrandale, etc.

I could be wrong. I hope it is just that earlier machines are not affected.

We need clarity on this.

Reply

1. fG!

July 6, 2015 at 11:31 pm

Yes, Snare’s talks in 2012 discuss unlocked firmmwares. Probably because the PCH didn’t

support it? Not sure about when those protections were introduced.

Reply

3. Timothy

July 9, 2015 at 3:36 am

Can anyone confirm if pre 2011 machines are affected by this?

If they are, then Apple left a lot of useable machines at risk.

Reply

1. fG!

Page 47: Reverse Engineering Mac OS X.pdf

July 9, 2015 at 3:41 am

Core 2 Duo are certainly vulnerable, since they have no flash locks.

Reply

4. Timothy

July 9, 2015 at 6:16 am

>Core 2 Duo are certainly vulnerable, since they have no flash locks.

So, Apple supports that hardware for Yosemite/El Capitan, yet they left it vulnerable!

Can you poke your contacts at Apple Security about this?

Reply

1. fG!

July 9, 2015 at 12:47 pm

I don’t think it’s that straightforward.

It’s still a guess but I think Core 2 Duo hasn’t the necessary flash protections on its chipset,

meaning, hardware doesn’t support it.

That needs to be confirmed by checking when the flash protections were introduced. My guess is

only Sandy Bridge with their implementation on PCH but this definitely needs to be researched.

Reply

1. Jimmy T

July 21, 2015 at 12:37 pm

> I think Core 2 Duo hasn’t the necessary flash protections on its chipset, meaning, hardware

doesn’t support it.

Page 48: Reverse Engineering Mac OS X.pdf

Do I understand this correctly- if the above is right does that mean even if Apple wanted to they

couldn’t protect/fix this on Core 2 Duo?

And if so is that not a major design flaw by Intel?

Great follow up on your initial disclosure. Great work. I, for one, appreciate it. Thank you.

Reply

1. fG!

July 21, 2015 at 2:05 pm

You can understand it as a flaw since that feature appears to be missing from those older

CPUs.

The same happens with newer CPU protections and so on. In theory and mostly in practice,

older machines will always be more vulnerable than newer machines.

Definitely not recommended machines for anyone that could be a target of such low level

attacks

Reply

5. Sam

August 3, 2015 at 7:09 pm

Hi,

For instance, a vulnerable MBP running Yosemite 10.10.4 (or earlier 10.10.x) cannot apply EFI

Security Update 2015-001 even if its EFI version is not the last one reported

here: https://support.apple.com/en-us/HT201518 .

Apparently this famous bug is not only related to Intel proc. generation but also to OS X one.

First, CVE-2015-3692 (for instance) says “Apple Mac EFI before 2015-001, as used in OS X before

Page 49: Reverse Engineering Mac OS X.pdf

10.10.4 and other products, does not enforce a locking protection… ” from what I tend to understand

Yosemite is “protective” since v10.10.4.

Second, Apple updates are only available for OS X 10.8.5 / 10.9.5: tends to mean 10.10 up to 10.10.3

versions installed on vulnerable MACs is “protective”.

Could you confirm please since it’s not fully clear for me?

Last but not least, is there a SIMPLE way (step by step) to check without risks if EFI of a vulnerable

Mac is compromised or not (since 10.10.4 update may fix vulnerability but not already overwritten

EFI)?

Thanks for helping a blond girl’s brain, French in addition (sorry for average English) :-).

Reply

1. fG!

August 3, 2015 at 7:51 pm

Hi,

What a big mess you have here.

The EFI patch for Yosemite installed machines is bundled inside Yosemite 10.10.4 update.

For other OS X versions there is Security Update 2015-001.

As long vulnerable machine is running Mountain Lion, Mavericks, Yosemite, the EFI will be

updated as soon as all available patches are downloaded and installed.

The only way to check EFI is with a hardware SPI reader. Anything from software can’t be trusted.

fG!

Reply

1. Sam

Page 50: Reverse Engineering Mac OS X.pdf

August 3, 2015 at 10:49 pm

So I understood well finally ;-).

Thanks :-).

Don’t know if Apple Genius Bar could control EFI with hardware SPI reader (don’t know what it

is and how to make it work).

Reply

1. fG!

August 4, 2015 at 11:14 pm

Probably not, they aren’t that smart.

You need to build one yourself, check the Secuinside 2015 slides, they have the schematics.

Reply

6. Sam

August 6, 2015 at 2:17 am

Thanks fG!

You’re right. I went to a Genius Bar today for another problem and asked about EFI checking

possibilities. Genius are able to test the way EFI runs (it takes about 24 up to 48 hours to get your

machine back) but they don’t think they can check EFI integrity.

Apple left Mac users a pretty long time with this vulnerability unpatched, IMHO the least Apple should

do is to provide tool or guide for checking EFI is OK. I will contact Apple Care tomorrow knowing I

hope a miracle!

Page 51: Reverse Engineering Mac OS X.pdf

Since I installed Yosemite 10.10.2 on a (vulnerable) Mac around the end of May, system is terribly

unstable and behaves like an infected Windows one (though 3 different AV scanning engines didn’t

find any threat)… no matter what I’ve tried. Among other curious things, I “meet” Kernel panics that

are not logged.

I will make some searches tomorrow regarding Secuinside 2015 slides but I certainly not have

necessary skills to go farther.

Thanks again for the info and your help :-).

Reply

1. fG!

August 7, 2015 at 3:16 pm

There’s no tool other than hardware dumping. Software dumps aren’t reliable for malware hunting

purposes.

Hardware dumping isn’t scalable since most Macs need full disassembly for reaching the flash

chip.

That’s problably something Apple needs to redesign and make the chip accessible in future

models.

Reply

https://reverse.put.as/2015/07/01/reversing-prince-harmings-kiss-of-death/#more-2372

BSides Lisbon and SECUINSIDE 2015 presentations July 21, 2015Papersrootkit

I guess my goal for the remaining 2015 of not doing any presentations will not happen.

Two weeks ago I presented at BSides Lisbon 2015 and last week at SECUINSIDE 2015.

I’m very happy to see BSides Lisbon returning after the first edition in 2013. Congrats to Bruno,

Tiago, and the rest of the team for making it happen. It’s still a small conference but I’m glad they are

making it happen, and I will always do my best to help the Portuguese scene going forward.

Everything went pretty well and met some new cool guys. I hope the conference returns in 2016 and

keeps growing. Maybe someday it can mutate into something independent of BSides and on its own.

Page 52: Reverse Engineering Mac OS X.pdf

Portugal is a great country for conferences, I’m just not the right person to start one, but I’ll definitely

help my best anyone who wants to give it a shot.

The presentation is the same as CodeBlue and SyScan since the CFP happened a few months ago.

Nothing new in the slides, a fix here and there.

The next was SECUINSIDE in Seoul. This is a very special conference to me because it was the first

ever conference I presented at, back in 2012. I never had any plans to ever present at security

conferences. I liked my low profile and this blog was good enough to spread the knowledge I wanted

to. But I try to be flexible and always open to new adventures, so at the time I accepted HiTCON

invitation (they were the first ones) and then SECUINSIDE’s invitation. SECUINSIDE was happening

first so at the time I created a different presentation. It was a crazy trip because I did a Porto – Seoul

roundtrip, and four days later I went to Taiwan. That was some crazy jetlag!

So this year I went back to SECUINSIDE. Thanks to beist, Ryan, trimo, and everyone else for the

great time in Seoul.

The presentation is a new one, made in just a week. It’s essentially an introduction to EFI reverse

engineering and hunting for EFI rootkits.

I received yesterday the good news that I was accepted to do the same presentation at 44Con. This

is great and I will have enough time to improve the slides. Probably add some new content and tools,

since there is good stuff expected out of Thunderstrike 2 presentation.

And here they are:

BSides Lisbon 2015 – BadXNU, A rotten apple!

SECUINSIDE 2015 – Is there an EFI monster inside your apple?

As usual, enjoy and have fun.

fG!

https://reverse.put.as/

London and Asia EFI monsters tour! November 6, 2015Mac Reversing, Securitybackdoor, EFI, rootkit

Finally back home from China and Japan tour, so it’s time to finally release the updated slides about

EFI Monsters. After Secuinside I updated them a bit, fixing stuff I wasn’t happy with and adding some

new content.

Page 53: Reverse Engineering Mac OS X.pdf

The updated version was first presented at 44CON London. I had serious reservations about going to

the UK (not even in transit!) but Steve Lord and Adrian charm convinced me to give it a

try. 44CON was great and it’s definitely a must attend European conference. It has the perfect size to

meet people and share ideas. I prefer single track conferences, dual track is the max I’m interested

in. More than that it’s just too big, too messy, too many choices to be made regarding what to see.

A big thanks to everyone at 44CON who made it possible!

Next was SyScan360 in Beijing. It was the fourth time it happened, and my third time in a row. I do

like very much to go there because even with language barriers you can feel what’s happening there.

Bought a bunch of (cheap) hardware gear made by 360 Unicorn team. Their “usb condom” is super

cheap and super small. Also bought a network tap and a USB to serial (don’t really needed it but it

was damn cheap). The SyScan360 badge as usual was super fun, this time with a micro Arduino,

Bluetooth and LED modules. Conference went pretty smooth and had lots of fun. They had a gigantic

LED panel where slides were displayed at. That was some gigantic TV they had there

Big thanks to everyone involved in SyScan360 2015.

Last stop, was CODE BLUE happening in my current favorite city outside Portugal, aka Tokyo. Third

time happening, my second in a row. Organization is top notch, everything goes smoothly. Congrats

to Kana, El Kentaro, Tessy, and everyone else involved.

This year it had two tracks, and a lot more attendees. It’s definitely a conference to put on your

calendar. The audience is super interested in learning. Japan is lagging behind in terms of security so

they are keen to finally catch up.

Some people approached me and shown some interested about (U)EFI security. This is great, that

was the goal of this presentation, to show people (U)EFI research isn’t that hard and that it is really

important its issues start to be fixed. We need to start building trustable foundations and not try to

solve everything in software on top of platforms we can’t really trust.

Last conference for the year is No cON Name happening in Barcelona next December.

For next year I already got something that hopefully I’ll be able to present at SyScan360 Singapore.

Their CFP is open and you should definitely think about submitting.

There were minor changes between 44CON and SyScan360/Code Blue slides. The latter included

more references than 44CON version and minor fixes.

Have fun,

fG!