# On insecure zip handling, Rubyzip and Metasploit RCE (CVE-2019-5624)

Rubyzip Entry#extract skips path validation when a destination path is passed, letting a malicious ZIP write arbitrary files; in Metasploit's workspace import this yielded root RCE via cron.

- Payout: Research
- Severity: high
- Program: Metasploit Framework
- Credited: Doyensec
- Disclosed: 2019-04-23
- CVE: CVE-2019-5624
- Write-up quality: 8/10
- Original: https://blog.doyensec.com/2019/04/24/rubyzip-bug.html
- Library page: https://bugbountylibrary.com/r/blog%3Ablog-doyensec-com-2019-04-24-rubyzip-bug-html
- Tags: domain:web3 impact:integrity impact:rce kind:research platform:research sev:high tech:ruby technique:chained technique:source-review vuln:path-traversal vuln:rce

**Target:** Rubyzip gem Entry#extract and Metasploit ZIP workspace import (zip.rb)
**Root cause:** Entry#extract only runs name_safe? when dest_path is nil; when a caller passes a destination path, no validation occurs, so entry names containing ../ escape the intended directory. Pathname concatenation also silently resolves ../ before the check.
**Steps:**
1. Generate malicious zip: `python evilarc.py exploit --os unix -p etc/cron.d/` containing a cron file with reverse shell payload
2. Embed a valid MSF workspace XML so Metasploit accepts the archive
3. In MSF Web UI: create Project, Import > From file, select evil zip, click Import
4. zip.rb line 157 does `target = File.join(@import_filedata[:zip_tmp], e.name); data.extract(e,target)` — traversal writes to /etc/cron.d/ as root
5. Start listeners on 4444/4445; cron executes payload and returns reverse shell
**How found:** Auditing a RoR app using Rubyzip with symlinks disabled, focused on path traversal. Read PR #376 code change and noticed name_safe? only runs when dest_path is nil, with a comment pushing safety onto the caller. Verified with evilarc PoC, then brainstormed popular Rubyzip consumers and spotted Metasploit on a VM, grepping for extract usage in zip.rb.
**Exploitation:** Attacker with authenticated access to Metasploit Web UI imports a crafted ZIP workspace. The traversal entry name escapes the temp dir and writes a cron job to /etc/cron.d/ since extraction runs as root, yielding a reverse shell on next cron tick.
**Escalation:** Chained arbitrary file write with cron.d drop for root RCE; for RoR apps, chained DoS + path disclosure to force reboot and load a malicious /config/initializers/*.rb.
**Insight:** When a library adds a safety check, read the guard's preconditions — here the check is bypassed whenever the caller supplies a destination path. Also note Pathname#+ resolves ../ before concatenation, so 'safe-looking' joined paths still traverse. Hunt popular apps that call such APIs with user-controlled dest paths.
