-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathfb-leak-search.py
336 lines (274 loc) · 10.3 KB
/
fb-leak-search.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
import requests
from bs4 import BeautifulSoup
from prettytable import PrettyTable
import json
import csv
import time
"""
Facebook 2021 Leak Search
Author: @curosim
About the script:
I must admit it looks a little too complicated for what it does, but keep in mind the script was written for interactive usage.
The captcha on the website is lovely - because of that and out of respect for the service operator, it's implemented in the script instead of solved automatically.
"""
# CONFIG #################
config = {
# URL of the Onion service
'url':'4wbwa6vcpvcr3vvf4qkhppgy56urmjcj2vagu2iqgp3z656xcmfdbiqd.onion',
# Tor socks proxy ports
'tor_socks_proxy_ports':[9050, 9150],
# Connectivity Check text
'connectivity_check_text':'<title>Fuck Facebook (TM)</title>',
# To find the captcha box, we search the HTML for the style attribute with following content.
'captcha_style':'border: 1px solid black;display: inline-block;margin: 0px; padding:0px;',
# Text which is shown on the page if a captcha is present
'captcha_present_text':'fill in captcha!',
# Results table style attribute content
'results_table_style':'min-width: 50%',
# Data Export Path
'data_export_path':'./exports/'
}
##########################
def get_tor_session(port):
""" Returns a requests session with the tor service set as socks proxy.
"""
session = requests.session()
# It's important to use the protocol 'socks5h' in order to enable remote DNS resolving
session.proxies['http'] = 'socks5h://localhost:{0}'.format(port)
session.proxies['https'] = 'socks5h://localhost:{0}'.format(port)
return session
class FacebookLeakSearch():
authentication_id = None
def __init__(self, onion_address):
self.hidden_service_url = 'http://{0}'.format(onion_address)
def set_tor_session(self, port):
""" Create requests session with TOR as proxy.
"""
self.session = get_tor_session(port)
def connectivity_check(self):
""" Check if TOR proxy works and the hidden service is up and running.
"""
for port in config['tor_socks_proxy_ports']:
self.set_tor_session(port)
if self.is_onion_reachable():
print("[*] TOR running (Local Port: {0}) and hidden service reachable.".format(port))
return True
return False
def is_onion_reachable(self):
""" Make request to hidden service to see if it's working.
"""
try:
resp = self.session.get(self.hidden_service_url)
if config['connectivity_check_text'] in resp.text: return True
except: pass
return False
def initial_request(self):
""" Simply for the first request in order to get the page source for the captcha.
"""
resp = self.session.get(self.hidden_service_url)
return resp.text
def is_captcha_present(self, source):
""" Checks if the captcha is present in the HTML source.
"""
if config['captcha_present_text'] in source:
return True
else:
return False
def extract_captcha_from_source(self, source):
""" The HTML source gets passed and the captcha text + hidden key will be extracted from it.
"""
soup = BeautifulSoup(source, 'html.parser')
captcha_text = soup.find('pre', attrs={'style':config['captcha_style']}).text
hidden_key = soup.find('input', attrs={'type':'hidden','name':'id'}).get('value')
return captcha_text, hidden_key
def solve_captcha(self, captcha_text, hidden_key):
""" Solve the captcha (Send solution to server).
"""
data = {
'captcha': captcha_text,
'id': hidden_key
}
url = self.hidden_service_url+'/captcha'
resp = self.session.post(url, data=data, allow_redirects=False)
# If the captcha was correct, we get redirected to another URL
# The new URL will be the same domain, but with a GET parameter 's' which acts as an identifier that we solved the captcha.
# All further requests must be made with the s parameter set, otherwise the captcha will show up again.
if resp.status_code == 302:
# Here we extract the ID from the response header
self.authentication_id = resp.headers['Location'].split('?s=')[1]
return True
else:
return False
def perform_search(self, user_id, first_name, last_name, phone_number, work, location, relationship_status='*any*', gender='*any*'):
params = {
's':self.authentication_id,
'i':user_id, # ID
'f':first_name, # firstname
'l':last_name, # lastname
't':phone_number, # phone number
'w':work, # work
'o':location, # location
'r':relationship_status, # relationship status
'g':gender # gender
}
url = self.hidden_service_url+'/search'
resp = self.session.post(url, params=params, allow_redirects=False)
return self.parse_results_table(resp.text)
def parse_results_table(self, source):
""" Parse HTML and extract the results from the <table> rows.
"""
results = []
soup = BeautifulSoup(source, 'html.parser')
# get the table with the results
table_rows = soup.find_all('tr')
for row in table_rows[1:]:
tds = row.find_all('td')
entry = {}
entry['user_id'] = tds[0].text
entry['phone_number'] = tds[1].text
entry['first_name'] = tds[2].text
entry['last_name'] = tds[3].text
entry['gender'] = tds[4].text
entry['relationship_status'] = tds[5].text
entry['work'] = tds[6].text
entry['hometown'] = tds[7].text
entry['location'] = tds[8].text
entry['country'] = tds[9].text
results.append(entry)
return results
class CommandLineInterface():
def __init__(self, fls):
self.fls = fls
def ask_for_captcha_solution(self, source=None):
""" Ask the user to solve the captcha.
It's a recursive function, in case the captcha solution is wrong.
"""
if source is None:
print('[!] To use the service, you have to solve a captcha first.')
source = self.fls.initial_request()
captcha_text, hidden_key = self.fls.extract_captcha_from_source(source)
print(captcha_text)
captcha_solution = input('[*] Enter the letters: ')
print("[*] Submitting captcha solution '{0}'".format(captcha_solution))
if self.fls.solve_captcha(captcha_solution, hidden_key) == True:
print("[*] Captcha correct! (Auth ID: {0})".format(self.fls.authentication_id))
else:
print("[!] The captcha solution was wrong! Try again.")
self.ask_for_captcha_solution()
def ask_for_search_params(self):
""" Ask the user for search parameters.
"""
print("[*] Please enter the search criteria (Leave blank if not needed):")
user_id = input(" > User ID: ")
first_name = input(" > First Name: ")
last_name = input(" > Last Name: ")
phone_number = input(" > Phone Number: ")
work = input(" > Work: ")
location = input(" > Location: ")
#relationship_status = input("[*] Relationship Status: ")
#gender = input("[*] Gender: ")
return (user_id, first_name, last_name, phone_number, work, location)
def present_results(self, search_results):
""" Displays the search results in a pretty CLI table :)
"""
if len(search_results) == 0:
print("[!] The search was successful but it returned 0 results :(")
else:
print("[*] Success! {0} results have been found!".format(len(search_results)))
results_table = PrettyTable()
results_table.field_names = ["User ID", "Phone", "Name", "Lastname", "Gender", "Work", "Hometown", "Location", "Country"]
results_table.align = 'l'
for result in search_results:
results_table.add_row(
[
result['user_id'],
result['phone_number'],
result['first_name'],
result['last_name'],
result['gender'],
result['work'],
result['hometown'],
result['location'],
result['country']
]
)
print(results_table)
def ask_how_to_continue(self, search_results):
""" Ask the user how he would like to continue after the search.
"""
print("[*] How would you like to continue?")
print(" 1 - Export results")
print(" 2 - Search again")
print(" 3 - Quit")
choice = input("[*] Enter your choice: ")
if choice == '1':
self.export_results(search_results)
elif choice == '2':
pass
elif choice == '3':
quit()
else:
print("[*] Please choose one of the options :)")
self.ask_how_to_continue(search_results)
def export_results(self, search_results):
""" Ask the user how he wants to export the search results.
"""
print("[*] In which format you want to export the results?")
print(" 1 - JSON")
print(" 2 - CSV")
choice = input("[*] Enter your choice: ")
# Appendix for files so we dont overwrite previous files...
timestamp = int(time.time())
if choice == '1':
filename = '{0}fbls_{1}.json'.format(config['data_export_path'], timestamp)
with open(filename, 'w') as f:
f.write(json.dumps(search_results, indent=4, sort_keys=True))
print("[*] Exported {0} results as JSON (Filename: {1})".format(len(search_results), filename))
elif choice == '2':
filename = '{0}fbls_{1}.csv'.format(config['data_export_path'], timestamp)
keys = list(search_results[0].keys())
with open(filename, 'w') as f:
w = csv.DictWriter(f, keys)
w.writeheader()
for search_result in search_results:
w.writerow(search_result)
print("[*] Exported {0} results as CSV (Filename: {1})".format(len(search_results), filename))
else:
print("[*] Please choose one of the options :)")
self.export_results(search_results)
def quit():
print("[*] Quitting FBLS")
exit()
def banner():
print(" ________ ______ _____ ______ ")
print("|_ __ ||_ _ \\ |_ _| .' ____ \\ ")
print(" | |_ \\_| | |_) | | | | (___ \\_| ")
print(" | _| | __'. | | _ _.____`. ")
print(" _| |_ _| |__) |_| |__/ || \\____) |")
print("|_____| |_______/|________| \\______.'")
print()
print("Facebook Leak Search - CLI Wrapper for the hidden service \"Fuck Facebook (TM)\"")
print("Author of the CLI Wrapper: @curosim")
print()
def main():
banner()
fls = FacebookLeakSearch(config['url'])
cli = CommandLineInterface(fls=fls)
if fls.connectivity_check() == False:
print("[!] Tor not working or hidden service not reachable (Local Tor ports tried: {0})".format(config['tor_socks_proxy_ports']))
else:
cli.ask_for_captcha_solution()
while True:
search_params = cli.ask_for_search_params()
print("[*] Performing 'API' Search")
search_results = fls.perform_search(*search_params)
cli.present_results(search_results)
if len(search_results) > 0:
cli.ask_how_to_continue(search_results)
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print()
print("[!] Execution interrupted with keyboard...")
quit()