ubuntils 1.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. ubuntils-1.5.0/LICENSE +21 -0
  2. ubuntils-1.5.0/PKG-INFO +562 -0
  3. ubuntils-1.5.0/README.md +525 -0
  4. ubuntils-1.5.0/pyproject.toml +60 -0
  5. ubuntils-1.5.0/setup.cfg +4 -0
  6. ubuntils-1.5.0/tests/test_base_classes.py +181 -0
  7. ubuntils-1.5.0/tests/test_cli.py +309 -0
  8. ubuntils-1.5.0/tests/test_collectors.py +553 -0
  9. ubuntils-1.5.0/tests/test_config.py +77 -0
  10. ubuntils-1.5.0/tests/test_detection_rules.py +474 -0
  11. ubuntils-1.5.0/tests/test_json_formatter.py +137 -0
  12. ubuntils-1.5.0/tests/test_remediators.py +489 -0
  13. ubuntils-1.5.0/tests/test_timeline.py +203 -0
  14. ubuntils-1.5.0/tests/test_tui.py +752 -0
  15. ubuntils-1.5.0/tests/test_utils.py +121 -0
  16. ubuntils-1.5.0/ubuntils/__init__.py +1 -0
  17. ubuntils-1.5.0/ubuntils/cli.py +195 -0
  18. ubuntils-1.5.0/ubuntils/collectors/__init__.py +19 -0
  19. ubuntils-1.5.0/ubuntils/collectors/base.py +11 -0
  20. ubuntils-1.5.0/ubuntils/collectors/cron.py +73 -0
  21. ubuntils-1.5.0/ubuntils/collectors/environment.py +86 -0
  22. ubuntils-1.5.0/ubuntils/collectors/network.py +58 -0
  23. ubuntils-1.5.0/ubuntils/collectors/processes.py +48 -0
  24. ubuntils-1.5.0/ubuntils/collectors/ssh.py +52 -0
  25. ubuntils-1.5.0/ubuntils/collectors/sudoers.py +75 -0
  26. ubuntils-1.5.0/ubuntils/collectors/systemd.py +77 -0
  27. ubuntils-1.5.0/ubuntils/collectors/users.py +74 -0
  28. ubuntils-1.5.0/ubuntils/detectors/__init__.py +3 -0
  29. ubuntils-1.5.0/ubuntils/detectors/engine.py +46 -0
  30. ubuntils-1.5.0/ubuntils/detectors/finding.py +38 -0
  31. ubuntils-1.5.0/ubuntils/detectors/rules.py +269 -0
  32. ubuntils-1.5.0/ubuntils/formatters/__init__.py +3 -0
  33. ubuntils-1.5.0/ubuntils/formatters/json_formatter.py +73 -0
  34. ubuntils-1.5.0/ubuntils/remediators/__init__.py +12 -0
  35. ubuntils-1.5.0/ubuntils/remediators/base.py +136 -0
  36. ubuntils-1.5.0/ubuntils/remediators/cron.py +34 -0
  37. ubuntils-1.5.0/ubuntils/remediators/environment.py +37 -0
  38. ubuntils-1.5.0/ubuntils/remediators/ssh.py +30 -0
  39. ubuntils-1.5.0/ubuntils/remediators/sudoers.py +42 -0
  40. ubuntils-1.5.0/ubuntils/timeline/__init__.py +1 -0
  41. ubuntils-1.5.0/ubuntils/timeline/builder.py +114 -0
  42. ubuntils-1.5.0/ubuntils/tui/__init__.py +6 -0
  43. ubuntils-1.5.0/ubuntils/tui/app.py +128 -0
  44. ubuntils-1.5.0/ubuntils/tui/confirm_modal.py +63 -0
  45. ubuntils-1.5.0/ubuntils/tui/findings_panel.py +166 -0
  46. ubuntils-1.5.0/ubuntils/tui/results_screen.py +106 -0
  47. ubuntils-1.5.0/ubuntils/tui/scan_screen.py +77 -0
  48. ubuntils-1.5.0/ubuntils/tui/stats_panel.py +44 -0
  49. ubuntils-1.5.0/ubuntils/tui/summary_screen.py +41 -0
  50. ubuntils-1.5.0/ubuntils/tui/timeline_panel.py +35 -0
  51. ubuntils-1.5.0/ubuntils/utils/__init__.py +0 -0
  52. ubuntils-1.5.0/ubuntils/utils/config.py +56 -0
  53. ubuntils-1.5.0/ubuntils/utils/logging.py +26 -0
  54. ubuntils-1.5.0/ubuntils/utils/shell.py +9 -0
  55. ubuntils-1.5.0/ubuntils/utils/since_parser.py +27 -0
  56. ubuntils-1.5.0/ubuntils/utils/validators.py +23 -0
  57. ubuntils-1.5.0/ubuntils.egg-info/PKG-INFO +562 -0
  58. ubuntils-1.5.0/ubuntils.egg-info/SOURCES.txt +60 -0
  59. ubuntils-1.5.0/ubuntils.egg-info/dependency_links.txt +1 -0
  60. ubuntils-1.5.0/ubuntils.egg-info/entry_points.txt +2 -0
  61. ubuntils-1.5.0/ubuntils.egg-info/requires.txt +10 -0
  62. ubuntils-1.5.0/ubuntils.egg-info/top_level.txt +1 -0
ubuntils-1.5.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 asmitdesai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,562 @@
1
+ Metadata-Version: 2.4
2
+ Name: ubuntils
3
+ Version: 1.5.0
4
+ Summary: Ubuntu incident response tool for forensic artifact collection, detection, and guided remediation
5
+ Author-email: Asmit Desai <asmitdesai02@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/asmitdesai/ubuntils
8
+ Project-URL: Repository, https://github.com/asmitdesai/ubuntils
9
+ Project-URL: Issues, https://github.com/asmitdesai/ubuntils/issues
10
+ Keywords: incident-response,forensics,ubuntu,security,dfir
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: System Administrators
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: POSIX :: Linux
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Security
23
+ Classifier: Topic :: System :: Monitoring
24
+ Requires-Python: >=3.9
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: click>=8.1.0
28
+ Requires-Dist: textual>=0.50.0
29
+ Requires-Dist: structlog>=24.0.0
30
+ Requires-Dist: pyyaml>=6.0
31
+ Requires-Dist: python-dateutil>=2.8
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest>=7.0; extra == "dev"
34
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
35
+ Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
36
+ Dynamic: license-file
37
+
38
+ # ubuntils
39
+
40
+ Forensic triage for live Ubuntu systems — automated artifact collection, persistence detection, and guided remediation in under 5 seconds.
41
+
42
+ [![CI](https://github.com/asmitdesai/ubuntils/actions/workflows/ci.yml/badge.svg)](https://github.com/asmitdesai/ubuntils/actions/workflows/ci.yml)
43
+ ![Python](https://img.shields.io/badge/python-3.9%2B-blue)
44
+ ![Tests](https://img.shields.io/badge/tests-240%20passing-brightgreen)
45
+ ![Coverage](https://img.shields.io/badge/coverage-90%25-green)
46
+ ![License](https://img.shields.io/badge/license-MIT-blue)
47
+ ![Arch](https://img.shields.io/badge/arch-amd64%20%7C%20arm64-lightgrey)
48
+ ![Ubuntu](https://img.shields.io/badge/ubuntu-20.04%20%7C%2022.04%20%7C%2024.04-orange)
49
+
50
+ ---
51
+
52
+ ## The Problem
53
+
54
+ When you suspect a Linux system is compromised, the first 30-40 minutes are usually spent running the same ten commands in sequence: check running processes, look for weird cron jobs, grep for LD_PRELOAD, scan authorized_keys for new entries, audit sudoers. Each step is manual, context-switching, and error-prone under pressure. Miss one source — say, `/etc/sudoers.d/` instead of just `/etc/sudoers`, or user crontabs in addition to `/etc/cron.d/` — and you have an incomplete picture.
55
+
56
+ Existing options don't solve this cleanly. `lynis` is a hardening auditor, not a triage tool — it reports configuration weaknesses on a clean system and generates noise on a hot one. `chkrootkit` and `rkhunter` check for known rootkit signatures but are blind to novel persistence techniques like abused systemd timers or legitimate-looking cron entries. Generic SIEM queries require log infrastructure that may not exist on the system you're looking at. And forensic suites like Volatility target memory images, not a live shell on a running host.
57
+
58
+ The gap is a tool that runs on the live system right now, covers the most common persistence vectors, correlates activity across log sources into a timeline, and tells you exactly what to look at — without requiring an external agent, a database, or an internet connection.
59
+
60
+ ---
61
+
62
+ ## What ubuntils does
63
+
64
+ ubuntils runs in four sequential stages:
65
+
66
+ 1. **Collection** — Eight collectors gather forensic artifacts concurrently from `/proc`, cron tables, systemd units, SSH keys, sudoers files, and environment definitions. Takes roughly 2.5 seconds on a typical system.
67
+ 2. **Detection** — A detection engine runs all eight rules over the collected artifacts, producing a ranked findings list in about one second.
68
+ 3. **Timeline** — A timeline builder reads syslog, journald, and auditd in parallel and correlates events chronologically, adding roughly 0.3 seconds.
69
+ 4. **Output** — Results appear either in an interactive four-tab TUI (default) or as structured JSON on stdout (`--json`).
70
+
71
+ No network calls are made. No data leaves the system.
72
+
73
+ ---
74
+
75
+ ## Installation
76
+
77
+ **Ubuntu 22.04+ and any system with PEP 668 (recommended):**
78
+
79
+ Ubuntu 22.04+ blocks system-wide `pip install`. Use `pipx` — it handles the environment transparently so you never think about it:
80
+
81
+ ```bash
82
+ sudo apt install pipx -y
83
+ cd ubuntils
84
+ pipx install -e .
85
+ ubuntils scan
86
+ ```
87
+
88
+ **Older systems / manual install:**
89
+
90
+ ```bash
91
+ git clone https://github.com/asmitdesai/ubuntils.git
92
+ cd ubuntils
93
+ pip install -r requirements.txt -e .
94
+ ubuntils scan
95
+ ```
96
+
97
+ ubuntils requires root for full artifact access. If you run `ubuntils scan` as a non-root user it will automatically re-invoke itself with `sudo`, preserving your PATH so the correct Python environment is used. Running without root will skip `/etc/shadow`, some `/proc` entries, and protected cron files, and will log warnings for each.
98
+
99
+ ---
100
+
101
+ ## Quick start
102
+
103
+ **Detection only — interactive TUI:**
104
+
105
+ ```bash
106
+ sudo ubuntils scan
107
+ ```
108
+
109
+ **Detection with JSON output saved to file:**
110
+
111
+ ```bash
112
+ sudo ubuntils scan --json > /tmp/triage-$(hostname)-$(date +%Y%m%d).json
113
+ ```
114
+
115
+ **Detection with CLI remediation preview (dry run — no changes applied):**
116
+
117
+ ```bash
118
+ sudo ubuntils scan --remediate
119
+ ```
120
+
121
+ **Detection with CLI remediation applied:**
122
+
123
+ ```bash
124
+ sudo ubuntils scan --remediate --confirm
125
+ ```
126
+
127
+ **Print version:**
128
+
129
+ ```bash
130
+ ubuntils version
131
+ ```
132
+
133
+ ---
134
+
135
+ ## Flags and configuration
136
+
137
+ ```
138
+ ubuntils scan [OPTIONS]
139
+ --json Output JSON to stdout instead of launching the TUI
140
+ --output FILE Write the JSON report to FILE (implies --json)
141
+ --remediate Run the remediation engine after detection
142
+ --confirm Required with --remediate to actually apply changes (else dry-run)
143
+ --config FILE YAML allowlist of findings to suppress (see below)
144
+ --since TIME Limit the timeline to events since TIME (e.g. '24h', '7d', '2026-05-20')
145
+ --verbose Verbose structlog output
146
+ ```
147
+
148
+ ### False-positive allowlisting (`--config`)
149
+
150
+ A freshly provisioned or CI-managed host generates expected noise — deploy keys, provisioning crontabs, baked-in shell init. Instead of teaching responders to mentally filter it, suppress it explicitly with a YAML allowlist:
151
+
152
+ ```yaml
153
+ # allowlist.yaml
154
+ allowlist:
155
+ rules:
156
+ - SHELL_RC_MODIFICATION # suppress this rule entirely
157
+ paths:
158
+ - /home/ci/.ssh/authorized_keys # suppress any finding on this exact path
159
+ ```
160
+
161
+ ```bash
162
+ sudo ubuntils scan --json --config allowlist.yaml
163
+ ```
164
+
165
+ Suppression is always explicit — by rule id and/or exact artifact path. There is no blanket "ignore everything" switch. A sample lives at [`examples/allowlist.yaml`](examples/allowlist.yaml).
166
+
167
+ ### Report integrity
168
+
169
+ Every `--json` report ends with a `report_sha256` field — a SHA-256 over the canonical report content. This makes a collected triage artifact tamper-evident and lets you reference a specific scan by digest in a case file. The report also records `tool_version`, `hostname`, and a UTC `generated_at` timestamp under `scan_metadata`.
170
+
171
+ ---
172
+
173
+ ## The TUI
174
+
175
+ Running `sudo ubuntils scan` (without `--json`) launches a full-terminal interactive TUI.
176
+
177
+ ### Scan screen
178
+
179
+ While collectors run, ubuntils shows a live checklist — one row per collector. Each row updates in real time as the collector finishes:
180
+
181
+ ```
182
+ Scanning system…
183
+
184
+ ✓ Process
185
+ ✓ Network
186
+ ✓ Users
187
+ ⠹ Cron
188
+ Systemd
189
+ SSH
190
+ Sudoers
191
+ Environment
192
+ ```
193
+
194
+ `✓` marks success, `✗` marks failure, the spinner marks the active collector, and blank rows are pending. Once all collectors finish and detection + timeline complete, the TUI switches automatically to the results screen.
195
+
196
+ ### Results screen
197
+
198
+ The results screen has four tabs navigated by number keys:
199
+
200
+ | Key | Tab | Contents |
201
+ |-----|-----|----------|
202
+ | `1` | Summary | Scan stats + top findings at a glance |
203
+ | `2` | Findings | Full findings list with inline detail and remediation |
204
+ | `3` | Timeline | Chronological correlated log events |
205
+ | `4` | Stats | Ubuntu version, architecture, duration, collector counts |
206
+
207
+ Press `q` or `Ctrl+C` to exit.
208
+
209
+ ### Summary tab (key `1`)
210
+
211
+ Shows scan metadata and the top findings on a single screen:
212
+
213
+ ```
214
+ Collectors: 8 run · 0 failed
215
+ Findings: 2 HIGH · 1 MEDIUM · 0 LOW
216
+ Timeline: 47 events
217
+ Duration: 2.8s
218
+
219
+ ● HIGH CRON_TMP_PATH /etc/cron.d/cleanup
220
+ ● HIGH LD_PRELOAD_INJECT /home/alice/.bashrc
221
+ ○ MED SSH_UNAUTHORIZED_KEY /home/bob/.ssh/authorized_keys
222
+ ```
223
+
224
+ On a clean system, this tab shows `System appears clean.`
225
+
226
+ ### Findings tab (key `2`)
227
+
228
+ A scrollable list of all findings, sorted HIGH → MEDIUM → LOW. Selecting a finding (Enter or arrow keys) expands a detail pane at the bottom showing the full description, artifact path, raw triggering value, and remediation information.
229
+
230
+ ```
231
+ HIGH CRON_TMP_PATH /etc/cron.d/cleanup
232
+ HIGH LD_PRELOAD_INJECT /home/alice/.bashrc
233
+ MED SSH_UNAUTHORIZED_KEY /home/bob/.ssh/authorized_keys
234
+ ───────────────────────────────────────────────────────
235
+ A cron job was found referencing /tmp, /var/tmp, or /dev/shm.
236
+ These directories are world-writable and commonly used as
237
+ attacker staging grounds.
238
+
239
+ Artifact: /etc/cron.d/cleanup
240
+ Raw: 0 * * * * root /tmp/.update
241
+ Fix: Will remove the offending cron entry from
242
+ /etc/cron.d/cleanup after creating a timestamped backup.
243
+
244
+ R: remediate
245
+ ```
246
+
247
+ ### In-TUI remediation
248
+
249
+ For findings that have automated remediation available, press `R` while the finding is selected. A confirmation modal appears:
250
+
251
+ ```
252
+ ┌─────────────────────────────────────────────────┐
253
+ │ Remediate CRON_TMP_PATH? │
254
+ │ │
255
+ │ Will remove the offending cron entry from │
256
+ │ /etc/cron.d/cleanup after creating a backup. │
257
+ │ Backup will be created at /var/backups/ubuntils/…│
258
+ │ │
259
+ │ Y: confirm Esc: cancel │
260
+ └─────────────────────────────────────────────────┘
261
+ ```
262
+
263
+ Press `Y` to confirm. The remediator runs in a background thread. When it completes, the finding row in the list is updated to `[fixed]` and the detail pane shows the outcome:
264
+
265
+ ```
266
+ ✓ Remediated
267
+ Backup: /var/backups/ubuntils/20260115_142201/etc_cron.d_cleanup
268
+ Rollback: cp /var/backups/ubuntils/20260115_142201/etc_cron.d_cleanup /etc/cron.d/cleanup
269
+ ```
270
+
271
+ On failure, the detail pane shows the error. The backup is always created before any change is attempted.
272
+
273
+ Press `Esc` to collapse the detail pane.
274
+
275
+ ### Timeline tab (key `3`)
276
+
277
+ A scrollable chronological list of correlated log events. Each row shows timestamp, source, and description. Events are pulled from syslog, journald, and auditd and deduplicated.
278
+
279
+ ### Stats tab (key `4`)
280
+
281
+ A summary view of the scan: detected Ubuntu version, architecture, scan duration, collector count and failures, and finding counts per severity alongside total timeline events.
282
+
283
+ ---
284
+
285
+ ## What it detects
286
+
287
+ | Rule ID | Severity | Remediable | What it checks |
288
+ |---|---|---|---|
289
+ | CRON_ROOT_EXEC | HIGH | Yes | Non-root user crontabs running commands in root-owned paths or inlining sudo |
290
+ | CRON_TMP_PATH | HIGH | Yes | Any cron job referencing /tmp, /var/tmp, or /dev/shm |
291
+ | LD_PRELOAD_INJECT | HIGH | Yes | LD_PRELOAD defined in /etc/ld.so.preload or any shell init file pointing outside /lib, /usr/lib, /lib64, /usr/lib64 |
292
+ | SUSPICIOUS_SYSTEMD_TIMER | HIGH | No | Systemd timers whose service ExecStart points to a world-writable directory or a path not owned by root |
293
+ | SSH_UNAUTHORIZED_KEY | MEDIUM | Yes | authorized_keys files modified within the last 7 days |
294
+ | USER_UID_ZERO | HIGH | No | Any account other than `root` with UID 0 (a hidden second superuser) |
295
+ | SUDOERS_NOPASSWD | MEDIUM | Yes | NOPASSWD sudoers grants for users with UID ≥ 1000 and a login shell |
296
+ | PROCESS_MASQUERADE | MEDIUM | No | Processes whose name matches a known system binary but whose executable path is outside /usr/bin, /usr/sbin, /bin, /sbin |
297
+ | SHELL_RC_MODIFICATION | LOW | No | Shell init files (bashrc, profile, zshrc, etc.) modified within the last 48 hours for any user with a login shell |
298
+
299
+ ### Why each rule exists
300
+
301
+ **CRON_ROOT_EXEC** — User crontabs run as the crontab owner. An entry invoking sudo or a root-owned interpreter means the user has arranged for code to run with root privileges on a schedule, without needing persistent sudo access. This survives password changes.
302
+
303
+ *Example finding:*
304
+ ```
305
+ [HIGH] CRON_ROOT_EXEC
306
+ Title: User crontab executing with sudo
307
+ Artifact: /var/spool/cron/crontabs/alice
308
+ Raw value: */5 * * * * sudo /usr/bin/python3 /tmp/beacon.py
309
+ Remediation: available
310
+ ```
311
+
312
+ **CRON_TMP_PATH** — World-writable directories like /tmp and /dev/shm are standard attacker staging grounds. A cron job pointing there means a payload can be swapped out between invocations without touching any persistent path.
313
+
314
+ *Example finding:*
315
+ ```
316
+ [HIGH] CRON_TMP_PATH
317
+ Title: Cron job references writable temp directory
318
+ Artifact: /etc/cron.d/cleanup
319
+ Raw value: 0 * * * * root /tmp/.update
320
+ Remediation: available
321
+ ```
322
+
323
+ **LD_PRELOAD_INJECT** — LD_PRELOAD causes the dynamic linker to load a specified shared library before all others, allowing arbitrary function interception in any dynamically linked binary. A value pointing outside standard library paths is a near-certain userspace rootkit indicator.
324
+
325
+ *Example finding:*
326
+ ```
327
+ [HIGH] LD_PRELOAD_INJECT
328
+ Title: LD_PRELOAD set to non-standard library path
329
+ Artifact: /home/alice/.bashrc
330
+ Raw value: export LD_PRELOAD=/tmp/.libssl.so
331
+ Remediation: available
332
+ ```
333
+
334
+ **SUSPICIOUS_SYSTEMD_TIMER** — Systemd timers are more persistent and less visible than cron jobs to most responders. A timer whose service unit executes from a temp directory or a user-owned path is a sign of attacker-created persistence. Flag-only — systemd unit removal requires human judgment.
335
+
336
+ *Example finding:*
337
+ ```
338
+ [HIGH] SUSPICIOUS_SYSTEMD_TIMER
339
+ Title: Systemd timer ExecStart points to suspicious path
340
+ Artifact: /etc/systemd/system/update-check.timer
341
+ Raw value: ExecStart=/tmp/.sys/update
342
+ Remediation: not available
343
+ ```
344
+
345
+ **SSH_UNAUTHORIZED_KEY** — A newly added SSH key grants persistent remote access independent of passwords. The 7-day window catches recent additions while avoiding noise from initial provisioning on older systems. Note: the rule uses file mtime, which reflects the last write to the authorized_keys file, not the insertion timestamp of each individual key.
346
+
347
+ *Example finding:*
348
+ ```
349
+ [MEDIUM] SSH_UNAUTHORIZED_KEY
350
+ Title: SSH authorized key added in last 7 days
351
+ Artifact: /home/bob/.ssh/authorized_keys
352
+ Raw value: ssh-rsa AAAAB3NzaC1... attacker@evil
353
+ Remediation: available
354
+ ```
355
+
356
+ **SUDOERS_NOPASSWD** — Password-free sudo for a human user account (UID ≥ 1000 with a login shell) is a privilege escalation vector that survives the removal of other persistence mechanisms. Legitimate NOPASSWD grants are almost always for service accounts with no login shell.
357
+
358
+ *Example finding:*
359
+ ```
360
+ [MEDIUM] SUDOERS_NOPASSWD
361
+ Title: NOPASSWD sudo grant for regular user
362
+ Artifact: /etc/sudoers.d/alice
363
+ Raw value: alice ALL=(ALL) NOPASSWD: ALL
364
+ Remediation: available
365
+ ```
366
+
367
+ **PROCESS_MASQUERADE** — Naming a malicious binary after a known system process (sshd, python3, bash) is a basic technique to avoid detection in `ps` output. This rule cross-references the process name from `/proc/<pid>/status` against the resolved exe path from `/proc/<pid>/exe`. Flag-only — killing a process requires human judgment.
368
+
369
+ *Example finding:*
370
+ ```
371
+ [MEDIUM] PROCESS_MASQUERADE
372
+ Title: Process masquerading as system binary
373
+ Artifact: /proc/1337/exe
374
+ Raw value: name=sshd, exe=/tmp/.sshd
375
+ Remediation: not available
376
+ ```
377
+
378
+ **USER_UID_ZERO** — Only `root` should hold UID 0. A second account mapped to UID 0 (CIS Ubuntu Benchmark 6.2.x) is a high-confidence backdoor: it grants full superuser rights without altering root's own credentials, and survives a root password reset. Near-zero false-positive rate. Flag-only — removing a UID-0 account requires human judgment.
379
+
380
+ *Example finding:*
381
+ ```
382
+ [HIGH] USER_UID_ZERO
383
+ Title: Non-root account with UID 0
384
+ Artifact: /etc/passwd
385
+ Raw value: toor:x:0:0:...:/bin/bash
386
+ Remediation: not available
387
+ ```
388
+
389
+ **SHELL_RC_MODIFICATION** — Shell init files are a reliable persistence vector because they execute on every user login. This rule surfaces recent modifications for human review. Flag-only — shell RC content requires reading before acting on it.
390
+
391
+ *Example finding:*
392
+ ```
393
+ [LOW] SHELL_RC_MODIFICATION
394
+ Title: Shell init file recently modified
395
+ Artifact: /root/.bashrc
396
+ Raw value: mtime=2024-01-15 14:22:01 (6 hours ago)
397
+ Remediation: not available
398
+ ```
399
+
400
+ ---
401
+
402
+ ## JSON output
403
+
404
+ `--json` writes a single JSON object to stdout. Nothing else is printed.
405
+
406
+ ```json
407
+ {
408
+ "scan_metadata": {
409
+ "tool_version": "1.1.0",
410
+ "hostname": "web-01",
411
+ "generated_at": "2026-06-10T08:22:03.114523+00:00",
412
+ "ubuntu_version": "Ubuntu 22.04.3 LTS",
413
+ "architecture": "x86_64",
414
+ "duration_s": 2.84,
415
+ "collector_failures": 0
416
+ },
417
+ "artifact_counts": {
418
+ "ProcessCollector": 142,
419
+ "NetworkCollector": 23,
420
+ "UserCollector": 4,
421
+ "CronCollector": 7,
422
+ "SystemdCollector": 12,
423
+ "SSHCollector": 3,
424
+ "SudoersCollector": 5,
425
+ "EnvironmentCollector": 18
426
+ },
427
+ "findings": [
428
+ {
429
+ "rule_id": "CRON_TMP_PATH",
430
+ "severity": "HIGH",
431
+ "title": "Cron job references writable temp directory",
432
+ "description": "A cron job was found referencing /tmp, /var/tmp, or /dev/shm. These directories are world-writable and commonly used as attacker staging grounds.",
433
+ "artifact_path": "/etc/cron.d/cleanup",
434
+ "raw_value": "0 * * * * root /tmp/.update",
435
+ "remediation_available": true,
436
+ "remediation_description": "Will remove the offending cron entry from /etc/cron.d/cleanup after creating a timestamped backup."
437
+ }
438
+ ],
439
+ "timeline": [
440
+ {
441
+ "timestamp": "2024-01-15T08:22:01+00:00",
442
+ "source": "syslog",
443
+ "description": "sshd: Accepted publickey for alice from 10.0.0.42 port 52341"
444
+ }
445
+ ],
446
+ "report_sha256": "a3f1c9…(64 hex chars)"
447
+ }
448
+ ```
449
+
450
+ `remediation_results` appears as an additional top-level key only when `--remediate` is passed. `report_sha256` is always present and is computed over the rest of the document.
451
+
452
+ ---
453
+
454
+ ## Remediation
455
+
456
+ Five of the eight detection rules have automated remediation: `CRON_ROOT_EXEC`, `CRON_TMP_PATH`, `LD_PRELOAD_INJECT`, `SSH_UNAUTHORIZED_KEY`, and `SUDOERS_NOPASSWD`. The remaining three (`SUSPICIOUS_SYSTEMD_TIMER`, `PROCESS_MASQUERADE`, and `SHELL_RC_MODIFICATION`) are flag-only and will never be auto-remediated.
457
+
458
+ ### In the TUI
459
+
460
+ Select any finding with a remediation in the Findings tab, then press `R`. A confirmation modal previews the planned action. Press `Y` to apply it — the remediator runs in a background thread so the TUI stays responsive. The finding row updates to `[fixed]` when done, with the backup path and exact rollback command shown inline.
461
+
462
+ ### From the CLI
463
+
464
+ `--remediate` without `--confirm` is a safe dry run: backups are created and validation runs, but no changes are applied. Pass both flags to actually make changes. The pipeline runs before the TUI launches in this mode.
465
+
466
+ ```bash
467
+ sudo ubuntils scan --remediate # dry run
468
+ sudo ubuntils scan --remediate --confirm # apply changes, then open TUI
469
+ ```
470
+
471
+ ### Safeguards
472
+
473
+ Every remediation follows the same pattern regardless of how it is triggered:
474
+
475
+ 1. Detect if the artifact path is a symlink — refuse if so (prevents root writing through attacker-controlled symlinks)
476
+ 2. Create a timestamped backup at `/var/backups/ubuntils/YYYYMMDD_HHMMSS/` with mode `0700`
477
+ 3. Validate current state (sudoers: `visudo -cf`)
478
+ 4. Apply the minimum possible change — cron entries removed line by line, LD_PRELOAD lines commented out rather than deleted, sudoers entries validated with `visudo -cf` before and after
479
+ 5. Verify the result
480
+
481
+ If any step fails, remediation stops immediately, the system is left unchanged, and the full error is reported with the backup path and rollback command. The sudoers remediator refuses to proceed if removing the entry would leave the system with no sudo rules.
482
+
483
+ ---
484
+
485
+ ## Collectors
486
+
487
+ | Collector | Artifacts gathered |
488
+ |---|---|
489
+ | ProcessCollector | Running processes from `/proc` and `ps` output |
490
+ | NetworkCollector | Open connections and listeners from `ss`/`netstat` |
491
+ | UserCollector | `/etc/passwd`, `/etc/shadow`, `/etc/group` |
492
+ | CronCollector | `/etc/cron*` directories and `/var/spool/cron/crontabs/*` |
493
+ | SystemdCollector | `systemctl list-timers` and `list-units` output |
494
+ | SSHCollector | `~/.ssh/authorized_keys` for all users |
495
+ | SudoersCollector | `/etc/sudoers` and all files under `/etc/sudoers.d/` |
496
+ | EnvironmentCollector | `/etc/environment`, `/etc/profile.d/*`, user shell init files |
497
+
498
+ ---
499
+
500
+ ## Compatibility
501
+
502
+ | | Supported |
503
+ |---|---|
504
+ | **Ubuntu** | 20.04, 22.04, 24.04 |
505
+ | **Architecture** | amd64, arm64 |
506
+ | **Python** | 3.9+ |
507
+ | **Privileges** | Root required for full artifact access |
508
+
509
+ Running without root produces a partial scan with warnings. Critical paths like `/etc/shadow`, protected crontab directories, and some `/proc` entries will be skipped.
510
+
511
+ ---
512
+
513
+ ## Roadmap
514
+
515
+ **v1.0.0**
516
+ - [x] All 8 collectors
517
+ - [x] All 8 detection rules
518
+ - [x] Timeline builder (syslog, journald, auditd)
519
+ - [x] Live scan progress screen with per-collector ✓/✗
520
+ - [x] Interactive four-tab TUI (Summary / Findings / Timeline / Stats)
521
+ - [x] In-TUI remediation with confirmation modal and background worker
522
+ - [x] JSON output mode
523
+ - [x] CLI remediation for 5 rules with backup, rollback, and symlink guard
524
+ - [x] Ubuntu 20.04/22.04/24.04 support
525
+ - [x] 240 tests at 90% coverage
526
+
527
+ **v1.1.0**
528
+ - [x] False-positive allowlisting by rule id or path (`--config`)
529
+ - [x] `--output FILE` to write reports directly
530
+ - [x] `--since` timeline windowing
531
+ - [x] Tamper-evident reports (`report_sha256`, hostname, timestamp)
532
+ - [x] `USER_UID_ZERO` detection rule
533
+
534
+ **v1.5.0**
535
+ - VirusTotal hash lookups for suspicious process executables
536
+ - MISP IOC export from findings
537
+ - Custom detection rules (not just allowlists) via YAML
538
+
539
+ **v2.0.0**
540
+ - Web dashboard for multi-host triage
541
+ - Wazuh integration for alert forwarding
542
+ - macOS support
543
+
544
+ ---
545
+
546
+ ## Contributing
547
+
548
+ The most useful contributions right now are new detection rules (added as standalone functions in `detectors/rules.py` with a matching test), additional collectors for artifact types not yet covered, remediation modules for `SUSPICIOUS_SYSTEMD_TIMER` and `SHELL_RC_MODIFICATION` (both currently flag-only by design, but safe auto-remediation paths may exist), test cases for edge cases on specific Ubuntu configurations, and documentation improvements.
549
+
550
+ Open an issue before starting a large contribution to avoid duplicate work.
551
+
552
+ ---
553
+
554
+ ## License
555
+
556
+ MIT
557
+
558
+ ---
559
+
560
+ ## Author
561
+
562
+ Built by Asmit — BTech Computer Science, PES University, Bengaluru. The tool came out of frustration with how long manual Ubuntu triage takes compared to what a well-scoped script can automate.