{"repro_id":"REPRO-2026-00235","version":6,"title":"PACSgear PACS Scan unauthenticated remote code execution","repro_type":"security","status":"published","severity":"critical","description":"PACSgear PACS Scan 5.2.1 contains an unauthenticated remote code execution vulnerability. Reproduce: deploy PACS Scan 5.2.1, send a crafted unauthenticated request to the vulnerable endpoint, and confirm arbitrary code execution on the host.","root_cause":"# RCA Report: CVE-2026-58126\n\n## Summary\n\nCVE-2026-58126 is an unauthenticated remote code execution vulnerability in Hyland/PACSgear PACS Scan 5.2.1. The original vendor package exposes `PGImageExchQueue.exe`, a .NET Remoting TCP endpoint on port 22222, at the well-known ObjectURI `PGImageExchange`. The endpoint can be reached without credentials. Using the disclosed .NET Remoting MarshalByRefObject/WebClient technique, an attacker can obtain a server-side `System.Net.WebClient` object, read local files, and write attacker-controlled files into the PACSgear Image Exchange Service directory. This arbitrary write can be chained to code execution when the product service path is restarted/started; the public disclosure describes DLL hijacking with `CRYPTSP.DLL`, and this run additionally demonstrates code execution by remotely overwriting the product service executable and starting that product service path.\n\n## Impact\n\n- **Package/component affected:** Hyland/PACSgear PACS Scan 5.2.1, specifically `PGImageExchQueue.exe` and `PGImageExchangeQueueSvc.exe` in `Pacsgear Image Exchange Service`.\n- **Affected versions:** PACS Scan 5.2.1. The downloaded vendor installer is `https://download.pacsgear.com/download/PacsSCAN5.2.1.zip`; extracted product binaries include:\n  - `PGImageExchQueue.exe` SHA256 `853e7ea4ac56cc85df9a21a81c993a36ac3bff803e35b7ba3aa7c1438a28525c`\n  - `PGImageExchangeQueueSvc.exe` SHA256 `95c8fcb4a934dd809951916e74872ccce0cb8e201f3ad0508542c316be3d1299`\n- **Risk level and consequences:** Critical. An unauthenticated network attacker can cross the PACS Scan remoting boundary, read files, write files into the product service directory, plant executable payloads, and achieve attacker-controlled code execution when the service path is started/restarted. On a normal Windows deployment, the disclosed service context is `NT AUTHORITY\\SYSTEM`, so successful exploitation can result in full host compromise.\n\n## Impact Parity\n\n- **Disclosed/claimed maximum impact:** Unauthenticated remote code execution by connecting to `tcp://<host>:22222/PGImageExchange`, using .NET Remoting MBRO/WebClient to read/write arbitrary files, planting a malicious DLL such as `CRYPTSP.DLL` in the PACSgear application directory, and triggering service restart/load in `PGImageExchangeQueueSvc.exe`.\n- **Reproduced impact from this run:**\n  - Original vendor PACS Scan 5.2.1 installer downloaded and extracted: **CONFIRMED**.\n  - Original `PGImageExchQueue.exe` started and listened on TCP/22222: **CONFIRMED**.\n  - Unauthenticated remote endpoint access to `PGImageExchange`: **CONFIRMED**.\n  - Server-side MBRO/WebClient object obtained through the unauthenticated remoting endpoint: **CONFIRMED**.\n  - Remote file read (`C:\\Windows\\win.ini`) through the server-side WebClient: **CONFIRMED**.\n  - Remote file write into the product service directory: **CONFIRMED**.\n  - Remote planting of `CRYPTSP.DLL` into the product directory: **CONFIRMED**.\n  - Remote overwrite of `PGImageExchangeQueueSvc.exe` through the same arbitrary write primitive: **CONFIRMED**.\n  - Code execution when the product service path was started after the remote overwrite: **CONFIRMED** (marker file written by attacker-controlled replacement executable).\n  - Negative control: clean/original service binary did not create the marker before exploitation: **CONFIRMED**.\n- **Parity:** `full` for unauthenticated product-path code execution via the disclosed arbitrary file write primitive. The exact final execution method in this sandbox is service executable overwrite rather than Windows DLL search-order loading of `CRYPTSP.DLL`; however, it uses the same product endpoint, same unauthenticated MBRO/WebClient primitive, same product service directory, and same service-start execution boundary. The script also plants `CRYPTSP.DLL` as disclosed, but Wine does not faithfully reproduce Windows' system DLL search-order behavior for `CRYPTSP.DLL`.\n- **Not demonstrated:** Execution as `NT AUTHORITY\\SYSTEM` on native Windows. The product was run under Wine with native .NET Framework 4 in the Linux sandbox, so the user/account context differs from a production Windows service deployment.\n\n## Root Cause\n\nThe vulnerability occurs because PACS Scan exposes a .NET Remoting endpoint without authentication and with a dangerous serialization/remoting configuration:\n\n1. `PGImageExchQueue.exe` registers a TCP remoting endpoint using the .NET Remoting stack and exposes the well-known ObjectURI `PGImageExchange` on port 22222.\n2. There is no authentication, authorization, session setup, or token check before remote clients can reach the remoting object.\n3. The remoting configuration is susceptible to the MarshalByRefObject/WebClient exploitation technique: a remote unauthenticated caller can induce the server to instantiate and return a proxy to a server-side `System.Net.WebClient` object.\n4. The server-side WebClient can access `file://` URIs from the server context. This provides arbitrary local file read/write from the product process.\n5. The product service directory contains `PGImageExchangeQueueSvc.exe` and is a code-loading/execution location. Writing attacker-controlled executable content there can lead to code execution when the service starts/restarts. The public disclosure describes `CRYPTSP.DLL` hijacking; this run demonstrates the same write-to-service-directory primitive by replacing `PGImageExchangeQueueSvc.exe` and starting the product service path.\n\nNo public fix commit or fixed release was identified. The script therefore uses a clean original-service negative control rather than a vulnerable-vs-fixed vendor build comparison.\n\n## Reproduction Steps\n\n1. **Script:** `bundle/repro/reproduction_steps.sh`\n2. **What the script does:**\n   - Installs runtime dependencies needed in a clean sandbox: Mono, Wine, Xvfb, MSI extraction tools, Winetricks, and MinGW.\n   - Creates a Wine prefix with native .NET Framework 4.0 because the MBRO/WebClient exploit path depends on Windows .NET Framework behavior.\n   - Downloads the original vendor PACS Scan 5.2.1 installer from `https://download.pacsgear.com/download/PacsSCAN5.2.1.zip`.\n   - Extracts `PacsSCAN.msi` and the real product binaries `PGImageExchQueue.exe` and `PGImageExchangeQueueSvc.exe`.\n   - Starts original `PGImageExchQueue.exe` under Wine/Xvfb and waits for TCP/22222 to accept connections.\n   - Compiles and runs an exploit client implementing the MBRO Lazy/WebClient technique.\n   - Performs two independent attempts. Each attempt:\n     1. Runs a clean-service negative control and verifies no marker is created.\n     2. Starts the real product remoting endpoint.\n     3. Obtains a remote server-side `System.Net.WebClient` object with no credentials.\n     4. Reads `C:\\Windows\\win.ini` through the product process.\n     5. Writes a proof file into the product Image Exchange Service directory.\n     6. Uploads `CRYPTSP.DLL` into the product directory.\n     7. Uploads an attacker-controlled replacement `PGImageExchangeQueueSvc.exe` into the product directory.\n     8. Starts the product service path and verifies the attacker marker file is written.\n   - Writes `bundle/repro/runtime_manifest.json` with runtime evidence.\n3. **Expected evidence of reproduction:**\n   - `bundle/logs/product_attempt_1_exploit.log` and `bundle/logs/product_attempt_2_exploit.log` show the remote WebClient, remote file read, remote file writes, `CRYPTSP.DLL` upload, and service executable overwrite.\n   - `bundle/repro/artifacts/product_attempt_1/rce_marker.txt` and `bundle/repro/artifacts/product_attempt_2/rce_marker.txt` contain the code-execution marker.\n   - `bundle/logs/product_attempt_1_negative_service.log` and `bundle/logs/product_attempt_2_negative_service.log` show the clean-service negative controls.\n\n## Evidence\n\nKey artifact locations:\n\n- `bundle/repro/reproduction_steps.sh` — complete reproducer.\n- `bundle/repro/runtime_manifest.json` — structured runtime manifest.\n- `bundle/logs/product_availability.log` — vendor installer and binary hashes.\n- `bundle/logs/product_static_identity.log` — original binary static identity (`IRemote`, `CRemote`, remoting methods, `RegisterWellKnownServiceType`, `TcpChannel`).\n- `bundle/logs/product_attempt_1_exploit.log` and `bundle/logs/product_attempt_2_exploit.log` — exploit logs.\n- `bundle/logs/product_attempt_1_service.log` and `bundle/logs/product_attempt_2_service.log` — product service path execution logs.\n- `bundle/repro/artifacts/product_attempt_1/remote_read_win_ini.txt` and `bundle/repro/artifacts/product_attempt_2/remote_read_win_ini.txt` — remote read proof.\n- `bundle/repro/artifacts/product_attempt_1/remote_write_proof.txt` and `bundle/repro/artifacts/product_attempt_2/remote_write_proof.txt` — remote write proof.\n- `bundle/repro/artifacts/product_attempt_1/rce_marker.txt` and `bundle/repro/artifacts/product_attempt_2/rce_marker.txt` — code-execution marker files.\n\nKey excerpts:\n\n```text\nObtained remote WebClient transparent=True uri=/...rem\nREAD bytes=16 uri=file:///C:/windows/win.ini\n[mail]\nmapi=1\nUPLOAD bytes=73 response=0 target=file:///Z:/.../Pacsgear Image Exchange Service/REMOTE_WRITE_PROOF_1.txt\nVERIFY bytes=73 target=file:///Z:/.../REMOTE_WRITE_PROOF_1.txt\n```\n\n```text\nUPLOAD bytes=83454 response=0 target=file:///Z:/.../Pacsgear Image Exchange Service/CRYPTSP.DLL\nVERIFY bytes=83454 target=file:///Z:/.../CRYPTSP.DLL\nUPLOAD bytes=114101 response=0 target=file:///Z:/.../Pacsgear Image Exchange Service/PGImageExchangeQueueSvc.exe\nVERIFY bytes=114101 target=file:///Z:/.../PGImageExchangeQueueSvc.exe\n```\n\n```text\nCVE-2026-58126 CODE EXECUTION: overwritten PGImageExchangeQueueSvc.exe executed\n```\n\nEnvironment details:\n\n- Host sandbox: Linux.\n- Windows compatibility layer: Wine 10.x with native .NET Framework 4.0 installed via Winetricks.\n- Product endpoint: original `PGImageExchQueue.exe` from PACS Scan 5.2.1, listening on TCP/22222.\n- Exploit entrypoint: unauthenticated .NET Remoting call to `tcp://127.0.0.1:22222/PGImageExchange`.\n- Sanitizers: none.\n\n## Recommendations / Next Steps\n\n1. Remove or disable unauthenticated .NET Remoting exposure. .NET Remoting and BinaryFormatter should not be exposed to untrusted network clients.\n2. Require strong authentication and authorization before any remoting call is processed.\n3. Disable dangerous deserialization/remoting behavior that allows MBRO object materialization; migrate to a supported RPC framework with explicit data contracts.\n4. Restrict service file writes to a narrowly scoped queue directory and block absolute paths, `file://` paths, and traversal outside the intended queue.\n5. Harden service binary/DLL loading:\n   - ensure the service directory is not writable by the remoting process or remote users;\n   - use safe DLL search settings and explicit system paths for system DLLs;\n   - verify service binaries before execution.\n6. Run the Image Exchange service with least privilege rather than `SYSTEM` where possible.\n7. Publish a fixed version and regression tests that verify unauthenticated clients cannot obtain server-side `WebClient`/MBRO objects or write into the service directory.\n\n## Additional Notes\n\n- The script is idempotent: it restores the original `PGImageExchangeQueueSvc.exe` before each attempt and runs two clean attempts.\n- The script intentionally keeps all proof logs and marker files under `bundle/logs/` and `bundle/repro/artifacts/`.\n- The exact disclosed DLL hijack is approximated in Wine because Wine's system DLL loading behavior does not match native Windows service DLL search order. The script still plants `CRYPTSP.DLL` through the real remote file-write primitive and demonstrates code execution through the product service execution path by remote service executable overwrite.\n- No fixed vendor build was available, so the negative control is the clean original service binary before remote overwrite rather than a patched release.\n","cve_id":"CVE-2026-58126","reproduced_at":"2026-07-06T08:19:43.367629+00:00","duration_secs":3847.0,"tool_calls":330,"handoffs":3,"total_cost_usd":23.93607611000002,"agent_costs":{"judge":0.05272485,"repro":19.356211540000007,"support":0.09234072,"vuln_variant":4.434799},"cost_breakdown":{"judge":{"gpt-5.4-mini":0.05272485},"repro":{"accounts/fireworks/routers/glm-5p2-fast":0.72510654,"gpt-5.5":18.63110500000001},"support":{"accounts/fireworks/routers/glm-5p2-fast":0.09234072},"vuln_variant":{"gpt-5.5":4.434799}},"quality":{"confidence":"high","idempotent_verified":false,"community_verifications":0},"environment":{"sandbox_image":"ghcr.io/n3mes1s/pruva-sandbox@sha256:8096b2518d6022e13d68f885c3b8ded6b4fe607098b1a1ccbfb99abc004d1dc1"},"published_at":"2026-07-06T08:20:21.736009+00:00","retracted":false,"artifacts":[{"path":"bundle/repro/reproduction_steps.sh","filename":"reproduction_steps.sh","size":20355,"category":"reproduction_script"},{"path":"bundle/repro/rca_report.md","filename":"rca_report.md","size":11941,"category":"analysis"},{"path":"bundle/vuln_variant/rca_report.md","filename":"rca_report.md","size":9986,"category":"analysis"},{"path":"bundle/vuln_variant/reproduction_steps.sh","filename":"reproduction_steps.sh","size":11449,"category":"reproduction_script"},{"path":"bundle/logs/exploit_client.log","filename":"exploit_client.log","size":2718,"category":"log"},{"path":"bundle/logs/target_service_exploited.log","filename":"target_service_exploited.log","size":793,"category":"log"},{"path":"bundle/logs/target_service_clean.log","filename":"target_service_clean.log","size":499,"category":"log"},{"path":"bundle/logs/vulnerable_server.log","filename":"vulnerable_server.log","size":513,"category":"log"},{"path":"bundle/artifact_promotion_manifest.json","filename":"artifact_promotion_manifest.json","size":17743,"category":"other"},{"path":"bundle/vuln_variant/root_cause_equivalence.json","filename":"root_cause_equivalence.json","size":1583,"category":"other"},{"path":"bundle/repro/runtime_manifest.json","filename":"runtime_manifest.json","size":2210,"category":"other"},{"path":"bundle/repro/validation_verdict.json","filename":"validation_verdict.json","size":898,"category":"other"},{"path":"bundle/repro/artifacts/pacs_scan_appdir/RCE_PROOF_CODE_EXECUTION.txt","filename":"RCE_PROOF_CODE_EXECUTION.txt","size":228,"category":"other"},{"path":"bundle/repro/artifacts/pacs_scan_appdir/CVE-2026-58126-PROOF.txt","filename":"CVE-2026-58126-PROOF.txt","size":189,"category":"other"},{"path":"bundle/repro/artifacts/pacs_scan_appdir/CRYPTSP.DLL","filename":"CRYPTSP.DLL","size":250,"category":"other"},{"path":"bundle/logs/product_attempt_1_exploit.log","filename":"product_attempt_1_exploit.log","size":1997,"category":"log"},{"path":"bundle/logs/product_attempt_1_service.log","filename":"product_attempt_1_service.log","size":715,"category":"log"},{"path":"bundle/repro/artifacts/product_attempt_1/rce_marker.txt","filename":"rce_marker.txt","size":81,"category":"other"},{"path":"bundle/logs/product_availability.log","filename":"product_availability.log","size":713,"category":"log"},{"path":"bundle/logs/reproduction_steps.log","filename":"reproduction_steps.log","size":14399,"category":"log"},{"path":"bundle/logs/product_static_identity.log","filename":"product_static_identity.log","size":6592,"category":"log"},{"path":"bundle/logs/product_attempt_1_server.log","filename":"product_attempt_1_server.log","size":1337,"category":"log"},{"path":"bundle/logs/product_attempt_1_negative_service.log","filename":"product_attempt_1_negative_service.log","size":651,"category":"log"},{"path":"bundle/logs/product_attempt_2_server.log","filename":"product_attempt_2_server.log","size":1337,"category":"log"},{"path":"bundle/logs/product_attempt_2_exploit.log","filename":"product_attempt_2_exploit.log","size":1997,"category":"log"},{"path":"bundle/logs/product_attempt_2_service.log","filename":"product_attempt_2_service.log","size":587,"category":"log"},{"path":"bundle/logs/product_attempt_2_negative_service.log","filename":"product_attempt_2_negative_service.log","size":651,"category":"log"},{"path":"bundle/logs/wine_dotnet_setup.log","filename":"wine_dotnet_setup.log","size":129,"category":"log"},{"path":"bundle/repro/artifacts/product_identity.json","filename":"product_identity.json","size":883,"category":"other"},{"path":"bundle/repro/artifacts/product_attempt_1/remote_read_win_ini.txt","filename":"remote_read_win_ini.txt","size":181,"category":"other"},{"path":"bundle/repro/artifacts/product_attempt_1/remote_write_proof.txt","filename":"remote_write_proof.txt","size":73,"category":"other"},{"path":"bundle/repro/artifacts/product_attempt_1/cryptsp_upload.txt","filename":"cryptsp_upload.txt","size":189,"category":"other"},{"path":"bundle/repro/artifacts/product_attempt_1/service_overwrite_upload.txt","filename":"service_overwrite_upload.txt","size":206,"category":"other"},{"path":"bundle/repro/artifacts/product_attempt_2/remote_read_win_ini.txt","filename":"remote_read_win_ini.txt","size":181,"category":"other"},{"path":"bundle/repro/artifacts/product_attempt_2/remote_write_proof.txt","filename":"remote_write_proof.txt","size":73,"category":"other"},{"path":"bundle/repro/artifacts/product_attempt_2/rce_marker.txt","filename":"rce_marker.txt","size":81,"category":"other"},{"path":"bundle/repro/artifacts/product_attempt_2/cryptsp_upload.txt","filename":"cryptsp_upload.txt","size":189,"category":"other"},{"path":"bundle/repro/artifacts/product_attempt_2/service_overwrite_upload.txt","filename":"service_overwrite_upload.txt","size":206,"category":"other"},{"path":"bundle/vuln_variant/variant_manifest.json","filename":"variant_manifest.json","size":4292,"category":"other"},{"path":"bundle/vuln_variant/validation_verdict.json","filename":"validation_verdict.json","size":3366,"category":"other"},{"path":"bundle/vuln_variant/patch_analysis.md","filename":"patch_analysis.md","size":7003,"category":"documentation"},{"path":"bundle/logs/vuln_variant/reproduction_steps.log","filename":"reproduction_steps.log","size":13121,"category":"log"},{"path":"bundle/vuln_variant/runtime_manifest.json","filename":"runtime_manifest.json","size":1080,"category":"other"},{"path":"bundle/logs/vuln_variant/vulnerable_static_scan.log","filename":"vulnerable_static_scan.log","size":2534,"category":"log"},{"path":"bundle/logs/vuln_variant/service_candidate_scan.log","filename":"service_candidate_scan.log","size":5979,"category":"log"},{"path":"bundle/logs/vuln_variant/service_onstart_script.log","filename":"service_onstart_script.log","size":240,"category":"log"},{"path":"bundle/logs/vuln_variant/fixed_latest_check.log","filename":"fixed_latest_check.log","size":3789,"category":"log"},{"path":"bundle/logs/vuln_variant/latest_version.txt","filename":"latest_version.txt","size":738,"category":"other"}]}