base64 File Transfer

base64 file transfer moves a small file through a text-only channel: encode the bytes into printable ASCII on one side, paste the blob across whatever channel you have, and decode it back into the exact bytes on the other. Reach for it when every real transfer is blocked (no outbound HTTP, SMB, or SCP) and all you can do is print and read text in a restricted or non-interactive shell.

Mental model: base64 -w0 FILE on the source, base64 -d > FILE on the destination, then sha256sum both ends.

New to base64 transfer? Core concepts
  • Encode: turn raw bytes into printable text, base64 -w0 FILE
  • Decode: turn that text back into the exact bytes, base64 -d
  • The alphabet: 64 printable characters A-Z a-z 0-9 + /, = padding; every 3 bytes map to 4 characters
  • Blob: the wall of encoded text you copy; it ends in =, ==, or nothing
  • Wrap: line breaks every N columns; -w0 disables wrapping so the whole thing is one selectable line
  • Direction: encode on the SOURCE, decode on the DESTINATION; the file’s origin decides which side is which
  • Inflation: the encoded output is roughly 33 percent larger than the input, which is why this is a small-file trick
  • Checksum: a hash on both ends (sha256sum) that proves the bytes arrived byte-for-byte intact
When to reach for base64 transfer (and when not)

Reach for it when egress is filtered (no outbound HTTP, FTP, SMB, or SCP), the shell is non-interactive or so locked down that “print text” and “read text” are the only reliable verbs, the file is small (a key, a config, one script, a binary under a few hundred KB), or the channel is serial or console-style where only ASCII survives.

Reach for something else the moment you regain a real network path or need throughput or many files: http-file-transfer, scp-ssh-transfer, smb-file-transfer, and netcat-file-transfer all beat pasting. Anything past a few hundred KB, or a channel that silently rewraps or truncates text, makes base64-by-paste miserable.

Install, update, verify#

sudo apt install -y coreutils   # Debian / Ubuntu / Kali (base64 lives in coreutils)
sudo dnf install -y coreutils   # Fedora / RHEL

base64 --version                # GNU coreutils version (confirms GNU, not BSD, flags)
command -v base64               # confirm it is on PATH
echo test | base64 | base64 -d  # self-test: should print "test"

Windows has no base64.exe; use certutil (present on every host) or PowerShell [Convert] (both shown below). macOS and BSD ship a different base64 where -w0 is not valid, so a Kali command can fail on a Mac.

Flags you’ll actually use#

base64 itself has only three flags; the rest of this table is the companion verbs that make up the technique. Skim these first; the cheat sheet below is these flags combined.

FlagDoesFlagDoes
-w0Encode to one unwrapped line (GNU)certutil -encodeWindows: encode a file to a blob
-dDecode base64 back to raw bytescertutil -decodeWindows: decode a saved blob
-iIgnore non-alphabet garbage on decode[Convert]::ToBase64StringPowerShell: encode bytes to text
fold -w 76Re-wrap a blob to fixed width[Convert]::FromBase64StringPowerShell: decode text to bytes
split -l NBreak a blob into N-line chunksWriteAllBytes / ReadAllBytesPowerShell: write / read raw bytes
sha256sumHash to verify the transferGet-FileHash -Algorithm SHA256Windows: hash to verify

Cheat sheet#

Each block builds up one flag at a time, so you can stop at the line that does the job.

# Encode on the source: stop at the line you need
base64 -w0 FILE                          # 1. one unwrapped line, ready to paste
base64 -w0 FILE > out.b64                 # 2. + stash the blob in a file
base64 -w0 FILE > out.b64 ; cat out.b64   # 3. + print it back to copy
sha256sum FILE                            # record the source hash BEFORE transfer

# Decode on the destination (Linux)
echo 'PASTE_BASE64' | base64 -d > /tmp/FILE   # quick inline decode
base64 -d > /tmp/FILE <<'EOF'                 # heredoc: safe for a long multi-line blob
PASTE_BASE64
EOF
base64 -d saved.b64 > /tmp/FILE               # decode a blob you saved to disk
base64 -di dirty.b64 > /tmp/FILE              # ignore stray prompt / whitespace garbage
chmod +x /tmp/FILE                            # only if it must run
chmod 600 /tmp/id_key                         # a private key needs 600 or ssh refuses it

# Verify (both ends must match)
sha256sum /tmp/FILE                                    # Linux hash
Get-FileHash C:\Windows\Temp\FILE -Algorithm SHA256   # Windows hash
certutil -hashfile C:\Windows\Temp\FILE SHA256        # Windows hash, no PowerShell

# Windows encode (no base64.exe)
certutil -encode C:\path\FILE C:\path\out.b64                        # certutil, every host
[Convert]::ToBase64String([IO.File]::ReadAllBytes("C:\path\FILE"))   # PowerShell, prints blob

# Windows decode
[IO.File]::WriteAllBytes("C:\Windows\Temp\FILE", [Convert]::FromBase64String("PASTE_BASE64"))
certutil -decode C:\Windows\Temp\out.b64 C:\Windows\Temp\FILE        # decode a saved blob

# Blob too long for one paste: chunk it
base64 -w0 FILE | fold -w 76 > out.b64 ; split -l 1000 out.b64 part_   # source: split
cat part_* > all.b64 && base64 -d all.b64 > /tmp/FILE                  # dest: rejoin, decode

# Portable / no-GNU fallbacks
base64 FILE | tr -d '\n'                    # one-line blob without GNU -w0
openssl base64 -A -in FILE                  # openssl encoder, single line
openssl base64 -d -in blob.b64 -out FILE    # openssl decode

Command breakdowns#

Each block explains one situation, the exact command, and what to expect back.

Encode a file to a single pasteable line (Linux)#

base64 -w0 files/payload.bin

-w0 disables line wrapping, so the whole blob is one line you can triple-click and copy cleanly. It ends in =, ==, or nothing. Redirect with > out.b64 if you would rather not re-run it.

Decode a pasted blob into a file (Linux)#

echo 'PASTE_BASE64_HERE' | base64 -d > /tmp/payload.bin

Nothing prints on success; the bytes land in the file. Use /tmp or /dev/shm (world-writable, and /dev/shm is memory-backed) so a low-privilege shell can write there.

Decode a long blob with a heredoc#

base64 -d > /tmp/payload.bin <<'EOF'
PASTE_BASE64_HERE
EOF

The quoted 'EOF' treats every line as literal text, so +, /, and newlines in the blob are never interpreted by the shell. Cleaner than a giant echo for a multi-line paste.

Decode a blob you saved to a file#

base64 -d payload.b64 > /tmp/payload.bin

When you dropped the blob on disk instead of pasting inline, decode the file directly. base64 ignores embedded newlines, so a wrapped file still decodes fine.

Strip stray characters from a dirty paste#

base64 -di dirty.b64 > /tmp/payload.bin

-i (ignore-garbage) drops non-alphabet bytes, a copied prompt fragment or a stray space, instead of erroring with invalid input. Reach for it when a clean re-copy is not practical.

Record and verify a checksum across the transfer#

sha256sum files/payload.bin      # source, BEFORE
sha256sum /tmp/payload.bin       # destination, AFTER

The two hashes must match exactly. A truncated paste, a dropped =, or a wrapped line yields a file that is almost right; the checksum is the only thing that catches it.

Push a key or binary to the target#

# attacker: base64 -w0 files/id_lab_key > encoded/key.b64 ; cat encoded/key.b64
echo 'PASTE_BASE64_HERE' | base64 -d > /tmp/id_lab_key && chmod 600 /tmp/id_lab_key

Encode on the source (attacker), decode on the destination (target). A private key needs chmod 600 or ssh refuses to use it.

Pull a file off the target#

# target: base64 -w0 /etc/lab/service.conf
echo 'PASTE_BASE64_HERE' | base64 -d > files/service.conf

Direction flips: encode ON the target, decode on your box. The file’s origin always decides which side encodes.

Encode on Windows without base64.exe#

[Convert]::ToBase64String([IO.File]::ReadAllBytes("C:\Windows\Temp\notes.txt"))

Windows has no base64 binary. ReadAllBytes reads the raw file, ToBase64String prints the blob to copy. Use certutil -encode FILE out.b64 when PowerShell is locked down.

Decode a blob on a Windows target (PowerShell)#

[IO.File]::WriteAllBytes("C:\Windows\Temp\payload.bin", [Convert]::FromBase64String("PASTE_BASE64_HERE"))

FromBase64String rebuilds the raw bytes; WriteAllBytes writes them verbatim with no encoding or CRLF translation. Never use > or Out-File for binary on Windows, it corrupts the bytes.

Decode on Windows without PowerShell (certutil)#

certutil -decode C:\Windows\Temp\payload.b64 C:\Windows\Temp\payload.bin

certutil -decode needs a file, not pasted text, so save the blob first. It tolerates a -----BEGIN...----- header and footer, which plain base64 tools do not.

Split a blob too long to paste#

base64 -w0 payload.bin | fold -w 76 > out.b64 ; split -l 1000 out.b64 part_
# target: cat part_* > all.b64 && base64 -d all.b64 > /tmp/payload.bin

fold re-wraps to a fixed width, split cuts it into part_aa, part_ab, and so on. Paste each part, cat them in order, then decode the whole file at once.

Sanity-check your base64 tooling#

echo "lorem ipsum" | base64 | base64 -d

Encodes then immediately decodes. If it round-trips to lorem ipsum, your binary is healthy, which is worth confirming before you blame a real transfer on the tool.

Get a portable one-line blob off macOS or BSD#

base64 FILE | tr -d '\n'      # works anywhere, no GNU -w0 needed
openssl base64 -A -in FILE    # openssl encoder, single line

-w0 is GNU-only and errors on macOS and BSD base64. Piping through tr -d '\n', or using openssl base64 -A, gets you the same single-line blob portably.

Reading the output#

  • A successful decode prints nothing. The file just appears on disk; silence is the expected result, so always follow with a hash check rather than trusting it.
  • The blob ends in =, ==, or nothing. That trailing = is padding and it is part of the data, so select it too.
  • certutil prints Input Length = ... Output Length = ... CertUtil: -decode command completed successfully. That is the healthy path for -encode, -decode, and -hashfile.
  • base64 -w0 output is one continuous line. Any newlines you see mean wrapping crept in: the wrong flag, or a non-GNU base64.

Troubleshooting#

ProblemCauseFix
base64: invalid inputStray non-alphabet chars (prompt text, smart quote)Re-copy cleanly, or decode with -i
Decoded file too smallPaste truncated mid-blobEncode with -w0, copy the whole line, or chunk it
Incorrect length (PowerShell)Lost the trailing = paddingRe-copy including the = / == at the end
Checksum mismatchWrapping or whitespace corruptionRe-encode with -w0, paste clean, redo
certutil Cannot decode objectBlob has stray lines / is not cleanSave raw base64 to a file, retry -decode
Binary will not run on WindowsWritten as text, CRLF addedUse WriteAllBytes, never > or Out-File
-w0: invalid optionmacOS / BSD base64, no -w flagDrop -w0; use tr -d newlines or openssl base64 -A
Key rejected by sshWrong permissions after decodechmod 600 the decoded key

Gotchas#

  • certutil -encode wraps the blob in -----BEGIN CERTIFICATE----- header and footer lines. certutil -decode tolerates them, but Linux base64 -d chokes on them, so strip the header and footer before decoding cross-platform.
  • base64 ignores whitespace on decode but not other garbage. Embedded newlines and spaces are fine, so a wrapped blob still decodes; a copied $ prompt or a smart quote is not, and it needs -i or a clean re-copy.
  • Encoding is inflation, not compression. The blob is roughly 33 percent larger than the file, so a “small” 300 KB binary becomes about 400 KB of text; past a few hundred KB the paste, not the tool, is the bottleneck.
  • -w0 is a GNU coreutils flag. macOS, BSD, and busybox base64 reject it, so a command that works on Kali fails elsewhere; prefer tr -d '\n' or openssl base64 -A when the platform is unknown.
  • Redirecting binary through > on Windows corrupts it. PowerShell > and Out-File re-encode as UTF-16 and add CRLF; only WriteAllBytes (or certutil -decode) writes the exact bytes.
  • The blob is silent on the wire but loud on the host. A command line with thousands of base64 characters, plus certutil -decode as a living-off-the-land binary, is high-signal for auditd and Sysmon even when no network sensor sees the transfer.

Pairs with#

file-transfer is the overview of every method, so you know when base64 is the right fallback and when it is not. The moment you regain a network path, http-file-transfer, scp-ssh-transfer, and smb-file-transfer move larger files faster, while netcat-file-transfer covers a raw TCP channel when higher-level protocols are blocked. On Windows, evil-winrm has built-in upload and download, so you rarely need base64 once you hold a WinRM session. sha256sum and Get-FileHash are the non-negotiable companions: always hash both ends.

Find us elsewhere

Merch, stickers, and moreSupport the work at the Solvere Labs shop