├── 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 |
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 |
--------------------------------------------------------------------------------