
Introduction
I’ve recently been playing the puzzle programming RPG Hackmud. The concept is pretty interesting. You are a user in a retro-style multi-user domain. Inside of this domain there are other players, as well as npc entities. Everything is accomplished by running scripts. The scripts are basically javascript functions with some custom language. Users can publish their script so that other players can use them. The scripts are used for a wide varity of purposes, from buying and selling items, to tracking down npc locations to hack.
There are risks involved with running the scripts of other players, and npc corps. Each script has a security level, from FULLSEC to NULLSEC. FULLSEC scripts can’t do much harm to your system, but as you go into lower and lower security scripts, more damage can be done to you. Things like having your money taken, your system upgrades taken, your user location leaked, and worse.
NPC locations can be hacked for money and upgrades, with better gold and upgrade rewards coming from higher tier npcs. Hacking higher tier npcs introduces more risk into your system, with most T3 locations being LOWSEC, capable of stealing your upgrades and money, even leaking your location. Publicly available hacking scripts also require these same permissions, and I’m not willing to risk it. So, I’m rolling my own. Tier 1 and 2 NPCs are basically solved due to the large number of publicly available scripts. The real trouble begins with Tier 3 NPCs.
Tier 3 NPCs
Hacking T3 NPCs breaks down into a few broad steps:
- Scan for available T3 NPC corp locations
- Hack into these corp locations
- Find username
- Find pin
- Get calendar ids
- Collect info from calendar
- Find backup server
- Get backup server name
- Scrape backup server for key
- Decrypt calendar info
- Scrape T3 NPC locations from the corp sites
- Hack into T3 NPCs
Scanning for T3 Corps
There are several npc prefixes:
futuretech,nuutec,core,halperyon,light,archaic,context,sn_w
and several suffixes:
priv,employee_login,internal,employees,emplogin,private,intern
We combine each together and check their security level with scripts.get_level, if it returns a valid number, we know that domain is up and working. Once we know that its up, we can begin the next step.
Hacking into T3 Corp Locations
Going to one of the available locations, we get the following view, using archaic.emplogin as an example:
Archaic Labs
Welcome to YOUR #FUTUREtech employee pro¦uctivity suite
enter in your username and 4 digit pin to continue.
Before we get started, you’re going to need to have some method of detecting timeouts and tracking state information for the scrape, my state information looks something like this:
state_info = {
script: context.this_script,
caller,
last_run_date: current_date,
stage: 0,
username: 0,
pin: 0,
data_object: {}
This state should be placed in the database for each step, since you will likely not hack the pin in one script call. /auto is your friend!
Finding Usernames
username is pretty easy. I’ve you’ve worked your way up to T3, you should already have harvested a large list of usernames and stored them in your personal database.
The best way through this is to specify a random username until you get a similar response:
Archaic Labs
Welcome to YOUR #FUTUREtech employee producti§ity suite
please provide pin as text.
A bad username will give a response like this:
Archaic Labs
Welcome to YOUR #FUTUREtech empl¡yee productivity suite
employee bad_user does not exist.
Finding Pin
There is no secret to this step. You simply have to guess every pin until you get the right one.
I’ve been running into issues with this part of the script. I don’t know if you’ve noticed, but hackmud’s text output is corrupted, with random characters being replaced with garbage chars. In the past, I’ve corrected this by simply running each command twice and filtering corruptions, but I don’t think that will be sutable for this. Instead, I’m going for fuzzy matching.
function handle_find_pin(){
state_info.data_object = { username, pin:pad(state_info.pin,4,"0") };
tc()
if (fuzzyLast(response, "incorrect pin.", 1)){
return state_info.pin = state_info.pin+1;
} else {
return state_info.stage++;
}
}
function fuzzyLast(input, m, a) {
//input, match string, misses
let sub = input.substr(input.length - m.length),
c = 0;
fz_compare = sub+"\n"+m;
for (let i = 0; i < sub.length; i++) {
if (sub[i] === m[i]) {
c = c + 1;
}
}
return sub.length - c <= a;
}
You’ll know when you succeed when you hit a prompt like this:
Archaic Labs
perform
+~~~~~~~~~~~+ +~~©~~~~~~~~+
+ Enhance + + Synergize +
'~~~~~~~~~~~' '~~~~~~~~~~~'
+~~~~~~~~~~~+ +~~~~~~~~~~~+
+ Flow + + Á¡ ¡ÁzÃ`Ã` +
'~~~~~~~~~~~' '~~~~~~~~~~~'
Collect Info from Calendar
From this point on you’re on a time crunch. You need to figure this out fast before the locations rotate.
It’s easy to get a little mixed up here, but you can bypass this section by adding a_t:"w_ek", perform:"flow", work:"calendar" to your args.
This gives you screen that looks like this:
2061AD
-D076----+D077----+D078----+D079----+
- mdnkyr | y5lhve | | bjbc84 -
- zxt59h | | | -
- | | | -
- | | | -
-D080----+D081----+D082----+D083----+
- jk8new | 3p6ijg | sagog8 | 1fzxi8 -
- dyneci | | | o5nw79 -
- | | | -
- | | | -
-D084----+D085----+D086----+D087----+
- qy3ej4 | | | z8e4nj -
- zjjc6g | | | 2vr835 -
- | | | pgpc40 -
- | | | -
=====================================
Those 6 digit codes look interesting, lets save them for later. Specifying an index i:xxxx gives you output something like this:
>>core.employee_login{username: "b3nd3r",pin: "5016",i: "ximboc", a_t: "work", perform: "flow", work: "calendar"}
calendar-
LNk8qkcO0yTt3Or63C1oiEPQtkvtHunMPDJEhQUtu+3J25dQkUX7dZsLeOiV16WyGE6PHk5+K9xGKUYeejfo/Ms0lzEUzAou4+vhzXZKg1YewT/tFJoEmjEbvxLtmKRZD/XHQ7hDddGo/IP
zliVbvxkA4PD+3aiEgxXCC3IKXCl09VXx=WdzN=2ZClxogzjHrTfLLd84pTpgxxD53f4PzT1d
These are clearly some kind of encrypted text. We should save them for later.
Finding the backup server
Keep pulling calendar entries until you find one that is unencrypted. These entries look like plain text rather than the base64 blob from before. The one you’re looking for will contain the username you’re currently logged in as. It will look something like this:
calendar-
D082 - Scheduled backup for b3nd3r
Server: anr7ea.backup
That server name is your backup server. Save it.
Scrape backup server for key
Access the backup server using the same username and pin. You’ll be presented with a list of entries, similar to the calendar. This is dangerous territory — most of the entries are traps. They’ll respond with something that sounds harmless but will silently wipe upgrades or drain GC.
The safe entry is the one that contains the key. It will always be a 7-character alphanumeric string. You can identify it because:
- It doesn’t trigger any side effects
- It returns a short string, nothing more
The approach I use is to extract the entry index values and test each one, but bail immediately if the response is longer than about 10 characters (traps tend to be verbose to distract you). Once you have a 7-char string, that’s your key. Save it and stop touching the backup server.
Decrypt calendar info
The encrypted calendar entries are base64-encoded strings that have been XOR’d with the 7-character key before encoding. To decrypt:
- Strip any whitespace or line breaks from the base64 string
- Decode from base64 to get the raw encrypted bytes
- XOR each byte against the key, cycling through it
function decrypt(ciphertext, key) {
const raw = atob(ciphertext.replace(/\s/g, ""));
return raw
.split("")
.map((ch, i) => String.fromCharCode(ch.charCodeAt(0) ^ key.charCodeAt(i % key.length)))
.join("");
}
In Hackmud’s scripting environment atob is available. Run this against every encrypted calendar entry you collected earlier. The result will be plaintext — a list of T3 NPC location identifiers, one per calendar slot.
Scraping T3 NPC Locations from Corp Sites
Once you have the decrypted calendar data, the NPC locations are embedded in the output. They follow the same naming convention as the corp locations — prefix.suffix — just with different identifiers. Pull them all out and store them in your database, tagged with the corp and the calendar day they came from (the D0XX numbers) so you can tell when a set is stale.
Locations rotate when the calendar does, so once you’ve extracted them, work fast.
Hacking into T3 NPCs
T3 NPC locations are LOWSEC scripts. When you call one for the first time you’ll get an access prompt asking for a token. The token for each NPC is embedded in the decrypted calendar entry alongside its location — it’s the other field in that line.
Calling the NPC with the correct token grants access and returns your reward: a combination of GC and tier 3 upgrades. The exact yield varies per NPC. Some locations also have a secondary prompt after the initial token that asks for a confirmation value derived from the token itself (usually a simple transform like a character sum mod 100, formatted as a zero-padded two-digit number), but not all T3s do this.
If a T3 NPC returns something unexpected after a valid-looking token, treat it as potentially hostile and do not continue interacting with it in a low-trust script context. The NPC itself is LOWSEC, which means it can see your upgrades — be deliberate about what you expose.
Once a location is harvested it typically won’t yield again until the calendar rotates, so track which ones you’ve already hit.