September 18, 2020     7 min read

Fighting API Abusers - Chaotic Good

Fighting API Abusers - Chaotic Good

One of the worst feelings you can have when running a website or service is to be randomly attacked by a bad actor. More often then not the attack will just be a Denial of service attack; which, while bad are actually pretty easy to deal with in the modern era with services like CloudFlare or AWS Shield.

An attack that can be more problematic is API abuse. This is where a malicious actor builds a website or service around your API without concent and begin funneling traffic your way for the purpose of fueling their product.

In this post we will cover:

Overview of the attack

On the 16th September 2020 I received a weird support ticket under my AWS account alerting me to the fact that a sizable portion of emails sent from my SES domain were being bounced.

AWS Support case SES Bounce
AWS Support case SES Bounce

"Your current bounce rate is 10.13%. We measured this rate over the last 10,109 eligible emails you sent. Our analysis covers the last 1.3 days."*

The ticket was flagged as Normal, so I wasn't too worried; however reading more deeply I went from mildly confused to very stressed.

"If we don't hear back from you or if the problem hasn't been resolved within the next 41,000 emails you send, we'll pause your ability to send email using Amazon SES in order to protect your reputation as a sender."

You heard it right, AWS support would have paused all SES messages outbound which would cripple Selfie2Anime entirely.

Investigating the attack in more detail I was able to see that the burst of bounced emails happened all at once.

SES bounce CloudWatch metrics
SES bounce CloudWatch metrics

I had recently setup X-Ray tracing on the stack so the first place I went for guidance was the service graph. I could immediately see that not only was I receiving a lot more traffic, but a fair few requests were failing.

X-Ray Service graph showing high error rate
X-Ray Service graph showing high error rate

Digging down into some of the statistics on what clients were hitting the service indicated a weird user agent of Amazon CloudFront.

High number of requests from client with user agent Amazon CloudFront
High number of requests from client with user agent Amazon CloudFront

This made it clear that the requests were coming from another host that was proxying requests and either proofing a user agent or something similar.

What attacks are there

Before diving into the details, it is important to understand what I mean when I say API abuse. The typical thing you will see people try to get away with if your API is discovered it to simply have the client connect to your API directly.

API Abuse Web Example
API Abuse Web Example

This approach is simple, and works really well if the API behind the scenes isn't implementing any form of Referrer-Policy checks.

Note: A Referrer-Policy check could be that the backend API checked to confirm that the website referring the user to the API matches a particular one allowed.

A more specialized, but equally simple attack could provide an API of their own however behind the scenes requests are just proxied to your API for fulfillment.

API Abuse Proxy Example
API Abuse Proxy Example

This particular attack is more annoying to deal with, as the request controlled on their server can forge things like referrer policies. Unfortunately this is the attack I was up against based on the information I had pulled from the user agent.

Stage 1 - Temporary Fix

Time was not on my side and every second passing was more bounced emails that were adding up on the total that would ban me from using the SES service.

Using the X-Ray traces from the first part of the investigation, I was able to find the email address domain that was causing us issues.

X-Ray trace provided document ID and therefore email address of spam
X-Ray trace provided document ID and therefore email address of spam

The domain of our attacker was Checking the website however it hit me that this was just a API driven temporary email address service. The issue was that they must have blocked my domain from sending too them and caused massive bounces.

I needed something done fast to stop the emails from flowing so I added some dirty code in the Lambda handler that receives the images and added a check to make sure that domain wasn't

email = body['email']
    email_domain = email.split('@')[1].lower()
    if email_domain == "":
        return response
except Exception as e:
    return response

I immediately saw the bounced emails begin to drop off and I was able to rest easy; or so I thought.

Stage 2 - Attacked Again

It wasn't for another 14 hours but I suddenly received a notification saying that there was a very high number of bounces coming through. Checking the metrics revealed that again, I was being attacked

SES bounce CloudWatch metrics day 2
SES bounce CloudWatch metrics day 2

I was quick to jump into action this time and found out that the requests were coming from a different temporary email site, this time I went to update the Lambda again with the new email but then got a magical ideal.

Return Fire

Whoever had decided to use my API service was likely harvesting email addresses of people and fronting it with their own Selfie2Anime clone.

Selfie2Anime Clone architecture
Selfie2Anime Clone architecture

The process would go as follows:

  • User submits photo and email to fake site
  • Fake backend receives the photo and email.
  • Fake backend creates a temporary email
  • Fake backend forwards the users photo to the real backend but using the temporary email
  • Temporary email receives the users finished photo
  • Fake backend sends this to the user under their name.

It seems like they've really got me caught! If I block the temporary email service they use they'll just find a new one. But they forgot one crucial thing. I am in control over what THEIR users see.

So I did what anyone in my situation would do... I began sending shirtless photos of older gentleman to the domains and

Fake email handling logic to send back shirtless photos of older gentleman
Fake email handling logic to send back shirtless photos of older gentleman

The idea is that whatever users the fake site does get won't be around for long; cutting off the head so to speak.

How to properly fix

To fix this issue properly was actually very simple with the help of AWS WAF (Web Application Firewall). This service allows IP based rate limits to be put in place effectively slowing down services that might be piggy backing your API.

Below is an example of some CloudFormation that can be deployed and attached to your API Gateway resource to protect it from mass API calls from a single IP address

    Type: AWS::WAFv2::WebACL
        Name: rate-limit-waf
        Description: Selfie2Anime Rate Limits
            Allow: {}
            SampledRequestsEnabled: true
            CloudWatchMetricsEnabled: true
            MetricName: rate-limits
        Scope: "REGIONAL"
            Name: rate-limiter
            Priority: 0
                Block: {}
                    Limit: 100
                    AggregateKeyType: IP
                SampledRequestsEnabled: true
                CloudWatchMetricsEnabled: true
                MetricName: rate-limiter

Summary - Did it work

It is difficult to say whether this solution is going to work long term, as the rate limiting is still able to be circumvented up to a point by deploying multiple services to multiple hosts in the cloud.

The solution where I send back lewd photos however will always be the fall back plan if the more conventional method isn't working out.

What did you think of both solutions? Which one did you prefer. I'm interested to hear if you have other ideas on how you would solve this problem! hit me up on Twitter @nathangloverAUS.


DevOpStar by Nathan Glover | 2024