{"id":30687904,"url":"https://github.com/byt3bl33d3r/doubletap","last_synced_at":"2025-09-02T00:07:57.568Z","repository":{"id":264897510,"uuid":"250075903","full_name":"byt3bl33d3r/DOUBLETAP","owner":"byt3bl33d3r","description":"An asynchronous proxy to proxy HTTP traffic through AWS API Gateway and rotate IP address on each request","archived":false,"fork":false,"pushed_at":"2020-10-14T06:32:31.000Z","size":207,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-08-26T05:47:40.394Z","etag":null,"topics":["apigateway","aws","mitmproxy","mitmproxy-addons","proxy"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/byt3bl33d3r.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-03-25T19:46:48.000Z","updated_at":"2025-08-14T12:40:33.000Z","dependencies_parsed_at":"2024-11-26T18:11:57.585Z","dependency_job_id":"ef6cfe6b-15a0-4efe-95bc-c8e780050901","html_url":"https://github.com/byt3bl33d3r/DOUBLETAP","commit_stats":null,"previous_names":["byt3bl33d3r/doubletap"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/byt3bl33d3r/DOUBLETAP","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byt3bl33d3r%2FDOUBLETAP","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byt3bl33d3r%2FDOUBLETAP/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byt3bl33d3r%2FDOUBLETAP/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byt3bl33d3r%2FDOUBLETAP/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/byt3bl33d3r","download_url":"https://codeload.github.com/byt3bl33d3r/DOUBLETAP/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byt3bl33d3r%2FDOUBLETAP/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273208792,"owners_count":25064209,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-01T02:00:09.058Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apigateway","aws","mitmproxy","mitmproxy-addons","proxy"],"created_at":"2025-09-02T00:07:48.691Z","updated_at":"2025-09-02T00:07:57.550Z","avatar_url":"https://github.com/byt3bl33d3r.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DOUBLETAP\n\nAn asynchronous proxy to proxy HTTP traffic through [AWS API Gateway](https://aws.amazon.com/api-gateway/) and rotate IP address on each request.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://media1.tenor.com/images/2ce301a35b13eea7b1e6d26a2ef98089/tenor.gif?itemid=12637269\"/\u003e\n\u003c/p\u003e\n\n## Sponsors\n[\u003cimg src=\"https://www.blackhillsinfosec.com/wp-content/uploads/2016/03/BHIS-logo-L-300x300.png\" width=\"130\" height=\"130\"/\u003e](https://www.blackhillsinfosec.com/)\n[\u003cimg src=\"https://handbook.volkis.com.au/assets/img/Volkis_Logo_Brandpack.svg\" width=\"130\" hspace=\"10\"/\u003e](https://volkis.com.au)\n[\u003cimg src=\"https://user-images.githubusercontent.com/5151193/85817125-875e0880-b743-11ea-83e9-764cd55a29c5.png\" width=\"200\" vspace=\"21\"/\u003e](https://qomplx.com/blog/cyber/)\n[\u003cimg src=\"https://user-images.githubusercontent.com/5151193/86521020-9f0f4e00-be21-11ea-9256-836bc28e9d14.png\" width=\"250\" hspace=\"20\"/\u003e](https://ledgerops.com)\n[\u003cimg src=\"https://user-images.githubusercontent.com/5151193/87607538-ede79e00-c6d3-11ea-9fcf-a32d314eb65e.png\" width=\"170\" hspace=\"20\"/\u003e](https://www.guidepointsecurity.com/)\n\n## Table of Contents\n\n- [DOUBLETAP](#doubletap)\n  * [What is this?](#what-is-this)\n  * [DOUBLETAP vs Other IP Rotation Approaches](#doubletap-vs-other-ip-rotation-approaches)\n  * [How does it work?](#how-does-it-work)\n  * [Limitations](#limitations)\n  * [Use Cases](#use-cases)\n  * [OPSEC Considerations, Detection \u0026 Defense](#opsec-considerations-detection--defense)\n    + [Offensive OPSEC Considerations](#offensive-opsec-considerations)\n    + [Defense \u0026 Detection](#defense--detection)\n  * [Installation](#installation)\n    + [Docker](#docker)\n    + [Source](#source)\n  * [Usage](#usage)\n    + [Using the Docker Image](#using-the-docker-image)\n    + [Using mitmproxy](#using-mitmproxy)\n    + [Proxy Options \u0026 Customization](#proxy-options--customization)\n    + [Sending Requests through the Proxy](#sending-requests-through-the-proxy)\n    + [Combining DOUBLETAP with Other Tools](#combining-doubletap-with-other-tools)\n      * [WitnessMe](#witnessme)\n      * [SprayingToolkit](#sprayingtoolkit)\n  * [To Do](#to-do)\n\n## What is this?\n\nThis is a [mitmproxy addon](https://docs.mitmproxy.org/stable/addons-overview/) that allows you to dynamically proxy HTTP traffic through [AWS API Gateway](https://aws.amazon.com/api-gateway/) in order to rotate IP address on each request.\n\nEssentially, it's a fully weaponized version of the underlying concept which tools such as [FireProx](https://github.com/ustayready/fireprox) and the [IP_Rotate](https://github.com/RhinoSecurityLabs/IPRotate_Burp_Extension) Burp extension have implemented albeit with a *lot* major improvements:\n\n- Written in Python 3\n- Fully asynchronous (which is *extremely* important for this particular implementation to work efficiently)\n- Works just like a regular proxy\n- Dynamically creates, stages and deploys [AWS API Gateway](https://aws.amazon.com/api-gateway/) endpoints for each new domain across multiple AWS regions concurrently and transparently redirects traffic.\n\n## DOUBLETAP vs Other IP Rotation Approaches\n\nWhen it comes to rotating IPs, there are a lot of ways of doing the same thing. This approach offers major benefits over the traditional methods but obviously there are also some cons.\n\n### Pros\n\n- Much easier to setup and use (you literally just need an AWS account, that's it).\n\n- Cost-wise it's pretty much free unless you go above 1 million requests a month. See the [AWS API Gateway pricing page](https://aws.amazon.com/api-gateway/pricing/) for details.\n\n- You have a much greater \"pool of IPs\" compared to the other approaches. The \"pool\" is even bigger when using multiple AWS regions (which DOUBLETAP does).\n\n- Connection speeds are extremely fast as the \"proxying\" is just an HTTP request to an AWS endpoint. There is basically no network overhead compared to other approaches.\n\n- You're \"proxying\" through a highly reputable and trusted (trust is obviously subjective) entity as supposed to a random service on the internet that may or may not be maliciously modifying/intercepting your traffic.\n\n- The IPs that the end service sees are in the AWS network range which are generally trusted.\n\n### Cons\n\n- You can only proxy HTTP, HTTP/2 and Websocket traffic. Not arbitrary TCP (currently DOUBLETAP only supports HTTP due to some mitmproxy limitations)\n\n- Can be somewhat easily detected by looking for a specific header that cannot be removed (see the [Defense \u0026 Detection](#defense--detection) section for more details)\n\n- Does not work against other services hosted on AWS API Gateway\n\n- It can take up to ~30 seconds to receive back a response when issuing a request to a new domain/URL. Subsequent requests to the same domain/URL will have normal response times (see the [limitations](#limitations) section for more details)\n\n## How Does it Work?\n\n[mitmproxy](https://mitmproxy.org/) exposes an [addon](https://docs.mitmproxy.org/stable/addons-overview/) system which allows you to create components of any complexity that interact with it's proxy engine.\n\nWhen you first fire up DOUBLETAP, it'll query [AWS API Gateway](https://aws.amazon.com/api-gateway/) to see if there's already an existing API called \"DOUBLETAP\" (by default) which was previously setup by the tool, and if so pulls down a list of each API endpoint and the domains they proxy traffic to so that it doesn't create them again.\n\nThe real \"magic\" comes into play when you send an HTTP request through the proxy. DOUBLETAP works by hooking the mitmproxy `request` event, which fires every time the proxy receives a HTTP request, it then performs the following actions:\n\n1. Constructs the root URL from the URL you requested\n2. Checks an internal dictionary structure to see if it already setup an API Gateway endpoint that proxies traffic to that domain\n3. If not, it'll concurrently create, stage and deploy a new API Geteway endpoint across multiple AWS regions ([10 by default](https://github.com/Porchetta-Industries/DOUBLETAP/blob/master/doubletap.py#L11)) to proxy traffic to the domain you requested (this makes the IP rotation rate even higher).\n4. During the API Gateway endpoint creation, it'll remap the `X-Forwarded-For` header: this allows us to specify an IP of our choosing that the end server will see in that header.\n5. DOUBLETAP can detect when the API is fully staged (usually takes anywhere between 10-30 seconds) and will only allow the requests to proceed once the API endpoints are ready across all regions.\n6. It'll then pick at random an API Gateway endpoint URL from the ones it created\n7. Generate a fake IP for the `X-Forwarded-for` header and change the User Agent of the request\n8. Finally it'll seamlessly redirect the modified request to the chosen API Gateway endpoint URL which will then proxy the request to the actual target.\n\nThe result is that the server you're hitting will see a truly unique IP on almost each request. Additionally it will not give away your real IP address through the `X-Forwarded-For` header as it's supplied a bogus IP.\n\nWhat's super important to note here is that all of this happens **asynchronously** in the background, meaning the proxy does *not* block every time it has to interact with AWS and/or on each HTTP request.\n\n## Limitations\n\nA fundamental limitation of this technique/implementation is that whenever you request a URL to a new domain that DOUBLETAP *hasn't* created an API Gateway endpoint for previously, it takes anywhere between 10-30 seconds before the Gateway endpoint is staged and ready to accept traffic.\n\nPractically speaking, this means an HTTP request to a new domain/URL will just sit there doing nothing for up to 30ish seconds until you receive back any data. Obviously, subsequent requests to that same domain/URL will not have this issue and you'll receive back the response instantly.\n\nAs far as I'm aware, there really isn't a way around this. Additionally, AWS doesn't provide a reliable way to determine whether an API Gateway endpoint has finished staging or not. DOUBLETAP handles this by spinning up background AsyncIO tasks that perform an HTTP request to the endpoint URLs and do a [signature check](https://github.com/Porchetta-Industries/DOUBLETAP/blob/master/doubletap/aws.py#L403-L421) on the response looking for specific status codes and data that I've found through testing mean the API is still staging.\n\nTo help alleviate this limitation, see the [section](#proxy-options--customization) on the `prestage` and `allowlist` options.\n\nAdditionally:\n\n- HTTP/2 requests are not supported. [mitmproxy](https://mitmproxy.org/) doesn't have the ability to redirect HTTP/2 connections (yet). However, AWS API Gateway does support HTTP/2 and Websocket connections so mitmproxy just needs to catch up.\n- Incredibly, if the end service you're trying access legitimately uses AWS API Gateway, the proxying won't work. It's like a cloud Judo move. Thankfully, not a lot of things use AWS API Gateway as I've only ran into this once in a year or so of using this tool.\n\n## Use Cases\n\n1. The new SprayingToolkit update is built to support proxying everything through DOUBLETAP. No more IP blacklisting on password sprays 😈\n2. ENTROPICFORESIGHT is built to support proxying everything through DOUBLETAP. No more API keys and/or rate limiting when trying to scrape OSINT data 😈\n2. Scraping with Headless Browsers (Note: You're going to want to use the `allowlist` and/or the `prestage` options if you do this. See [this](proxy-options--customization) section.)\n3. Anything that can benefit from a new IP on each request 😈 make the possibilities flow through you.\n\n## OPSEC Considerations, Detection \u0026 Defense\n\n\n### Offensive OPSEC Considerations\n\n- The underlying technique can be detected by looking for the `x-amz-apigw-id` header which is sent on each request through AWS API Gateway. There is no way to avoid this. (See the [Defense \u0026 Detection](#defense-\u0026-detection) section for more details)\n- While the IP on each request does change *most* of the time there is always a slight possibility it doesn't as this isn't a \"legit\" or predictable feature of AWS API Gateway. Either way, your real IP won't ever be revealed.\n- By default, a random IP is generated and sent along in the `X-Forwarded-For` header using the Python [Faker](https://github.com/joke2k/faker) library.\n\n### Defense \u0026 Detection\n\nWhile the IP address does change on each connection, there are some things that are \"baked\" into how AWS API Gateway works that can be used to detect this technique.\n\n**Note: all of the below is from my current understanding of how things work which is subject to change 😜. I'm by no means an AWS expert. Feel free to reach out if something is inaccurate.**\n\nThe most effective way of identifying the *underlying technique* of this tool (this is a non-fragile detection) is to look for the `x-amz-apigw-id` header in HTTP requests. This header contains a Base64 encoded value which identifies the API Gateway being used and there isn't a way to overwrite it/remove it (unlike the `X-Forwarded-For` header). This can be used to identify the AWS account which has deployed the specific gateway, so you could give the header value to AWS in order to file an abuse complaint if needed.\n\nYou probably shouldn't be receiving HTTP requests from AWS API Gateway anyway so I feel pretty confident in saying this is safe way of blocking/detecting this technique. Creating an IDS/IPS rule looking for that header should be pretty trivial.\n\nIf you're using AWS API Gateway legitimately to host your service in the first place, you're implicitly safe from this technique as proxying to another service hosted on AWS API Gateway won't work! I call this Cloud Judo.\n\nFinally, by default a fake IP is generated and sent along in the `X-Forwarded-For` header using the Python [Faker](https://github.com/joke2k/faker) library. You could check this IP to make sure it's valid and correlate it with WHOIS data. Or just take a look at the Faker library closer to see if there's a way of predicting the IPs it generates.\n\n## Installation\n\n### Docker\n\n```console\ndocker build github.com/Porchetta-Industries/DOUBLETAP\n```\n\n### Source\n\n```console\ngit clone https://github.com/Porchetta-Industries/DOUBLETAP \u0026\u0026 cd DOUBLETAP\npip3 install -r requirements.txt\n```\n\n## Usage\n\nDOUBLETAP needs AWS credentials in order to interact with AWS API Gateway. By default it'll look for the `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables containing the AWS access key and AWS secret key respectively. If it doesn't find those environment variables, it'll try to grab the access and secret key from the `~/.aws/credentials` file which is setup when you install the [AWS CLI utility](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html).\n\nThe proxy will bind on all interfaces on port `8080` by default.\n\n### Using the Docker Image\n\n```console\ndocker run -p 8080:8080 -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_KEY --rm -it $IMAGE_ID\n```\n\nOptionally you can put the environment variables in a `.env` file and use that:\n\n```console\n# Print out the .env file\n$ cat .env\nAWS_ACCESS_KEY_ID=\u003cmy_aws_access_key_id\u003e\nAWS_SECRET_ACCESS_KEY=\u003cmy_aws_secret_access_key\u003e\n\n# Run DOUBLETAP and pass it the .env file\n$ docker run -p 8080:8080 --env-file .env --rm -it $IMAGE_ID\n```\n\n### Using mitmproxy\n\nYou can also start DOUBLETAP directly using the `mitmdump` utility which comes with the `mitmproxy` Python package. This is useful if you want to avoid using the docker image.\n\n```console\nmitmdump --no-http2 -k -s doubletap.py\n```\n\n### Proxy Options \u0026 Customization\n\nThere are a few options you can pass to DOUBLETAP in order to customize the proxying behavior, prestage proxies and/or allow only certain domains/URLs to be proxied. To pass option(s) to the proxy you have to use the `--set` flag followed by `option=value`. (This is just how [mitmproxy addons work](https://docs.mitmproxy.org/stable/addons-options/#simple-example))\n\nExamples:\n\n```console\n# Using mitmdump directly\n$ mitmdump --no-http2 -k -s doubletap.py --set allowlist='.*slashdot.org,.*google.com' --set cleanup=true --set prestage='https://www.slashdot.org'\n\n# Using the docker image\n$ docker run -p 8080:8080 --env-file .env --rm -it $IMAGE_ID --set allowlist='.*slashdot.org' --set cleanup=true --set prestage=~/urls.txt\n```\n\n#### allowlist\n\nThe `allowlist` option accepts a file containing regexes (one per line) or a comma separated list of regex(s). If this option is given, DOUBLETAP will only proxy requests **if** the root URL or domain matches one of the regexes.\n\nThis is extremely useful when proxying headless browsers through DOUBLETAP as webpages tend to load a bunch of resources from third-party domains. Because of the [~30 seconds you have to wait for each new URL/domain](#limitations), it would take forever to load a webpage through a headless browser without this option.\n\n#### cleanup\n\nAs you might think, the `cleanup` option will destroy all existing proxies in AWS so you have a \"clean slate\" to start with.\n\nOnly accepts `true`, defaults to `false`.\n\n**Note: it can take up to a minute for the resource to get destroyed on AWS's side. Unfortunately there's no way of checking this from DOUBLETAP.**\n\n#### prestage\n\nThe `prestage` option accepts a file of root URLs (one per line) or a comma separated list of root URLs that will be \"prestaged\" before the proxy starts up.\n\nPractically speaking, prestaging URLs sets up the proxies in AWS before hand so you don't have to wait those [~30 seconds](#limitations) before getting back a response when you start proxying traffic.\n\n### Sending Requests through the Proxy\n\nThis really comes down to what you're trying to do/tool you're using. Generally, most tools have HTTP proxy support. You can also use ProxyChains to \"force\" something to use a proxy.\n\nHere's some commands I recommend trying to test the proxy is working and to get an idea how things work:\n\n**Note: please read the [limitations](#limitations) section. First time you request a URL/domain it can take up to 30 seconds to receive back a response. Depending on the tool, you might need to set a higher timeout threshold in order to accommodate this.**\n\n#### cURL:\n```console\ncurl --insecure -x http://127.0.0.1:8080 -v https://ifconfig.me/all\n```\n\n#### HTTPie:\n```console\nhttp --verify no -v --proxy http://127.0.0.1:8080 https://ifconfig.me/all\n```\n\nIf you run one of the above commands, you should see a new IP in the response on each request 🔥😈\n\n### Combining DOUBLETAP with Other Tools\n\n#### WitnessMe\n\nAs of v1.5.0, WitnessMe supports proxies. You can combine WitnessMe with DOUBLETAP to screenshot webpages while rotating IPs! Super useful to assess attack surfaces on Red Teams.\n\nIn the following example our target is `contoso.com`. We've done some OSINT already and gathered a list of subdomains that we want to screenshot.\n\nIn one terminal window start DOUBLETAP:\n\n```console\ndocker run -p 8080:8080 --env-file .env --rm -it $IMAGE_ID --set allowlist='.*contoso.com'\n```\n\nIn another terminal window, start WitnessMe and give it the `HTTP_PROXY` environment variable pointing to DOUBLETAP:\n\n```console\nHTTP_PROXY=http://127.0.0.1:8080 witnessme --timeout 40 screenshot /my_contoso_subdomains.txt\n```\n\nThe above command will force WitnessMe to proxy all traffic through DOUBLETAP and sets the timeout to 40 seconds so the connection doesn't time out before the proxy is setup in AWS.\n\n#### SprayingToolkit\n\nTo Do\n\n\n# To Do\n\n- ~~Implement \"domain/URL pre-loads\"~~\n- ~~Implement \"domain/URL allow/deny\" support with regexes~~\n- Allow customization of how DOUBLETAP chooses the API Gateway proxy URL (e.g. Round-Robin as supposed to at random)\n- Allow customization of User-Agent replacement.\n- Allow customization of how the bogus IP in the `X-Forwarded-For` header is generated\n- ~~Expose a \"cleanup\" command to remove stages from API Gateway~~\n- ~~Better logging~~\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbyt3bl33d3r%2Fdoubletap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbyt3bl33d3r%2Fdoubletap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbyt3bl33d3r%2Fdoubletap/lists"}