linux-find.md (12106B)
1 --- 2 title: "Linux find Command" 3 description: "find recipes: by name/type/time/perm/size, SUID hunting, exec actions and pruning noisy paths." 4 category: linux-it 5 tags: [linux, cli, search] 6 tools: [find] 7 difficulty: intermediate 8 updated: "2026-08-09" 9 source: "vault:Linux/Find Command.md" 10 --- 11 12 # Linux find Command 13 14 ## Overview 15 16 `find` recursively searches directory trees for files and directories matching specified criteria (name, type, size, time, permissions, ownership) and optionally executes actions on results. Part of GNU findutils, it's essential for system enumeration, privilege escalation reconnaissance, and incident response. 17 18 **Key capabilities:** 19 1. Pattern-based file and directory matching 20 2. Time-based searches (modification, access, inode change) 21 3. Permission-based filtering (SUID/SGID, world-writable) 22 4. Command execution on matched files via `-exec` or pipe to `xargs` 23 5. Minimal overhead with powerful filtering capabilities 24 25 > **Prerequisites** 26 > 1. GNU findutils installed (standard on all major Linux distributions). 27 > 2. Read permission on target directories; root/sudo required for full system scans. 28 > 3. Current stable version: GNU findutils 4.10.0 (4.9.x widely deployed). 29 > 4. Understand Linux permission octal notation: SUID=4000, SGID=2000, world-writable=0002. 30 > 5. Timestamps depend on filesystem and mount options (`noatime`, `relatime`). 31 32 --- 33 34 ## General File Search 35 36 ### Basic Syntax 37 38 ```bash 39 find [starting-point...] [expression] 40 ``` 41 42 - **starting-point**: Directory path(s) to begin search (default: current directory) 43 - **expression**: Combination of tests, actions, and operators (evaluated left-to-right with implicit AND) 44 45 ### Search Criteria Options 46 47 | Flag | Description | Example | 48 |:---|:---|:---| 49 | `-name "pattern"` | Match filename (case-sensitive, wildcards `*`, `?`) | `find . -name "*.conf"` | 50 | `-iname "pattern"` | Case-insensitive `-name` | `find . -iname "*.PHP"` | 51 | `-type f/d/l/s/p` | File type: `f`=file, `d`=dir, `l`=symlink, `s`=socket, `p`=named pipe | `find / -type f` | 52 | `-size [+/-]n[cwbkMG]` | File size; `+`=greater, `-`=less; units `c`/`k`/`M`/`G` | `find / -size +100M` | 53 | `-user / -group` | Owner / group name or UID/GID | `find /home -user john` | 54 | `-perm mode` | Exact permissions match | `find . -perm 0644` | 55 | `-perm -mode` | All permission bits set | `find / -perm -4000` | 56 | `-perm /mode` | Any permission bit set | `find / -perm /6000` | 57 | `-maxdepth n` | Limit traversal depth | `find /etc -maxdepth 2` | 58 | `-mindepth n` | Start traversal at depth n | `find / -mindepth 2` | 59 | `-xdev` | Stay on same filesystem (do not descend into other mounts) | `find / -xdev` | 60 | `-empty` | Empty files or directories | `find /tmp -empty` | 61 62 ### Output Control Options 63 64 | Flag | Description | Use Case | 65 |:---|:---|:---| 66 | `-print` | Print full path (default) | Standard output | 67 | `-print0` | Null-delimit output | Safe piping to `xargs -0` | 68 | `-printf "format"` | Custom output format | `%M` mode, `%u` user, `%T+` timestamp | 69 | `-ls` | Long listing format | Permissions, owner, size, date | 70 71 ### Basic Search Examples 72 73 ```bash 74 # Find files by name (case-insensitive) 75 find /home -iname "*.conf" -type f 76 77 # Find files larger than 100MB 78 find / -type f -size +100M 2>/dev/null 79 80 # Find files modified between two dates 81 find /data -newermt "2025-12-01" ! -newermt "2026-01-01" 82 83 # Find and delete empty directories 84 find /tmp -type d -empty -delete 85 ``` 86 87 > **Warning —** `-delete` is immediate and irreversible. Test with `-print` before using `-delete` to verify targets. `-delete` implies `-depth` traversal. 88 89 --- 90 91 ## SUID/SGID and World-Writable File Discovery 92 93 Files with SUID/SGID bits or world-writable permissions are high-value targets for privilege escalation: 94 95 1. **SUID (Set User ID)**: Executes with file owner's privileges (typically root) 96 2. **SGID (Set Group ID)**: Executes with file group's privileges 97 3. **World-writable**: Any user can modify the file 98 4. Cross-reference SUID binaries with GTFOBins (https://gtfobins.github.io/) for exploitation paths 99 5. World-writable config files in `/etc` are critical escalation vectors 100 101 ### SUID/SGID Discovery Commands 102 103 ```bash 104 # Find SUID files (at least the SUID bit set) 105 find / -type f -perm -4000 2>/dev/null 106 107 # Find SGID files 108 find / -type f -perm -2000 2>/dev/null 109 110 # Find SUID or SGID files 111 find / -type f \( -perm -4000 -o -perm -2000 \) 2>/dev/null 112 113 # SUID binaries owned by root (common priv-esc targets) 114 find / -type f -perm -4000 -user root 2>/dev/null 115 116 # SUID/SGID with detailed output 117 find / -type f \( -perm -4000 -o -perm -2000 \) -exec ls -la {} \; 2>/dev/null 118 ``` 119 120 > **Note —** The `-` prefix on `-perm` means "at least these bits" — the file may have additional permissions. Common legitimate SUID binaries: `/usr/bin/passwd`, `/usr/bin/sudo`, `/bin/ping`. Compare output against a baseline for anomaly detection. 121 122 ### World-Writable Discovery Commands 123 124 ```bash 125 # Find world-writable files 126 find / -type f -perm -0002 2>/dev/null 127 128 # Find world-writable directories (sticky bit often expected) 129 find / -type d -perm -0002 2>/dev/null 130 131 # World-writable files excluding /proc and /sys 132 find / -path /proc -prune -o -path /sys -prune -o -type f -perm -0002 -print 2>/dev/null 133 134 # World-writable directories without sticky bit (dangerous) 135 find / -type d \( -perm -0002 -a ! -perm -1000 \) 2>/dev/null 136 ``` 137 138 > **Danger —** A world-writable directory without the sticky bit (octal 1000) allows any user to delete any file. Writable files owned by root, and writable configs sourced by privileged processes, are the highest priority. World-writable dirs like `/tmp` normally carry the sticky bit. 139 140 > **OPSEC considerations** 141 > 1. Full system scans generate high I/O and may trigger file-integrity monitoring (AIDE, OSSEC). 142 > 2. Redirect stderr (`2>/dev/null`) to avoid permission-denied noise. 143 > 3. Use `-maxdepth` to limit scope and reduce detection surface. 144 > 4. Combine with `-xdev` to avoid traversing network mounts. 145 146 --- 147 148 ## Command Execution with `-exec` and `xargs` 149 150 Three primary methods for executing commands on found files: 151 152 1. **-exec cmd {} \;**: Forks command once per file (slower, more visible) 153 2. **-exec cmd {} +**: Batches files into single command invocation (faster, less visible) 154 3. **find | xargs**: Batches via pipe, respects ARG_MAX, supports parallelism 155 156 ### Execution Method Comparison 157 158 | Method | Behaviour | Performance | Use Case | OPSEC Impact | 159 |:---|:---|:---|:---|:---| 160 | `-exec cmd {} \;` | Forks cmd once per file | Slow | Small sets, complex per-file logic | High (many processes) | 161 | `-exec cmd {} +` | Batches files into one cmd | Fast | Large sets, simple commands | Low (few processes) | 162 | `find \| xargs` | Batches via pipe | Fast | Very large sets, custom batching | Low (few processes) | 163 | `find -print0 \| xargs -0` | Null-delimited batching | Fast | Filenames with spaces/newlines | Low (safe handling) | 164 | `xargs -P N` | Parallel execution | Fastest | CPU-bound operations | Medium (concurrent processes) | 165 166 ### `-exec` Examples 167 168 ```bash 169 # -exec with \; (one command per file – slower) 170 find . -type f -name "*.log" -exec rm {} \; 171 172 # -exec with + (batched arguments – faster) 173 find . -type f -name "*.log" -exec rm {} + 174 175 # Grep for pattern in PHP files (batched) – potential webshell indicator 176 find /var/www -type f -name "*.php" -exec grep -l "eval(" {} + 177 178 # Change ownership in batches 179 find /data -type f -exec chown appuser:appgroup {} + 180 ``` 181 182 ### `xargs` Examples 183 184 ```bash 185 # Pipe to xargs (batched, handles large sets) 186 find . -type f -name "*.log" -print0 | xargs -0 rm 187 188 # xargs with parallelism (up to 4 concurrent processes) 189 find . -type f -name "*.log" -print0 | xargs -0 -P 4 rm 190 191 # Safe delete with confirmation (interactive) 192 find . -name "*.tmp" -print0 | xargs -0 -p rm 193 194 # Compress logs older than 30 days (parallel) 195 find /var/log -type f -mtime +30 -name "*.log" -print0 | xargs -0 -P 4 gzip 196 ``` 197 198 > **Note —** The `-print0 | xargs -0` pairing is critical for safe handling of filenames with spaces, newlines, or special characters. 199 200 ### Additional `xargs` Options 201 202 | Flag | Description | Example Use | 203 |:---|:---|:---| 204 | `-n N` | Max N arguments per invocation | `xargs -n 1` processes one file at a time | 205 | `-r` | Don't run if input is empty | Prevents errors when find returns nothing | 206 | `-I {}` | Replace string placeholder | `xargs -I {} mv {} /backup/` | 207 | `-t` | Print command before executing | Debugging and logging | 208 | `--show-limits` | Display ARG_MAX and buffer sizes | System capability check | 209 210 > **Common errors** 211 > 1. Missing `-0` with xargs when filenames contain spaces — always use the `-print0 | xargs -0` pairing. 212 > 2. Forgetting `\;` or `+` at the end of `-exec` — required terminator. 213 > 3. Using `-delete` before other predicates — evaluation order matters; `-delete` implies `-depth`. 214 > 4. Forgetting `-r` with xargs when find returns nothing. 215 216 --- 217 218 ## Time-Based File Searches 219 220 `find` supports three timestamp types for file matching: 221 222 1. **mtime**: File modification time (content changed) 223 2. **atime**: File access time (content read) 224 3. **ctime**: Inode change time (metadata changed — permissions, ownership, name) 225 4. Each has day-based (`-mtime`) and minute-based (`-mmin`) variants. 226 227 ### Time Predicate Syntax 228 229 | Predicate | Meaning | Measurement Unit | 230 |:---|:---|:---| 231 | `-mtime n` | Modified exactly n days ago | 24-hour periods | 232 | `-mtime +n` | Modified more than n days ago | 24-hour periods | 233 | `-mtime -n` | Modified within last n days | 24-hour periods | 234 | `-atime n/+n/-n` | Access time variants | 24-hour periods | 235 | `-ctime n/+n/-n` | Inode change time variants | 24-hour periods | 236 | `-mmin n/+n/-n` | Modification time | Minutes | 237 | `-amin n/+n/-n` | Access time | Minutes | 238 | `-cmin n/+n/-n` | Inode change time | Minutes | 239 | `-newermt "date"` | Modified after specified date | ISO 8601 format | 240 | `-newer reference` | Modified more recently than file | File comparison | 241 | `-daystart` | Measure from start of today | Changes reference point | 242 243 ### Time-Based Search Examples 244 245 ```bash 246 # Files modified in the last 24 hours (rolling window from now) 247 find /var/log -type f -mtime 0 248 249 # Files modified more than 30 days ago 250 find /tmp -type f -mtime +30 251 252 # Files modified in the last 60 minutes 253 find /home -type f -mmin -60 254 255 # Files modified yesterday (calendar day, using -daystart) 256 find /data -daystart -mtime 1 -type f 257 258 # Files modified between two dates 259 find /logs -newermt "2025-12-01" ! -newermt "2025-12-31" 260 261 # Files modified more recently than a reference file 262 find /app -newer /app/deploy.timestamp 263 ``` 264 265 > **Note —** `-mtime 0` is a rolling 24-hour window, not "today". For calendar-day semantics use `-daystart -mtime 0`, and `-daystart` must appear before `-mtime` in the expression. Variants: `-anewer` (atime), `-cnewer` (ctime). 266 267 ### Advanced Time-Based Queries 268 269 ```bash 270 # Files modified today (calendar day) with ISO timestamp output 271 find /var/log -type f -daystart -mtime 0 -printf "%T+ %p\n" 272 273 # Files NOT accessed in the last 90 days (candidates for archival) 274 find /archive -type f -atime +90 -ls 275 ``` 276 277 > **Forensic considerations** 278 > 1. Recursive `find` on atime can update atime and modify the evidence you're searching. 279 > 2. `noatime` / `relatime` mounts make atime stale — verify with `mount | grep atime`. 280 > 3. Timestomping: adversaries modify timestamps, so mtime/atime are less reliable. 281 > 4. ctime cannot be modified by standard tools — more forensically reliable than mtime/atime. 282 283 > **Performance tips** 284 > 1. Combine time predicates with `-type` early in the expression for faster evaluation. 285 > 2. Use `-maxdepth` to limit search scope. 286 > 3. Redirect stderr (`2>/dev/null`) to avoid permission-denied overhead. 287 > 4. Use `-xdev` to avoid crossing mount points and network filesystems. 288 289 --- 290 291 ## References 292 293 1. GNU findutils Manual — https://www.gnu.org/software/findutils/manual/html_mono/find.html 294 2. Linux find Man Page — https://man7.org/linux/man-pages/man1/find.1.html 295 3. GTFOBins — https://gtfobins.github.io/ 296 4. MITRE ATT&CK: File and Directory Discovery (T1083) 297 5. HackTricks: Linux Privilege Escalation