// HackTheBox  ·  writeup

Ore

HackTheBox Medium grepPythonCatScalebash_history
DFIR

Scenario

You’ll grapple with an anomaly in Forela’s AWS infrastructure, managed by a technical partner. After deploying an EC2 instance to host the Grafana application, the CPU usage unexpectedly soared to a constant 98%+ due to a process named “xmrig”. This task calls on the you to probe into this anomalous behavior, leveraging your understanding of cloud infrastructure, EC2 instances, and system processes. You’ll need to identify what “xmrig” is, why it’s consuming so much CPU power, and if it poses a threat to the organization’s operations.

Executive Summary

An internet-facing Grafana instance on an EC2 host was exploited through an unauthenticated plugin path traversal, giving the actor arbitrary file read, including a Grafana configuration file with an administrator password in clear text. The actor then logged on over SSH as the Grafana service account, pulled a loader script from their own server with one download tool and the miner and its config with another. A script that an administrator cron job already ran as root was edited to start the miner with root rights, which then mined Monero at near-constant 98% CPU. The binaries were moved into a hidden directory and the loader was shredded to defeat recovery. The SOC’s own CatScale collection later left a footprint on the same host.

Artifacts

Timestamp note: The attacker’s commands all fall on 2022-11-24 between 09:55 and 09:59 in host log time, after an SSH logon the day before. Q8 needs the full YYYY-MM-DD hh:mm:ss form.

Initial Access

Q1 — Which CVE lead to the initial compromise of the EC2?

Grafana logs every HTTP request, so start in usr/share/grafana/data/log. Requests to /public/plugins/alertlist/../../..//etc/passwd and to the Grafana defaults.ini are an unauthenticated traversal in the plugin static-file handler, an arbitrary file read in Grafana 8.x. That behaviour maps to the 2021 Grafana plugin path traversal CVE.

grep -a remote_addr usr/share/grafana/data/log/*

Tip: The format mask fits a 2021 CVE number ending in 8. The traversal string in the log is enough to confirm it.

Click flag to reveal CVE-2021-43798
Q2 — Please detail all malicious IP addresses used by the threat actor (TA) targeting our organisation.

Two sources feed this. The Grafana request log shows two addresses sending the etc/passwd and defaults.ini traversal requests. The parsed Sysmon events (built in Q5) show wget and curl pulling the loader and miner from a third address, which is the payload server. List all three in ascending order.

grep -a remote_addr usr/share/grafana/data/log/* | grep -a 'etc/passwd\|defaults.ini'
grep -aE 'wget|curl' sysmon1.txt
Click to reveal answer 44.204.18.94,95.181.232.32,195.80.150.137
Q3 — Which account to the TA utilise to authenticate to the host OS?

From Q2, take the traversal address that also appears as an SSH source and read its accepted logons in auth.log. It authenticated with a password on Nov 23 at 11:17:18, about a day before the mining commands, using the account that runs the Grafana service.

grep 'Accepted' var/log/auth.log | grep 195.80.150.137
Click flag to reveal grafana

Credential Access

Q9 — Please confirm the password left by the system administrator in some Grafana configuration files.

The file the traversal went after (Q1) is the one that matters. The CatScale copy of the Grafana tree has the same conf/defaults.ini, and the admin_password line under [security] holds a real value rather than the stock default.

grep -a admin_password usr/share/grafana/conf/defaults.ini
Click flag to reveal f0rela96789!

Command and Control

Q5 — Which program did the TA utilise to download the injector.sh script?

Plain grep handles Sysmon’s XML in syslog badly, so parse the EID 1 events into one line per process with Python and search that. The loader request goes to the payload server from Q2 on port 80, and the first word of the command line is the answer.

python3 -I parse_sysmon.py var/log/syslog > sysmon1.txt
grep -a injector.sh sysmon1.txt
Click flag to reveal wget
Q7 — Which program did the TA utilise to download both the crypto mining binary & configuration file?

A different tool from Q5. A single command with two -O flags fetches both the miner and its config from the same server.

grep -a 'xmrig' sysmon1.txt | grep -a -- '-O'
Click flag to reveal curl
Q6 — Where was the crypto mining binary & config file initially downloaded to?

-O writes into the working directory, so the answer is the CurrentDirectory field of the same Sysmon event as Q7, not anything in the command line.

grep -a 'CurrentDirectory' sysmon1.txt | grep -a xmrig
Click to reveal answer /opt/automation/

Privilege Escalation

Q4 — Which file did the TA modify in order to escalate privileges and run the mining service as "root"?

The Grafana account’s .bash_history shows the actor opening a script in nano, in the same directory as the Q6 download. That script also appears in root’s cron listing, so anything added to it runs as root.

cat usr/share/grafana/.bash_history
grep -r updater.sh cron/
Click to reveal answer /opt/automation/updater.sh

Persistence

Q14 — How often does the cronjob created by our IT admins run for the script modified by the TA?

Read the schedule on root’s crontab entry for the Q4 script. Cron fields run minute, then hour, then day-of-month, month and weekday, so the first two fields give the time of day and the wildcards give the frequency.

grep -r updater.sh cron/

Gotcha: Cron lists minute before hour, so the time in hh:mm order is the reverse of the first two fields.

Click to reveal answer daily - 08:30

Impact

Q10 — What was the mining threads value set to when xmrig was initiated?

Filter the parsed Sysmon events to the xmrig launch after the 08:30 cron run. The command line passes the config with -c and ends with a threads argument after --.

grep -a 'xmrig' sysmon1.txt
Click to reveal answer 0
Q11 — Our CISO is requesting additional details surrounding which mining pool this may have been utilising. Please confirm which (if any) mining pool this the TA utilised.

xmrig prints its own banner to syslog on start, and the POOL #1 line names the pool and port, so no network telemetry is needed.

grep -a 'POOL #1' var/log/syslog
Click flag to reveal monero.herominers.com

Defense Evasion

Q12 — We couldn't locate the crypto mining binary and configuration file in the original download location. Where did the TA move them to on the file system?

A mv event in the attacker window moves the miner and config out of the Q6 directory into a dot-prefixed directory under /usr/share, which hides it from a default listing and reads as a log folder at a glance.

grep -a ' mv ' sysmon1.txt
Click to reveal answer /usr/share/.logstxt/
Q13 — We have been unable to forensically recover the "injector.sh" script for analysis. We believe the TA may have ran a command to prevent us doing recovering the file. What command did the TA run?

Search the parsed events for the loader name after the Q5 download. A secure-delete call at 09:59:49 overwrites the file before unlinking it, so there is nothing for carving to find. The format mask gives the shape of the command.

grep -a 'injector.sh' sysmon1.txt
Click flag to reveal shred -u ./injector.sh

Response Context

Q8 — We need to confirm the exact time the SOC team began artefact collection as this was not included in the report. They utilise the same public facing IP address as our system administrators in Lincoln.

This one was reasoned rather than read from a single artifact. The collector leaves its own script, Cat-Scale.sh, in /tmp, uploaded from the Lincoln admin address. The earliest sign that collection started is that file’s creation (birth) time in the CatScale file-system timeline, so that is the value that fits the question.

grep -a Cat-Scale catscale_out/Misc/*full-timeline.csv

Gotcha: Logon and first-command times from the Lincoln IP look like the obvious answer but are not what the question wants. It asks when collection began, and the script’s file creation time is the match.

Click to reveal answer 2022-11-24 15:01:00

Attack Summary

PhaseAction
Initial AccessPath traversal against the Grafana plugin handler reads /etc/passwd and defaults.ini
Credential AccessAdministrator password recovered from the Grafana configuration
Initial AccessSSH logon as the grafana account from 195.80.150[.]137
Command and Controlwget pulls injector.sh from 44.204.18[.]94
Command and Controlcurl -O pulls xmrig and config.json into /opt/automation
Privilege Escalation/opt/automation/updater.sh, run by root’s cron, edited to launch the miner
Impactxmrig mines to monero.herominers[.]com
Defense EvasionMiner and config moved to /usr/share/.logstxt/
Defense Evasionshred -u ./injector.sh removes the loader
ResponseSOC uploads Cat-Scale.sh to /tmp from the admin IP and starts collection

IOCs

TypeValue
IP (payload server)44.204.18[.]94
IP (traversal)95.181.232[.]32
IP (traversal + SSH)195.80.150[.]137
Accountgrafana
CVECVE-2021-43798
File/opt/automation/updater.sh (modified)
Directory/usr/share/.logstxt/
Domain (pool)monero[.]herominers[.]com (port 10191)
Commandshred -u ./injector.sh

MITRE ATT&CK

TechniqueIDDescription
Exploit Public-Facing ApplicationT1190CVE-2021-43798 file read against Grafana
Valid AccountsT1078SSH logon as grafana
Unsecured Credentials: Credentials In FilesT1552.001Admin password in the Grafana config
Ingress Tool TransferT1105wget for the loader, curl for the miner and config
Scheduled Task/Job: CronT1053.003Root cron job’s script edited to run the miner as root
Resource HijackingT1496xmrig mining to a Monero pool at 98%+ CPU
Hide Artifacts: Hidden Files and DirectoriesT1564.001Miner moved to the dot directory .logstxt
Indicator Removal: File DeletionT1070.004shred -u on the loader

Defender Takeaways

Patch or front internet-facing Grafana. A single unauthenticated request path gave the actor arbitrary file read. Updating past the fixed release, or putting the service behind a proxy that blocks .. in paths, would have removed the entry point.

Stop leaving real passwords in defaults.ini. The file leaked through the traversal and held an administrator password in clear text. Pass secrets through environment variables or a secrets store, and rotate anything that could have been read.

Root cron jobs must not run scripts that a service account can write. The Grafana account could edit /opt/automation/updater.sh, which root ran every day at 08:30. Make such scripts root-owned and not group- or world-writable, and alert on changes to files under cron-referenced paths.

Sysmon for Linux in syslog was the best evidence on the host. It gave full command lines and working directories for wget, curl, mv and shred. Forward those events off the box, because the shredded loader was unrecoverable from disk while its command line was still in the logs.

Alert on sustained CPU and known miner strings. A constant 98% CPU alert plus pool names and the xmrig banner in logs would have surfaced this earlier than a billing or performance complaint.

Conclusion

One unpatched Grafana plugin path exposed a password, and a world-reachable service account plus a root cron script turned that into a root-run Monero miner. The loader was shredded, but Sysmon’s process events recorded every command, so the logging, not the disk, told the story.