- TTP Catalog
- Credential Access
- T1552.001
T1552.001
Credentials In Files
What it is
Storing a credential in a plain, readable file is a shortcut nearly every environment takes somewhere. A config file with a database password, a script with an embedded API key, a saved SSH config pointing at a private key path: these are all files created for entirely ordinary, practical reasons, not carelessness in the usual sense.
The problem is that a file's convenience for an administrator is identical to its convenience for an attacker. Once a credential is written in plain text, whoever can read that file has the credential, full stop, regardless of whether they're the person the file was written for.
How adversaries use it
Once an attacker has any foothold on a system, one of the highest-value, lowest-effort next steps is simply looking for files known to commonly hold credentials: configuration files, deployment scripts, saved connection strings, cloud CLI config directories, SSH client configs. None of this requires exploiting anything, since reading a file the attacker's current account already has permission to read isn't a vulnerability in the technical sense.
What makes this technique so persistently effective is scale. A single found credential can open access far beyond the system it was found on, especially when it belongs to a service account or gets reused across multiple systems, which happens constantly in real environments.
Detect it in Elastic
Data source: host file-access audit records naming the process and account that opened the credential file, plus process-execution telemetry for that reader
Key fields
A read of a watched credential file appears as an auditd file-access event with event.action: "opened-file", one per read regardless of the program; Windows has no file-read event, so the reading command's process creation (Sysmon Event ID 1), its PowerShell script block (Event 4104) or a file-share access on the server (Security Event 5145) stands in. Key fields: file.path, event.action (opened-file for a read, checked-metadata-of for a stat or listing), process.name and process.args (the reader), process.parent.name (its lineage), user.name, and on Windows winlog.event_data.CommandLine or the 4104 script block text.
Example event
{
"file": {
"path": "/etc/example-app/settings.json"
},
"host": {
"name": "web-example"
},
"user": {
"name": "svc_ledger"
},
"event": {
"action": "opened-file",
"module": "auditd"
},
"process": {
"args": [
"cat",
"/etc/example-app/settings.json"
],
"name": "cat",
"parent": {
"name": "dash"
}
}
}Prebuilt Elastic rule: No dedicated prebuilt rule (file-access monitoring plus hunting-command detection)
Hunt guidance
Reading a file is not one action, and each method leaves different evidence. On Linux, any read of a file under an audit read watch produces one file-access event (opened-file) naming the reader, its arguments and the full path, whether the reader is cat, less, grep, a script's open(), or a copy by cp, scp or tar; a stat or directory listing produces a metadata-only event (checked-metadata-of) with no bytes read; an editor adds write-side events (a rename, an open for write) if the file is saved; a read from inside the owning process, a web shell reading in-process included, carries the application's own process name, so only the parent and session lineage distinguish it; and a read of an unwatched file leaves no file event at all, only the reader's process-execution record, with the path present only if it was an argument.
On Windows there is no file-read event unless object-access auditing (4663) with a SACL is configured, and Sysmon has none. cmd built-ins such as type and copy create no process and log nothing unless the whole command was passed to a fresh cmd.exe /c; separate binaries (findstr, more, notepad, certutil, xcopy, robocopy) produce a process-creation event with the path in the command line; PowerShell cmdlets (Get-Content, Select-String, Copy-Item) create no process, so their evidence is the script block text in Event 4104; a read or copy over SMB produces Event 5145 on the file server, one per file; and a local copy produces a file-creation event only if the destination extension or path is on the file telemetry's allowlist.
Hunt both halves. Where a read watch exists, start from file-access events on the credential paths, keep only genuine open-for-read actions (a listing satisfies "credential file touched by a non-owner process" without a byte being read), exclude the owning process by name, read the lineage, and confirm that the watch really records reads of the files inside a watched directory, since on some setups only per-file watches do. Where none exists, hunt the reader: process arguments and command lines, and script block text, for recursive searches carrying credential keywords (password, secret, token, key, connectionstring). Then follow the credential to its first use: a database connection as the role the file holds, a file-share session or logon as the account, or an application login. A read followed within minutes by a first-ever use of that credential from the same host, or a hunting command immediately followed by a read of a file it would have surfaced, is far stronger than either alone.
Ruling out legitimate activity
Configuration files legitimately contain credentials constantly, and legitimate processes read them constantly as part of normal operation. The presence of a readable credential file, or even the fact that it gets read, proves very little by itself.
The distinguishing signal isn't the file. It's who read it, and whether that account had any ordinary reason to. The application that owns a config file reading its own credentials is unremarkable; an unrelated account or process reading the same file, especially shortly after an unusual login or alongside a broader search pattern across the filesystem, is a different story entirely.
Two shapes look like credential access and are not. A metadata-only event (a stat, or a directory listing) is not a read: a backup agent, a file manager, or ls -la touches the attributes of every file in a directory and reads none of them. And the owning application re-reading its own configuration, on a timer or at restart, is the steady baseline every real read has to stand out against; exclude it by process name and lineage, not by removing the file from the watch.
References
Practice it
1 scenario in this catalog covers T1552.001. We don't name it — identifying the technique is part of what a scenario grades. Explore the dashboard →