I bypassed AWS API Gateway auth with a trailing slash. Got $12K bounty

ofcyes1 pts0 comments

I bypassed AWS API Gateway auth with a trailing slash. Got $12K bounty. – GuptaLog

Skip to content

GuptaLog

I was poking at a fintech’s mobile API and noticed something that made no sense. GET /v1/accounts returned 401. GET /v1/accounts/ returned 200 with full account data. One character. Completely different security posture.

What I was looking at

The API ran on AWS HTTP API — the newer, cheaper alternative to REST API. Lambda authorizer checked a JWT against Cognito, returned an IAM policy. Standard.

Routes in OpenAPI:

The authorizer ran on every request. But HTTP API makes two decisions: does this route exist, and does the authorizer allow it? Those two layers didn’t agree on what a “match” meant.

The weird results

I ran ffuf on the path. The results were… inconsistent.

RequestResponseGET /v1/accounts401 UnauthorizedGET /v1/accounts/200 OK + full dataGET /v1/accounts//200 OKGET /v1/accounts?foo=bar401 UnauthorizedGET /v1/accounts%2f404 Not Found

The pattern: any path that sort-of matched a route prefix triggered the authorizer, then fell through to the integration without re-checking auth.

HTTP API does greedy path matching by default. /v1/accounts/ matched /v1/accounts as a prefix. The authorizer ran and returned Allow. Then the integration executed — but the integration mapping was fuzzy. The path got rewritten, the auth context got dropped, and suddenly I was inside without a valid JWT.

How the bypass actually worked

I traced it carefully. The $default route in HTTP API is a catch-all. The fintech had set it to return 404. But they’d also attached a mock integration for health checks at some point. That mock didn’t check auth — just returned {"status": "ok"}.

But /v1/accounts/ wasn’t hitting the mock. It was hitting the real backend. API Gateway’s greedy match rewrote the trailing-slash path, stripped the slash, and forwarded to the /v1/accounts integration. The auth check happened on the original path. The integration ran on the rewritten path. The rewrite dropped the auth context.

I confirmed it with a custom header. The authorizer sets context.authorizer.userId. The integration reads it. When I hit /v1/accounts/, the integration received userId: undefined. The integration didn’t validate userId. It just returned all accounts for the API key — which wasn’t even required here because auth was supposed to be the JWT.

The real damage

Same bypass worked on POST /v1/transfers/. I could initiate wire transfers without a valid JWT.

The backend checked that fromAccount belonged to the user. But userId was undefined, so it defaulted to a system account. I stopped after one $0.01 test transfer. It went through.

Telling them

I wrote it up. Screenshots of the 401 vs 200. The ffuf output. The exact path rewrite behavior. They fixed it the next day.

Switched from HTTP API to REST API (stricter path matching)

Added userId validation in every Lambda, not just the authorizer.

I got $12,000 bounty for it. Planning to go to Dubai 🙂

Leave a Comment Cancel Reply<br>Your email address will not be published. Required fields are marked *<br>Type here..

Name*

Email*

Website

Save my name, email, and website in this browser for the next time I comment.

Customize<br>Reject All<br>Accept All

Powered by

Necessary Cookies

Always Active

Necessary cookies enable essential site features like secure log-ins and consent preference adjustments. They do not store personal data.

None

Functional Cookies<br>Remark

Functional cookies support features like content sharing on social media, collecting feedback, and enabling third-party tools.

None

Analytical Cookies<br>Remark

Analytical cookies track visitor interactions, providing insights on metrics like visitor count, bounce rate, and traffic sources.

None

Advertisement Cookies<br>Remark

Advertisement cookies deliver personalized ads based on your previous visits and analyze the effectiveness of ad campaigns.

None

Unclassified Cookies<br>Remark

Unclassified cookies are cookies that we are in the process of classifying, together with the providers of individual cookies.

None

Reject All<br>Save My Preferences<br>Accept All

Powered by

Scroll to Top

accounts cookies path integration auth authorizer

Related Articles