def attack(self, block): """Attack a given Range Query with a distinguishable first block This function can only be used under specific circumstances, which is why it is not the default function. To use it, change the Dictionary of the getAttackerFor-function in DRQPatternAttack.py to point to DFBPatternBRQ instead of DFBPatternPRQ, but be aware that it will not always work on small data sets. @param fb: The first block, as set @param rq: The remaining range query, as set @return: List of possible results """ fb, rq = block res = [] suspected_n = float(len(fb)) rq.update(fb) rqlen = len(rq) pattern_length_max = math.ceil(rqlen / suspected_n) pattern_length_max += 2 * math.ceil(pattern_length_max / suspected_n) # Increase maximum pattern length, because duplicates could lead to a miscalculation of up to floor(real_pattern_length/real_N). # We are using ceil() to avoid border cases where the real M would lead to x in that calculation, while our detected M # only leads to x-1. Those cases would be few and far between, considering the chances of actually getting so many duplicates, # but nevertheless, they should be dealt with. pattern_length_min = math.floor(rqlen / (suspected_n+1)) for key in fb: # Iterate through all elements of the first block if DB.isValidTarget(key) and (pattern_length_min <= DB.getPatternLengthForHost(key) <= pattern_length_max): # if the current element is a beginning of a pattern with the correct length... if DB.getPatternForHost(key) <= rq: # Check if the pattern is a subset of the remaining range query. res.append(key) return res
def generateBaseDRQ(self, domain): """Generator for Basic DNS Range Queries (randomly generated query sets) Queries are unique inside their respective sets, but may appear more than once across different query blocks. @param domain: Domain for which a DNS Range Query should be generated @return: List of Sets, in order, each set representing a query block """ if not DB.isValidTarget(domain): Error.printErrorAndExit(domain + " is not a valid target") patlen = DB.getPatternLengthForHost(domain) block = [set()] pattern = DB.getPatternForHost(domain) # Get the actual pattern of the target randoms = DB.getRandomHosts((Config.RQSIZE-1)*len(pattern)) # Get random hosts (dummies) pattern.remove(domain) block[0].add(domain) i = 1 for subquery in pattern: # Create the blocks that will hold dummies and actual queries block.append(set()) block[i].add(subquery) # Add the actual query to its respective block i += 1 for query, index in zip(randoms, cycle(range(patlen))): # distribute the randomly chosen dummy queries as evenly as possible across the blocks block[index].add(query) return block
def attack(self, rq): """Attack a given Range Query using the assumption from the class description. @param rq: A Range Query, as returned by generate.DRQ @return: list of possible results """ res = [] for element in rq: # Iterate through all elements (queries) of the given range query if DB.isValidTarget(element): # If the current element is the beginning of a pattern... # This checks if the pattern of the current element is a subset of the range query inter = rq & DB.getPatternForHost(element) if len(inter) == DB.getPatternLengthForHost(element): res.append(element) return res
def attack(self, block): """Attack a given Range Query with a distinguishable first block @param fb: The first block, as set @param rq: The remaining range query, as set @return: List of possible results """ fb, rq = block res = [] rq.update(fb) for key in fb: # Iterate through all queries in the first block if DB.isValidTarget(key): # If the current query is a valid beginning of a pattern... if DB.getPatternForHost(key) <= rq: # Check if the pattern is a subset of the second block. res.append(key) return res
def attack(self, blocklist): """Attack a given range query with fully distinguishable blocks @param blocklist: A list of sets, each set representing a block, the main target in the first block. @return: List of possible results """ res = [] length = len(blocklist) for key in blocklist[0]: # Iterate through all candidates for the main target (as it must be in the first block) if DB.isValidTarget(key) and DB.getPatternLengthForHost(key) == length: # If it is the beginning of a pattern of the correct length... # The following is a method of determining if every block contains exactly one element of the pattern of the current candidate. tmp = blocklist[1:] cnt = {} for i in range(length-1): cnt[i] = 0 for query in DB.getPatternForHost(key): if query != key: for i in range(len(tmp)): if query in tmp[i]: cnt[i] += 1 if not 0 in cnt.values(): res.append(key) return res
def generateBaseDRQ(self, domain): """Generator for Pattern-Based DNS Range Queries (trying to fill the query blocks with patterns) Queries are unique inside their respective sets, but may appear more than once across different query blocks. @param domain: Domain for which a DNS Range Query should be generated @return: List of Sets, in order, each set representing a query block """ if not DB.isValidTarget(domain): Error.printErrorAndExit(domain + " is not a valid target") pattern_length = len(DB.PATTERNS[domain]) block = [set()] num_of_available_patterns = DB.getNumberOfHostsWithPatternLength(pattern_length) - 1 if num_of_available_patterns >= Config.RQSIZE: hosts = set([domain]) hosts.update(set(DB.getRandomHostsByPatternLengthB(pattern_length, Config.RQSIZE-1, hosts))) pattern_copy = {} for host in hosts: pattern_copy[host] = DB.getPatternForHost(host) pattern_copy[host].remove(host) block[0].add(host) for i in range(1, pattern_length, 1): block.append(set()) for host in pattern_copy: block[i].add(pattern_copy[host].pop()) else: num_of_needed_patterns = Config.RQSIZE - (num_of_available_patterns+1) padding = [] for i in range(num_of_needed_patterns): # Find patterns whose lengths sum to pattern_length (if any exist that have not been chosen yet) pad1_len = pad2_len = -1 for pad1_len, pad2_len in zip(range(1, pattern_length/2+1, 1), range(pattern_length-1, pattern_length/2-1, -1)): # This is a construct that generates numbers that sum to pattern_length. It is used instead of truly random # numbers because it will not get stuck when no more patterns are available. if ((DB.getNumberOfHostsWithPatternLengthB(pad1_len, block[0]) > 0) and \ (DB.getNumberOfHostsWithPatternLength(pad2_len) > 0)): break elif pad1_len == pattern_length/2: # No patterns of the correct length have been found, abort pad1_len = -1 if (pad1_len == -1): # Break out of loop as no further patterns can be found. break # The following few lines get the dummy patterns from the database and saves them to the list of dummy-patterns pad1_host = DB.getRandomHostsByPatternLengthB(pad1_len, 1, block[0])[0] pad1_pattern = DB.getPatternForHost(pad1_host) pad1_pattern.remove(pad1_host) block[0].add(pad1_host) padding.append([pad1_host]) for host in pad1_pattern: padding[i].append(host) pad2_host = DB.getRandomHostsByPatternLength(pad2_len, 1)[0] pad2_pattern = DB.getPatternForHost(pad2_host) pad2_pattern.remove(pad2_host) padding[i].append(pad2_host) for host in pad2_pattern: padding[i].append(host) # We now have as many dummy patterns as we will get. Start distributing them. pattern_copy = {} block[0].add(domain) pattern_copy[domain] = DB.getPatternForHost(domain) pattern_copy[domain].remove(domain) for element in DB.getRandomHostsByPatternLengthB(pattern_length, num_of_available_patterns, block[0]): # Get all patterns with the correct length and add them to the range query pattern_copy[element] = DB.getPatternForHost(element) pattern_copy[element].remove(element) block[0].add(element) for i in range(1, pattern_length, 1): # Distribute the remaining patterns (those whose lengths sum to the correct length) block.append(set()) for host in pattern_copy: block[i].add(pattern_copy[host].pop()) for pattern in padding: block[i].add(pattern[i]) return block