-
-
Notifications
You must be signed in to change notification settings - Fork 891
Require positive confirmation for lightfuzz code-change findings #3357
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -41,20 +41,20 @@ def _ascii_xor_score(b): | |||||||||||
|
|
||||||||||||
| def _is_structured_id_pair(bytes_a, bytes_b, xored, zero_run): | ||||||||||||
| """True when the pair looks like structured identifiers (MongoDB ObjectIds, | ||||||||||||
| hex timestamps, sequential counters, time-ordered UUIDs) rather than | ||||||||||||
| reused-keystream ciphertexts. | ||||||||||||
| hex timestamps, sequential counters, record GUIDs) rather than reused-keystream | ||||||||||||
| ciphertexts. | ||||||||||||
|
|
||||||||||||
| Structured IDs share a long byte prefix (timestamp, process ID, ...) and | ||||||||||||
| differ only in a short counter/random suffix. Their XOR has a long leading- | ||||||||||||
| zero run followed by a sparse or random tail -- superficially identical to | ||||||||||||
| keystream reuse with shared-prefix plaintexts, but distinguishable by what | ||||||||||||
| the tail looks like. | ||||||||||||
| Structured IDs share a byte prefix (timestamp, process ID, ...) and differ only | ||||||||||||
| in a short counter/random suffix. Their XOR has a leading-zero run followed by | ||||||||||||
| a sparse or random tail -- superficially identical to keystream reuse with | ||||||||||||
| shared-prefix plaintexts, but distinguishable by what the tail looks like. | ||||||||||||
|
|
||||||||||||
| Real many-time-pad: the tail is XOR of diverging ASCII plaintexts -- dense, | ||||||||||||
| diverse bytes mostly in [0x00, 0x60]. | ||||||||||||
|
|
||||||||||||
| Structured IDs: the tail is either a small counter delta (sparse zeros with | ||||||||||||
| one nonzero byte) or random machine/process bytes (fails ASCII-XOR check). | ||||||||||||
| Structured IDs: the tail is a small counter delta (sparse zeros with one nonzero | ||||||||||||
| byte), random machine/process bytes (fails the ASCII-XOR check), or a fixed-field | ||||||||||||
| template whose matching segments zero out scattered through the tail. | ||||||||||||
| """ | ||||||||||||
| tail = xored[zero_run:] | ||||||||||||
|
|
||||||||||||
|
|
@@ -68,6 +68,15 @@ def _is_structured_id_pair(bytes_a, bytes_b, xored, zero_run): | |||||||||||
| if len(bytes_a) == len(bytes_b) and len(tail) <= 4: | ||||||||||||
| return True | ||||||||||||
|
|
||||||||||||
| # Zeros scattered *through* the tail mean the two values keep re-converging at | ||||||||||||
| # fixed offsets -- a shared field template (an instance/table/timestamp segment | ||||||||||||
| # in a record GUID). Two ciphertexts under a reused keystream zero out only where | ||||||||||||
| # their plaintexts still coincide, which is the leading run, or a shared suffix. | ||||||||||||
| # Trailing zeros are stripped first so shared-suffix plaintexts aren't caught here. | ||||||||||||
| core = tail.rstrip(b"\x00") | ||||||||||||
| if sum(1 for b in core if b == 0) >= 2: | ||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This rule costs real detections. Ablated the two edits separately, 500 many-time-pad pairs of equal-length tokens sharing fixed fields:
Dropping the Could not find a narrower fix: contiguous run does not separate (FP 2, real avg 12.1), nor printability (0.38 vs 0.37). With two samples the cases look ambiguous. If so, this is a precision/recall tradeoff, worth stating in the PR body. |
||||||||||||
| return True | ||||||||||||
|
|
||||||||||||
| # For longer tails: real many-time-pad XOR reveals XOR of diverging ASCII | ||||||||||||
| # plaintexts -- dense nonzero bytes mostly in [0x00, 0x60]. Random | ||||||||||||
| # suffixes (UUID v7 random bits, different-process machine bytes) and | ||||||||||||
|
|
@@ -369,11 +378,10 @@ def detect_keystream_reuse(self, probe_value): | |||||||||||
| # At least 2 leading zero bytes OR ≥90% of bytes in ASCII-XOR-ASCII range | ||||||||||||
| if zero_run < 2 and ascii_score < 0.9: | ||||||||||||
| continue | ||||||||||||
| # A leading-zero run alone is the hallmark of structured hex | ||||||||||||
| # identifiers (MongoDB ObjectIds, hex timestamps, sequential | ||||||||||||
| # counters, time-ordered UUIDs) sharing a byte prefix -- not | ||||||||||||
| # keystream reuse. Filter those out by examining the tail. | ||||||||||||
| if zero_run >= 2 and _is_structured_id_pair(bytes_a, bytes_b, xored, zero_run): | ||||||||||||
| # Both entry conditions are met just as easily by structured hex identifiers | ||||||||||||
| # as by real ciphertexts, so every candidate pair goes through the same | ||||||||||||
| # discrimination on what the diverging region actually looks like. | ||||||||||||
| if _is_structured_id_pair(bytes_a, bytes_b, xored, zero_run): | ||||||||||||
| continue | ||||||||||||
| pair_score = (zero_run, ascii_score) | ||||||||||||
| if best is None or pair_score > (best[0], best[1]): | ||||||||||||
|
|
||||||||||||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
>= 2,<= 4,0.7,0.9,min(4, ...)and0x55are unnamed. These are the detection knobs someone tunes when a FP lands, and they are only findable by reading the function. Precedent:PER_PARENT_CAPinchaos.py.