{
	"id": "ae3f5cde-530c-4098-b3a9-c0b22d120923",
	"created_at": "2026-04-06T03:37:16.573462Z",
	"updated_at": "2026-04-10T13:12:54.790246Z",
	"deleted_at": null,
	"sha1_hash": "0dc6084c1d1d830191ad1cd604a9d695d1c04a55",
	"title": "Linux auditd for Threat Detection [Part 1]",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 1105812,
	"plain_text": "Linux auditd for Threat Detection [Part 1]\r\nBy IzyKnows\r\nPublished: 2025-10-16 · Archived: 2026-04-06 03:17:11 UTC\r\n11 min read\r\nJan 26, 2022\r\nPart 2: Linux auditd for Threat Detection [Part 2]\r\nA few years ago, I was asked to define an auditd configuration which would serve as the primary detection\r\ntechnology for a large organization. While I had a fair understanding of Linux systems, I found surprisingly little\r\non utilizing auditd for at-scale security monitoring purposes.\r\nThe topics I look to cover in this article are\r\nQuick intro to the Linux Audit System\r\nTips when writing audit rules\r\nDesigning a configuration for security monitoring\r\nWhat to record with auditd\r\nTips on managing noise\r\nThe end audience for this article is the security practitioners, IR, blue teamers who need to define what logs they\r\nneed in their SIEM to detect bad activity. Going forward, I’m hoping things will get a lot easier with ePBF but\r\nwe’ve never been good at completely eradicating older technologies now have we? :)\r\nThere may be better ways to configure and utilize auditd, so feel free to share your experiences and I’d be happy\r\nto incorporate them here.\r\nThe Linux Audit System\r\nThe Linux Audit system provides a way to log events that happen on a Linux system. The recording options\r\noffered by the Audit system is extensive — process, network, file, user login/logout events, etc. In this series, I\r\nonly focus on the security-relevant events from a detection standpoint. The entire list of types that can be recorded\r\nare listed here. This list will come handy when analyzing logs.\r\nBy default, the packages required to use the Linux audit system are installed on many common distros. I won’t be\r\ncovering the installation part here. “auditd” is the audit daemon that leverages the Linux audit system to write\r\nevents to the disk. User-space applications make system calls which the kernel passes through certain filters and\r\nhttps://izyknows.medium.com/linux-auditd-for-threat-detection-d06c8b941505\r\nPage 1 of 8\n\nthen finally through the “exclude” filter. The filters are important when it comes to writing auditd rules. I really\r\nlike the diagram from Red Hat explaining it.\r\nAudit System Architecture (source: RedHat)\r\nBy default, log files are written to /var/log/audit/audit.log. You may find multiple audit.log files in the above\r\ndirectory all of particular max length.\r\nThe two files we particularly care about in the audit system are\r\naudit.rules: Tells the audit daemon what to record. This is where most of one’s time should go — deciding\r\nwhat events are most important to you\r\naudit.conf: Governs how the audit daemon runs. Log file location, buffer size, log rotation criteria, etc. I\r\nwould not mess around too much with this file, but we’ll get to some important parameters of it later\r\nThe above was just a brief intro. There are better explanations of the Audit subsystem out there. The link to\r\nRedHat’s docs I mentioned above is a good one. Other good documents I can recommend are — Understanding\r\nLinux Audit from Suse, The Linux Audit Documentation Project\r\nWriting Audit rules\r\nWhat you specify in the audit.rules file is what finally lands up in the audit.log. When the audit daemon (auditd) is\r\nstarted (ensure that’s on system startup), rules defined in this file is what gives you events in the audit.log.\r\nThe audit.rules file is located in /etc/audit/audit.rules. This is the final file that auditd refers when writing events to\r\ndisk. Why I mention this is because, as mentioned at the top of the etc/audit/audit.rules file,\r\n## This file is automatically generated from /etc/audit/rules.d\r\nThat doesn’t mean you can’t add to it manually. However, in a large environment, you may have different rule\r\nfiles managed by different parties, that need to run together. You can place these different *.rules files within the\r\n/etc/audit/rules.d/ directory. A utility named augenrules does the job of compiling the different *.rules files (in\r\nhttps://izyknows.medium.com/linux-auditd-for-threat-detection-d06c8b941505\r\nPage 2 of 8\n\nnatural sort order) into the final /etc/audit/audit.rules . It’s worth mentioning that augenrules strips away comments\r\nand empty lines before generating audit.rules. I mention this as I’ve been accused of ridiculous things on this point\r\nin the past.\r\nComing to the rule writing. Let’s focus on the 2 types of rules that we’re interested in configuring\r\nFile watches — can watch read, write, execute or attribute changes\r\nSyscalls — record syscalls sent to the kernel by the application\r\nI’m not going to go into the depths of writing auditd rules, I recommend reading the man page for that.\r\nSome good references to look into when writing rules\r\nInbuilt examples — https://github.com/linux-audit/audit-userspace/tree/master/rules\r\nMITRE-based rules — https://github.com/bfuzzy/auditd-attack/tree/master\r\nFlorian Roth’s best practice ruleset — https://github.com/Neo23x0/auditd\r\nhttps://slack.engineering/syscall-auditing-at-scale/\r\nA few notes to keep in mind when building rules\r\nUtilize tagging — audit rules allow for tagging (-k) which is very helpful when analyzing events later. You\r\ncould use your own custom tags or even tag rules with MITRE IDs (see bfuzzy1’s link above)\r\nWhen monitoring syscalls, it’s often better to monitor the syscall upon exit rather than entry (-always,exit).\r\nImportant parameters may not be available at the time of function entry because they aren’t defined yet,\r\nhence you miss it too\r\nWhen doing syscall auditing always try to combine rules where you can (-a always,exit -S rmdir -S unlink\r\n-S rename). Each syscall rule gets evaluated for every syscall every program makes. It adds up, thereby\r\nimpacting performance\r\nWhen writing rules, you may come across a filter criteria ‘-F auid!=4294967295’. This number is the\r\nequivalent of 0xFFFFFFFF which is the highest unsigned int number. It evaluates to -1 which in auditd\r\nworld is equivalent to “unset”, i.e, it’s not defined yet. It’s common for this to happen with processes that\r\ninitialize before the audit daemon. As in the audit.rules man page —\r\n“The audit system considers uids to be unsigned numbers. The audit\r\nsystem uses the number -1 to indicate that a loginuid is not set.\r\nThis means that when it’s printed out, it looks like 4294967295.\r\nBut when you write rules, you can use either “unset” which is\r\neasy to remember, or -1, or 4294967295. They are all equivalent.”\r\nA note on exclusions — ensure your exclusions (-a never,exclude) are specific and placed on top so they\r\nget matched first. There are debates on whether specific includes or excludes should go on top. Specific\r\nincludes would be the best for performance but I don’t find this a feasible option for large environments.\r\nOften times, the syscalls (like execve) are where you gain maximum visibility from a security detection\r\nperspective. Were you to place them right on top, you’d have little scope for performance tuning thereafter.\r\nConfiguration of the Audit Daemon\r\nhttps://izyknows.medium.com/linux-auditd-for-threat-detection-d06c8b941505\r\nPage 3 of 8\n\nGet IzyKnows’s stories in your inbox\r\nJoin Medium for free to get updates from this writer.\r\nRemember me for faster sign in\r\nThere are two places you can specify configurations for the audit daemon. One place is directly at the top of the\r\naudit.rules file as control rules and the other is in the audit.conf file. The configurations you can modify in both\r\nthese places differ. Unless for a specific reason, I would not toy with the configurations of auditd except for a few\r\nBacklog size: This can be set by using the -b keyword followed by the number of audit messages to buffer. The\r\nbacklog option limits the number of messages that can be queued up waiting to be written to the log. Here, it’s\r\nrecommended you start with the default and work your way up. On production level systems, I’ve seen 8192 as a\r\nfeasible option. When the limit is breached, you should see a “backlog limit exceeded” in the logs and that can be\r\nyour indicator to increase this buffer. Keep in mind, the higher buffer value, the more memory consumption on the\r\nsystem\r\nFailure flag: Set with -f, it instructs auditd what to do when the above buffer is full. In production systems, you\r\ndefinitely don’t want disruption hence I recommend setting this to 1 so it prints in the log and nothing else\r\nEnabled flag: Set with -e, can help ensure your audit configuration is not modified, i.e, immutable. Setting it to 2\r\nwill ensure that any changes will be denied and logged. Only a restart of the system can change the configuration.\r\nRemember, ensure this rule is the last rule in your ruleset.\r\nThese are all the configuration files I’d recommend modifying. The audit.conf file you can have a look into but\r\nnormally, I wouldn’t change too much there. Depending upon your environment and if you want the auditd\r\ndaemon to deal with, for example, what to do when the log file is full, the audit.conf file may be worth looking\r\ninto. Normally, the default value have worked for me.\r\nWhat to record?\r\nThe Linux audit subsystem allows us to record a lot of information. The question I look to provide some clarity to\r\nis — what’s worth recording and what isn’t?\r\nFirst, let’s have a look at the most relevant data sources from MITRE.\r\nPress enter or click to view image in full size\r\nhttps://izyknows.medium.com/linux-auditd-for-threat-detection-d06c8b941505\r\nPage 4 of 8\n\nSource: https://github.com/mitre-attack/attack-datasources\r\nThe above graph is based on Mitre’s ATT\u0026CK Enterprise framework and the data source that is associated by\r\ndifferent sub-techniques in the framework. As we can see, majority of the techniques are detectable by command\r\nexecution and process creation events. Reason being, these normally contain the command line executed.\r\nComing back to auditd, majority of these data sources can be monitored by auditd (at a price of course). Here’s\r\nmy shot at correlating the above list with auditd record types that give us this information.\r\nPress enter or click to view image in full size\r\nData Source to Audit Rule Mapping\r\nBecause Medium is a bitch with formatting, the above is an image. I’ve also uploaded the same here:\r\nhttps://github.com/izysec/linux-audit/blob/main/DS-to-audit.MD\r\nThe above list is just to help you prioritize your rule writing better. Auditd in general is going to be noisy and\r\nwhile exact impacts depends on your environment, if you had to keep a ballpark figure in mind, I would start with\r\na 10–15% increase in terms of CPU utilization when running a security-centric auditd configuration on\r\n“production” systems. We can go into the nuances of the % and what “production” means but it’s only a ballpark\r\nfigure to help you set expectations right. Have no doubts, auditd is heavy, and depending on the nature of the\r\nendpoint you deploy on, you can experience “heavy” in different flavors. For example, there was once a\r\nproduction database server. The servers job was just to store data for a large public facing web application with\r\nhttps://izyknows.medium.com/linux-auditd-for-threat-detection-d06c8b941505\r\nPage 5 of 8\n\nseveral users. This meant millions of database write operations in a day. The auditd daemon was eating CPU and\r\nspecifically the rules that monitored file-action related syscalls (open, for example). Since the nature of the\r\ndatabase was to constantly perform several file operations, not only was the audit.log being filled up with\r\nminimally useful entries but everytime a file operation occurred (essentially always), auditd was monitoring and\r\nwriting it into the logs. It’s lucky that size of the audit.log is capped else, we would’ve had space issues as well.\r\nEventually, we decided the best course of action was to write exclusions for certain folders that we know were\r\nconstantly being written to and we placed this rule on top of the one causing issues so it would match first. We\r\nsaw similar impacts with servers of a different business nature with regard to network-related syscalls (connect).\r\nWhile I can’t tell you exactly how auditd would impact your production environment, I can share stories of how it\r\nimpacted mine in hopes it will evoke premature thoughts about yours. I would say the major performance\r\nconsideration with auditd comes in the form of CPU utilization and memory consumption but it may be hard to\r\ndetermine until you actually see it in effect. My advice here is work with your operations team, explain to them\r\nthe potential issues auditd could cause and their knowledge of the environment may help you arrive at a near-reality expectation of production-behavior. Configure acceptable auditd log file sizes, consider capping auditd\r\nresource utilization (check nice, cpulimit and cgroups), roll out in batches and finally learn and adapt the\r\nconfiguration based on the learning.\r\nBecause auditd has so many different record types, monitoring all of them may not feasible. I’ve found that, aside\r\nfrom being strict with the rules you write, excluding message types can also be a nice way to trim down noise.\r\nHere are some message types I think are worth monitoring from a security perspective\r\nPress enter or click to view image in full size\r\nhttps://izyknows.medium.com/linux-auditd-for-threat-detection-d06c8b941505\r\nPage 6 of 8\n\nWhen writing your audit.rules, it makes sense to explicitly specify which record types are not to be recorded.\r\nWe’ve found this as a good way to reduce noise from auditd. You can do so using rules like the following\r\n-a never,exclude -F msgtype=ANOM_ACCESS_FS\r\nEach line in the auditd rule file gets evaluated sequentially and while compounding rules are generally a good idea\r\nfrom a performance perspective, unfortunately you cannot do that with the above exclusions. So for each msgtype\r\nyou’d like to exclude, you’d need to have it as a separate line. Thanks to sqall01 for pointing it out.\r\nPress enter or click to view image in full size\r\nAs for syscalls, the auditd engine intercepts each syscall that a program makes, attempting to match it against\r\nsystem call rules. These rules, when sent to the kernel, the syscall fields are all put into a mask so that one\r\ncompare can determine if the syscall matches or not. So in that case, combining syscalls in one rule is efficient.\r\nAdditionally, you can find the entire list of record types here. The list of main and auxiliary record types can also\r\nbe found directly in the code.\r\nAn event on a Linux system may trigger multiple auditd events. There is one primary event followed by auxiliary\r\nevents of different record types. These auxiliary events have supporting information for the event. The number of\r\nauxiliary records an event may have depends upon the path a syscall takes through the kernel and where auditd is\r\ndesigned to hook into it. At the moment, there’s no mapping table between syscall and record types generated that\r\nI know of so I started to make one. An example of an auxiliary record would be the record type CWD which you\r\nwill see many times in the audit.log. CWD records gives you the current working directory from the main event\r\nhttps://izyknows.medium.com/linux-auditd-for-threat-detection-d06c8b941505\r\nPage 7 of 8\n\ntook place. But you don’t see it on the list above. Reason being, while it can be useful to know this in some cases,\r\nit’s also not the most critical piece of information when you’re trying to detect bad activity and you can make\r\nsome good guesses based on the other data you have. We’re trying to even out security vs. feasibility and in doing\r\nso, you can’t have absolutely everything.\r\nClosing Notes\r\nI believe auditd can indeed be used as a means for detecting threats on Linux endpoints, even in large\r\nenvironments. Throwing in a pre-configured rule set from the internet will give you issues however. Knowing\r\nexactly what you want to monitor and eliminating noisy events using methods described in this post, will help you\r\narrive at a configuration suitable for even large, complex environments.\r\nThe approach I would take is this — monitor your syscalls of interest (execve, connect) right on top followed by\r\nthe files/directories of importance and then finally simpler rules to look for monitoring specific binaries like nmap,\r\ntshark, etc. if that’s important to you. When monitoring files, don’t forget to monitor your auditd configuration as\r\nwell. You don’t want an attacker to get away after tinkering with your logging configs (Kudos to\r\nfor this point!)\r\nI plan to write another blog dedicated to tips to better analyze audit logs as well as share sample log snippets of\r\ndifferent activity types so you know what they look like in the logs. Not sure when I’ll get around to doing this,\r\ndepends on how useful this one is to begin with. Thanks for making it this far :)\r\nSuggestions/feedback, please drop me a message on Twitter.\r\n[22 October 2022] Yes, part 2 is in the making. Should be out in a few months :)\r\n[16 October 2025] Edit: Minor link fix (thanks PeterUprise!)\r\nLink to Part 2.\r\nSource: https://izyknows.medium.com/linux-auditd-for-threat-detection-d06c8b941505\r\nhttps://izyknows.medium.com/linux-auditd-for-threat-detection-d06c8b941505\r\nPage 8 of 8",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"MITRE"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://izyknows.medium.com/linux-auditd-for-threat-detection-d06c8b941505"
	],
	"report_names": [
		"linux-auditd-for-threat-detection-d06c8b941505"
	],
	"threat_actors": [],
	"ts_created_at": 1775446636,
	"ts_updated_at": 1775826774,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/0dc6084c1d1d830191ad1cd604a9d695d1c04a55.pdf",
		"text": "https://archive.orkl.eu/0dc6084c1d1d830191ad1cd604a9d695d1c04a55.txt",
		"img": "https://archive.orkl.eu/0dc6084c1d1d830191ad1cd604a9d695d1c04a55.jpg"
	}
}