K7 Ultimate Security 17.0.2045 restricted standard users in its settings UI, yet its SYSTEM service accepted registry content submitted by a low-privilege client. Subsequent patches exposed a second boundary problem: the service's trusted-client set did not match the driver's protected-process set.
Three questions matter here: who can reach the pipe, which objects a request may modify, and whether client identity implies runtime integrity. The analysis distinguishes historical behavior from static findings and uses an offline model to check framing. It does not claim a reproduction against current releases.
Follow the settings action to the service
| Object | Role in the historical evidence |
|---|---|
| K7 Ultimate Security 17.0.2045 | Initial product version |
K7TSMain.exe |
UI process running as a standard user |
K7TSMngr.exe |
SYSTEM service process |
K7TSMngrService1 |
Named pipe associated with the settings operation |
K7MailProxyV1 |
Null-DACL enumeration lead, not the confirmed settings channel |
A standard user encountered a restriction dialog, while an administrator could enable settings changes by non-administrators. The useful step was correlating that UI action with pipe traffic, rather than assuming that the most permissive ACL identified the relevant feature. Windows pipe access checks govern connection access; application operations still need their own authorization.

Interface illustration. It explains the two entry points, not the appearance or state of a particular experiment.
The captured settings request contained 213 bytes: a 28-byte header and a 185-byte body. The reply contained 36 bytes. The body included HKLM\Software\K7 Computing\K7TotalSecurity\CommonInfo and three value names:
AdminNonAdminIsValid: whether non-administrators may change settings.AdminChangesNeedPassword: whether changes require a password.AdminChangesPasswordHash: a password-related state field.
That resembles a general registry-text transport rather than a dedicated Boolean-setting API. A path in a request does not establish arbitrary registry access, however. The service's resulting writes must be checked.
Separate length checks from value restrictions
This header comes from the variant whose body was shortened by one byte. It consists of seven little-endian DWORDs.
magic0x00–0x03version0x04–0x07header_size0x08–0x0Breserved0x0C–0x0Fcommand0x10–0x13body_length0x14–0x17auxiliary_length0x18–0x1B
| Offset | Value | Interpretation |
|---|---|---|
0x00 |
0x4b375453 |
Raw bytes spell ASCII ST7K |
0x04 |
0x1010 |
Protocol identifier |
0x08 |
0x1c |
Header length, 28 |
0x0c |
0 |
Reserved field in this sample |
0x10 |
0x10044 |
Registry request command in the capture |
0x14 |
0xb8 |
Body length, 184 |
0x18 |
0 |
Auxiliary length field in this sample |
The differential tests separated a possible value-name allowlist from a framing check. Success here means that the new value was observed, not merely that the client received a reply.
| Change | Value-name bytes | Declared body length | Historical result |
|---|---|---|---|
AdminNonAdminIsValid |
20 | 185 | Original settings request |
Change to aaaaa, retain old length |
5 | 185 | Unsuccessful |
Same-length AdminNonAdminIsValie |
20 | 185 | New value written |
Shorten to AdminNonAdminIsVali, update length |
19 | 184 | New value written |
A successful same-length substitution shows that the service was not limited to the original value name. Success after correcting the shorter body's length explains why an earlier modification failed. This establishes control over the value name; path scope remains a separate question.
A general encoder must update all four bytes at offset 0x14. Editing one byte works only for particular lengths below 256. Count encoded bytes, including the line endings and terminator actually transmitted, rather than characters.
import struct
header = bytes.fromhex(
"53 54 37 4b 10 10 00 00 1c 00 00 00 00 00 00 00 "
"44 00 01 00 b8 00 00 00 00 00 00 00"
)
fields = struct.unpack("<7I", header)
assert fields == (0x4b375453, 0x1010, 28, 0, 0x10044, 184, 0)
body = b"x" * 300 # Illustrative bytes, not registry syntax.
updated = bytearray(header)
struct.pack_into("<I", updated, 20, len(body))
assert updated[20:24] == b"\x2c\x01\x00\x00"The offline check passed: the captured header declares 28 header bytes and 184 body bytes; an illustrative length of 300 encodes as 2c 01 00 00. The sample body is filler. This code neither connects to a pipe nor modifies the registry.
Verify replay through backend state
The settings recording first shows the standard-user restriction. After the request receives a 36-byte reply, reopening the page shows that non-administrator settings access is enabled. The reply alone establishes only that data came back. The UI state and backend writes support the authorization consequence.
Process Monitor attributes the writes to SYSTEM and shows HKLM\SOFTWARE\WOW6432Node\K7 Computing\K7TotalSecurity\CommonInfo. That is not the exact spelling in the request: WOW64 registry redirection distinguishes the 32-bit and 64-bit views. Record process bitness and registry view instead of treating these paths as contradictory evidence.
| Evidence | What it supports | What it does not establish |
|---|---|---|
| Low-privilege initiating process | No administrator UI action is required for this request | Every product version accepts it |
| 36-byte reply | The service returned data on the connection | The registry write succeeded |
| Registry writes by SYSTEM | A privileged service applied the request | Every path is writable |
| New value names and changed settings | Client influence over object contents | Every subsequent operation escalates privileges |
Connect registry writes to process creation
The early-version record extends path control to Image File Execution Options (IFEO), specifically the Debugger configuration for K7TSHlpr.exe. IFEO debugger configuration affects new instances of the target program. When a SYSTEM service starts that helper, registry-controlled data can therefore redirect a privileged launch.
flowchart TD A["Low-privilege client"] --> B["Service accepts registry content"] B --> C["SYSTEM writes IFEO configuration"] C --> D["Service starts helper"] D --> E["Debugger setting redirects the launch"] E --> F["Privileged child and account-state changes"]
The order of the recorded controls matters. The test account initially does not exist, and direct account creation is denied. Following the pipe operations, the process tree shows a SYSTEM command interpreter beneath K7TSMngr.exe. The final account query lists the new account in the local Administrators group. This is stronger evidence than a success message printed by the script.
The chain depends on three independent conditions: the write primitive reaches the relevant registry location, the target image is actually launched, and that launch has sufficient privilege. Changing a settings toggle and achieving SYSTEM execution are distinct findings.
Patches expose different trust sets
| Historical stage | Observation | Boundary |
|---|---|---|
| Initial 17.0.2045 | Low-privilege script replay works | Incomplete service-side authorization |
| Client identification added | Ordinary PowerShell requests fail; requests from a product process still have a successful recorded path | File identity is not runtime integrity |
K7Sentry.sys 22.0.0.70 |
The previous carrier path is blocked, but the K7QuervarCleaningTool.exe path still changes the setting |
Accepted clients and protected processes differ |
The intermediate tests also report different outcomes for ordinary module loading and manual mapping. That makes the client's hosting process relevant; it does not establish that every signed file or mapping technique works. Driver 22.0.0.70 is an intermediate tested version, not the final fixed version named by the vendor.
The service-side static analysis describes ProcessPipeConnection reading a 28-byte header, checking the magic, protocol identifier and header size, and limiting both lengths to 1 MiB. Its client-validation gate has this semantic form:
command == 0x1004C || old_windows_condition || ValidatePipeClient(this)The exception uses 0x1004C, whereas the settings request uses 0x10044. Without command-dispatch analysis and a runtime trace, the settings operation should not be assigned to that exception. This expression is a semantic excerpt from static analysis, not a complete compilable function.
Align accepted and protected identities
ValidatePipeClient obtains the client PID through GetNamedPipeClientProcessId, then uses OpenProcess and QueryFullProcessImageNameA to obtain its image path. The recorded acceptance routes include installation-path matching, a file-digest cache hit, and vendor-signature verification.
The driver's ShouldProtectProcess uses different rules: a name listed in VDefProtectedProcs, or a K7 filename prefix together with the normalized installation-path condition. A path substring is not a strict directory-membership test. A signature on a disk file does not establish the integrity of its process memory.
Let A mean that the service accepts a client, and P that the driver applies the expected protection:
| A | P | Meaning |
|---|---|---|
| false | false | Neither accepted nor protected by this rule |
| false | true | Protected, but not accepted by this service |
| true | false | Trusted carrier without the corresponding protection |
| true | true | The two rules agree for this process |
The offline model enumerates all four combinations; only A and not P identifies the gap. This checks design consistency, not a vulnerability in a new release. A candidate process still needs runtime conditions such as controllable request logic and service acceptance of the operation.
Later advisory and remediation
The vendor's 2025-12-22 advisory identifies CVE-2025-67826, confirms the local named-pipe issue in 17.0.2045, and sets the minimum update to K7 Ultimate Security 17.0.2057 with K7Sentry.sys 22.0.0.74 or later. This advisory postdates the original publication; it is a later update, not a fix state validated by the earlier experiments.
The service should separate connection access, product-file identity, and permission to modify a particular setting. Configuration APIs should constrain keys, values, types and permitted state transitions. If client trust depends on process protection, maintain the accepted and protected sets together and stop sensitive operations when identity or integrity checks fail.
The lesson is the verification order, not the pipe's particular name: establish valid framing, establish the object mutation, then establish the privileged effect. Evidence at one layer does not prove the next.