colaianni.us

HORUS

In the summer of 2023 I worked as an analyst intern in Clemson University’s Security Operations Center (SOC), which hires students to give them industry experience. Much of our time went to reviewing Duo two-factor (2FA) activity for signs of a compromised account, and the process for it was slow and noisy. It annoyed me enough that I wrote HORUS, a desktop app that gathers everything an analyst needs for these checks into one window and does the first pass for them. A cleaned-up copy of the source is on GitHub.

HORUS screenshot

The Job

Around the office, reviewing Duo 2FA logs was called “Duo multi”. An analyst looks through flagged 2FA activity, gathers context on each user, and decides whether the account is safe, suspicious, or compromised.

Duo multi ran on a Splunk query and dashboard built long before my time, and it had two big problems:

  1. It flagged many false positives and had no ranking, so a fraud report sat in the list with the same weight as a request that timed out.
  2. An analyst had to work through every row, then gather context by hand: who is this user, where do they usually log in from, and which IPs and devices belong to them. Each answer lived in a different tool.

Ranking Alerts with Duplex

Duplex replaces the Duo multi dashboard. It pulls 2FA logs and looks for the following red flags:

Impossible travel is only as good as the location behind each IP. No single geolocation source is right every time, and a wrong location can fake an impossible travel flag. HORUS asks three sources about each IP, MaxMind’s dataset bundled into HORUS and two online APIs, and compares their answers to find the most likely location.

Each red flag adds points to a user’s score, and some flags are worth more than others. Fraud reports always come first, and everyone else is sorted by score, so the most likely cases sit at the top of the list.

Two filters cut out most of the noise:

  1. Users created in the past 6 months are skipped. New users are still learning Duo and fail often for harmless reasons.
  2. Users whose activity comes only from their home state are skipped.

Gathering Context

Duplex decides who to look at. The next three tools answer the questions an analyst used to chase across different tools.

Simplex

Simplex is Duplex for a single user. Give it a username and it shows their 2FA logs and their details from our in-house people search tool, which I’ll call Rolodex, with no scoring or filters. It lets an analyst dig into one user with the same Rolodex lookups and IP geolocation.

Visor

Visor shows a user’s VPN history. It compares each session to the one before it by source IP and MAC address. Matches show green and mismatches show red, so a session from a new device or network stands out.

Sonar

Give Sonar a username, IP, or MAC address and it searches the network logs for the other two. I wrote it because we often had one piece of information but needed the others for the next step.

Closing a Case

With the context in hand, the analyst makes a call. If the activity looks suspicious, they open a Cherwell ticket to ask the user whether it was theirs. If the account is clearly compromised, they reset the password and open a ticket to tell the user why. Either way, they mark the user as investigated in Duplex, which hides them from HORUS for 24 hours so no one checks the same case twice.

How It’s Built

HORUS is written in Rust. egui draws the UI and rayon spreads the work across threads. I built release binaries and copied them to our analyst machines, which ran Kali Linux at the time.

It pulls from four sources:

A local SQLite database caches Rolodex results and API lookups, so repeat checks don’t hit those services again.

Zeppelin and Osiris

One part of HORUS never worked. My boss asked if we could track metrics, such as who handled which alerts. Zeppelin is the part of HORUS that reports those numbers when an analyst triggers it, and Osiris is a separate server that stores them. I started Osiris near the end of my internship and never finished it. The server we planned to run it on had RHEL 8, if I remember right, and its glibc was too old for my build. We asked the system administrators to re-image it, but they never got around to it.

Results

My coworkers loved it. Compared to the old dashboard, Duplex cut false positives by 65%. The bigger win was a 90% drop in investigation time. That came as much from smarter checks as from having the logs, user details, and network history in one place, already cross-referenced.

HORUS was always meant as a stopgap. The SOC planned to move to TheHive, but the deal was still in progress during my internship. HORUS stayed in use for about a semester and a half after I left, and even got updated once to keep up with a change in Splunk’s Duo log format. Then TheHive replaced it, as planned.

What I Learned

Most of my lessons were about structure:

  1. Keeping the UI responsive meant moving log processing onto background threads and passing results back, so the window never froze mid-query.
  2. I learned software architecture the hard way: how to split code into components and design ahead to avoid refactors. Refactors happened anyway. Rust tends to do that.

Two build mistakes I’d fix today:

  1. I loaded MaxMind’s data by reading the CSV at build time and parsing it into a Rust struct compiled into the binary, so release builds used about 23 GB of RAM. Today I’d convert the data into a SQLite table once and ship it inside the database HORUS already used for its cache.
  2. Osiris died on an old glibc. Building it against musl would have removed its need for the server’s libc, and I could have gotten metrics working before my internship ended.

Closing Notes

HORUS lived about as long as it was meant to, and it saved my friends and me a lot of tedious work along the way. Write to me with questions; I love questions.

Previous:
Rust GUIs With Egui
Next:
Image Indexer