Tarek Cheikh
Founder & AWS Cloud Architect
My first pentest found 5 findings (2 verified), and none of them were Critical. I ran the exact same test again with one change: I provided login credentials.
The result was 13 verified findings, 6 of them Critical. Same application, same configuration. The only variable was a username and password. That application is the same vuln-catalog target from the previous post (../code/apps/vuln-catalog), deployed by the lab-core Terraform in ../code/infra/.
This post covers how to configure authentication for a pentest, every authentication method the agent supports, where your credentials are actually stored, what the login agent does step by step, and why providing credentials more than doubled the findings.
Authentication is Step 3 of the four-step pentest creation wizard:
Step 3 is labeled optional. By default the page is empty with a single "Add credential" button. If you skip it and click Next, the pentest runs unauthenticated. That is what I did in run-001.
For run-002, I clicked "Add credential." The wizard created a credential card named "Credential #1" (renamable with an "Edit name" button) with two radio options:
With "Input credentials" selected, the form shows five fields.
| Field | What it does | What I entered |
|---|---|---|
| Username (required) | The login username | admin |
| Password (required) | The login password (shown as dots) | admin123 |
| 2FA / TOTP (optional) | TOTP secret or QR code upload for 2FA-protected apps | (empty, the app has no 2FA) |
| Access URL | Dropdown of target URLs from Step 1, maps this credential to a specific target | https://vuln.lab.example.com |
| Agent Space login prompt (optional) | Natural-language instruction telling the agent how to log in | "This actor should use the following domains for authentication: https://vuln.lab.example.com/login" |
The login prompt is a free-text field. The agent reads it as plain English and figures out the login flow. You could write "POST to /login with username and password form fields" or "This app uses OAuth, start at /auth/login." It is the agent's only instruction for how to authenticate, which is critical for non-standard login flows.
The TOTP field is worth calling out. You can paste a TOTP secret or upload a QR code image, and the agent can then handle 2FA-protected applications. Most automated scanners cannot do this.
Below the card, an "Add another credential" button lets you add multiple credential sets. You could provide one admin account and one regular user account, or credentials mapped to different target URLs, which enables testing as different user roles (admin vs regular user vs guest). I used a single credential set.
Step 4 (Additional learning resources) lets you attach files, GitHub repositories, or S3 links for application context. I left it empty and clicked "Create penetration" (the alternative button is "Create and execute", which starts the run immediately), then started the run manually.
The two radio options in the form are the front door, but the agent supports a broader set of authentication methods for authenticated pentests (AWS docs):
For two-factor auth, only TOTP is supported. AWS's docs are explicit that SMS, email, push notifications, hardware keys, and OAuth are not.
The "Input credentials" path is the simplest: the agent stores the static credentials in Secrets Manager for you. The "Advanced setting" path is where you bring your own secret or your own Lambda.
When you use the Advanced setting with your own secret, the console shows a warning: "This secret is not pre-configured in your agent space. To use it, you must manually grant the service role read permissions for this secret in IAM." This matters: the automatic credential storage only works with "Input credentials." The Advanced setting requires you to handle IAM yourself. The pentest service role needs read permission on the secret, or invoke permission on the Lambda, depending on which method you choose.
When you use "Input credentials," the UI says "We'll securely store them in your AWS Secrets Manager on your behalf." I checked, and it does. The credentials are not stored in AWS's account or inside the Security Agent service itself. They land in Secrets Manager in your own account.
Secret name: secagent-lab-run-002-with-credentials-secret-XXXXXXXX
Created: 2026-03-21T01:50:14 (local time, UTC+1, matching the CreatePentest row in the article on the agent's AWS footprint; same second as pentest creation)
Content: {"username": "admin", "password": "admin123"}
The naming pattern is secagent-lab-{pentest-name}-secret-{random}. The secagent-lab- prefix comes from the Agent Space name. The secret lives in your AWS account, so you own it and can see it in the Secrets Manager console.
Three things to know:
secretsmanager:GetSecretValue and secretsmanager:DescribeSecret to read it. The credential secret name starts with your Agent Space name (secagent-lab-*), not securityagent-*, and the pentest service role has the broader access needed to read it.When credentials are provided, a new agent type appears in the pentest pipeline: the LOGIN agent. This is a browser-based agent that handles authentication before any testing begins. It did not exist in run-001.
I downloaded the CloudWatch logs for the LOGIN agent stream and reconstructed every step. Here is the full sequence with actual log excerpts.
Step 1: Initialize a browser session.
{
"interaction_number": 2,
"agent_id": "00000000-0000-0000-0000-000000000000",
"response": {
"content": [
{"type": "text", "text": "I'll help you authenticate to the service using the provided credentials. Let me start by initializing a browser session and navigating to the login page."},
{"type": "tool_use", "name": "browse",
"input": {"browser_input": {"action": {"type": "init_session",
"session_name": "auth-session",
"description": "Session to authenticate to vuln.lab.example.com"}}}}
]
}
}
Step 2: Navigate to the login URL.
{"type": "tool_use", "name": "browse",
"input": {"browser_input": {"action": {
"session_name": "auth-session",
"type": "navigate",
"url": "https://vuln.lab.example.com/login"}}}}
Response: "Navigated to https://vuln.lab.example.com/login"
Step 3: Take a screenshot of the login page.
{"type": "tool_use", "name": "browse",
"input": {"browser_input": {"action": {
"session_name": "auth-session", "type": "screenshot"}}}}
The agent took a screenshot, received it as an image, and said:
"Good! I can see the login page with username and password fields. Now let me get the HTML to identify the correct selectors for the input fields."
Step 4: Read the HTML to find form selectors.
<form action="/login" method="POST">
<label>username: <input type="text" name="username"></label><br>
<label>password: <input type="password" name="password"></label><br>
<button type="submit">Login</button>
</form>
Steps 5 and 6: Type credentials into the form.
{"type": "tool_use", "name": "browse",
"input": {"browser_input": {"action": {
"session_name": "auth-session",
"type": "type",
"selector": "input[name='username']",
"text": "[REDACTED]"}}}}
Response: "Typed '[REDACTED]' into input[name='username']". The same followed for the password field with selector input[name='password'].
Step 7: Click submit.
{"type": "tool_use", "name": "browse",
"input": {"browser_input": {"action": {
"selector": "button[type='submit']",
"session_name": "auth-session",
"type": "click"}}}}
Response: "Clicked element: button[type='submit']"
Step 8: Post-login screenshot and verification. The agent took another screenshot and reported what it saw:
"Excellent! The login was successful. I can see:
- The page now shows "Welcome, [REDACTED]"
- There's a token displayed on the page
- The page shows various menu items (Dashboard, View Users, Search, Update Profile, Upload File)"
Step 9: Extract the JWT token from the page HTML. The agent called get_html again on the post-login page and found the token in a <code> element:
Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.REDACTED_ADMIN_JWT.REDACTED_SIG
Step 10: Get browser cookies.
{"type": "tool_use", "name": "browse",
"input": {"browser_input": {"action": {
"session_name": "auth-session", "type": "get_cookies"}}}}
Step 11: Save credentials for other agents. This is the critical step. The agent called save_credentials, a tool that stores both cookies and HTTP headers so the Crawler, attack workers, and validators can all make authenticated requests:
{"type": "tool_use", "name": "save_credentials",
"input": {"save_credentials_input": {
"description": "Authentication credentials for vuln.lab.example.com...",
"actions": [
{"type": "save_browser_cookies",
"session_name": "auth-session",
"cookie_names": ["session_token"]},
{"type": "save_headers",
"headers": ["Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."]}
]}}}
Response: "done!"
The save_credentials tool has two action types. save_browser_cookies extracts named cookies from the browser session, and save_headers stores HTTP headers like the Authorization Bearer token. Other agents reuse both. The entire login process took about 52 seconds across 24 interactions. Two LOGIN streams appeared in run-002, meaning the agent logged in twice, likely for different phases of the test.
Here is the full results table. CVSS (Common Vulnerability Scoring System) is a 0-to-10 scale measuring how dangerous a vulnerability is, where 10.0 is maximum severity.
| # | Finding | Severity | CVSS | Confidence | Risk Type | New? |
|---|---|---|---|---|---|---|
| 1 | Critical Command Injection in /cmdi/obvious via host parameter | Critical | 9.9 | High | Command Injection | YES |
| 2 | Critical Blind SSRF in /ssrf/hidden Webhook Endpoint | Critical | 9.9 | High | SSRF | YES |
| 3 | JWT 'none' Algorithm Acceptance - Complete Auth Bypass | Critical | 10.0 | High | JWT Vulnerabilities | YES |
| 4 | SSTI with RCE in /ssti/obvious endpoint | Critical | 9.9 | High | SSTI | YES |
| 5 | SSRF via URL Parameter (/ssrf/obvious) | Critical | 9.1 | High | SSRF | YES |
| 6 | JWT Signature Verification Not Implemented - Complete Auth Bypass | Critical | 9.8 | High | JWT Vulnerabilities | YES |
| 7 | Critical IDOR - Unauthenticated Access to All User Data | High | 7.5 | High | IDOR | YES |
| 8 | LFI Filter Bypass via Incomplete Blacklist in /lfi/obvious | High | - | High | LFI | YES |
| 9 | File Existence Oracle via Path Traversal in lang Parameter | Medium | - | High | Path Traversal | YES |
| 10 | Critical XXE Injection in /xxe/obvious endpoint | Medium | - | - | XXE | YES |
| 11 | File Upload MIME Type Validation Bypass | Medium | - | - | Arbitrary File Upload | YES |
| 12 | Path Traversal in /path-traversal/obvious endpoint | Medium | - | - | Path Traversal | No |
| 13 | Broken RBAC on /clean/users - Regular Users Access Admin Endpoint | Medium | - | - | Privilege Escalation | YES |
12 of the 13 findings are new relative to run-001; only the Medium path-traversal finding also surfaced there. All 6 Criticals appeared only in the credentialed run (run-001 produced zero, so 0 to 6). Most of the new findings needed a valid session to reach at all; three of them (#1, #4, #5) sit on public endpoints that run-001's crawl simply missed, so the credentialed run's broader discovery is what surfaced those.
Findings #10 to #13 show a dash for risk score and confidence: the agent's exported report listed them as confirmed findings but did not populate those two fields. They still count among the 13, all of which appear in the confirmed report rather than the unverified, hidden-by-default bucket that run-001's extra findings fell into. That is why I describe all 13 as verified even though four of them carry no explicit confidence value.
One more reading note: where a finding title begins with the word "Critical" (for example #7 and #10), that is the agent's own generated name for the finding, not its assigned severity. The Severity column is the authoritative rating, which is why finding #7 is titled "Critical IDOR" but scored High, and #10 is titled "Critical XXE" but scored Medium. The six findings that are actually Critical severity are #1 through #6.
For the full machine-readable breakdown, see the data/pentest-reports/run-002-with-credentials-summary.md file in the companion repository.
Finding 3 was scored at the maximum 10.0, reflecting unauthenticated, system-wide impact (a CVSS 3.1 base score reaches 10.0 only with Scope:Changed, where compromised authentication lets the attacker affect resources beyond the vulnerable component). Here is what happened.
The JWT attack workers received a valid token from the LOGIN agent. They then tested whether the server validates token signatures. One test: forge a token with "alg": "none" (the "none" algorithm tells the server to skip signature verification) and set arbitrary claims.
Forged token header: {"alg": "none", "typ": "JWT"}
Forged token payload: {"user_id": 999, "username": "hacker", "role": "superadmin"}
Server response: {"message":"Welcome hacker","role":"superadmin"}
The server accepted an unsigned token from a nonexistent user with a made-up "superadmin" role. No secret key required. The agent also tested algorithm case variations (none, None, NONE) and the server accepted all of them.
Finding 6 confirmed a related problem: the server does not verify JWT signatures at all. You can sign a token with any secret and the server will trust it. Two Critical JWT vulnerabilities that reached confirmed-Critical status only with credentials. (Without a session the agent could still brute-force the weak signing secret and forge tokens, as it did in run-001, but it found no exploitable role-based access to confirm, so the issue stayed an unverified finding.)
Nine of the new findings required authentication to discover or to exploit, each for a specific reason. They group into eight themes below, with the two JWT findings (3 and 6) sharing one. The other three new findings (#1, #4, #5) are not in this list: they sit on public endpoints that run-001 never crawled, so credentials are not what unlocked them, broader discovery is.
JWT attacks (3, 6): What credentials unlocked here was validator-confirmed impact, not the testing itself. In run-001 the agent already probed JWT unauthenticated, brute-forcing the weak secret and forging tokens, but it found no role-based access to exploit, so the finding stayed unverified. With a valid session the LOGIN agent supplied a real token and the attack workers then modified the algorithm, claims, and signature to probe the server's validation logic, which is what turned these into confirmed Criticals.
IDOR (7): The vulnerable user-data endpoint was only linked from authenticated pages, so the unauthenticated Crawler never found it in run-001. With credentials, the Crawler discovered it, and the agent then logged in as admin (user_id=1) and tried accessing user_id=2 and user_id=3. Both returned data. It also tested without any token and still got data. The finding documents both an authorization bypass and an authentication bypass. The bug itself would respond to an unauthenticated request; it stayed invisible in run-001 because the endpoint was never discovered, not because there was no session to compare against.
Blind SSRF in /ssrf/hidden (2): The /ssrf/hidden endpoint requires authentication, so the Crawler could not find it in run-001. With credentials, the Crawler discovered it and the SSRF worker tested 37+ payloads against internal services, AWS IMDS metadata at 169.254.169.254, the file protocol, and IP encoding bypasses (decimal, octal, hex, IPv6). The endpoint had a 100 percent acceptance rate with zero security controls.
Broken RBAC (13): The agent tested /clean/users (an admin-only endpoint) with different role contexts and found regular users could access it. Testing role-based access control requires having a session with a known role.
LFI filter bypass (8): The Crawler mapped more endpoints when authenticated, discovering /lfi/obvious and testing blacklist bypass techniques the unauthenticated Crawler never reached.
XXE injection (10): The /xxe/obvious endpoint was only discovered through authenticated crawling.
File upload bypass (11): Upload endpoints require authentication. The agent tested MIME type spoofing, extension manipulation, and path traversal in filenames.
Path traversal oracle (9): The lang parameter on authenticated pages was probed for path traversal, revealing a file existence oracle that leaks whether files exist on the server.
| Metric | Run-001 (no credentials) | Run-002 (with credentials) |
|---|---|---|
| Findings | 5 | 13 |
| Critical findings | 0 | 6 |
| JWT findings | 0 | 2 |
| IDOR findings | 0 | 1 |
| RBAC findings | 0 | 1 |
| SSRF findings | 0 | 2 |
| Duration | ~2.5 hours | 2h 38m |
| CloudWatch log streams | 47 | 57 |
| CloudWatch log size | 7.1 MB | 8.6 MB |
| LOGIN agents | 0 | 2 |
| Validator tasks | 7 | 18 |
| Cleanup agents | 4 | 5 |
The validator count jumped from 7 to 18. More findings means more validators needed to independently confirm each one. The cleanup agent count went from 4 to 5 because the agent made more modifications during testing with authenticated access.
The agent also chained attacks across findings:
/ssti/obvious to run sqlite3 commands and dump the users table, extracting all credentials./upload/obvious, then accessed it through path traversal.Configuring Authentication Resources took about 10 minutes. Fill in a username, a password, select the target URL, write a one-sentence login prompt. That was it.
The result was a 160 percent increase in findings. That figure compares total findings (5) to verified findings (13); the verified-to-verified jump (2 to 13) is larger still. Five vulnerabilities became thirteen. Zero JWT findings became two Critical JWT findings. Zero authorization findings became an IDOR and a broken RBAC finding. Entire categories of vulnerabilities (authentication mechanisms, authorization controls, endpoints behind login) were invisible without credentials.
Without credentials, the agent cannot test what it cannot reach. Providing credentials is not a nice-to-have. It is the difference between a partial scan and a real pentest.
Next up: advanced pentesting against private targets, VPC resources, and cross-account shared VPCs.
This article is just the start. Get the full picture with our free whitepaper - 8 chapters covering IAM, S3, VPC, monitoring, agentic AI security, compliance, and a prioritized action plan with 50+ CLI commands.
Toc Consulting: AWS Security & Cloud Architecture
Our team helps engineering teams secure and architect AWS the right way: assessment in week one, a prioritized action plan in week two.
Part 16 of 16 in the AWS Security Agent: From Zero to Hero series. Closing the series: the numbers this whole project is built on, the honest verdict distilled to its essentials, what AWS has shipped since my hands-on testing, and an evidence-based look at how AI pentesting stacks up against traditional consultancy today.
Part 15 of 16 in the AWS Security Agent: From Zero to Hero series. What AWS Security Agent leaves behind in your own account. CloudTrail events, Secrets Manager entries, and VPC Flow Logs, with the commands to audit and observe every run yourself.
Part 14 of 16 in the AWS Security Agent: From Zero to Hero series. What AWS itself documents about AWS Security Agent as its own attack surface: the guardrails it publishes, how it limits its own blast radius, and how domain ownership and data handling are enforced. Sourced entirely from AWS's public documentation, not hands-on testing.