HackPorts was developed as a penetration testing framework with accompanying tools and exploits that run natively on Mac platforms. HackPorts is a ‘super-project’ that leverages existing code porting efforts, security professionals can now use hundreds of penetration tools on Mac systems without the need for Virtual Machines. Zzuf – Multi-Purpose Application Input Fuzzing Tool. Zzuf is a transparent application input fuzzing tool or fuzzer. Its purpose is to find bugs in applications by corrupting their user-contributed data (which more than often comes from untrusted sources on the Internet). It works by intercepting file and network operations and changing random bits in the program’s input.
With each new macOS release, new APIs are added. Due to the wide range of platforms that Firefox runs on,and due to the wide range of SDKs that we support building with,using macOS APIs in Firefox requires some extra care.
- Using special encoding and fuzzing techniques lfifuzzploit will scan for some known and some not so known LFI filter bypasses and exploits using some advanced encoding/bypass methods to try to bypass security and achieve its goal which is ultimately, exploiting a Local file inclusion.In addition to LFIfuzzploit's fuzzing and encoding techniques, it also has built in methods for LFI exploitation including /proc/self/environ shell exploit, File descriptor shell and LFI shell via log injection.
- Tool for managing Unix network daemons on Mac OS X 10.5. A tool for managing Unix network daemon.on Mac OS X 10.5.
Availability of APIs, and runtime checks¶
First of all, if you use an API that is supported by all versions of macOS that Firefox runs on,i.e. 10.9 and above, then you don’t need to worry about anything:The API declaration will be present in any of the supported SDKs, and you don’t need any runtime checks.
If you want to use a macOS API that was added after 10.9, then you have to have a runtime check.This requirement is completely independent of what SDK is being used for building.
The runtime check should have the following form(replace 10.14
with the appropriate version):
@available
guards can be used in Objective-C(++) code.(In C++ code, you can use these nsCocoaFeatures
methods instead.)
For each API, the API declarations in the SDK headers are annotated with API_AVAILABLE
macros.For example, the definition of the NSVisualEffectMaterial
enum looks like this:
The compiler understands these annotations and makes sure that you wrap all uses of the annotated APIsin appropriate @available
runtime checks.
Frameworks¶
In some rare cases, you need functionality from frameworks that are not available on all supported macOS versions.Examples of this are Metal.framework
(added in 10.11) and MediaPlayer.framework
(added in 10.12.2).
In that case, you can either dlopen
your framework at runtime (like we do for MediaPlayer),or you can use -weak_framework
like we do for Metal:
Using new APIs with old SDKs¶
If you want to use an API that was introduced after 10.12, you now have one extra thing to worry about.In addition to the runtime check described in the previous section, you alsohave to jump through extra hoops in order to allow the build to succeed with older SDKs, becausewe need to support building Firefox with SDK versions all the way down to the 10.12 SDK.
In order to make the compiler accept your code, you will need to copy some amount of the API declarationinto your own code. Copy it from the newest recent SDK you can get your hands on.The exact procedure varies based on the type of API (enum, objc class, method, etc.),but the general approach looks like this:
See the Supporting Multiple SDKs docs for more information on the MAC_OS_X_VERSION_MAX_ALLOWED
macro.
Keep these three things in mind:
Copy only what you need.
Wrap your declaration in
MAC_OS_X_VERSION_MAX_ALLOWED
checks so that, if an SDK is used thatalready contains these declarations, your declaration does not conflict with the declaration in the SDK.Include the
API_AVAILABLE
annotations so that the compiler can protect you from accidentallycalling the API on unsupported macOS versions.
Our current code does not always follow the API_AVAILABLE
advice, but it should.
Enum types and C structs¶
If you need a new enum type or C struct, copy the entire type declaration and wrap it in the appropriate ifdefs. Example:
New enum values for existing enum type¶
If the enum type itself already exists, but gained a new value, define the value in an unnamed enum:
(This is an example of an interesting case: NSVisualEffectMaterialSelection
is available starting withmacOS 10.10, but it’s only defined in SDKs starting with the 10.12 SDK.)
Objective-C classes¶
For a new Objective-C class, copy the entire @interface
declaration and wrap it in the appropriate ifdefs.
I haven’t personally tested this. If this does not compile (or maybe link?), you can use the following workaround:
Define your methods and properties as a category on
NSObject
.Look up the class at runtime using
NSClassFromString()
.If you need to create a subclass, do it at runtime using
objc_allocateClassPair
andclass_addMethod
.Here’s an example of that.
Objective-C properties and methods on an existing class¶
If an Objective-C class that already exists gains a new method or property, you can “add” it to theexisting class declaration with the help of a category:
Functions¶
With free-standing functions I’m not entirely sure what to do.In theory, copying the declarations from the new SDK headers should work. Example:
I’m not sure what the linker or the dynamic linker do when the symbol is not available.Does this require __attribute__((weak_import))
annotations?
And maybe this is where .tbd files in the SDK come in? So that the linker knows which symbols to allow?So then that part cannot be worked around by copying code from headers.
Anyway, what always works is the pure runtime approach:
Define types for the functions you need, but not the functions themselves.
At runtime, look up the functions using
dlsym
.
Notes on Rust¶
If you call macOS APIs from Rust code, you’re kind of on your own. Apple does not provide any Rust“headers”, so there isn’t really an SDK to speak of. So you have to supply your own API declarationsanyway, regardless of what SDK is being used for building.
In a way, you’re side-stepping some of the build time trouble. You don’t need to worry about any#ifdefs
because there are no system headers you could conflict with.
On the other hand, you still need to worry about API availability at runtime.And in Rust, there are no availability attributeson your API declarations, and there are no@available
runtime check helpers,and the compiler cannot warn you if you call APIs outside of availability checks.
Next article: Friday Q&A 2015-05-29: Concurrent Memory Deallocation in the Objective-C Runtime
Previous article: Friday Q&A 2015-04-17: Let's Build Swift.Array
Tags: fridayqnafuzzingsecurity
With computer security high on everyone's minds these days, tools that help assess and improve the security of our code are extremely useful. Today I'm going to talk about one such tool, afl-fuzz
, which has seen a lot of attention lately and produces some interesting results. I'll discuss how it works and how to use it on your own code.
Fuzzing
American Fuzzy Lop, or afl-fuzz
, is a fuzzing tool. To understand what it does and how to use it, you must first understand the basic concept of fuzzing.
The idea is to stress-test programs by feeding them large quantities of computer-generated input. It's similar in concept to what you as a programmer might do when writing unit tests. You'd come up with what you think are some representative inputs and run them through the code being tested to make sure it behaves properly. With a fuzzer, the computer is generating the inputs and constantly trying your program with them. Rather than testing for correct output, it's searching for bugs that manifest in the form of crashes, hangs, or assertion failures.
There are different kinds of fuzzers, based on how they generate the input. It's possible to write a fuzzer that generates purely random inputs, but this won't produce useful results for many types of programs. An XML parser, for example, will return an error almost immediately when presented with pure random data, meaning that most of the code in the parser goes untested.
One way to improve on this is to give the fuzzer knowledge of what kind of structure the program expects. For the example of an XML parser, you might make a fuzzer that generates random legal XML documents and tests against that. Minor illegal variations on legal XML would help test error handling and edge cases. This can work extremely well, but requires a lot of domain-specific work to give the fuzzer knowledge of the program's inputs.
Another approach is to start with examples, and then produce test cases by mutating the examples by flipping bits, shuffling data around, inserting or removing data, or mixing data from multiple examples. Coming up with examples to give to the fuzzer is typically much less work than coding it to produce good examples on its own, but the fuzzer will be limited to things similar to the examples given.
afl-fuzz
afl-fuzz
is an open source fuzzer that runs on various UNIX-like OSes, including Mac OS X. It can be found here:
It takes an interesting approach that combines certain aspects of the usual approaches. It uses example inputs and mutation to generate new inputs. However, it intelligently guides these mutations using the target's behavior to select interesting inputs for further mutation.
The target's behavior is monitored by injecting additional code at compile time. afl-fuzz
provides a wrapper around either gcc
or clang
. If you compile a program using the wrapper, the resulting binary has the necessary instrumentation injected. (afl-fuzz
is also able to monitor uninstrumented binaries by running them in QEMU, but this currently doesn't work on the Mac.)
The instrumentation monitors branches taken by the program. Any given input to the program will produce a sequence of branches. Moving from one branch to another branch is a transition, and each transition is recorded. The fuzzer then favors inputs which generate new transitions, with the result that inputs are generated which explore new paths through the code. Unusual paths through the code are more likely to turn up bugs, and this helps generate those unusual paths.
In short, afl-fuzz
searches for inputs that make your program behave differently. When it finds such inputs, it then alters them further to see if it can make your program behave even more differently. This lets it cover large chunks of your program's potential execution space without any special knowledge of the structure of the input it expects.
Building
The source code for afl-fuzz
can be obtained from its web page above. It builds out of the box on Mac OS X by simply typing make
inside the source directory. It's a fairly small program and the build completes nearly instantaneously. The built binaries are placed in the source directory. You can install them wherever you want, or just leave them in place and run them using the appropriate path.
Creating Test Cases
You can't typically take an arbitrary program, compile it with afl-fuzz
, and run it in the fuzzer. It won't know how your program expects to receive input. To settle this question, afl-fuzz
passes inputs to the program by sending them on standard input. If your program doesn't already read from standard input, you'll need to write a small test harness that reads from stdin
and then invokes the code you want to fuzz.
If you're using Cocoa (and yes, afl-fuzz
works fine with Objective-C), such a test harness could be written like this:
Just what you do with the data is up to you. If you're testing a UTF-8 parser, you'd pass the data to your parser. If you have an image decoder, treat the data like an image and try to decode it. If you're testing a data structure, you could treat each line of input as a piece of data to insert or delete. Note that while non-instrumented libraries like the ones provided by the system will happily run under afl-fuzz
, the lack of instrumentation means that they're a black box to the fuzzer, and only the behavior of your own code informs its explorations. So don't try [NSImage imageWithData: data]
and expect anything interesting to happen.
Of course, it's not necessary to use NSFileHandle
like this. If your code deals with input byte-by-byte, calling getchar()
in a loop works nicely. If your code wants an NSStream
, create one from /dev/stdin
and pass it in.
Compiling Test Cases
To compile a test case, compile it as you normally would at the command line, but use the afl-clang
tool rather than the normal clang
command. afl-clang
will compile your code with clang
, inserting the necessary instrumentation as it goes along. For a simple test case that doesn't use anything besides the standard C library, you can compile it by just passing the name of the source file:
This creates an executable named a.out
which you can then run directly, or have the fuzzer work on.
For Objective-C programs, you need to also link against the Foundation framework, or the Cocoa framework if you use any of the UI parts:
If your test case encompasses multiple files, you can compile them all together by passing them all into the command:
If you prefer a name other than a.out
, you can specify it with -o
:
Runningafl-fuzz
needs at least example test case to start from. You can provide more than one if you like. The test cases are plain files that live in a subdirectory that you pass to the fuzzer. We'll call it testcases
:
Fuzzer results go in another subdirectory, which we'll call findings
. There's no need to create it, as afl-fuzz
will create it when needed. We can then run afl-fuzz
on the test case, giving it the testcases
and findings
directories:
This doesn't quite work properly on a standard Mac. afl-fuzz
distinguishes between hangs and crashes by waiting for a timeout to expire. If the program is still running when the timeout fires, it's considered a hang. If the program crashes before that, it's a crash. Otherwise it's a successful run. The problem is that the OS X crash reporter triggers when the test case crashes, and generating the crash report takes substantially longer than afl-fuzz
's default timeout of 20 milliseconds. This causes afl-fuzz
to report all crashes as hangs.
The best workaround for this would be to disable the system crash reporter while fuzzing. A simpler workaround is simply to increase the timeout, which can be done by passing the -t
flag. I used 20000 milliseconds instead of the default. This will seriously hurt performance if there are hangs, but works well enough:
This will present you with a nice terminal interface:
There's a lot of info here, most of which I'll leave you to explore on your own. There are a few bits that are particularly interesting:
uniq crashes
anduniq hangs
show how many crashes and hangs have been discovered so far. The moment these say something other than zero, you've found something interesting!last new path
is how long it's been since the fuzzer found a new path through the program. This is an indication of how much progress it's making. If it's been a long time since the fuzzer found a new path, it may no longer be able to find any interesting inputs.exec speed
is how quickly it's able to run test cases. The higher this number, the more test cases the fuzzer can run through, and the faster it can discover interesting things.
As the fuzzer runs, it places information in the findings
directory. Inputs that cause crashes are placed in files in the crashes
subdirectory. Inputs that cause hangs go in the hangs
subdirectory. The queue
subdirectory contains the inputs that are interesting but haven't caused any crashes or hangs, and can be used to get an idea of the fuzzer's progress, and what kind of inputs it's working with.
Example
I wrote a small test case that quickly exercises the fuzzer and verifies that it's working as expected. This test case reads standard input and calls abort()
if the input starts with deadbeef
:
The structure of this program is pretty weird. A single memcmp
call could be used instead of the massive nested if
statements. I wrote it this way to take advantage of the fact that the fuzzer instruments branches. With a memcmp
call, the fuzzer is unlikely to ever discover the magic deadbeef
input needed to crash the program. With each character checked separately, the fuzzer's branch instrumentation can discover them one by one.
The program also doesn't bother to check the length of the input. As a result, if it receives fewer than eight bytes, it'll end up reading garbage. Since the intent of the program is to have problems for the fuzzer to find, this really is a feature, not a bug.
Let's compile it and make sure it works as intended:
Perfect! Let's make a simple test case and get the fuzzer started:
Eventually, uniq crashes
goes to one; the fuzzer reports a crash. This took a couple of hours on my computer. We can see what input the fuzzer found to trigger a crash:
Exactly as expected.
It's interesting to look at the other interesting inputs the fuzzer generated along the way:
The progression shows up clearly, as the fuzzer works out the sequence byte by byte by seeing what triggers a new code path. There's some garbage at the end of some of the inputs, which is to be expected since it's a randomized process and surplus suffixes don't affect how the program operates.
This same principle works on more complex programs, with the fuzzer generating input that takes it down new and interesting paths in the code, and potentially finding bugs along the way.
Conclusion
There isn't much that's more entertaining than feeding crazy inputs to your programs and watching them crash. afl-fuzz
automates the process and makes it much more likely that something amusing will occur. It can help you make your code more secure too, which is a nice bonus.
That's it for today. Come back next time for more computer amusements. Friday Q&A is driven by reader suggestions, so if you have an idea for a topic to cover next time or any other time, please send it in!
WARNING: Fuzzing on MacOS X is slow because of the unusually high overhead of
fork() on this OS. Consider using Linux or *BSD. You can also use VirtualBox
(virtualbox.org) to put AFL inside a Linux or *BSD VM.
Should this advice be followed? Or is this just a symptom of the CrashReporter issue mentioned in the article.
launchctl unload -w /System/Library/LaunchAgents/com.apple.ReportCrash.plist
sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.ReportCrash.Root.plist
Add your thoughts, post a comment:
Fuzzing Tool For Mac Os X 10.10
Spam and off-topic posts will be deleted without notice. Culprits may be publicly humiliated at my sole discretion.
Fuzzing Tool For Mac Os X 10.13
JavaScript is required to submit comments due to anti-spam measures. Please enable JavaScript and reload the page.