Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# Change Log
All notable changes to the Sandcastle script will be documented in this file.

## 1.3.0 - 2022-12-01
* development resumed by @securityshrimp
* Ported and refactored for use with Python3
* Added Mac support for homebrew installed AWS command line utils
* Added support for AWS Cli tools installed in non standard locations with the -a parameter


## 1.2.4 – 2017-05-28
* Temporarily removed PyPi distribution channel
* Shipping an updated bucket names wordlist
Expand Down
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

<img src="https://cloud.githubusercontent.com/assets/4115778/24827505/eab7322a-1c42-11e7-96f3-dbc772da5f10.png" width="70%" alt="Sandcastle logo - AWS S3 bucket enumeration">

Inspired by a conversation with Instacart's [@nickelser](https://github.com/nickelser) on HackerOne, I've optimised and published Sandcastle – a Python script for AWS S3 bucket enumeration, formerly known as bucketCrawler.
Originally inspired by a conversation with Instacart's [@nickelser](https://github.com/nickelser) on HackerOne, 0xsearches optimized and published Sandcastle – a Python script for AWS S3 bucket enumeration, formerly known as bucketCrawler.

In December 2022, Securityshrimp updated this code to work with Python 3 while adding Mac support, and support for any other flavor of *nix by adding the ability to provide a custom path the the AWS command line binary

The script takes a target's name as the stem argument (e.g. `shopify`) and iterates through a file of bucket name permutations, such as the ones below:

Expand All @@ -19,8 +21,9 @@ The script takes a target's name as the stem argument (e.g. `shopify`) and itera
## Getting started
Here's how to get started:

1. Clone this repo (PyPi distribution temporarily disabled).
2. Run `sandcastle.py` with a target name and input file (grab an example from this repo)
1. Clone this repo.
2. pip install requests if you dont have it installed
2. Run `sandcastle.py` with a target name (`-t name`) and input file (`-f foo.txt`)
3. Matching bucket permutations will be identified, and read permissions tested.

```
Expand All @@ -33,6 +36,7 @@ arguments:
-f inputFile, --file inputFile
Select a bucket permutation file (default: bucket-
names.txt)
-a aws binary, --awscli-path /path/to/aws binary
```

```
Expand All @@ -41,7 +45,7 @@ arguments:
_\ \/ _ `/ _ \/ _ / __/ _ `(_-</ __/ / -_)
/___/\_,_/_//_/\_,_/\__/\_,_/___/\__/_/\__/

S3 bucket enumeration // release v1.2.4 // ysx
S3 bucket enumeration // release v1.3.0 // SecurityShrimp


[*] Commencing enumeration of 'shopify', reading 138 lines from 'bucket-names.txt'.
Expand Down Expand Up @@ -74,6 +78,7 @@ From the Amazon [documentation](http://docs.aws.amazon.com/AmazonS3/latest/dev/U
> In terms of implementation, buckets and objects are resources, and Amazon S3 provides APIs for you to manage them.

## Closing remarks
* I have taken over this project from 0xSearches. Their gitub has been inactive since 2017
* This is my first public security project. Sandcastle is published under the MIT License.
* Usage acknowlegements:
* Castle (icon) by Andrew Doane from the Noun Project
Expand Down
66 changes: 37 additions & 29 deletions sandcastle.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,51 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys, os, commands, requests
import sys, os, subprocess, requests, platform
from argparse import ArgumentParser

print """
____ __ __ __
/ __/__ ____ ___/ /______ ____ / /_/ /__
print( """
____ __ __ __
/ __/__ ____ ___/ /______ ____ / /_/ /__
_\ \/ _ `/ _ \/ _ / __/ _ `(_-</ __/ / -_)
/___/\_,_/_//_/\_,_/\__/\_,_/___/\__/_/\__/

S3 bucket enumeration // release v1.2.4 // ysx
/___/\_,_/_//_/\_,_/\__/\_,_/___/\__/_/\__/

"""
S3 bucket enumeration // release v1.3.0 // SecurityShrimp

""")
targetStem = ""
inputFile = ""

parser = ArgumentParser()
parser.add_argument("-t", "--target", dest="targetStem",
help="Select a target stem name (e.g. 'shopify')", metavar="targetStem", required="True")
help="Select a target stem name (e.g. 'shopify')", metavar="targetStem", required="True")
parser.add_argument("-f", "--file", dest="inputFile",
help="Select a bucket permutation file (default: bucket-names.txt)", default="bucket-names.txt", metavar="inputFile")
help="Select a bucket permutation file (default: bucket-names.txt)", default="bucket-names.txt", metavar="inputFile")
parser.add_argument("-a", "--awscli-path", dest="aws_path",
help = "Specify the file path for the location on disk of the AWS CLI binary",
metavar = "awspath",default=None)
args = parser.parse_args()

with open(args.inputFile, 'r') as f:
bucketNames = [line.strip() for line in f]
lineCount = len(bucketNames)

print "[*] Commencing enumeration of '%s', reading %i lines from '%s'." % (args.targetStem, lineCount, f.name)

for name in bucketNames:
r = requests.head("http://%s%s.s3.amazonaws.com" % (args.targetStem, name))
if r.status_code != 404:
# macOS, coming soon: os.system("notify Potential match found! %s%s: %s" % (args.targetStem, name, r.status_code))
print "[+] Checking potential match: %s%s --> %s" % (args.targetStem, name, r.status_code)
check = commands.getoutput("/usr/local/bin/aws s3 ls s3://%s%s" % (args.targetStem, name))
print check
else:
sys.stdout.write('')

print "[*] Enumeration of '%s' buckets complete." % (args.targetStem)
# macOS, coming soon: os.system("notify Enumeration of %s buckets complete." % (args.targetStem))
if platform.system() == "'Windows'":
print("Sorry Windows is not currently a supported platform for Sandcastle")
else:
with open(args.inputFile, 'r') as f:
bucketNames = [line.strip() for line in f]
lineCount = len(bucketNames)

print(f"[*] Commencing enumeration of '{args.targetStem}', reading {lineCount} lines from '{f.name}'.")

for name in bucketNames:
r = requests.head(f"http://{args.targetStem}{name}.s3.amazonaws.com")
if r.status_code != 404 and r.status_code != 400:
print (f"[+] Checking potential match: {args.targetStem}{name} --> Bucket HTTP Response code:{r.status_code}")
if platform.system() == "'Darwin'":
check = subprocess.getoutput("/opt/homebrew/bin/aws s3 ls s3://{args.targetStem}{name}")
print(f"{check}")
elif platform.system() == "'Linux'":
check = subprocess.getoutput("/usr/local/bin/aws s3 ls s3://{args.targetStem}{name}")
print(f"{check}")
elif args.aws_path != None:
check = subprocess.getoutput("{args.aws_path} s3 ls s3://{args.targetStem}{name}")
print(f"{check}")
print (f"[*] Enumeration of '{args.targetStem}' buckets complete.")