Skip to content

Commit 1f228ec

Browse files
committed
adds binary analysis flake and documentation
1 parent afd833c commit 1f228ec

2 files changed

Lines changed: 311 additions & 0 deletions

File tree

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
description = "Environment todo binary analysis on packages";
3+
inputs = {
4+
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
5+
flake-utils.url = "github:numtide/flake-utils";
6+
};
7+
8+
outputs = { self, nixpkgs, flake-utils}:
9+
flake-utils.lib.eachDefaultSystem (system:
10+
let
11+
pkgs = nixpkgs.legacyPackages.${system};
12+
13+
in {
14+
devShells.default = pkgs.mkShell {
15+
buildInputs = [
16+
pkgs.curl
17+
pkgs.gnutar
18+
pkgs.binutils
19+
pkgs.dpkg
20+
];
21+
};
22+
});
23+
}
Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
---
2+
title: Investigate CVE Findings in Debian Packages
3+
description: A repeatable, evidence-based playbook for determining whether a CVE in a Debian package is a false positive or requires an accept-risk justification using binary analysis.
4+
---
5+
6+
# How to Investigate CVE Findings in Debian Packages (False Positive vs. Accept Risk)
7+
8+
This playbook describes a repeatable, evidence-based process for evaluating CVE findings in Debian packages — specifically for determining whether a vulnerability is a **False Positive** or requires an **Accept Risk** justification.
9+
10+
It applies to any context where Debian packages are present in your workload: container images, VMs, bare-metal servers, or CI pipelines.
11+
12+
---
13+
14+
## Background
15+
16+
Vulnerability scanners report CVEs based on the presence of a package. However, a package being present does not mean the vulnerable code path is actually reachable. This process uses binary analysis to prove whether the vulnerable function is ever called.
17+
18+
**Two possible outcomes:**
19+
20+
| Outcome | Meaning |
21+
|---|---|
22+
| **False Positive** | The vulnerable function is never called in this workload. The code path is structurally unreachable. |
23+
| **Accept Risk** | The vulnerable function could theoretically be called, but the risk is acceptable (e.g. deprecated feature, DoS-only, Debian classifies as minor). |
24+
25+
---
26+
27+
## Required Tools
28+
29+
- `curl` — to download `.deb` packages
30+
- `ar` + `tar` — to extract `.deb` packages
31+
- `nm` / `objdump` — to inspect binary symbols (part of `binutils`)
32+
- `dpkg` — to inspect package metadata
33+
- `python3` — to parse `config.amd64.json`
34+
35+
### Reproducible Environment with Nix
36+
37+
All required tools are provided as a Nix flake for a fully reproducible analysis environment. This is the recommended way to run the analysis, especially on macOS or in CI.
38+
39+
[Download flake.nix](/labs/binary-analysis/flake.nix)
40+
41+
```nix
42+
# flake.nix — provides: curl, tar, binutils (nm, objdump, ar), dpkg
43+
```
44+
45+
**Usage:**
46+
47+
```bash
48+
# Enter the dev shell (installs all tools automatically)
49+
nix develop
50+
51+
# Or run a single command without entering the shell
52+
nix develop --command nm -D <binary>
53+
```
54+
55+
> **Note:** The flake targets `x86_64-linux` for Debian binary analysis. On macOS (Apple Silicon), use a remote builder or a Linux VM/container. The `nm -D` analysis must be run against the amd64 Debian binaries, not your host's native architecture.
56+
57+
---
58+
59+
## Step-by-Step Process
60+
61+
### Step 1 — Understand the Dependency Path
62+
63+
Note the full path shown in your scanner, e.g.:
64+
65+
```
66+
Your application → debian/wget → debian/libgnutls30t64 → debian/libtasn1-6
67+
```
68+
69+
This tells you:
70+
- Which package is **vulnerable** (the last node)
71+
- Which package **pulls it in** (the intermediate node)
72+
- You need to prove whether the intermediate package actually calls the vulnerable function
73+
74+
---
75+
76+
### Step 2 — Research the CVE
77+
78+
Before doing any binary analysis, understand what you're looking for.
79+
80+
**Search for:**
81+
```
82+
CVE-XXXX-XXXXX <library-name> vulnerable function details
83+
CVE-XXXX-XXXXX debian security tracker
84+
```
85+
86+
**Key questions to answer:**
87+
1. **What is the exact vulnerable function?** (e.g. `asn1_expand_octet_string`)
88+
2. **What triggers the vulnerability?** Does it require a specific call pattern or input?
89+
3. **How does Debian classify it?**
90+
- Check: `https://security-tracker.debian.org/tracker/CVE-XXXX-XXXXX`
91+
- `<no-dsa>` = Minor issue, Debian does not plan a security update → additional argument for Accept Risk
92+
93+
---
94+
95+
### Step 3 — Get the Package URLs
96+
97+
You need the **exact version** of the package that is present in your container image. Debian's [snapshot archive](https://snapshot.debian.org) preserves every historical package version and is the canonical source for downloading them.
98+
99+
#### 3a — Find the exact version
100+
101+
The version is usually reported directly by your scanner. If you need to verify it on the system itself:
102+
103+
```bash
104+
dpkg -l | grep <package-name>
105+
# Example output: ii libgnutls30t64 3.8.3-1.1 amd64 GNU TLS library ...
106+
```
107+
108+
#### 3b — Look up the package on Debian Snapshot
109+
110+
Go to **https://snapshot.debian.org/binary/\<package-name\>/** and find the exact version. The direct download URL follows this pattern:
111+
112+
```
113+
https://snapshot.debian.org/archive/debian/<timestamp>/pool/<section>/<letter>/<package>/<package>_<version>_amd64.deb
114+
```
115+
116+
**Example** for `libgnutls30t64` version `3.8.3-1.1`:
117+
```
118+
https://snapshot.debian.org/archive/debian/20240831T204032Z/pool/main/g/gnutls28/libgnutls30t64_3.8.6-2_amd64.deb
119+
```
120+
121+
> **Tip:** If you know the package version but not the timestamp, use the binary search page at `https://snapshot.debian.org/binary/<package-name>/` — it lists all available versions with direct download links.
122+
123+
124+
---
125+
126+
### Step 4 — Download and Extract the .deb Packages (Run in `nix develop`)
127+
128+
```bash
129+
# Download
130+
curl -L "<url-from-config>" -o <package>.deb
131+
132+
# Extract
133+
mkdir <package>-extracted && cd <package>-extracted
134+
ar x ../<package>.deb
135+
tar xf data.tar.xz
136+
cd ..
137+
```
138+
139+
Repeat for each relevant package in the dependency chain.
140+
141+
---
142+
143+
### Step 5 — Binary Analysis
144+
145+
This is the core of the investigation. Use `nm -D` to inspect which external symbols a binary references at runtime.
146+
147+
> **How to read `nm -D` output:**
148+
> - `U` = Undefined = the binary **calls** this function from an external library
149+
> - If a function does **not appear** in the output → it is **never called**
150+
151+
#### 5a — Is the vulnerable library linked at all?
152+
153+
```bash
154+
nm -D <binary> | grep -i "<library-keyword>"
155+
# e.g. for libexpat:
156+
nm -D usr/bin/git | grep -i "xml"
157+
```
158+
159+
No output → the binary is not linked against the library → **strong False Positive signal**
160+
161+
#### 5b — Is the specific vulnerable function called?
162+
163+
```bash
164+
nm -D <library>.so* | grep -i "<vulnerable_function>"
165+
# e.g.:
166+
nm -D usr/lib/x86_64-linux-gnu/libgnutls.so.30* | grep -i "expand_octet"
167+
```
168+
169+
No output → the function is never called by this library → **False Positive**
170+
171+
#### 5c — For packages with multiple binaries (e.g. git-core)
172+
173+
```bash
174+
for f in usr/lib/git-core/*; do
175+
result=$(nm -D "$f" 2>/dev/null | grep -i "<keyword>")
176+
if [ -n "$result" ]; then
177+
echo "=== $f ==="
178+
echo "$result"
179+
fi
180+
done
181+
```
182+
183+
This reveals exactly which sub-binary uses the vulnerable library.
184+
185+
---
186+
187+
### Step 6 — Make the Decision
188+
189+
| Situation | Decision |
190+
|---|---|
191+
| Vulnerable function does not appear in any binary's symbol table | **False Positive** → "Vulnerable Code Not In Execute Path" |
192+
| Vulnerable function is only used by a legacy/deprecated sub-binary that is never invoked | **False Positive** → with justification |
193+
| Vulnerable function is called, but the specific trigger condition is never met | **False Positive** → with detailed justification |
194+
| Vulnerable function is reachable, risk is low (DoS-only, `<no-dsa>`, mitigated by other controls) | **Accept Risk** |
195+
196+
---
197+
198+
### Step 7 — Write the VEX Comment (English)
199+
200+
A good VEX comment always contains these three parts:
201+
202+
1. **What is the vulnerability** — the vulnerable function and what it does
203+
2. **Binary analysis result** — what `nm -D` showed (or didn't show) and why that proves the code path is unreachable
204+
3. **Supporting evidence** (optional but recommended) — Debian's `<no-dsa>` classification, deprecated feature status, etc.
205+
206+
**False Positive template:**
207+
```
208+
CVE-XXXX-XXXXX is assessed as a false positive.
209+
210+
The vulnerability is a <type> in the <function_name> function of <library>.
211+
According to the official security advisory, exploitation requires that the
212+
target program explicitly calls <function_name>.
213+
214+
Binary analysis of the extracted Debian package (<package_version>.deb) via
215+
nm -D confirms that <intermediate_library> does not reference or call
216+
<function_name> at all. Since <top_level_package> accesses <library>
217+
exclusively through <intermediate_library>, and <intermediate_library> never
218+
invokes the vulnerable function, the vulnerable code path is never reached.
219+
220+
[Optional: Additionally, Debian's security team has classified this CVE as a
221+
minor issue (<no-dsa>) for Trixie and does not plan a dedicated security update.]
222+
223+
This finding is therefore classified as a false positive.
224+
```
225+
226+
**Accept Risk template:**
227+
```
228+
CVE-XXXX-XXXXX is assessed as accepted risk.
229+
230+
The vulnerability affects <library> version <version>, which is present as a
231+
[direct/transitive] dependency of <package>. The vulnerable function
232+
<function_name> is called by <binary>, which implements <feature>.
233+
234+
Risk is considered acceptable for the following reasons:
235+
1. <reason 1 — e.g. feature is deprecated/not used in this workload>
236+
2. <reason 2 — e.g. vulnerability class is DoS only, no RCE>
237+
3. <reason 3 — e.g. Debian classifies as <no-dsa>, no patch planned>
238+
239+
We will re-evaluate once Debian ships a patched version.
240+
```
241+
242+
---
243+
244+
## Real Examples
245+
246+
### Example 1 — False Positive: CVE-2025-59375 (libexpat via git)
247+
248+
**Path:** `Your application → debian/git → debian/libexpat1`
249+
250+
**CVE:** Stack overflow in libexpat via recursive XML entity expansion (DoS). Fixed in 2.7.2.
251+
252+
**Analysis:**
253+
```bash
254+
nm -D usr/bin/git | grep -i xml
255+
# → no output (git binary not linked against libexpat)
256+
257+
for f in usr/lib/git-core/*; do
258+
result=$(nm -D "$f" 2>/dev/null | grep -i xml)
259+
if [ -n "$result" ]; then echo "=== $f ==="; echo "$result"; fi
260+
done
261+
# → only git-http-push uses XML symbols
262+
```
263+
264+
**Decision:** False Positive — libexpat is only used by `git-http-push`, a deprecated legacy binary implementing WebDAV-based HTTP push, which is never invoked in this workload.
265+
266+
---
267+
268+
### Example 2 — False Positive: CVE-2025-13151 (libtasn1 via libgnutls via wget)
269+
270+
**Path:** `Your application → debian/wget → debian/libgnutls30t64 → debian/libtasn1-6`
271+
272+
**CVE:** Stack-based buffer overflow in `asn1_expand_octet_string`. Exploitation requires the target program to **explicitly call** this function.
273+
274+
**Analysis:**
275+
```bash
276+
nm -D usr/lib/x86_64-linux-gnu/libgnutls.so.30* | grep -i "expand_octet"
277+
# → no output
278+
```
279+
280+
**Decision:** False Positive — libgnutls never calls `asn1_expand_octet_string`. Since wget accesses libtasn1 exclusively through libgnutls, the vulnerable function is never reached.
281+
282+
---
283+
284+
## Notes
285+
286+
- `nm -D` only works on **dynamically linked** binaries. Statically linked binaries require `nm` without `-D`
287+
- The absence of a symbol in `nm -D` output is **definitive proof** that the function is never called at runtime via dynamic linking
288+
- Debian's `<no-dsa>` classification is supporting evidence, not proof by itself — always combine with binary analysis

0 commit comments

Comments
 (0)