
AWS Security Agent のペネトレーションテスト実行ログを CloudWatch Logs で確認してみた
いわさです。
先日、AWS Security Agent のペネトレーションテストを OWASP Juice Shop に対して実行してみました。
この時はテストを途中で中断したのですが、CloudWatch Logs にペネトレーションテストの実行ログが出力されていることに気づきました。
Web App の「Penetration test logs」タブでも実行中の様子は確認できるのですが、ログを確認してみたところ CloudWatch Logs のほうがエージェントの思考プロセスや生成コードの全文が見られます。
なかなか面白かったので紹介します。
ロググループとログストリームの構成
ペネトレーションテストの設定画面(Penetration test configurations タブ)を見ると、「CloudWatch log group」の欄にロググループ名が表示されています。

ロググループの命名規則は以下のようになっています。
/aws/securityagent/{agent-space名}/{pentest-id}
今回の場合は /aws/securityagent/hoge-security-agent/pt-44e6fe5b-b34b-47f1-8859-9b663aec840c でした。
テスト実行後にCloudWatch Logs コンソールで確認すると、ロググループの中にタスクごとのログストリームが作成されていました。

ストリーム名にタスク種別が含まれていますね。
重複するタスク名のストリームが存在しますが、まずまずどのタスクのログかが目視できそうです。
今回のテストは途中で中断しちゃったのですが 1時間弱のテストで 27個のログストリームが作成されていました。
| タスク種別 | ストリーム数 |
|---|---|
| ARBITRARY-FILE-UPLOAD | 1 |
| CODE-INJECTION | 2 |
| COMMAND-INJECTION | 1 |
| CRAWLER | 1 |
| CROSS-SITE-SCRIPTING | 2 |
| INSECURE-DIRECT-OBJECT-REFERENCE | 1 |
| JSON-WEB-TOKEN-VULNERABILITIES | 1 |
| LOCAL-FILE-INCLUSION | 1 |
| PATH-TRAVERSAL | 1 |
| PRIVILEGE-ESCALATION | 1 |
| SCANNER | 1 |
| SERVER-SIDE-REQUEST-FORGERY | 1 |
| SERVER-SIDE-TEMPLATE-INJECTION | 1 |
| SQL-INJECTION | 1 |
| TLS-SCANNER | 1 |
| VALIDATOR-TASK | 7 |
| XML-EXTERNAL-ENTITY | 1 |
| preflight-validation | 1 |
| setup-toolbox | 1 |
CROSS-SITE-SCRIPTING や CODE-INJECTION は2ストリームありまして、どうやら同じカテゴリでも複数のサブタスクに分かれて実行されるみたいです。
VALIDATOR-TASK が7つあるのは、おそらく検出された脆弱性ごとに検証タスクが走っているのではないかと思います。
なお、今回のテストは途中で中断しているためか、SQL-INJECTION のストリームはログが空でした。
実行が開始される前に中断されたタスクはストリームだけ作られて中身が空になるようです。たぶん。
ログの中身を見てみる
各ログストリームには JSON 形式でイベントが記録されています。
主なフィールドは以下の通りです。
| フィールド | 内容 |
|---|---|
| timestamp | Unix タイムスタンプ |
| interaction_number | エージェントのインタラクション番号(連番) |
| agent_id | エージェントの ID |
| request | エージェントへの入力(ツール実行結果など) |
| response | エージェントの出力(テキストまたはツール呼び出し) |
エージェントの思考プロセスは response フィールドの中に記録されています。
interaction_number が 1 から始まって連番で増えていくみたいで、エージェントが何を考えて何を実行したかを時系列で追跡できそう。
PRIVILEGE-ESCALATION(権限昇格)
まず最初のインタラクション(interaction_number: 2)を見てみます。
{
"response": {
"content": [
{
"type": "text",
"text": "I'll conduct a comprehensive privilege escalation security assessment on the target application. Let me start by exploring the application structure and authentication mechanisms."
},
{
"type": "tool_use",
"name": "bash",
"input": {"command": "curl -s -I http://hogejuiceshop.tak1wa.com/ 2>&1 | head -50"}
},
{
"type": "tool_use",
"name": "bash",
"input": {"command": "curl -s http://hogejuiceshop.tak1wa.com/ 2>&1 | head -100"}
}
]
}
}
「まずアプリケーションの構造と認証メカニズムを調べよう」と宣言してから、HTTP ヘッダーとレスポンスボディを取得しています。
ここからインタラクションを重ねて、最終的に 63 インタラクションで以下のサマリを出力しています。
### Reconnaissance
- Identified the target as OWASP Juice Shop (Express 4.22.1 backend, Angular frontend)
- Extracted 33 API/REST endpoint paths from the main.js bundle
- Two primary API namespaces discovered: /api/ (Sequelize REST) and /rest/ (custom routes)
### Authentication Baseline
- Successfully authenticated as admin@juice-sh.op using default credentials
- Registered a test customer account (testuser@test.com) to establish a low-privilege baseline
- Confirmed JWT tokens use RS256 algorithm and embed full user data including role in the payload
### Vulnerabilities Found
#### Critical — Vertical Privilege Escalation via Mass Assignment (CVSS 9.1)
Endpoint: POST /api/Users
The user registration endpoint passes the raw request body directly to the ORM's create() method
without field whitelisting. By including "role":"admin" in the registration JSON, an unauthenticated
attacker can create an account with full admin privileges.
main.js から 33 の API エンドポイントを抽出し、認証の仕組みを確認してから攻撃に入ってます。なるほど、Security Agent 実行時に最初にスキャナーみたいなフェーズが動いてましたが、それがこの辺りか。
「default credentials」でログインしたと書いてありますが、Juice Shop のデフォルトパスワード admin123 をどこから知ったのかはログからは読み取れませんでした。広く公開されているクレデンシャルを一応試しているのだろうか。
CROSS-SITE-SCRIPTING(XSS)
XSS タスクのサマリです。
### Key Discovery in Source Code
Analyzing main.js revealed 13 uses of DomSanitizer.bypassSecurityTrustHtml(),
which disables Angular's built-in XSS protection. One critical instance was:
this.searchValue = this.sanitizer.bypassSecurityTrustHtml(e)
This directly renders the raw search query value into the DOM.
### XSS Exploitation
Target endpoint: /#/search?q=<PAYLOAD>
Payload 1 — img onerror:
/#/search?q=<img src=x onerror=window._xssTest=1>
- JavaScript execution confirmed: window._xssTest === 1 after page load
Payload 2 — iframe javascript: protocol:
/#/search?q=<iframe src="javascript:window._xssIframe=document.domain">
- iframe.contentDocument.body.innerHTML = hogejuiceshop.tak1wa.com
Cookie Access Confirmed:
- document.cookie returned live session cookies
ソースコードの main.js を解析して bypassSecurityTrustHtml() の使用箇所を 13 箇所特定し、そこからペイロードを組み立てて実行確認まで行っています。
静的解析と動的テストを組み合わせたアプローチをエージェンティックに行っているようです。
PATH-TRAVERSAL(パストラバーサル)
パストラバーサルタスクは 121 インタラクションと最も多くのやり取りを行っていました。
### Testing Approach
I followed a progressive path traversal methodology:
1. Baseline establishment — confirmed normal file access on .md files
2. Extension filter testing — confirmed the /ftp/ endpoint blocks non-.md/.pdf files with HTTP 403
3. Null byte injection — tested %2500 (double URL-encoded null byte) as an extension filter bypass
4. Directory traversal — tested ../ sequences and various encodings to escape the /ftp/ directory
5. Authenticated testing — registered a test user, obtained JWT tokens, and tested authenticated endpoints
### Vulnerability Confirmed: Null Byte Injection on /ftp/ Endpoint
Confirmed exploits:
| Payload | HTTP Status | File Served |
|---------|------------|-------------|
| GET /ftp/package.json.bak | 403 | Blocked |
| GET /ftp/package.json.bak%2500.md | 200 | Full file contents |
| GET /ftp/incident-support.kdbx%2500.md | 200 | KeePass database (3246 bytes) |
| GET /ftp/suspicious_errors.yml%2500.md | 200 | YAML config (723 bytes) |
| GET /ftp/coupons_2013.md.bak%2500.md | 200 | Encrypted coupon data (131 bytes) |
| GET /ftp/encrypt.pyc%2500.md | 200 | Python bytecode (573 bytes) |
Scope limitation: Directory traversal (../) to escape /ftp/ was blocked by a separate
ForbiddenError guard in the application, so the vulnerability is confined to files within
the /ftp/ directory only.
正常系を確認し、拡張子フィルタの存在を確認し、Null Byte インジェクションでバイパスを試み、さらにディレクトリトラバーサルで /ftp/ の外に出られるかも確認していますね。私攻撃手法に対して詳しくないのですが、このあたり見てると勉強になるな。
出られないことを確認した上で「スコープは /ftp/ ディレクトリ内に限定」と報告している感じです。
INSECURE-DIRECT-OBJECT-REFERENCE(IDOR)
IDOR タスクは 27 インタラクションで完了しています。
### Authentication
I authenticated with default admin credentials (admin@juice-sh.op / admin123) to obtain an admin JWT token,
then authenticated as a regular customer (jim@juice-sh.op / ncc-1701) to obtain a non-privileged user token.
### IDOR Testing - Basket Endpoint
| Requested Basket ID | Owner UserId | Owner Account | Result |
|---|---|---|---|
| 2 | 2 | jim@juice-sh.op | ✅ Legitimate |
| 1 | 1 | admin@juice-sh.op | ❌ IDOR |
| 3 | 3 | bender@juice-sh.op | ❌ IDOR |
| 4 | 11 | amy@juice-sh.op | ❌ IDOR |
| 5 | 16 | uvogin@juice-sh.op | ❌ IDOR |
### Key Pitfall Noted
During loop-based testing of multiple basket IDs, I did not include delays between requests.
This was flagged as a violation of responsible request rate guidelines.
Subsequent testing should include sleep 0.5 between loop iterations.
複数のユーザーアカウントを使い分けて、正当なアクセスと不正アクセスをそれぞれ試してます。
最後の「Key Pitfall Noted」ではリクエストレートが高すぎたことを自分で反省して、次回は sleep 0.5 を入れるべきだと記録しています。
CRAWLER
Crawler タスクのログには、エージェントが生成した Python コードがそのまま記録されていました。
エージェントが直接試すパターンと、エージェントが生成したコードを実行するパターンとあるっぽいです。
import subprocess
import json
base_url = "http://hogejuiceshop.tak1wa.com"
result = subprocess.run(['curl', '-s', '-X', 'POST', base_url + '/rest/user/login',
'-H', 'Content-Type: application/json',
'-d', '{"email":"admin@juice-sh.op","password":"admin123"}',
'--max-time', '15'],
capture_output=True, text=True, timeout=20)
login_data = json.loads(result.stdout)
token = login_data['authentication']['token']
python ツールを呼び出して、その中で curl コマンドを実行する構造になっています。
リクエストとレスポンスの内容もすべて記録されているので、エージェントが何を試して何が返ってきたのかを追跡できます。
Crawler は 80 以上のインタラクションを経ており、アプリケーション全体のエンドポイントを網羅的に探索していました。
エージェントの内部構造が見える
ログから見えるエージェントの動作をまとめると:
- 各タスクは独立したエージェント(
agent_idが異なる)として並列実行される - エージェントは
bashやpythonツールを使ってコードを生成・実行する - ツール実行結果が
requestとして返され、次のアクションを決定する - 最終的に
AgentAlertReportツールで脆弱性レポートを構造化データとして出力する - その後サマリテキストを生成して終了する
AgentAlertReport の中身
ステップ4の AgentAlertReport がどういうデータを出力しているのか見てみます。
PRIVILEGE-ESCALATION タスクの interaction_number: 60 で以下のようなツール呼び出しが記録されていました。
{
"type": "tool_use",
"name": "AgentAlertReport",
"input": {
"risks": [
{
"risk_type": "PRIVILEGE_ESCALATION",
"name": "Vertical Privilege Escalation via Mass Assignment on User Registration",
"description": "The user registration endpoint POST /api/Users is vulnerable to mass assignment...",
"evidence": "Step 1 - Send registration request with injected role field:\n\nPOST /api/Users HTTP/1.1\n...",
"endpoint": "http://hogejuiceshop.tak1wa.com/api/Users",
"http_request": "POST /api/Users HTTP/1.1\nHost: hogejuiceshop.tak1wa.com\n...",
"input_vector": "JSON request body field 'role' in POST /api/Users registration request"
},
{
"risk_type": "INSECURE_DIRECT_OBJECT_REFERENCE",
"name": "IDOR - Any Authenticated User Can Access Any Other User's Profile Data",
"description": "The GET /api/Users/{id} endpoint does not enforce any ownership or role-based access control checks...",
"evidence": "Step 1 - Login as regular customer user...",
"endpoint": "http://hogejuiceshop.tak1wa.com/api/Users/1",
"http_request": "GET /api/Users/1 HTTP/1.1\n...",
"input_vector": "URL path parameter {id} in GET /api/Users/{id}"
}
]
}
}
構造化されたデータとして risk_type、name、description、evidence、endpoint、http_request、input_vector が出力されています。
このデータが最終的に Web App の Findings 画面や PDF レポートの Detailed Findings に反映されてるっぽい。
ツール呼び出しの結果として "Successfully validated AgentAlertReport structured output" が返ってきているので、出力されたデータのバリデーションも行われているようです。
タスクごとのインタラクション数を見ると:
| タスク | インタラクション数 |
|---|---|
| PATH-TRAVERSAL | 121 |
| CRAWLER | 80+ |
| PRIVILEGE-ESCALATION | 63 |
| INSECURE-DIRECT-OBJECT-REFERENCE | 27 |
タスクの複雑さに応じてインタラクション数が変わっていますね。
さいごに
本日は AWS Security Agent のペネトレーションテスト実行ログを CloudWatch Logs から確認してみました。
Web App の「Penetration test logs」タブでも実行中の様子は確認できるのですが、CloudWatch Logs のほうが全文が見られるので、エージェントが何を考えてどう攻撃したかを詳細に追跡できます。
Security Agent のレポートって現在は英語しか対応されてないのですが、このログをインプットに自前で好きなフォーマットやら日本語やらでレポート作れそうですね。やってみようかな。








