# Second bite on GitLab, and some interesting Ruby functions/features

GitLab wiki attachment upload trusted a duck-typed file hash, letting attackers pass file[tempfile]=/etc/passwd to read arbitrary files and, via pipe-prefixed paths, achieve RCE.

- Payout: $10,000
- Severity: critical
- Program: GitLab
- Credited: Nyangawa
- Disclosed: 2018-12-12
- CVE: CVE-2018-18649
- Write-up quality: 8/10
- Original: https://blog.nyangawa.me/security/CVE-2018-18649-Gitlab-RCE/
- Library page: https://bugbountylibrary.com/r/blog%3Ablog-nyangawa-me-security-cve-2018-18649-gitlab-rce
- Tags: domain:web impact:data-leak impact:rce kind:bounty platform:blog sev:critical tech:ruby technique:chained technique:source-review vuln:file-upload vuln:path-traversal vuln:rce

**Target:** Grape API endpoint POST :id/wikis/attachments
**Root cause:** Grape's type:File validation only checks that the param hash contains a :tempfile key, so a plain form field file[tempfile] passes validation; the value is then passed to File.read, which inherits IO.read and executes a subprocess when the path starts with '|'.
**Steps:**
1. Send `curl -d "file[tempfile]=/etc/passwd&file[filename]=123" http://host:port/:id/wikis/attachments` to read arbitrary files
2. Send `file[tempfile]=|id` to trigger command execution via File.read's inherited IO.read pipe behavior
3. Observe the command output returned in the attachment content
**How found:** Code review of GitLab's Grape API params and commit_params; noticed the type:File validator only checks for a :tempfile key (duck-typing comment), then recalled Ruby's Kernel#open/IO.read pipe behavior and that File inherits IO methods, connecting the two.
**Exploitation:** Attacker with API access to a project's wiki attachment endpoint submits a crafted form where file[tempfile] is an arbitrary path; the server reads it via File.read and stores the content as a wiki attachment, which the attacker then retrieves. Prefixing the path with '|' turns the read into subprocess execution, yielding RCE as the GitLab service user.
**Escalation:** Arbitrary file read escalated to remote command execution by abusing the pipe-prefix behavior of File.read inherited from IO.read.
**Insight:** When a framework validates a file upload by checking for a :tempfile key rather than the actual Rack upload object, try supplying file[tempfile] as a plain form field; then check whether the downstream sink is File.read/IO.read, since a leading '|' in the path spawns a subprocess.
