Hunting for Evil Launch Daemons - Identifying Suspicious Behavior with Osquery
Last week, Malwarebytes posted an article highlighting new malware discovered by John Lambert (Microsoft), Patrick Wardle (Objective-See and Digita Security) and Adam Thomas (Malwarebytes), and sure enough, persistence using launchd is still a common thing.
macOS uses launchd to initialize processes and services on startup and on user login. These are called launch daemons and agents respectively, though functionally they are quite similar.
Since malware often needs to persist on systems, launchd is an obvious way to ensure required persistence. Additionally, since macOS malware is often quite basic compared to Windows malware (probably due to the fact that very few users run security software on Mac compared to Windows) more advanced methods are not always needed for malware to take hold.
In fact, this is used so often that roughly 2/3rds of the osquery osx-attacks.conf pack is made of queries looking for malicious launch items. Many of those launch items try to look like system items by using names that are similar to Apple, Flash, or common applications and browsers.
You can easily see the items that will run on your systems by using a query on the launchd table in osquery, and you can join it to other tables like the signature table, to know if they are signed with a legitimate certificate or not.
Almost every time malware is discovered for Mac, malicious launch items are involved. This makes monitoring your systems for suspicious launch items is a very time and cost effective practice for catching malware. It is especially effective because there is no dependence on signatures or definitions, so the same process works for finding old and new malware. Detecting suspicious behaviors pays off more than identifying very specific file signatures in the long run.
With osquery, you can easily create precise queries to find suspicious launch daemons:
select * FROM signature s JOIN launchd d ON d.program_arguments = s.path WHERE signed=0 AND d.run_at_load=1;
This query returns launchd programs that run at boot and are not signed.
That’s a good place to start and refine until you establish a baseline, and only have to worry about the recently added launch agents and daemons!