VideoHelp Forum




+ Reply to Thread
Results 1 to 14 of 14
  1. Video page:
    https://www.maxmeldpunt.nl/uitzendingen/oud-ptters-lopen-pensioenaanvulling-mis/
    MPD:
    Code:
    https://npo-nl-ams-p16-am3.cdn.streamgate.nl/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MTI1MTA2NDQsInVyaSI6Ilwvdm9kXC9ucG9cL3VzcFwvVEVTVFwvbnBvXC9kYXNoX2NlbmNcL1BPV18wNTgwNjU1OVwvUE9XXzA1ODA2NTU5X3YxNzEyMzQzMTEwLmlzbSIsImNsaWVudF9pcCI6IjgyLjE2My40Ny4yMDQiLCJ2aWV3ZXIiOiJ2aWV3ZXIiLCJyaWQiOiI5MDdlNTNhIn0.uGmQapaw7VCXT1ckdrXrd3aRlUICo-zR7z-FzuDCNrw/vod/npo/usp/TEST/npo/dash_cenc/POW_05806559/POW_05806559_v1712343110.ism/stream.mpd
    PSSH:
    Code:
    AAACqnBzc2gAAAAAmgTweZhAQoarkuZb4IhflQAAAoqKAgAAAQABAIACPABXAFIATQBIAEUAQQBEAEUAUgAgAHgAbQBsAG4AcwA9ACIAaAB0AHQAcAA6AC8ALwBzAGMAaABlAG0AYQBzAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAvAEQAUgBNAC8AMgAwADAANwAvADAAMwAvAFAAbABhAHkAUgBlAGEAZAB5AEgAZQBhAGQAZQByACIAIAB2AGUAcgBzAGkAbwBuAD0AIgA0AC4AMAAuADAALgAwACIAPgA8AEQAQQBUAEEAPgA8AFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBFAFkATABFAE4APgAxADYAPAAvAEsARQBZAEwARQBOAD4APABBAEwARwBJAEQAPgBBAEUAUwBDAFQAUgA8AC8AQQBMAEcASQBEAD4APAAvAFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBJAEQAPgAzAHIATwB3AGoAagAxAG8AcAB1AHcANgBXAEUARABDAEQAdgBSADAASwB3AD0APQA8AC8ASwBJAEQAPgA8AEMASABFAEMASwBTAFUATQA+AFMANgB6AFcAYQBUAFAAWQBHAGgAcwA9ADwALwBDAEgARQBDAEsAUwBVAE0APgA8AEwAQQBfAFUAUgBMAD4AaAB0AHQAcAA6AC8ALwBwAGwAYQB5AHIAZQBhAGQAeQAuAHMAdAByAGUAYQBtAGcAYQB0AGUALgBuAGwALwByAGkAZwBoAHQAcwBtAGEAbgBhAGcAZQByAC4AYQBzAG0AeAA8AC8ATABBAF8AVQBSAEwAPgA8AC8ARABBAFQAQQA+ADwALwBXAFIATQBIAEUAQQBEAEUAUgA+AA==
    There are multiple PSSH strings to be found, mutitple DRM methods I guess but whatever, we can brute them all later, right?

    License URL:
    Code:
    https://npo-drm-gateway.samgcloud.nepworldwide.nl/authentication?custom_data=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJucG8iLCJpYXQiOjE3MTI0MzY4NjMsImxpY2Vuc2VfcHJvZmlsZSI6IndlYiIsImRybV90eXBlIjoid2lkZXZpbmUiLCJjbGllbnRfaXAiOiI4Mi4xNjMuNDcuMjA0In0.3GFBAX4y_1Jlds5WiTVUgkla7tIiNMbXtGGvQVyYYmE
    So far so good, but not actually the challenge I'm facing. https://cdrm-project.com/ doesn't work for me, maybe it geo-blocks getting the keys or the keys are not known. So I started my own journey after HOURS of reading.

    This is what I have (using Linux):
    - Android Studio
    - Android 10 virtual device Pixel 6 API 29
    - adb shell with su, running frida-server-16.2.1-android-x86
    - python3 dumper/dump_keys.py

    Problem:
    When I visit the video page url it doesn't dump the private key, it only dumps license_request.bin. The dumper-console looks fine, stuff like:
    Code:
    2024-04-06 07:08:52 PM - Helpers.Scanner - 75 - DEBUG - {
        "from": "android.hardware.drm@1.2-service.widevine",
        "message": "GetLevel3_LoadDeviceRSAKey",
        "payload": {
            "Status": "OEMCrypto_SUCCESS",
            "Session": 25601,
            "Length": 1312,
            "Context": "160471f05b4571b4dbb9cab301a4180a634bf42f8316598e32e7ffc419695a1916cbc9ea1950f2ee372d8aab8cb795fac3a966a0e814b9827f2150a2f02434ecf4e925a9f7ebed3ff49eb0207f29c47e7982df814b11822bb4c6ebefe22a35df93331017b2948a52a3fad5d6219273263e15c32e1d350f716f9a186be356244b7513158dbc0c15cbfd9173b8585f5ef966d479fb0a28162340d4bbf9ae8dcb0adcad9cd7cecfc3c8e5c95f6e1ca10c7bc9f7f7327facf93ebc346d637742a9479257488f1ff9ca17d1ffe9daed5d39191cef0b34dbbea5fa7ce727e854f15b --snip--
    Same problem while testing with https://bitmovin.com/demos/drm

    I tried fixing this by updating dumper/Helpers/script.js (found that somewhere) and edited to reflect as much as dynamic functions as I could find, but this didn't work (also after examining libwvhidl.so it didn't reveal extra strings to add).

    Could it be because this exploit is not possible above OEM Crypto API 13? When I install and run the DRM Info app on Android it says WideVine CDM 15. Also tried doing this using diazole dumper (https://github.com/Diazole/dumper) but this one never gives output except for initialization lines that ends with "INFO - Functions hooked, now open the DRM stream test on Bitmovin from your Android device! https://bitmovin.com/demos/drm" (and then never anything happens)

    If the Crypto API version is the problem than there is another problem. The video page I'm visiting only loads on Android 11 android images that I tried... Maybe this website is clever and on purpose didn't support older Android versions to frustrate the key dumping proces? On the other hand I also tried on Android 9 and 10 with the test site https://bitmovin.com/demos/drm and these also didn't dump keys.
    Last edited by cosmicdrm; 6th Apr 2024 at 15:55.
    Quote Quote  
  2. Here's the key to your video, all it takes is a basic pywidevine script (cdrm-project like sites should work as well, no headers needed):
    Code:
    --key 8eb0b3de683deca63a5840c20ef4742b:d23409ed222d8f82db5b48f3aa5c0bde
    For dumping your own CDM try to use this dumper with the --cdm-version flag or the new KeyDive (was updated 3 hours ago with Android 14 support).
    Quote Quote  
  3. Originally Posted by white_snake View Post
    Here's the key to your video, all it takes is a basic pywidevine script (cdrm-project like sites should work as well, no headers needed):
    Code:
    --key 8eb0b3de683deca63a5840c20ef4742b:d23409ed222d8f82db5b48f3aa5c0bde
    For dumping your own CDM try to use this dumper with the --cdm-version flag or the new KeyDive (was updated 3 hours ago with Android 14 support).
    Thanks!
    Code:
    python3 dump_keys.py --cdm-version 15 --function-name 'ofskesua'
    worked!

    Do you know how to know which PSSH to use? (because the manifest shows multiple)
    All of this for me is mainly to learn from it
    Quote Quote  
  4. Member aqzs's Avatar
    Join Date
    Mar 2024
    Location
    Paris
    Search Comp PM
    Originally Posted by cosmicdrm View Post
    Do you know how to know which PSSH to use? (because the manifest shows multiple)
    All of this for me is mainly to learn from it
    You have to use the PSSH under widevine :
    Code:
    <!-- Widevine -->
          <ContentProtection
            schemeIdUri="urn:uuid:EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED">
            <cenc:pssh>AAAAXHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADwIARIQjrCz3mg97KY6WEDCDvR0KxoIdXNwLWNlbmMiGGpyQ3ozbWc5N0tZNldFRENEdlIwS3c9PSoAMgA=</cenc:pssh>
          </ContentProtection>
    Code:
    AAAAXHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADwIARIQjrCz3mg97KY6WEDCDvR0KxoIdXNwLWNlbmMiGGpyQ3ozbWc5N0tZNldFRENEdlIwS3c9PSoAMgA=
    Quote Quote  
  5. Originally Posted by aqzs View Post
    Originally Posted by cosmicdrm View Post
    Do you know how to know which PSSH to use? (because the manifest shows multiple)
    All of this for me is mainly to learn from it
    You have to use the PSSH under widevine :
    Code:
    <!-- Widevine -->
          <ContentProtection
            schemeIdUri="urn:uuid:EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED">
            <cenc:pssh>AAAAXHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADwIARIQjrCz3mg97KY6WEDCDvR0KxoIdXNwLWNlbmMiGGpyQ3ozbWc5N0tZNldFRENEdlIwS3c9PSoAMgA=</cenc:pssh>
          </ContentProtection>
    Code:
    AAAAXHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADwIARIQjrCz3mg97KY6WEDCDvR0KxoIdXNwLWNlbmMiGGpyQ3ozbWc5N0tZNldFRENEdlIwS3c9PSoAMgA=
    Makes sense. It's a license server of this organisation and they provide in multiple DRM methods. Widevine is one of them so use that PSSH if the browser's traffic indicates that's the one which is used
    Quote Quote  
  6. Image
    [Attachment 78176 - Click to enlarge]

    Btw, CDRM-project.com does not work in this case

    AAAAXHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADwIARIQjr Cz3mg97KY6WEDCDvR0KxoIdXNwLWNlbmMiGGpyQ3ozbWc5N0tZ NldFRENEdlIwS3c9PSoAMgA=

    https://npo-drm-gateway.samgcloud.nepworldwide.nl/authentication?custom_data=eyJhbGciO...6LKp872pYPzAwk
    Quote Quote  
  7. Member aqzs's Avatar
    Join Date
    Mar 2024
    Location
    Paris
    Search Comp PM
    Originally Posted by cosmicdrm View Post
    Image
    [Attachment 78176 - Click to enlarge]

    Btw, CDRM-project.com does not work in this case

    AAAAXHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADwIARIQjr Cz3mg97KY6WEDCDvR0KxoIdXNwLWNlbmMiGGpyQ3ozbWc5N0tZ NldFRENEdlIwS3c9PSoAMgA=

    https://npo-drm-gateway.samgcloud.nepworldwide.nl/authentication?custom_data=eyJhbGciO...6LKp872pYPzAwk
    CDRM-project.com could work but you need a fresh token and a header.
    Here is a code doing all the job :
    Code:
    from pywidevine.cdm import Cdm
    from pywidevine.device import Device
    from pywidevine.pssh import PSSH
    import requests
    import json
    import xml.etree.ElementTree as ET
    import re
    from dotenv import load_dotenv
    import os
    load_dotenv()
    
    link = 'https://www.maxmeldpunt.nl/uitzendingen/oud-ptters-lopen-pensioenaanvulling-mis/'
    link = input("Link for the wepage: ")
    
    
    def getpssh(mpd_url):
        response = requests.get(mpd_url)
        root = ET.fromstring(response.content)
        try:
            return root[0][1][4][0].text
        except:
            return 'pssh not found'
    
    headers = {'accept': '*/*', 'accept-language': 'fr-FR,fr;q=0.5', 'content-type': 'application/json', 'origin': 'https://www.maxmeldpunt.nl', 'referer': 'https://www.maxmeldpunt.nl/', 'sec-ch-ua': '"Brave";v="123", "Not:A-Brand";v="8", "Chromium";v="123"','user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36'}
    r = requests.get(link, headers=headers)
    match = re.compile(r'let jwtnpoplayer(.*?)\"').search(r.text)
    if match:
        word = 'let jwtnpoplayer' + match.group(1)
        pattern = re.compile(f'{word}"(.*?)"')
        match2 = pattern.search(r.text)
        if match2:
            token = match2.group(1)
    
            headers = {'accept': '*/*', 'accept-language': 'fr-FR,fr;q=0.5', 'content-type': 'application/json', 'origin': 'https://www.maxmeldpunt.nl', 'referer': 'https://www.maxmeldpunt.nl/', 'sec-ch-ua': '"Brave";v="123", "Not:A-Brand";v="8", "Chromium";v="123"','user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
                'authorization': f'{token}'}
            data = '{"profileName":"dash","drmType":"widevine","referrerUrl":"https://www.maxmeldpunt.nl/uitzendingen/oud-ptters-lopen-pensioenaanvulling-mis/"}'
            r = json.loads(requests.post('https://prod.npoplayer.nl/stream-link', headers=headers, data=data).text)
            mpd_url = r['stream']['streamURL']
            print('mpd_url: ', mpd_url)
            drm_token = r['stream']['drmToken']
    
            headers = { 'accept': '*/*', 'accept-language': 'fr-FR,fr;q=0.5', 'origin': 'https://www.maxmeldpunt.nl', 'referer': 'https://www.maxmeldpunt.nl/', 'sec-ch-ua': '"Brave";v="123", "Not:A-Brand";v="8", "Chromium";v="123"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"macOS"', 'sec-fetch-dest': 'empty', 'sec-fetch-mode': 'cors', 'sec-fetch-site': 'cross-site', 'sec-gpc': '1', 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',}
            pssh = getpssh(mpd_url)
            print('pssh: ', pssh)
            pssh = PSSH(pssh)
            lic_url = f'https://npo-drm-gateway.samgcloud.nepworldwide.nl/authentication?custom_data={drm_token}'
    
    
            device = Device.load(os.getenv('DEVICEPATH'))
            cdm = Cdm.from_device(device)
            session_id = cdm.open()
            challenge = cdm.get_license_challenge(session_id, pssh)
            licence = requests.post(lic_url, headers = headers, data=challenge)
            licence.raise_for_status()
            cdm.parse_license(session_id, licence.content)
            for key in cdm.get_keys(session_id):
                if key.type=='CONTENT':
                    print(f"\n--key {key.kid.hex}:{key.key.hex()}")
            cdm.close(session_id)
    Quote Quote  
  8. Nice. But do yo manually create a script everytime you want to do something like this? I mean, how deterministic is it? Seeing things like return root[0][1][4][0].text feels that it it tailor made, or are these xml index positions predictable enough in mpd files to have some script templates available or even a generator?
    Quote Quote  
  9. Member aqzs's Avatar
    Join Date
    Mar 2024
    Location
    Paris
    Search Comp PM
    Originally Posted by cosmicdrm View Post
    Nice. But do yo manually create a script everytime you want to do something like this? I mean, how deterministic is it? Seeing things like return root[0][1][4][0].text feels that it it tailor made, or are these xml index positions predictable enough in mpd files to have some script templates available or even a generator?
    If you want the key it take approx 30s without any script, if you want to automate the process it could take 3 sec
    I just like to make scrip like that, reverse engineering the way they generate token etc
    Quote Quote  
  10. Originally Posted by aqzs View Post
    Originally Posted by cosmicdrm View Post
    Image
    [Attachment 78176 - Click to enlarge]

    Btw, CDRM-project.com does not work in this case

    AAAAXHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADwIARIQjr Cz3mg97KY6WEDCDvR0KxoIdXNwLWNlbmMiGGpyQ3ozbWc5N0tZ NldFRENEdlIwS3c9PSoAMgA=

    https://npo-drm-gateway.samgcloud.nepworldwide.nl/authentication?custom_data=eyJhbGciO...6LKp872pYPzAwk
    CDRM-project.com could work but you need a fresh token and a header.

    --snip--
    I just reopend the site in an ingocnito browser and checked the data it gave, but token looks the same. Hmmm
    Quote Quote  
  11. After reading 100 methods, what would be the latest way to convert device_private_key to a usable key?
    Quote Quote  
  12. Search, Learn, Download! Karoolus's Avatar
    Join Date
    Oct 2022
    Location
    Belgium
    Search Comp PM
    Originally Posted by cosmicdrm View Post
    After reading 100 methods, what would be the latest way to convert device_private_key to a usable key?
    Not how this works at all, read the stickies and try to get an understanding that way.
    Quote Quote  
  13. Originally Posted by Karoolus View Post
    Originally Posted by cosmicdrm View Post
    After reading 100 methods, what would be the latest way to convert device_private_key to a usable key?
    Not how this works at all, read the stickies and try to get an understanding that way.
    tbh, the stickies are not the best if you want to fathom the general idea behind it all. I do think I understand it. To give back to the community I made a more general description. Please let me know where it can be approved, it's based on the virtual android device approach:

    1. Set Up Virtual Android Environment with Frida:
    - Create a virtual Android environment (e.g., using an emulator or a rooted device).
    - Load Frida, which intercepts internal Android system calls, onto the virtual Android system.

    2. Run KeyDive on the Host Machine:
    - On your host machine, execute a program like KeyDive. KeyDive interacts with the virtual Android device via ADB (Android Debug Bridge) in the background.
    - KeyDive allows you to analyze and manipulate the Android app's behavior during runtime.

    3. Access Video Content:
    - While the virtual Android environment with Frida and KeyDive is active, visit a video site that presents content.
    - Let the site load the MPD file, which contains information about video streams and encryption methods.

    4. Extract Client ID and Private Key:
    - Because you've opened your target video KeyDive intercepts this and will extract the following files:
    - client_id.bin: Represents the client ID within the built-in Android Content Decryption Module (CDM).
    - private_key.pem: Contains the private key associated with the CDM.

    5. Prepare the .wvd Device File:
    - Generate a .wvd device file using pywidevine's create-device command.
    - This file simulates a Widevine CDM device and can be used as your own prepped CDM device in a script.

    6. Retrieve DRM Keys from the .wvd Device:
    - Utilize the pywscript.py script to extract DRM keys from the .wvd device.
    - The script requires the following inputs:
    - Path to the .wvd file
    - Path to the manifest .mpd file (which describes the video streams)
    - PSSH (Protection System Specific Header) information
    - Authentication server URL
    - The script will use the prepped CDM (via the .wvd device) to request and display the DRM keys in cleartext.


    7. Download Encrypted Video:
    - Finally, use the extracted DRM keys along with a tool like N_m3u8DL-RE to download the DRM-encrypted video via its .mpd URL.
    Quote Quote  
  14. Originally Posted by Karoolus View Post
    Originally Posted by cosmicdrm View Post
    After reading 100 methods, what would be the latest way to convert device_private_key to a usable key?
    Not how this works at all, read the stickies and try to get an understanding that way.
    tbh, the stickies are not the best if you want to fathom the general idea behind it all. I do think I understand it. To give back to the community I made a more general description. Please let me know where it can be improved, it's based on the virtual android device approach:

    1. Set Up Virtual Android Environment with Frida:
    - Create a virtual Android environment (e.g., using an emulator or a rooted device).
    - Load Frida, which intercepts internal Android system calls, onto the virtual Android system.

    2. Run KeyDive on the Host Machine:
    - On your host machine, execute a program like KeyDive. KeyDive interacts with the virtual Android device via ADB (Android Debug Bridge) in the background.
    - KeyDive allows you to analyze and manipulate the Android app's behavior during runtime.

    3. Access Video Content:
    - While the virtual Android environment with Frida and KeyDive is active, visit a video site that presents content.
    - Let the site load the MPD file, which contains information about video streams and encryption methods.

    4. Extract Client ID and Private Key:
    - Because you've opened your target video KeyDive intercepts this and will extract the following files:
    - client_id.bin: Represents the client ID within the built-in Android Content Decryption Module (CDM).
    - private_key.pem: Contains the private key associated with the CDM.

    5. Prepare the .wvd Device File:
    - Generate a .wvd device file using pywidevine's create-device command.
    - This file simulates a Widevine CDM device and can be used as your own prepped CDM device in a script.

    6. Retrieve DRM Keys from the .wvd Device:
    - Utilize the pywscript.py script to extract DRM keys from the .wvd device.
    - The script requires the following inputs:
    - Path to the .wvd file
    - Path to the manifest .mpd file (which describes the video streams)
    - PSSH (Protection System Specific Header) information
    - Authentication server URL
    - The script will use the prepped CDM (via the .wvd device) to request and display the DRM keys in cleartext.The script can be found at the end of this sticky

    7. Download Encrypted Video:
    - Finally, use the extracted DRM keys along with a tool like N_m3u8DL-RE to download the DRM-encrypted (and decrypt on-the-fly) video via its .mpd URL. Or download the encrypted file using yt-dlp --allow-u and decrypt afterwards with mp4decrypt.
    Last edited by cosmicdrm; 7th Apr 2024 at 16:56.
    Quote Quote  



Similar Threads

Visit our sponsor! Try DVDFab and backup Blu-rays!