├── README.md └── fsp /README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 | FireStorePwn (fsp) 4 |

5 | 6 |

Firestore Database Vulnerability Scanner Using APKs

7 | 8 | --- 9 | 10 | fsp scans an APK and checks the Firestore database for rules that are not secure, testing with or without authentication. 11 | 12 | If there are problems with the security rules, attackers could steal, modify or delete data and raise the bill. 13 | 14 | ## How it works 15 |

16 | fsp-flow 17 |

18 | 19 | # Install fsp 20 | 21 | ```sh 22 | sudo wget https://raw.githubusercontent.com/takito1812/FireStorePwn/main/fsp -O /bin/fsp 23 | sudo chmod +x /bin/fsp 24 | ``` 25 | 26 | ### Running fsp 27 | 28 | #### Scanning an APK without authentication 29 | 30 | ```sh 31 | fsp app.apk 32 | ``` 33 | 34 | #### Scanning an APK with authentication 35 | 36 | With email and password. 37 | 38 | ```sh 39 | fsp app.apk test@test.com:123456 40 | ``` 41 | 42 | With a token. 43 | 44 | ```sh 45 | fsp app.apk eyJhbGciO... 46 | ``` 47 | -------------------------------------------------------------------------------- /fsp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # FireStorePwn 4 | 5 | # Color variables 6 | resetColor="\e[0m" 7 | redColor="\e[31m" 8 | greenColor="\e[32m" 9 | yellowColor="\e[33m" 10 | magentaColor="\e[35m" 11 | cyanColor="\e[36m" 12 | 13 | echo -e "${magentaColor} _____ 14 | _/ ____\____________ ${cyanColor}FireStorePwn${magentaColor} 15 | \ __\/ ___/\____ \ 16 | | | \___ \ | |_> > ${cyanColor}Firestore Database Vulnerability Scanner Using APKs${magentaColor} 17 | |__| /____ >| __/ 18 | \/ |__| ${cyanColor}by Víctor García (@takito1812)${resetColor} 19 | " 20 | 21 | checkTool() { 22 | if ! dpkg -l "$1" >/dev/null 2>&1; then 23 | echo -e "${yellowColor}[!] Installing $1...${resetColor}\n" 24 | apt-get update -y >/dev/null 2>&1 25 | apt-get install "$1" -y >/dev/null 2>&1 26 | fi 27 | } 28 | 29 | quit() { 30 | rm -rf "$filename" 31 | exit 32 | } 33 | 34 | checkCollections() { 35 | echo 36 | if [[ "$1" ]]; then 37 | token="$1" 38 | if echo "$1" | grep -q ":"; then 39 | if ! apiKey=$(grep -i "google_api_key" "$filename/res/values/strings.xml"); then 40 | echo -e "${redColor}[-] google_api_key not found in res/values/strings.xml file.${resetColor}" 41 | quit 42 | else 43 | echo -e "${greenColor}[+] google_api_key found in res/values/strings.xml file:${resetColor}" 44 | apiKey=$(echo "$apiKey" | sed -n 's:.*\(.*\).*:\1:pI') 45 | echo -e "$apiKey\n" 46 | fi 47 | 48 | email=$(echo "$1" | cut -d: -f1) 49 | password=$(echo "$1" | cut -d: -f2) 50 | 51 | token=$(curl -s -X POST -H "Content-Type: application/json" -d "{ 52 | 'email':'$email', 53 | 'password':'$password', 54 | 'returnSecureToken':true 55 | }" "https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=$apiKey" | jq -r '.idToken') 56 | 57 | if [[ "$token" == "null" ]]; then 58 | echo -e "${redColor}[-] Failed to get an authentication token.${resetColor}" 59 | quit 60 | else 61 | echo -e "${greenColor}[+] It was possible to obtain an authentication token:${resetColor}\n$token\n" 62 | fi 63 | 64 | fi 65 | declare -a authHeader=('-H' "Authorization: Bearer $token") 66 | fi 67 | for c in "${collections[@]}"; do 68 | outputReadable=$(curl "${authHeader[@]}" -s "https://firestore.googleapis.com/v1beta1/projects/$projectID/databases/(default)/documents/$c") 69 | if echo "$outputReadable" | grep -q '"error":'; then 70 | echo -e "${redColor}[-] The collection $c is not readable.${resetColor}" 71 | else 72 | echo -e "${greenColor}[+] The collection $c is readable:${resetColor}\n$outputReadable" 73 | fi 74 | outputWritable=$(curl "${authHeader[@]}" -X POST -s "https://firestore.googleapis.com/v1/projects/$projectID/databases/(default)/documents/$c" -H "Content-Type: application/json" -d '{ 75 | "fields": { 76 | "fspPoC": { 77 | "stringValue": "writable" 78 | }, 79 | } 80 | }') 81 | if echo "$outputWritable" | grep -q '"error":'; then 82 | echo -e "${redColor}[-] The collection $c is not writable.${resetColor}" 83 | else 84 | echo -e "${greenColor}[+] The collection $c is writable:${resetColor}\n$outputWritable" 85 | writtenCollectionID=$(echo "$outputWritable" | jq -r '.name' | rev | cut -d'/' -f 1 | rev) 86 | sleep 2 87 | curl "${authHeader[@]}" -X DELETE -s "https://firestore.googleapis.com/v1beta1/projects/$projectID/databases/(default)/documents/$c/$writtenCollectionID" 88 | fi 89 | echo 90 | done 91 | } 92 | 93 | checkTool "apktool" 94 | checkTool "jq" 95 | 96 | if [[ -f "$1" ]]; then 97 | filename=$(basename -- "$1") 98 | extension="${filename##*.}" 99 | filename="fsp-${filename%.*}" 100 | 101 | if [[ "$extension" == "apk" ]]; then 102 | echo -e "${yellowColor}[!] The specified APK is $1.${resetColor}\n" 103 | 104 | if apktool d "$1" -o "$filename" >/dev/null 2>&1; then 105 | echo -e "${greenColor}[+] Successful decompilation with apktool.${resetColor}\n" 106 | else 107 | echo -e "${redColor}[-] Decompilation failed with apktool.${resetColor}" 108 | quit 109 | fi 110 | 111 | if ! grep -qi "firebase" "$filename/AndroidManifest.xml"; then 112 | echo -e "${redColor}[-] Firebase not found in the AndroidManifest.xml${resetColor}" 113 | quit 114 | else 115 | echo -e "${greenColor}[+] Firebase found in the AndroidManifest.xml${resetColor}\n" 116 | if ! projectID=$(grep -i "project_id" "$filename/res/values/strings.xml"); then 117 | echo -e "${redColor}[-] project_id not found in res/values/strings.xml file.${resetColor}" 118 | quit 119 | else 120 | echo -e "${greenColor}[+] project_id found in res/values/strings.xml file:${resetColor}" 121 | projectID=$(echo "$projectID" | sed -n 's:.*\(.*\).*:\1:pI') 122 | echo -e "$projectID\n" 123 | matchString="lcom/google/firebase/firestore/FirebaseFirestore" 124 | for c in $(grep -hA 2 "$matchString" -irw "$filename"/smali* 2>/dev/null | grep -iv "$matchString" | grep const-string | sed 's/[^"]*"\([^"]*\)".*/\1/' | sort -u | sed 's/Provided data must not be null.//g'); do 125 | collections+=("$c") 126 | done 127 | 128 | if [ "${#collections[@]}" -eq 0 ]; then 129 | echo -e "${redColor}[-] No collections found in .smali files.${resetColor}" 130 | quit 131 | else 132 | echo -e "${greenColor}[+] ${#collections[@]} Collection(s) found in .smali files.${resetColor}" 133 | for c in "${collections[@]}"; do 134 | echo "$c" 135 | done; echo 136 | 137 | echo -e "${yellowColor}[!] IMPORTANT: Consulting collections can have an economic impact on the objective." 138 | echo -e " Firestore has a daily expense depending on the number of operations performed.${resetColor}\n" 139 | 140 | while true; do 141 | read -rp "[!] Do you want to check the permissions of these collections anyway? [y/n] " yn 142 | case "$yn" in 143 | [Yy]* ) checkCollections "$2"; quit;; 144 | [Nn]* ) quit;; 145 | * ) echo -e "\n${yellowColor}[!] Please answer yes or no.${resetColor}\n";; 146 | esac 147 | done 148 | fi 149 | fi 150 | fi 151 | else 152 | echo -e "${redColor}[-] The specified file does not have an .apk extension.${resetColor}" 153 | quit 154 | fi 155 | else 156 | echo -e "${yellowColor}[!] Usage: $(basename "$0") [CREDS/TOKEN]${resetColor}" 157 | fi 158 | --------------------------------------------------------------------------------