-
-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Solved: Decrypting frc cookie #42
Comments
Okay, I've found out how to decrypt the frc cookie myself. The 'pw' is the device serial. |
Here is an example of the JSON data from a decrypted frc cookie {
"ApplicationVersion": "3.35.1",
"DeviceOSVersion": "iOS/14.1",
"ScreenWidthPixels": "375",
"TimeZone": "+02:00",
"ScreenHeightPixels": "812",
"ApplicationName": "Audible",
"DeviceJailbroken": false,
"DeviceLanguage": "de-DE",
"DeviceFingerprintTimestamp": "...",
"ThirdPartyDeviceId": "A UUID4 string",
"DeviceName": "iPhone",
"Carrier": "..."
} |
Here is my optimized code to encrypt and decrypt the frc cookie: import base64
import gzip
import hashlib
import hmac
import json
import secrets
from io import BytesIO
from functools import partialmethod
from typing import Tuple, Union
from pyaes import AESModeOfOperationCBC, Encrypter, Decrypter
FRC_SIG_SALT: bytes = b'HmacSHA256'
FRC_AES_SALT: bytes = b'AES/CBC/PKCS7Padding'
class FrcCookieHelper:
def __init__(self, password: str) -> None:
self.password = password.encode()
def _get_key(self, salt: bytes) -> bytes:
return hashlib.pbkdf2_hmac('sha1', self.password, salt, 1000, 16)
get_signature_key = partialmethod(_get_key, FRC_SIG_SALT)
get_aes_key = partialmethod(_get_key, FRC_AES_SALT)
@staticmethod
def unpack(frc: str) -> Tuple[bytes, bytes, bytes]:
pad = (4 - len(frc) % 4) * '='
frc = BytesIO(base64.b64decode(frc+pad))
frc.seek(1) # the first byte is always 0, skip them
return frc.read(8), frc.read(16), frc.read() # sig, iv, data
@staticmethod
def pack(sig: bytes, iv: bytes, data: bytes) -> str:
frc = b'\x00' + sig[:8] + iv[:16] + data
frc = base64.b64encode(frc).strip(b'=')
return frc.decode()
def verify_signature(self, frc: str) -> bool:
key = self.get_signature_key()
sig, iv, data = self.unpack(frc)
new_signature = hmac.new(key, iv + data, hashlib.sha256).digest()
return sig == new_signature[:len(sig)]
def decrypt(self, frc: str, verify_signature: bool = True) -> bytes:
if verify_signature:
self.verify_signature(frc)
key = self.get_aes_key()
sig, iv, data = self.unpack(frc)
decrypter = Decrypter(AESModeOfOperationCBC(key, iv))
decrypted = decrypter.feed(data) + decrypter.feed()
decompressed = gzip.decompress(decrypted)
return decompressed
def encrypt(self, data: Union[str, dict]) -> str:
if isinstance(data, dict):
data = json.dumps(data, indent=2, separators=(',', ' : ')).encode()
compressed = BytesIO()
with gzip.GzipFile(fileobj=compressed, mode='wb', mtime=False) as f:
f.write(data)
compressed.seek(8)
compressed.write(b'\x00\x13')
compressed = compressed.getvalue()
key = self.get_aes_key()
iv = secrets.token_bytes(16)
encrypter = Encrypter(AESModeOfOperationCBC(key, iv))
encrypted = encrypter.feed(compressed) + encrypter.feed()
key = self.get_signature_key()
signature = hmac.new(key, iv + encrypted, hashlib.sha256).digest()
return self.pack(signature, iv, encrypted) |
Hey, I don't have the knowledge of Amazon's code to verify or compare it, or to evaluate this -- but: If both of these are true:
Or if this is:
You could use something like In any case, if you can get the native function to be callable from Python, then you'd have a pretty foolproof way of ensuring that your results match theirs. Note: Inter-language frameworks can be a rabbit-hole. This isn't a request for you to use the options mentioned, I just thought this info might be interesting or useful. |
Hi, thank you for your hint. Life would much easier using Python and Java Script in one project. But: Have Amazon changed encrypting the frc cookies? |
Here is the working code to decrypt/encrypt a frc cookie. As far as I found out, the password is the device serial.
What you mean with this? Have you an example? |
This is awesome work! Great job! Where did you get the original Java implementation from? I am looking at some other Amazon constructs and am looking for some hints. Was this from the Android Audible app? |
@seidnerj What you are looking for exactly? Maybe I can help. |
Specifically there's a cookie called CES that I suspect has something to do with captcha authentication. If it is not present/incorrect, even if the captcha is entered correctly you get an error message that's identical to the one you get when you enter an incorrect captcha. |
I've never heard of a CES cookie. Maybe it's a kind of CSRF cookie that the server sends to the client and just needs to be sent back?! In which app or Amazon service is this cookie required? |
Sorry, I meant it is a field in the login form, not a cookie. It is in the Amazon shopping app/mobile web and only appears during a captcha challenge. It is indeed similar to the CSRF in the sense that it is sent from the server and posted back but for some reason if you try to login programmatically it seems that the value for CES sent from the server and then reposted is incorrect. |
It is worth noting that I noticed that sometimes, in the actual html file (not the posted value), the metadata1 field value appears in the form of Now back to the subject at hand - here is an example of the CES value:
The part after "ape:" is base64 encoded, after decoding it you get something that looks similar to what is in the metadata1 field (after the "ECdITeCs:" prefix):
I tried decoding it using the same method used for decoding the encrypted potion of metadata1 but it didn't work unfortunately, failing due to "'utf-8' codec can't decode byte". |
I doesn’t know anything about the „ape:“ prefix. But from the name I would think first on „Authenticated Permutation-Based Encryption for Lightweight Cryptography (APE)“?! But I‘m not sure. |
I looked it up, maybe - either way I haven't been able to make progress there for time being 🤷♂️ |
I would also like to take a closer look. Can I also use the Amazon iOS Shopping App for this? Or does it work differently there? |
Yes, thanks a lot! You'd need to get it to issue you with a captcha challenge though, otherwise this field isn't going to be present. I think an easier way is to get this through the mobile web version of amazon.com. For the most part, the Amazon app is simply a wrapper for the mobile website. |
Okay. Now I have to figure out how to force a captcha prompt. I‘m think I must disable 2FA?! |
If you start a session with no cookies at all, then type the wrong password a couple of times, you usually get captcha 🙂 |
I'm not sure about having to disable 2FA, in my case it sometimes sends an email with an OTP, so I guess the 2FA is enabled but I still get a captcha if I do what I suggested in my previous post. |
Pyv8 is no longer maintained, luckily it was forked by Cloudare and is still being actively maintained. Under project name "Stpyv8", see here: https://github.com/cloudflare/stpyv8 |
@seidnerj
So my Safari browser must calculate this fields or they are provided in the previous html response. If they are calculated my browser must receive a js script before to know how the must do this. So you can take a look in the js files you receive. |
Yes, there are more form values that appear with this same prefix, thing is, in all other cases (such as the ones you've listed below), the data after that prefix its simply base64 encoded and when you decode it you get plain text that's not encrypted. For example, for the values you provided:
Whereas In both cases, the "payload" is simply base64 encoded but not encrypted. In the case of the CES value, it seems the data is encrypted prior to the base64 encoding. |
This issue has not been updated for a while and will be closed soon. |
I have this java script which encrypts a strigified JSON object.
Amazon apps using this string as the "frc" cookie. My app creates the frc cookie from custom bytes. Amazon still accepts this frc cookie. But I will be prepared for the future and build the frc cookie the same way as Amazon does.
I've tried to rebuild the java script to python
Can someone with JAVA and Python knowledge take a look on my code if I have take a misstake?
The text was updated successfully, but these errors were encountered: