Skip to content

Commit 398b604

Browse files
DredsenDredsen
authored andcommitted
Init Posts
1 parent b301b65 commit 398b604

8 files changed

Lines changed: 414 additions & 17 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
################################################################################
44

55
/.vs
6+
/.claude

assets/css/style.css

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,19 @@ a:hover {
117117
}
118118

119119
.post-meta {
120-
font-size: 0.85rem;
121-
color: var(--text-muted);
122-
margin: 0 0 0.6rem;
120+
margin: 0.4rem 0 0.75rem;
121+
}
122+
123+
.date-chip {
124+
display: inline-block;
123125
font-family: var(--font-mono);
126+
font-size: 0.75rem;
127+
color: var(--text-muted);
128+
background: var(--code-bg);
129+
border: 1px solid var(--border);
130+
border-radius: 4px;
131+
padding: 0.15rem 0.55rem;
132+
letter-spacing: 0.03em;
124133
}
125134

126135
.post-summary {
@@ -162,7 +171,7 @@ article.post h1 {
162171
}
163172

164173
article.post .post-meta {
165-
margin: 0;
174+
margin: 0.5rem 0 0;
166175
}
167176

168177
article.post h2 {

assets/js/home.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,17 @@
2424
a.textContent = p.title || p.slug;
2525
h2.appendChild(a);
2626

27-
const meta = document.createElement('p');
28-
meta.className = 'post-meta';
29-
meta.textContent = p.date || '';
30-
3127
const summary = document.createElement('p');
3228
summary.className = 'post-summary';
3329
summary.textContent = p.summary || '';
3430

3531
li.appendChild(h2);
36-
li.appendChild(meta);
32+
if (p.date) {
33+
const meta = document.createElement('p');
34+
meta.className = 'post-meta';
35+
meta.innerHTML = '<span class="date-chip">' + p.date + '</span>';
36+
li.appendChild(meta);
37+
}
3738
if (p.summary) li.appendChild(summary);
3839
list.appendChild(li);
3940
}

assets/js/post.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
if (meta.date) {
3838
const date = document.createElement('p');
3939
date.className = 'post-meta';
40-
date.textContent = meta.date;
40+
date.innerHTML = '<span class="date-chip">' + meta.date + '</span>';
4141
header.appendChild(date);
4242
}
4343
article.appendChild(header);
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
*Author: [Dredsen](https://dredsen.github.io/)*
2+
3+
---
4+
5+
## Background
6+
7+
This is my first proper driver write-up, so a quick scene-setter before the technical bits.
8+
9+
**LOLDrivers** ("Living Off the Land Drivers") is a community-maintained catalogue of legitimately signed Windows kernel drivers that contain exploitable vulnerabilities. The idea behind **BYOVD** (Bring Your Own Vulnerable Driver) attacks is simple: load one of these signed drivers yourself, abuse its exposed kernel primitives, and walk away with capabilities that would otherwise require an unsigned driver - bypassing Driver Signature Enforcement entirely, since the driver is already signed by a real vendor.
10+
11+
The driver here is `FADA64.sys` - the **Broadcom Frame Access Driver**, a 2003-era NDIS protocol driver that shipped inside HP Smart Update Firmware DVD 9.30.
12+
13+
---
14+
15+
## File Metadata
16+
17+
| Field | Value |
18+
|---|---|
19+
| Filename | `FADA64.sys` |
20+
| Original Name | `FAD.SYS` |
21+
| MD5 | `ce9692c1cc7a575f71a899932d895603` |
22+
| SHA1 | `9d75631fec6c8296d70201b5e72c982d5a661ab6` |
23+
| SHA256 | `3e9b3937a792007d9d959b0749c2a61cf62fd56b12d2bf0857d761f89fa7e112` |
24+
| Size | 21,344 bytes |
25+
| Architecture | x64 |
26+
| Compiled | 2003-04-24 |
27+
| Company | Broadcom Corporation |
28+
| Description | Frame Access Driver |
29+
| Source | HP Smart Update Firmware DVD 9.30 - `hp\swpackages\cp013583.exe` |
30+
31+
---
32+
33+
## Certificate
34+
35+
| Field | Value |
36+
|---|---|
37+
| Subject | `CN=Broadcom Corporation, OU=Engineering Software` |
38+
| Issuer | VeriSign Class 3 Code Signing 2004 CA |
39+
| Thumbprint | `9C3FBCEB8F2A6CAC510DE477E2F65A38DD760BB9` |
40+
| Serial | `3232677944AFAE8837C58AF50DD11A8A` |
41+
| Valid | 2006-08-24 → 2009-10-23 (expired) |
42+
| Signature Type | Catalog signed - `cpqteam.cat` / `cpqtmmp.cat` |
43+
| Status | Expired - WHQL-era, accepted with DSE bypass or on older OS |
44+
45+
The driver is **catalog signed** rather than embedded-signed, meaning the signature lives in a separate `.cat` file rather than the driver PE itself. Both `.cat` files are included in the original HP package.
46+
47+
---
48+
49+
## Vulnerability Summary
50+
51+
The core vulnerability is an **arbitrary physical memory read primitive**.
52+
53+
The driver creates a device `\\.\FAD` with a **NULL DACL** - meaning any user on the system can open a handle to it without any privilege check. Through IOCTL `0x223EF4`, an unprivileged caller can supply an arbitrary physical address and a byte count. The driver maps that physical address into kernel virtual address space using `MmMapIoSpace` and copies the contents back to the caller's output buffer via `RtlMoveMemory`.
54+
55+
No `RequestorMode` check. No validation of the supplied physical address. No access control beyond the ability to open the device - which anyone can do.
56+
57+
**What this gives an attacker:** the ability to read arbitrary physical memory from user mode. Practically useful for kernel ASLR bypass (read known physical offsets to locate kernel structures), memory enumeration, or credential hunting without touching `LSASS` at the virtual-address level.
58+
59+
**What this does *not* give:** an arbitrary write. The write path was fully traced and ruled out - more on that below.
60+
61+
---
62+
63+
## Device Setup
64+
65+
The driver's `DriverEntry` at `0x17000` creates the device with no security descriptor (7-parameter `IoCreateDevice` call, `NULL` for the SD argument) and sets `DO_BUFFERED_IO`:
66+
67+
```c
68+
// DriverEntry @ 0x17000
69+
IoCreateDevice(DriverObject, 0x38u, L"\\Device\\FAD", 0x22u, 0, 0, &DeviceObject);
70+
DeviceObject->Flags |= 0x10u; // DO_BUFFERED_IO
71+
IoCreateSymbolicLink(L"\\DosDevices\\FAD", L"\\Device\\FAD");
72+
```
73+
74+
| Field | Value |
75+
|---|---|
76+
| Device Name | `\Device\FAD` |
77+
| Symlink | `\DosDevices\FAD` → `\\.\FAD` |
78+
| Device Type | `0x22` (`FILE_DEVICE_UNKNOWN`) |
79+
| DevExt Size | `0x38` |
80+
| Exclusive | No |
81+
| `DO_BUFFERED_IO` | Yes (`Flags |= 0x10`) |
82+
| Security Descriptor | **NULL** (world-accessible) |
83+
84+
`DO_BUFFERED_IO` means the I/O manager handles buffer copying between user and kernel - the driver works with a kernel-mode copy of the user's buffer rather than directly with the user pointer. This is relevant when sizing the input/output struct.
85+
86+
---
87+
88+
## IOCTL Surface
89+
90+
The dispatch handler lives at `sub_12750` (registered as `MajorFunction[14]`).
91+
92+
| IOCTL Code | Handler | Description |
93+
|---|---|---|
94+
| `0x223EE4` | `sub_11370` | Open NDIS adapter by name (string from user buffer) |
95+
| `0x223EF4` | `sub_12640` | **Physical memory read** - `MmMapIoSpace` + `RtlMoveMemory` |
96+
| `0x223EB4` | inline | Read 16-byte stats from per-open context |
97+
| `0x223EB8` | inline | Zero internal counters |
98+
| `0x223EE8` | inline | `NdisRequest` to open adapter |
99+
| `0x223EEC` | inline | Set config word |
100+
101+
All codes use `METHOD_BUFFERED` (bits 1–0 = `0`). There is **no `RequestorMode` check anywhere in the dispatch handler**.
102+
103+
---
104+
105+
## The Vulnerable IOCTL - `0x223EF4`
106+
107+
`sub_12640` is the physical memory read handler. The input buffer layout is simple: 8 bytes of physical address followed by 4 bytes of size. The driver iterates in 4KB pages, mapping each physical page into kernel VA space with `MmMapIoSpace(MmNonCached)`, copying its contents into the output buffer, then unmapping:
108+
109+
```c
110+
// Input/output buffer layout (METHOD_BUFFERED, minimum size 0x10):
111+
// [+0x00] PHYSICAL_ADDRESS PhysAddr - physical address to read (user-controlled)
112+
// [+0x08] UINT32 Size - byte count to read (user-controlled)
113+
114+
__int64 sub_12640(__int64 a1, PHYSICAL_ADDRESS *a2, unsigned int InputLen,
115+
unsigned int OutputLen, _DWORD *BytesOut)
116+
{
117+
PHYSICAL_ADDRESS i = *a2; // physical address - straight from user buffer
118+
UINT32 remaining = a2[1].LowPart; // size - straight from user buffer
119+
120+
for (; remaining > 0x1000; remaining -= 0x1000) {
121+
PVOID kva = MmMapIoSpace(i, 0x1000, MmNonCached);
122+
RtlMoveMemory(a2, kva, 0x1000); // copy physical → output buffer
123+
MmUnmapIoSpace(kva, 0x1000);
124+
a2 += 512; // advance output pointer 0x1000 bytes
125+
i.QuadPart += 0x1000;
126+
}
127+
if (remaining) {
128+
PVOID kva = MmMapIoSpace(i, remaining, MmNonCached);
129+
RtlMoveMemory(a2, kva, remaining);
130+
MmUnmapIoSpace(kva, remaining);
131+
}
132+
}
133+
```
134+
135+
The driver does validate that `InputLen >= 0x10` before entering `sub_12640`, so you need at least 16 bytes in your input buffer. Otherwise: no bounds checking, no address validation, no privilege check.
136+
137+
---
138+
139+
## Exploit Chain
140+
141+
```
142+
1. OpenFile \\.\FAD (no credentials required - NULL DACL, any user)
143+
144+
2. Build input struct:
145+
PHYSICAL_ADDRESS phys = TARGET_PA; // e.g. 0x0 (IVT), 0x400 (BDA), or wherever
146+
UINT32 size = 0x1000; // up to a full page per call
147+
148+
3. DeviceIoControl(
149+
hFAD,
150+
0x223EF4,
151+
&struct, sizeof(struct), // input buffer
152+
&struct, sizeof(struct), // output buffer (same, BUFFERED)
153+
&bytes,
154+
NULL
155+
)
156+
157+
4. struct now contains bytes read from physical address TARGET_PA
158+
```
159+
160+
Interesting targets at known physical offsets (no ASLR at the physical layer):
161+
162+
- `0x0` - Interrupt Vector Table (real-mode era, but still mapped)
163+
- `0x400` - BIOS Data Area
164+
- `0x1000` and up - early physical RAM, often contains kernel image remnants post-boot
165+
166+
For a kernel ASLR bypass: read physical memory to locate the kernel image, extract the base, then pivot to virtual-address operations via a second primitive.
167+
168+
---
169+
170+
## Ruling Out a Write Primitive
171+
172+
Before finalising the assessment I traced every write-capable import to its call site to confirm there is no write path back to physical or kernel memory.
173+
174+
**`MmMapLockedPagesSpecifyCache`** (`sub_11CD0`): maps a user-supplied MDL, then calls `RtlCopyMemory(mapped_va, ndis_packet_buffer, length)`. Data flows **from the network into the user's own buffer** - this is the packet receive path. No user data is written to any kernel or physical destination.
175+
176+
**`MmMapIoSpace`** (`sub_12640`): maps the user-supplied physical address, then `RtlMoveMemory(output_buffer, kva, size)`. Copy direction is **physical → output buffer**, read-only. No reverse path.
177+
178+
**`NdisRequest`** (IOCTL `0x223EE8` path): issues NDIS OID SET requests to the NIC. Writes only to **the network adapter's own hardware registers** via the NDIS miniport - not to kernel memory or physical memory outside the NIC. OID and data are user-supplied but constrained to what the NIC's miniport accepts.
179+
180+
**Conclusion:** every `RtlMoveMemory` / `RtlCopyMemory` call site copies either physical → output buffer or network packet → user MDL. **No arbitrary write primitive exists in this driver.**
181+
182+
---
183+
184+
## Impact
185+
186+
| Property | Assessment |
187+
|---|---|
188+
| Primitive | Arbitrary physical memory **read** |
189+
| Privilege required | **User** (any logged-in user, NULL DACL) |
190+
| Write primitive | None |
191+
| DSE bypass required | No - driver is legitimately signed (expired cert, but accepted on older OS or with catalog tricks) |
192+
| CVE | None assigned |
193+
| MITRE ATT&CK | T1068 - Exploitation for Privilege Escalation |
194+
| Practical use | Kernel ASLR bypass, physical memory enumeration, credential search |
195+

posts/hello-world.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
1-
Welcome to **// 0xDEADBEEF** a public notebook for someone just starting to take security research seriously as a hobby.
1+
Welcome to **// 0xDEADBEEF** - a public notebook for someone just starting to take security research seriously as a hobby.
22

33
## Who's writing this
44

5-
Not a professional. Not a researcher at least not yet. Just someone who got curious about why software fails in interesting ways, decided that *reading* about it wasn't enough, and started poking at things.
5+
Not a professional. Not a researcher - at least not yet. Just someone who got curious about why software fails in interesting ways, decided that *reading* about it wasn't enough, and started poking at things.
66

77
I have a day job that isn't this. Everything here happens in evenings, weekends, and stolen lunch breaks.
88

99
## Why a blog
1010

1111
Mostly to keep myself honest. Writing forces me to actually understand what I'm doing instead of vaguely following along with someone else's write-up. If I can't explain it, I haven't really learned it.
1212

13-
Also beginners doing this in public seems rare. Most security blogs come from people who are already very good at it. I'd rather show the messy middle than wait until I sound like an expert.
13+
Also - beginners doing this in public seems rare. Most security blogs come from people who are already very good at it. I'd rather show the messy middle than wait until I sound like an expert.
1414

1515
## What you'll find here
1616

1717
Posts here will fall into a few buckets:
1818

19-
- **Vulnerabilities I find** the bug, the path I took to find it, and what the impact actually looks like
20-
- **CVE disclosures** timelines, write-ups, and what the vendor did (or didn't do) with the report
21-
- **Security bugs and flaws** worth flagging even when they don't rise to a CVE misconfigurations, weak defaults, sharp edges
19+
- **Vulnerabilities I find** - the bug, the path I took to find it, and what the impact actually looks like
20+
- **CVE disclosures** - timelines, write-ups, and what the vendor did (or didn't do) with the report
21+
- **Security bugs and flaws** worth flagging even when they don't rise to a CVE - misconfigurations, weak defaults, sharp edges
2222
- **Proof-of-concept code** when it's safe and useful to share
2323

2424
Expect mistakes. Corrections welcome.

posts/index.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,20 @@
11
[
2+
{
3+
"slug": "fada64-physical-memory-read",
4+
"title": "FADA64.sys - Arbitrary Physical Memory Read via Unauthenticated IOCTL",
5+
"date": "2026-05-03",
6+
"summary": "Broadcom Frame Access Driver exposes an arbitrary physical memory read to any user via a NULL DACL device and unauthenticated IOCTL. Confirmed read-only, no write primitive."
7+
},
8+
{
9+
"slug": "reporting-vulnerable-drivers",
10+
"title": "How to Report a Vulnerable Driver and Get a CVE",
11+
"date": "2026-05-02",
12+
"summary": "A plain-English walkthrough for beginners — who to report to, how coordinated disclosure works, and how to request a CVE number from MITRE."
13+
},
214
{
315
"slug": "hello-world",
416
"title": "Hello World",
5-
"date": "2026-05-06",
17+
"date": "2026-05-01",
618
"summary": "First post — who I am, why this blog exists, and what to expect."
719
}
820
]

0 commit comments

Comments
 (0)