├── .github └── workflows │ └── semgrep.yml ├── Gemfile ├── README.md ├── objectify-s3.sh ├── sample_output.png ├── setup.sh └── vulnobj.rb /.github/workflows/semgrep.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request: {} 3 | push: 4 | branches: 5 | - main 6 | - master 7 | paths: 8 | - .github/workflows/semgrep.yml 9 | schedule: 10 | - cron: '0 0 * * 0' 11 | name: Semgrep 12 | jobs: 13 | semgrep: 14 | name: Scan 15 | runs-on: ubuntu-20.04 16 | env: 17 | SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} 18 | container: 19 | image: returntocorp/semgrep 20 | steps: 21 | - uses: actions/checkout@v3 22 | - run: semgrep ci 23 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # Gemfile 2 | source 'https://rubygems.org' do 3 | gem 'aws-sdk-s3' 4 | gem 'thread' 5 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Twitter URL](https://img.shields.io/twitter/url/https/twitter.com/bukotsunikki.svg?style=social&label=Follow%20%400xGaurav)](https://twitter.com/0xGaurav) 2 | 3 | # objectify-s3 4 | Objectify-s3 is a fully automated scanner that recursively scans all AWS S3 buckets and objects in your AWS account for misconfigured permissions. Unlike most S3 auditing tools that only show bucket policy misconfigurations, this one checks object level ACLs as well, recursively.
5 | 6 | # Requirements 7 | 1. [Ruby](https://www.ruby-lang.org/en/documentation/installation/) (Comes preinstalled with Mac and most Linux distributions) 8 | 2. [awscli](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) (Objectify-s3 will install, if not found) 9 | 3. [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) (Comes preinstalled with all Mac and linux distributions) 10 | 11 | # Installation 12 | - Set up your awscli credentials, if not done already. [Follow these instructions](https://github.com/emgaurav/objectify-s3/wiki/Setting-up-awscli-credentials) 13 | - Remove previous installation, if any 14 | ``` 15 | rm -rf ~/objectify-s3 16 | ``` 17 | 18 | - Then, Use this one liner 19 | ``` 20 | cd ~; git clone https://github.com/emgaurav/objectify-s3.git; cd objectify-s3; bash setup.sh 21 | ``` 22 | - Finally, Run `source ~/.bashrc` or `source ~/.zshrc` depending on your shell type to source the alias. Alternatively, you can close and reopen your terminal window. 23 | 24 | 25 | # Usage 26 | - To run a fully automated scan 27 | ``` 28 | objectify-s3 29 | ``` 30 | - To scan a single bucket 31 | ``` 32 | objectify-s3 -b bucket-name 33 | ``` 34 | - To scan a list buckets 35 | ``` 36 | objectify-s3 -r /full/path/to/file.txt 37 | ``` 38 | ***An HTML report is generated and stored at ~/objectify-s3/out.html***

39 | _Note: You must provide the full path to file even if it is in your current directory_
40 | _`objectify-s3` runs with 'default' aws credentials profile. Custom profiles are not supported yet._ 41 | 42 | **Press _Ctrl_ + \\ to skip finding objects from current bucket or directory**
43 | 44 | # Sample Output 45 | drawing 46 | 47 | # Supported Platforms 48 | 1. Linux 49 | 2. MAC 50 | 51 | # Credits/References 52 | https://faraday.ai/blog/finding-public-s3-objects/ 53 | -------------------------------------------------------------------------------- /objectify-s3.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #error handler 3 | trap "exit" SIGINT SIGSTOP 4 | trap "echo $'\n'; kill 0" EXIT 5 | #'$(tput bold)..bye!' 6 | 7 | #help 8 | function help() { 9 | 10 | echo $'\n' 11 | echo "$(tput bold)Usage : $(tput sgr 0)"$'\n' 12 | echo "• To Run a fully automated scan:$(tput sgr 0)"$'\n' 13 | echo " $(tput bold)\$ objectify-s3$(tput sgr 0)"$'\n' 14 | echo "• To scan a single bucket:$(tput sgr 0)"$'\n' 15 | echo " $(tput bold)\$ objectify-s3 -b $(tput setaf 3)$(tput sgr 0)"$'\n' 16 | echo "• To scan a list of buckets:$(tput sgr 0)"$'\n' 17 | echo " $(tput bold)\$ objectify-s3 -r $(tput setaf 3)$(tput sgr 0)" 18 | } 19 | 20 | #refresh files 21 | rm ~/.objectify-s3/allbuckets.txt 2>/dev/null; touch ~/.objectify-s3/allbuckets.txt 2>/dev/null 22 | rm ~/.objectify-s3/vulnbuckets.txt 2>/dev/null; touch ~/.objectify-s3/vulnbuckets.txt 2>/dev/null 23 | rm ~/objectify-s3/out.html 24 | 25 | #check arguments 26 | if [ ! -z "$1" ]; then 27 | if [[ $1 == "-r" ]]; then 28 | if [ ! -z "$2" ]; then 29 | file=$2; 30 | else 31 | echo $'\n'"$(tput setaf 1)Target file not specified$(tput sgr 0)"; 32 | help; 33 | exit; 34 | fi 35 | elif [[ $1 == "-b" ]]; then 36 | if [ ! -z "$2" ]; then 37 | echo $2>~/.objectify-s3/allbuckets.txt; 38 | file=~/.objectify-s3/allbuckets.txt; 39 | else 40 | echo $'\n'"$(tput setaf 1)Target bucket not specified$(tput sgr 0)"; 41 | help; 42 | exit; 43 | fi 44 | elif [[ $1 != "-r" || $1 != "-b" ]]; then 45 | echo $'\n'"$(tput setaf 1)Invalid argument$(tput sgr 0)"; 46 | help; 47 | exit; 48 | fi 49 | fi 50 | 51 | #function to print banner 52 | function printbanner() { 53 | tput clear; 54 | tput bel; 55 | tput bold; 56 | printf " _ _ _ _ __ _____ 57 | ___ | |__ (_) ___ ___| |_(_)/ _|_ _ ___|___ / 58 | / _ \| '_ \| |/ _ \/ __| __| | |_| | | |_____/ __| |_ \ 59 | | (_) | |_) | | __/ (__| |_| | _| |_| |_____\__ \___) | 60 | \___/|_.__// |\___|\___|\__|_|_| \__, | |___/____/ 61 | |__/ |___/ 62 | " 63 | tput sgr 0; 64 | } 65 | 66 | #checks and installs updates 67 | function checkupdates() { 68 | echo $'\n'"$(tput bold)Checking for updates.. $(tput sgr 0)" 69 | cd ~/objectify-s3; git reset --hard >/dev/null 2>&1; 70 | git pull > ~/.objectify-s3/tmp.txt 2>/dev/null 71 | if cat ~/.objectify-s3/tmp.txt|grep -q -i 'changed'; then 72 | echo "$(tput bold)Updated Successfully" 73 | echo "Relaunching.." 74 | sleep 1; 75 | bash ~/objectify-s3/objectify-s3.sh; 76 | else 77 | echo "$(tput setaf 2)$(tput bold)Using latest version$(tput sgr 0)"$'\n'; 78 | fi 79 | } 80 | 81 | function checkaws() { 82 | #echo "checking awscli configuration" 83 | if ! aws configure list-profiles|grep -q -i "default"; then 84 | echo "$(tput bold)$(tput setaf 1)awscli is not configured. You must configure using 'aws configure' command.$(tput sgr 0)"$'\n' 85 | exit 0 86 | fi 87 | 88 | echo "----------------------------------------" 89 | } 90 | #Lists all available buckets 91 | function listbuckets () { 92 | echo $'\n'"$(tput smso)$(tput setaf 2)Listing available buckets $(tput sgr 0)"$'\n' 93 | echo '
'>>~/objectify-s3/out.html 94 | 95 | if [[ ! $file ]]; then 96 | aws s3 ls| awk '{print $3}'>>~/.objectify-s3/allbuckets.txt 97 | file=~/.objectify-s3/allbuckets.txt 98 | fi 99 | tput setaf 2; cat $file; 100 | #echo "

Available Buckets

">>out.html 101 | for i in $(cat $file); do echo "
  • $i">>~/objectify-s3/out.html; done 102 | echo "
  • ">>~/objectify-s3/out.html 103 | #echo $'\n'; 104 | } 105 | 106 | #finds vulnerable buckets 107 | function findvulnbuckets() { 108 | #aws s3api get-public-access-block --bucket $bucket >tmp.txt 2>&1 --ignore 109 | if aws s3api get-public-access-block --bucket $bucket 2>&1|grep -q -i -e 'false' -e 'NoSuchPublicAccessBlockConfiguration'; then 110 | echo $bucket>> ~/.objectify-s3/vulnbuckets.txt 111 | echo "$(tput bold)$(tput setaf 1)Found:$(tput sgr 0) $bucket" 112 | echo "
  • $bucket
  • ">>~/objectify-s3/out.html 113 | fi 114 | } 115 | 116 | #Calls another function find misconfigured buckets 117 | function printmisconfbuckets () { 118 | echo $'\n'"$(tput smso)$(tput setaf 172)Finding misconfigured buckets. It takes a few seconds..$(tput sgr 0)"$'\n' 119 | #echo "

    Misconfigured Buckets

    ">>out.html 120 | echo '
    '>>~/objectify-s3/out.html 121 | for bucket in `cat $file` 122 | do 123 | #this function checks vulnerable buckets from all buckets 124 | findvulnbuckets & 125 | done && wait 126 | echo "
    ">>~/objectify-s3/out.html 127 | } 128 | 129 | #this function checks for vulnerable objects from file vulnbuckets.txt 130 | function findvulnobj() { 131 | regionparam=`aws s3api get-bucket-location --bucket $bucket| grep -i 'constraint'| cut -d'"' -f4` 132 | if [ -z $regionparam ]; then region="us-east-1"; else region=$regionparam; fi 133 | bundle exec ruby vulnobj.rb $bucket $region 2>/dev/null 134 | } 135 | 136 | printbanner; checkupdates; checkaws; 137 | echo '' >>~/objectify-s3/out.html 138 | echo "">>~/objectify-s3/out.html 139 | echo '

    objectify-s3


    ' >>~/objectify-s3/out.html 140 | listbuckets; printmisconfbuckets; 141 | echo $'\n'"$(tput bold)$(tput setab 7)$(tput setaf 1)Listing public objects from all buckets now $(tput sgr 0)"$'\n' 142 | echo '

    Public Objects Found ▼

    '>>~/objectify-s3/out.html 143 | for bucket in `cat ~/.objectify-s3/vulnbuckets.txt` 144 | do 145 | echo $'\n'"$(tput bold)$(tput setaf 1)Bucket - > $bucket $(tput sgr 0)"; 146 | findvulnobj; 147 | tmpfile=~/objectify-s3/tmp.html 148 | if [ -f $tmpfile ]; then 149 | echo '
    '>>~/objectify-s3/out.html 150 | cat ~/objectify-s3/tmp.html >> ~/objectify-s3/out.html 2>/dev/null 151 | rm ~/objectify-s3/tmp.html 2>/dev/null 152 | echo "
    ">>~/objectify-s3/out.html 153 | fi 154 | 155 | 156 | done 157 | echo "
    ">>~/objectify-s3/out.html 158 | open ~/objectify-s3/out.html 159 | 160 | echo $'\n'"$(tput smso) $(tput setaf 2) <<<<<<<<<<<<<< COMPLETED >>>>>>>>>>>>>> $(tput sgr 0)"$'\n' 161 | -------------------------------------------------------------------------------- /sample_output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emgaurav/objectify-s3/889e7a173b985dbc6fed3d00293a1f6e1e29b8e3/sample_output.png -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "$(tput clear)$(tput bel)$(tput bold)" 3 | printf " _ _ _ _ __ _____ 4 | ___ | |__ (_) ___ ___| |_(_)/ _|_ _ ___|___ / 5 | / _ \| '_ \| |/ _ \/ __| __| | |_| | | |_____/ __| |_ \ 6 | | (_) | |_) | | __/ (__| |_| | _| |_| |_____\__ \___) | 7 | \___/|_.__// |\___|\___|\__|_|_| \__, | |___/____/ 8 | |__/ |___/ 9 | " 10 | tput sgr 0; 11 | if [ ! -d ~/.objectify-s3 ]; then 12 | mkdir ~/.objectify-s3 13 | fi 14 | touch ~/.objectify-s3/vulnbuckets.txt; 15 | touch ~/.objectify-s3/allbuckets.txt; 16 | echo -e "\n\n" 17 | echo "$(tput bold)$(tput setaf 2)<<<< Beginning Installation >>>>$(tput sgr 0)"; 18 | echo "-----------------------------------" 19 | #setting aliases 20 | chmod +x objectify-s3.sh 21 | #ln -s objectify-s3.sh objectify-s3 2>/dev/null 22 | echo 'alias objectify-s3="bash ~/objectify-s3/objectify-s3.sh"' >> ~/.bashrc 23 | echo 'alias objectify-s3="bash ~/objectify-s3/objectify-s3.sh"' >> ~/.zshrc 24 | echo 'export PATH="$PATH:~/objectify-s3/"' >> ~/.bashrc 25 | echo 'export PATH="$PATH:~/objectify-s3/"' >> ~/.bash_profile 26 | echo 'export PATH="$PATH:~/objectify-s3/"' >> ~/.zshrc 27 | 28 | echo "$(tput bold)Finding ruby" 29 | if which ruby; then 30 | echo "$(tput bold)$(tput setaf 2)Found$(tput sgr 0)" 31 | echo "-----------------------------------" 32 | echo "Installing required gems" 33 | bundle install 34 | echo "$(tput bold)$(tput setaf 2)Done$(tput sgr 0)" 35 | else 36 | echo "$(tput setaf 1)$(tput bold)it seems ruby is not installed$(tput sgr 0)" 37 | exit 0; 38 | fi 39 | echo "-----------------------------------" 40 | echo "$(tput bold)Finding awscli" 41 | if which aws; then 42 | echo "$(tput bold)$(tput setaf 2)Found$(tput sgr 0)" 43 | echo "-----------------------------------" 44 | else 45 | echo "$(tput setaf 1) $(tput bold)it seems awscli is not installed.$(tput sgr 0)" 46 | echo "$(tput setaf 2) $(tput bold)Trying to install now. $(tput sgr 0)" 47 | if which brew; then 48 | brew install awscli 49 | brew link awscli 50 | echo "Now you need to set up your credentials for awscli." 51 | echo "-----------------------------------" 52 | else 53 | curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" 54 | unzip awscliv2.zip 55 | sudo ./aws/install 56 | aws --version 57 | echo "$(tput setaf 2) $(tput bold) $(tput bel)awscli should be installed now. you must set up your aws access using $(tput sgr 0) aws configure" 58 | echo "-----------------------------------" 59 | fi 60 | fi 61 | #echo "$(tput bold)All Done. Run 'source ~/.bashrc' to set alias $(tput sgr 0)"$'\n' 62 | source ~/.bash_profile 2>/dev/null 63 | source ~/.bashrc 2>/dev/null 64 | source ~/.zshrc 2>/dev/null 65 | 66 | 67 | echo "$(tput setaf 2)$(tput bold)$(tput bel)<<<< Installation Complete >>>>$(tput sgr 0)"$'\n' 68 | 69 | -------------------------------------------------------------------------------- /vulnobj.rb: -------------------------------------------------------------------------------- 1 | # find_public_s3_objects.rb 2 | require 'aws-sdk-s3' # v2: require 'aws-sdk' 3 | require 'thread/pool' 4 | 5 | BUCKET = ARGV[0] or raise("expected bucket name") 6 | 7 | s3 = Aws::S3::Resource.new(region: ARGV[1]) 8 | region = ARGV[1] 9 | 10 | count = 0 11 | comp = 500 12 | pool = Thread.pool 50 13 | mutex = Mutex.new 14 | s3.bucket(BUCKET).objects.each do |object| 15 | pool.process do 16 | grants = object.acl.grants 17 | mutex.synchronize do 18 | count += 1 19 | if count % comp == 0 20 | $stdout.write "Objects Scanned : #{count}" 21 | string = " - Press Ctrl + \\ to skip scanning this directory" 22 | puts string 23 | comp = comp * 2 24 | 25 | end 26 | end 27 | if grants.map { |x| x.grantee.uri }.any? { |x| x =~ /AllUsers|AuthenticatedUsers/ } 28 | mutex.synchronize do 29 | puts " ⭕ https://#{BUCKET}.s3.#{region}.amazonaws.com/"+object.key 30 | File.open("tmp.html","a") do |f| 31 | f.puts "
  • /"+object.key+"
  • " 32 | end 33 | end 34 | end 35 | end 36 | end 37 | 38 | pool.shutdown 39 | --------------------------------------------------------------------------------