Writeup: NPST 2020
This is my writeup of the challenges and easter eggs I managed to complete in PST's CTF advent calendar, NPST 2020 π . By the end I solved 11 challenges and 5 easter eggs.
Over all, it was a great learning experience. I got to work on my python, learn about ASN.1, and start to understand assembly better. I'm impressed with the quality of the whole event!
I'll be sure to come back next year as an alvebetjent!
December 1st
The first challenge came from Mellomleder in form of a mail. We need to answer this message with the verification code he sent us, altho he seems to have dropped it in a salad before sending it, so we need to fix it before answering.
My guess was that it has been through the Caesar Cipher (hence the salad hint π₯). Ducking Ceasar Cipher decoder and picking the top one let's us change the character offset easily. After iterating through the offset, and knowing the first three characters are PST, we can easily find out that the flag has been shifted 24(π ) characters forward.
December 1st flag:
PST{HeiHoNΓ₯ErDetJulIgjen}
December 2nd
The second challenge came with a zip file that had been taken by customs from Pen GWYN, an Intelligence Officer, in transit from the North Pole the 25th of November. The confiscated zip file contained two files:
The 7z had two more files which were both encrypted, and we needed a password. I tried some guessing, but no luck. We will need to come back once we have the password.
Now first thing I did was validating both file types with the file:
>: file pen_gwyn_greatest_hits.mid
pen_gwyn_greatest_hits.mid: Standard MIDI data (format 1) using 1 track at 1/480
>: file privat.7z
privat.7z: 7-zip archive data, version 0.4
They both looked alright. Now trying to open the .mid file, it plays a simple melody. Here you can see it in Audacity:
Now it seems to be three patterns:
- The first pattern increments one note
- The second pattern is all over the place, but a pattern
- The third pattern decrements one note
This did not really give me much, other than that there are some patterns. Next thing was to run strings to see if I could find any low-hanging fruits.
>: strings pen_gwyn_greatest_hits.mid
MThd
MTrk
Nothing... Next step is hexdump:
>: xxd pen_gwyn_greatest_hits.mid
.
.
000000e0: 4081 7a90 5040 817a 8050 4081 7a90 5140 @.z.P@.z.P@.z.Q@
000000f0: 817a 8051 4081 7a90 5240 817a 8052 4081 .z.Q@.z.R@.z.R@.
00000100: 7a90 5340 817a 8053 4081 7a90 5040 817a z.S@.z.S@.z.P@.z
00000110: 8050 4081 7a90 5340 817a 8053 4081 7a90 .P@.z.S@.z.S@.z.
00000120: 5440 817a 8054 4081 7a90 7b40 817a 807b T@.z.T@.z.{@.z.{
00000130: 4081 7a90 4240 817a 8042 4081 7a90 6140 @.z.B@.z.B@.z.a@
00000140: 817a 8061 4081 7a90 6240 817a 8062 4081 .z.a@.z.b@.z.b@.
00000150: 7a90 7940 817a 8079 4081 7a90 5040 817a z.y@.z.y@.z.P@.z
00000160: 8050 4081 7a90 6540 817a 8065 4081 7a90 .P@.z.e@.z.e@.z.
00000170: 6e40 817a 806e 4081 7a90 4740 817a 8047 n@.z.n@.z.G@.z.G
00000180: 4081 7a90 7740 817a 8077 4081 7a90 7940 @.z.w@.z.w@.z.y@
.
.
Now this might be the pattern! Also notice the { and }, which tells me that it might be a flag here! Next thing was to remove all the noise. One could use some fancy commands chaining, scripting or anything in between, however I threw everything into VS Code and did it manually. Here you can see the smooth process:
December 2nd flag:
PST{BabyPenGwynDuhDuhDuhDuhDuhDuh}
Also, we got a mail from our colleague regarding the 7z file, he figured out the password.
Perhaps it will play a role later on... π€
December 3rd
Turns out the password to the zip-file was "til zip-fila," which he stated in the last message.
Now with that in mind we can both take a look at the picture and the txt-file.
cupcake.png:
kladd.txt:
π·π·π·π·π·π·π·π· N4Igzg9grgTgxgUwMIQCYJALhAZQKIAqBABDAIwA0xADAB5kA6AdgGo4DSAkgGInlXlm+IqWpUAbAE4h7AEqcWo5gAkA8gAV1NWgCYEzZgAsAtsczMAdABEAggRvaALI4p1xAVle0ECLwA4Adi8AI3F-MTpUSjpHXzoAQy9HRMiAMy9JVMtbe214iNp3VC8AnS94sro-AGYMuNpJTzoEJtpUAtQwhJSGnWy7BzoA+tS4ENq6auCQ9Lp3aO9phPqEYqrK+jG6HUcDJnUAGQBVHBxRAUYmDnlFGGoZG6Urwj4xbQDgh4UngiPZYjAAE9UAAHCAAGwQTGYhxOZzuFy+t3u0KuBA06j2QNBEKh5meInILiewj4ZE8pEuzG4nAAcrTiCZjMwABp4A4HPD-GBlUh9Al8Cl0KlMCDGADWADdVvjOfDqswDjYcHxdkw2RyuaRaqQ1YrOOw+QIFUwAEJqTTEMFgAAu4KgqAAllBmVc5N8YCbYadzqR3CoMcQxVLVntrXaHc6zEIXr6iTHCZRKf6mLJCH89swQBQQI6mCCoDasCAyKVJPFgnBqqkpn5JI5quJxMEdAF4qka3B5sEyKh2u1EHBm2RJJJgn50KkAtQ4JIdKgdO5hhUdH4fGQaj4PDsQABfIA
Now at the homepage of dass.npst.no we have some tools that we have yet to use which caught my attention. β¨Improve was one of them. Uploading cupcake.png to the tool, well, it improved the quality of it. Repeat this, and it would continuously be improved until it zoomed in on the yellow post-it note hanging on the tree in the background. Here we could make out today's flag:
December 3nd flag:
PST{HuskMeteren}
December 4rd
After looking at the csv-file and checking out the sql-files I figured that this challenge wanted us to calculate the days between PΓ₯skeaften (Holy Saturday) from one year to the next, and add it to the total.
After some quick googling I found a function that does exactly what I needed and implemented it in a script that gets the value for the flag (quick n dirty):
// https://gist.github.com/johndyer/0dffbdd98c2046f41180c051f378f343
// https://www.codeproject.com/Articles/10860/Calculating-Christian-Holidays
function getEaster(year) {
var f = Math.floor,
// Golden Number - 1
G = year % 19,
C = f(year / 100),
// related to Epact
H = (Cβ-βf(C / 4) - f((8 * C + 13)/25) + 19 * G + 15) % 30,
// number of days from 21 March to the Paschal full moon
I = H - f(H/28) * (1 - f(29/(H + 1)) * f((21-G)/11)),
// weekday for the Paschal full moon
J = (year + f(year / 4) + I + 2 - C + f(C / 4)) % 7,
// number of days from 21 March to the Sunday on or before the Paschal full moon
L = I - J,
month = 3 + f((L + 40)/44),
day = L + 28 - 31 * f(month / 4);
console.log(day);
console.log(month);
// Substract one day to get Holy Saturday
return [month,day -1];
}
let year = 2020;
const oneDay = 24 * 60 * 60 * 1000;
var aftener = []
// grab all pΓ₯skeaftener's date between 2020-2040
while (year<2041) {
currentEasterHolySaturaday = getEaster(year);
console.log(year + " " + currentEasterHolySaturaday);
// -1 on the month since js counts january as 0
aftener.push(new Date(year, (currentEasterHolySaturaday[0]-1), currentEasterHolySaturaday[1]))
year++;
}
current_sum = 43930;
total_sum = 0;
try {
counter = 0;
while (counter < 21) {
console.log();
console.log(aftener[counter]);
total_sum += current_sum;
current_sum += Math.round(Math.abs((aftener[counter] - aftener[counter+1]) / oneDay));
console.log(counter + " " + current_sum);
counter++;
}
} catch (error) {
console.log(current_sum);
}
console.log(total_sum);
December 4nd flag:
PST{999159}
December 5rd
There has been some trouble when accessing the documentation vault, and we are handed the logs too look into it. Scrolling through it, we see a lot of common Norwegian names, as well as some meta names such as Jule Nissen (Santa) and Mellomleder (our contant at boss). I wanted to see all users that had any activity:
cat log.csv | awk '{print $2}' | cut -d ';' -f 2 >> test.txt
Get-Content .\test.txt | Sort-Object | Get-Unique
With a little mixing of Bash and Powershell, I get the names and find one that is suspicious! This turns out to be the flag.
December 5nd flag:
PST{879502f267ce7b9913c1d1cf0acaf045}
December 6rd
Today was the day we got introduced to SLEDE8.
After inputting the e-module code we are presented with the challenge: The first byte of the input represents the amount of following bytes that you should add together, then output that sum. Here you can see my solution. After finishing we get the flag:
December 6nd flag:
PST{ATastyByteOfSled}
December 7th
A strange signal has been captured at Santa's workshop and Mellomleder wants us to figure out what it is. First I needed to know what a complex16u-file was. After some googling I ended up on Universal Radio Hacker, a tool that lets you investigate wireless protocols. After finding an example of how to use it, I gave it a go and found the flag!
December 7nd flag:
PST{0n_0ff_k3y1ng_1s_34sy!}
December 8th
This is the first time I have heard of ASN.1, a standard interface description language for defining data structures. After doing a lot of research, I found this useful tool that would let me decode the ASN.1 input and find the flag based on the definition also provided.
Spec DEFINITIONS ::= BEGIN
LinkedList ::= Node
Node ::= SEQUENCE {
child CHOICE {
node Node,
end NULL
},
value CHOICE {
digit [0] INTEGER(0..9),
lowercase [1] INTEGER(0..25),
uppercase [2] INTEGER(0..25),
leftCurlyBracket [3] NULL,
rightCurlyBracket [4] NULL
}
}
END
MIIBOTCCATAwggEnMIIBHjCCARUwggEMMIIBAzCB+zCB8zCB6zCB4zCB2zCB0zCByzCBwzCBuzCBszCBqzCBozCBnDCBlDCBjDCBhDB9MHYwbzBoMGEwWjBTMEwwRTA+MDcwMTAqMCMwHDAVMA4wBwUAoQMCAROgAwIBA6EDAgEMogMCAQChAwIBE6ADAgEBoQMCARKkAgUAoQMCARShAwIBDqIDAgEYoQMCAQShAwIBEqEDAgEOoQMCAQ6hAwIBB6IDAgECogMCAQigAwIBAaIDAgENogMCARKiAwIBAKMCBQCiAwIBE6IDAgESogMCAQ+hAwIBEaEDAgEOoQMCAQugAwIBAKIDAgEDoQMCAQyhAwIBFKEDAgESoQMCAQ+gAwIBAaEDAgEMoAMCAQOhAwIBEaEDAgEOogMCAQs=
December 8nd flag:
PST{ASN1IChooseYou}
December 9th
We are given a list of 16 emojis and a string that apparently is encoded in HEXMAS. My first thought was to substitute the emojis with their hex value counterpart:
hexmas_chars = "π
π€Άββπππ―πβ¨π₯π₯£πΆππΌπ¦π·"
hex_chars = "0123456789abcdef"
hexmas_string = "π€Άπ·β¨πΆπ
β¨π
π
π·π€Άππ₯ππ¦ππ·π
βπ·π·π
πΆπ
β¨π
π¦π₯£π₯π·π¦βπ
ππ·π·π₯ππ¦π
β¨π¦π¦π―πΆπ
π€Άπ¦βππ―π
β¨πΆπΌπππ―πβπΌπ
π
π€ΆβππΌππ₯ππ·π€ΆπΌπ
π
π
π
π
π
"
hex_string = ""
translator = dict(zip(hexmas_chars, hex_chars))
for hexmas_char in hexmas_string:
hex_string += translator[hexmas_char]
print(hex_string) # 1f8b0800f149ce5f02ff0b080ea9fe307ff94e08ee6b01e25608bd7c672d00124dc95f1d000000
Now I wanted to quickly turn it back to ASCII with CyberChef, but it also notice it has been gunziped. That lead me to the flag!
December 9nd flag:
PST{π§Ήπ§Ήππ
ππ§Ή}
December 10th
More SLEDE8. This time we provide the e-learning module and get the task to: Output the result of (number1 + number 2) mod 256 as an ASCII string. Here is my solution.
December 10nd flag:
PST{++AndKissesWillBeAwardedToYou}
December 11th
So there has been an unauthorized modification to the database containing the nice and naughty list. They are after the changed MD5 value. We get the following files:
liste.db
liste.db-shm
liste.db-wal
Running file on the main .db file shows us it is a sqlite3 database. After opening it in DB Browser for SQLite we can see two tables, snille and slemme. Both tables contain three columns: fornavn, etternavn, and md5. Now after some trial and error I eventually found out the MD5 hash was generated from fornavn + etternavn.
Knowing how the MD5 hash is created, we can validate every hash in the database with python and find the modified one:
import sqlite3
import hashlib
from sqlite3 import Error
def validate_table(conn, table):
cur = conn.cursor()
query = "SELECT * FROM " + table
cur.execute(query)
rows = cur.fetchall()
print("π¬ Validating " + str(len(rows)) + " rows in table: " + table + "\n")
for row in rows:
fornavn = str(row[0])
etternavn = str(row[1])
db_md5 = str(row[2])
md5input = fornavn+etternavn
real_md5 = hashlib.md5(md5input.encode()).hexdigest()
if db_md5 != real_md5:
print("β Anomaly found in table: " + table + "\n")
print(row)
print()
database = ".\liste.db"
try:
conn = sqlite3.connect(database)
except Error as e:
print(e)
with conn:
validate_table(conn, "snille")
validate_table(conn, "slemme")
>: python .\solve.py
π¬ Validating 10000 rows in table: snille
β Anomaly found in table: snille
('Agnes', 'Brekke', '49422712408d5409a3e40945204314e6')
π¬ Validating 1000 rows in table: slemme
December 11nd flag:
PST{49422712408d5409a3e40945204314e6}
Easter Eggs
Egg #2
In challenge 2 we got an unused file for the challenge itself, but it contained 8 emojis of sleds. This was a hint, and looking at how sharing code in SLEDE8, one can figure out it is a link to SLEDE8.
Running that code gives us easter egg #2!
Egg #3
Easter egg number 3 could be found by running the "enhanced" pictures through stenography tools, in my case zsteg.
Egg #3:
EGG{MeasureOnceCutTwice}
Egg #4
Simple egg that was sent to us in a message.
Egg #5
Easter egg number 5 was found after completing the Hello World e-learning module.
Egg #6
After Systemupdate 1, we got access to a paint tool in the browser. Checking the Help tab let us open 3D Paint (x86). This however "blue screened" the system and we got an "error code":
After learning about how to compile this, I managed to find the sixth easter egg:
Egg #6:
EGG{x86_machinE_codEr}
Conclusion
I'm happy that I managed to do about half of the challenges and find some eggs aswell! Really looking forward to this years Easter and Christmas, since that's when PST do these kind of CTFs.
Itj fΓ₯rrΓ₯ nΓ₯lles, HB