#!/usr/bin/env bash
# FireStorePwn
# Color variables
resetColor="\e[0m"
redColor="\e[31m"
greenColor="\e[32m"
yellowColor="\e[33m"
magentaColor="\e[35m"
cyanColor="\e[36m"
echo -e "${magentaColor} _____
_/ ____\____________ ${cyanColor}FireStorePwn${magentaColor}
\ __\/ ___/\____ \
| | \___ \ | |_> > ${cyanColor}Firestore Database Vulnerability Scanner Using APKs${magentaColor}
|__| /____ >| __/
\/ |__| ${cyanColor}by Víctor García (@takito1812)${resetColor}
"
checkTool() {
if ! dpkg -l "$1" >/dev/null 2>&1; then
echo -e "${yellowColor}[!] Installing $1...${resetColor}\n"
apt-get update -y >/dev/null 2>&1
apt-get install "$1" -y >/dev/null 2>&1
fi
}
quit() {
rm -rf "$filename"
exit
}
checkCollections() {
echo
if [[ "$1" ]]; then
token="$1"
if echo "$1" | grep -q ":"; then
if ! apiKey=$(grep -i "google_api_key" "$filename/res/values/strings.xml"); then
echo -e "${redColor}[-] google_api_key not found in res/values/strings.xml file.${resetColor}"
quit
else
echo -e "${greenColor}[+] google_api_key found in res/values/strings.xml file:${resetColor}"
apiKey=$(echo "$apiKey" | sed -n 's:.*\(.*\).*:\1:pI')
echo -e "$apiKey\n"
fi
email=$(echo "$1" | cut -d: -f1)
password=$(echo "$1" | cut -d: -f2)
token=$(curl -s -X POST -H "Content-Type: application/json" -d "{
'email':'$email',
'password':'$password',
'returnSecureToken':true
}" "https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=$apiKey" | jq -r '.idToken')
if [[ "$token" == "null" ]]; then
echo -e "${redColor}[-] Failed to get an authentication token.${resetColor}"
quit
else
echo -e "${greenColor}[+] It was possible to obtain an authentication token:${resetColor}\n$token\n"
fi
fi
declare -a authHeader=('-H' "Authorization: Bearer $token")
fi
for c in "${collections[@]}"; do
outputReadable=$(curl "${authHeader[@]}" -s "https://firestore.googleapis.com/v1beta1/projects/$projectID/databases/(default)/documents/$c")
if echo "$outputReadable" | grep -q '"error":'; then
echo -e "${redColor}[-] The collection $c is not readable.${resetColor}"
else
echo -e "${greenColor}[+] The collection $c is readable:${resetColor}\n$outputReadable"
fi
outputWritable=$(curl "${authHeader[@]}" -X POST -s "https://firestore.googleapis.com/v1/projects/$projectID/databases/(default)/documents/$c" -H "Content-Type: application/json" -d '{
"fields": {
"fspPoC": {
"stringValue": "writable"
},
}
}')
if echo "$outputWritable" | grep -q '"error":'; then
echo -e "${redColor}[-] The collection $c is not writable.${resetColor}"
else
echo -e "${greenColor}[+] The collection $c is writable:${resetColor}\n$outputWritable"
writtenCollectionID=$(echo "$outputWritable" | jq -r '.name' | rev | cut -d'/' -f 1 | rev)
sleep 2
curl "${authHeader[@]}" -X DELETE -s "https://firestore.googleapis.com/v1beta1/projects/$projectID/databases/(default)/documents/$c/$writtenCollectionID"
fi
echo
done
}
checkTool "apktool"
checkTool "jq"
if [[ -f "$1" ]]; then
filename=$(basename -- "$1")
extension="${filename##*.}"
filename="fsp-${filename%.*}"
if [[ "$extension" == "apk" ]]; then
echo -e "${yellowColor}[!] The specified APK is $1.${resetColor}\n"
if apktool d "$1" -o "$filename" >/dev/null 2>&1; then
echo -e "${greenColor}[+] Successful decompilation with apktool.${resetColor}\n"
else
echo -e "${redColor}[-] Decompilation failed with apktool.${resetColor}"
quit
fi
if ! grep -qi "firebase" "$filename/AndroidManifest.xml"; then
echo -e "${redColor}[-] Firebase not found in the AndroidManifest.xml${resetColor}"
quit
else
echo -e "${greenColor}[+] Firebase found in the AndroidManifest.xml${resetColor}\n"
if ! projectID=$(grep -i "project_id" "$filename/res/values/strings.xml"); then
echo -e "${redColor}[-] project_id not found in res/values/strings.xml file.${resetColor}"
quit
else
echo -e "${greenColor}[+] project_id found in res/values/strings.xml file:${resetColor}"
projectID=$(echo "$projectID" | sed -n 's:.*\(.*\).*:\1:pI')
echo -e "$projectID\n"
matchString="lcom/google/firebase/firestore/FirebaseFirestore"
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
collections+=("$c")
done
if [ "${#collections[@]}" -eq 0 ]; then
echo -e "${redColor}[-] No collections found in .smali files.${resetColor}"
quit
else
echo -e "${greenColor}[+] ${#collections[@]} Collection(s) found in .smali files.${resetColor}"
for c in "${collections[@]}"; do
echo "$c"
done; echo
echo -e "${yellowColor}[!] IMPORTANT: Consulting collections can have an economic impact on the objective."
echo -e " Firestore has a daily expense depending on the number of operations performed.${resetColor}\n"
while true; do
read -rp "[!] Do you want to check the permissions of these collections anyway? [y/n] " yn
case "$yn" in
[Yy]* ) checkCollections "$2"; quit;;
[Nn]* ) quit;;
* ) echo -e "\n${yellowColor}[!] Please answer yes or no.${resetColor}\n";;
esac
done
fi
fi
fi
else
echo -e "${redColor}[-] The specified file does not have an .apk extension.${resetColor}"
quit
fi
else
echo -e "${yellowColor}[!] Usage: $(basename "$0") [CREDS/TOKEN]${resetColor}"
fi