# AngstromCTF 2019

These are our writeups for the challenges presented in this year's AngstromCTF.

Categories index
Misc - Rev - Web - Crypto - Binary

# Misc

## The Mueller Report

The redacted version of the Mueller report was finally released this week! There’s some pretty funny stuff in there, but maybe the report has more beneath the surface.

The attached PDF had corrupt matadata, so the next easy thing to do was running it through strings. A hex dump also shows that the flag is located at offset +0x48E86D7:

strings full-mueller-report.pdf | grep "actf{"
hexdump -C full-mueller-report.pdf | grep -A 5 -B 5 "actf{"


🏁 actf{no0o0o0_col1l1l1luuuusiioooon}

## Paper Bin

defund accidentally deleted all of his math papers! Help recover them from his computer’s raw data.

A paper_bin.dat file was given, and as usual it did not give many info about itself at first glance:

$file paper_bin.dat paper_bin.dat: data  While displaying raw data can be harmful at times, this time it gave us an useful hint: $ head -6 paper_bin.dat
��%PDF-1.4
%����
3 0 obj
<<
/Length 2877
/Filter /FlateDecode


With the notion of the presence of one or more PDF files, checking how many of them are actually included in the file and extracting them is pretty straightforward:

$binwalk paper_bin.dat | grep -v "Unix path" | grep -v "Zlib" DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 222 0xDE PDF document, version: "1.4" // 18 more entries... 6770910 0x6750DE PDF document, version: "1.4"$ foremost -t pdf -i paper_bin.dat -v
// Extraction takes place...
pdf:= 20

$pdfgrep "actf\{" ./output/pdf/*.pdf ./output/pdf/00011880.pdf: actf{proof by triviality}  Fix the formatting to match the other flags and submit it right away: 🏁 actf{proof_by_triviality} ## Lithp My friend gave me this program but I couldn’t understand what he was saying - what was he trying to tell me? Author: fireholder ;LITHP (defparameter *encrypted* '(8930 15006 8930 10302 11772 13806 13340 11556 12432 13340 10712 10100 11556 12432 9312 10712 10100 10100 8930 10920 8930 5256 9312 9702 8930 10712 15500 9312)) (defparameter *flag* '(redacted)) (defparameter *reorder* '(19 4 14 3 10 17 24 22 8 2 5 11 7 26 0 25 18 6 21 23 9 13 16 1 12 15 27 20)) (defun enc (plain) (setf uwuth (multh plain)) (setf uwuth (owo uwuth)) (setf out nil) (dotimes (ind (length plain) out) (setq out (append out (list (/ (nth ind uwuth) -1)))))) (defun multh (plain) (cond ((null plain) nil) (t (cons (whats-this (- 1 (car plain)) (car plain)) (multh (cdr plain)))))) (defun owo (inpth) (setf out nil) (do ((redth *reorder* (cdr redth))) ((null redth) out) (setq out (append out (list (nth (car redth) inpth)))))) (defun whats-this (x y) (cond ((equal y 0) 0) (t (+ (whats-this x (- y 1)) x)))) ;flag was encrypted with (enc *flag*) to give *encrypted*  The code with which we are welcomed, after some research, turned up to be Lisp. After rolling up our sleeves and getting a grip of Lisp basics, an analysis of the program shows the following: • enc(plain) Is the initial function at which is passed the plaintext flag. It then passes the flag to multh() and what’s returned is further processed throught owo(). Berfore returning the resulting list, the sign of every number is inverted; • whats-this(x y) This one is just an obfuscation of a simple multiplication between x and y; • multh(plain) Every number “x” form plain list is multiplied by “1-x” (with whats-this()) and the added to a list that’s at the end returned; • owo(inpth) Scramble inpth following indexes of *reoder* global list Knowing how the program works, is just a matter of writing a script that does all this backwards. 🏁 actf{help_me_I_have_a_lithp} ## Just Letters Hope you’ve learned the alphabet! nc NNN.NNN.NNN.NNN PPPPP The challenge’s text pointed to an EsoLangs page explaining the AlphaBeta language. It surely isn’t the most comfortable tool to use, but it has a nice resemblance with Assembly and thus it’s not hard to write once you get the hang of it. Some instruction become much clearer if you peek at the interpreter’s source. Connecting to the given host you were greeted by a prompt, allowing you to enter a single line of AlphaBeta: $ nc NNN.NNN.NNN.NNN PPPPP
Welcome to the AlphaBeta interpreter! The flag is at the start of memory. You get one line:
>


There were two ways to solve this challenge - as most problems in the programming world, really: the quick way and the right way.

### Quick way:

Read from memory, print out a char, advance to next memory slot. Manually repeat as needed.

GCLSGCLSGCLS...

Instruction Description
G Sets register 1 to the memory at the memory pointer
C Sets register 3 to the value of register 1
L Outputs a character to the screen
S Adds 1 to the register

### (Bonus) Hacky way:

Variant of the following Right solution, but iterating over a number given through user input. Discarded because input wasn’t really working on the hosted interpreter.

KGCLShxZYSZOZUSSSS

Instruction Description
K Input a value from the keyboard and store it in register 2
h Subtracts 1 from register 2
x Clears register 1
Z Switches in-between modes (starts on memory pointer)
Y Sets the register to 0
O If register 1 does not equal register 2, goto the position at the position register
U Adds 10 to the register

### Right way:

Read from memory, print out a char, advance to next memory slot. Loop as long as a terminator isn’t found.

GCLShyZYZOZUSSS

Instruction Description
y Clears register 2

🏁 actf{esolangs_sure_are_fun!}

## Scratch It Out

An oddly yellow cat handed me this message - what could it mean?

An unformatted JSON file was given, and running it through jq helps making sense of it:

{
"targets": [
{
"isStage": true,
"name": "Stage",
"variables": {
"jEk@4|i[#Fk?(8x)AV.-my variable": [
"my variable",
75
]
},
"lists": {
"DDcejh^KamcM{M3I4TYi": [
"flag",
[]
]
},
"blocks": {},
"currentCostume": 0,
"costumes": [
{
"assetId": "cd21514d0531fdffb22204e0ec5ed84a",
"name": "backdrop1",
"md5ext": "cd21514d0531fdffb22204e0ec5ed84a.svg",
"dataFormat": "svg",
"rotationCenterX": 240,
"rotationCenterY": 180
}
],
// ...


Being the whole file 7.3KB of JSON, an excerpt will do for the time being.

A yellow cat? Stages, blocks, costumes? project.json? Scratch it out?

Scratch’s documentation confirmed this hypothesis, and since the metadata was indicating that this project was created with the latest, completely online version of Scratch, it was just a matter of repacking and uploading it:

$cat project.json | jq .meta.semver "3.0.0"$ zip project.zip project.json

"password": {"$gt": ""} }  Instead of {"gt": ""} many other variations could be used: {"$exists": true}, {"$not": {"$size": 0}}, {"$ne": null} Insomnia is a handy tool for working with HTTP requests; remember to extract the session JWT from the browser’s token cookie and apply it, though. 🏁 actf{no_sql_doesn’t_mean_no_vuln} # Crypto ## Classy Cipher (20 pts) Every CTF starts off with a Caesar cipher, but we’re more classy. Author: defund from secret import flag, shift def encrypt(d, s): e = '' for c in d: e += chr((ord(c)+s) % 0xff) return e assert encrypt(flag, shift) == ':<M?TLH8<A:KFBG@V'  This is nothing more than a regular implementation of a caesar cipher. Knowing the flags format ( “actf{…}” ), displacement can be easily acquired subtracting any known character with its encrypted one. def decrypt(d, s): e = '' for c in d: e += chr((ord(c)-s) % 0xff) return e enc = ':<M?TLH8<A:KFBG@V' flag = decrypt(enc, ord(':')-ord('a'))  🏁 actf{so_charming} ## Really Secure Algorithm (30 pts) I found this flag somewhere when I was taking a walk, but it seems to have been encrypted with this Really Secure Algorithm! Author: lamchcl p = 8337989838551614633430029371803892077156162494012474856684174381868510024755832450406936717727195184311114937042673575494843631977970586746618123352329889 q = 7755060911995462151580541927524289685569492828780752345560845093073545403776129013139174889414744570087561926915046519199304042166351530778365529171009493 e = 65537 c = 7022848098469230958320047471938217952907600532361296142412318653611729265921488278588086423574875352145477376594391159805651080223698576708934993951618464460109422377329972737876060167903857613763294932326619266281725900497427458047861973153012506595691389361443123047595975834017549312356282859235890330349  As the variables names suggest, this is RSA. For deciphering the message is needed the private key that’s not so easily given. But it can be generated since everything required is provided. For working with RSA, there’s an awesome tool: RsaCtfTool ./RsaCtfTool.py -p$P -q $Q -e$E --uncipher \$C


Remove the “\x00” padding, et voilà!

🏁 actf{really_securent_algorithm}

## Half and Half (50 pts)

Mm, coffee. Best served with half and half!

Author: defund

from secret import flag

def xor(x, y):
o = ''
for i in range(len(x)):
o += chr(ord(x[i])^ord(y[i]))
return o

assert len(flag) % 2 == 0

half = len(flag)//2
milk = flag[:half]
cream = flag[half:]

assert xor(milk, cream) == '\x15\x02\x07\x12\x1e\x100\x01\t\n\x01"'


This challenge code, halves the flag and xor together the two parts. The goal is to recover the initial flag from the given result of the xor.

The length of the flag is just twice the length of the resulting xor. That is 24 (look out for those not hex encoded chars).

Being xor a function that, if applied twice with the same value cancels itself: A ⨁ B ⨁ B = A, to start off, flag format characters ( “actf{}” ) can be used to retrieve a few others.

a c t f { ? ? ? ? ? ? _
t a s t e ? ? ? ? ? ? }

a c t f { ? ? ? ? ? ? _ t a s t e ? ? ? ? ? ? }


As the challenge description suggests, the word coffee can be tried seeing that it fits in and would also make sense with the following word taste.

That was indeed the right word, so the entire flag is revealed.

a c t f { c o f f e e _
t a s t e s _ g o o d }

🏁 actf{coffee_tastes_good}
`