{"id":28392296,"url":"https://github.com/secforce/droidground","last_synced_at":"2026-02-17T14:11:40.400Z","repository":{"id":296249905,"uuid":"973730580","full_name":"SECFORCE/droidground","owner":"SECFORCE","description":"A flexible playground for Android CTF challenges.","archived":false,"fork":false,"pushed_at":"2026-02-07T12:50:39.000Z","size":2186,"stargazers_count":107,"open_issues_count":0,"forks_count":7,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-07T21:51:09.327Z","etag":null,"topics":["android","ctf","cybersecurity","playground"],"latest_commit_sha":null,"homepage":"https://droidground.com","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SECFORCE.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-04-27T16:08:51.000Z","updated_at":"2026-02-07T12:50:41.000Z","dependencies_parsed_at":"2025-05-29T18:44:03.533Z","dependency_job_id":"2a1bc356-3ede-4479-b9ab-a8770574682b","html_url":"https://github.com/SECFORCE/droidground","commit_stats":null,"previous_names":["secforce/droidground"],"tags_count":19,"template":false,"template_full_name":null,"purl":"pkg:github/SECFORCE/droidground","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SECFORCE%2Fdroidground","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SECFORCE%2Fdroidground/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SECFORCE%2Fdroidground/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SECFORCE%2Fdroidground/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SECFORCE","download_url":"https://codeload.github.com/SECFORCE/droidground/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SECFORCE%2Fdroidground/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29546852,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-17T13:00:00.370Z","status":"ssl_error","status_checked_at":"2026-02-17T12:57:14.072Z","response_time":100,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","ctf","cybersecurity","playground"],"created_at":"2025-05-31T12:12:23.258Z","updated_at":"2026-02-17T14:11:40.384Z","avatar_url":"https://github.com/SECFORCE.png","language":"TypeScript","readme":"\u003ch1 align=\"center\"\u003e\n  \u003cbr\u003e\n    \u003cimg src=\"./logo.png\" alt= \"droidground\" width=\"200px\"\u003e\n\u003c/h1\u003e\n\u003cp align=\"center\"\u003e\n    \u003cb\u003eDroidGround\u003c/b\u003e\n\u003cp\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/SECFORCE/droidground/blob/main/README.md\"\u003e\u003cimg src=\"https://img.shields.io/badge/Documentation-complete-green.svg?style=flat\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/SECFORCE/droidground/blob/main/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/License-GPL3-blue.svg\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://blackhat.com/eu-25/arsenal/schedule/index.html#droidground-a-flexible-playground-for-android-ctf-challenges-47803\"\u003e\u003cimg src=\"./docs/blackhat-2025.svg\"\u003e\u003c/a\u003e\n    \u003cbr /\u003e\n    \u003ca href=\"https://droidground.com\" target=\"_blank\"\u003eWebsite\u003c/a\u003e |\n    \u003ca href=\"https://droidground.com/demo\" target=\"_blank\"\u003eDemo\u003c/a\u003e\n\u003c/p\u003e\n\nIn traditional **CTF challenges**, it's common to hide flags in files on a system, requiring attackers to exploit vulnerabilities to retrieve them. However, in the Android world, this approach doesn't work well. APK files are easily downloadable and reversible, so **placing a flag on the device usually makes it trivial** to extract using static analysis or emulator tricks. This severely limits the ability to create realistic, runtime-focused challenges.\n\n_DroidGround_ is designed to solve this problem.\n\nIt is a custom-built platform for hosting Android mobile hacking challenges in a **controlled** and **realistic** environment, where **attackers are constrained just enough** to require solving challenges in the intended way.\n\nImportantly, participants may be **jailed inside the app environment**. The modularity of the tool allows to set if the user can or cannot spawn a shell, read arbitrary files, or sideload tools. Everything can be setup so that the only way to retrieve the flag is through understanding and exploiting the app itself.\n\n## 📋 Table of Contents\n\n- [🧭 Overview](#-overview)\n- [✨ Features](#-features)\n- [📸 Screenshots](#-screenshots)\n- [⚙️ Configuration](#️-configuration)\n- [🧩 Use Cases](#-use-cases)\n- [⚙️ Usage](#️-usage)\n- [💡 Tips](#-tips)\n- [🛠 Development](#-development)\n- [🤝 Contributing](#-contributing)\n- [📚 Credits](#-credits)\n- [🪪 License](#-license)\n\n## 🧭 Overview\n\n_DroidGround_ enables a wide variety of _Android_ challenges that are otherwise hard to implement in traditional CTF setups. For example, in a remote code execution (RCE) challenge, players might receive an APK for local analysis. After discovering a vulnerability, they can develop a Frida script and run it through DroidGround on the real target device to extract the flag from internal storage. Other challenge types can involve hidden activities, custom broadcast intents, service exploitation, or dynamic analysis using preloaded tools.\n\nWith real-time device streaming, fine-grained control over features, Frida integration, and customizable setup and reset scripts, DroidGround empowers CTF organizers to build secure, flexible, and realistic Android challenges that go far beyond what is typically possible.\n\n## ✨ Features\n\nDroidGround provides a rich set of server-controlled features.\n\n- **Real-Time Device Screen** (via `scrcpy`)\n- **Reset Challenge State**\n- **Restart App / Start Activity / Start Service**\n- **Send Broadcast Intent**\n- **Shutdown / Reboot Device**\n- **Download Bugreport (bugreportz)**\n- **Frida Scripting**\n  - Run from preloaded library (jailed mode)\n  - Run arbitrary scripts (full mode)\n- **File Browser**\n- **Terminal Access**\n- **APK Management**\n- **Logcat Viewer**\n- **Exploit Server** (if team mode is enabled)\n\nAlmost all features are **modular** and defined via environment variables, ensuring precise control over the challenge scope.\n\n## 📸 Screenshots\n\n| ![Screenshot Overview](docs/overview.png)              | ![Screenshot Start Activity](docs/start-activity.png) |\n| ------------------------------------------------------ | ----------------------------------------------------- |\n| Overview                                               | Start Activity                                        |\n| ![Screenshot Frida Jailed Mode](docs/frida-jailed.png) | ![Screenshot Frida Full Mode](docs/frida-full.png)    |\n| Frida Jailed Mode                                      | Frida Full Mode                                       |\n| ![Screenshot File Browser](docs/file-browser.png)      | ![Screenshot App Manager](docs/app-manager.png)       |\n| File Browser                                           | App Manager                                           |\n| ![Screenshot Terminal](docs/terminal.png)              | ![Screenshot Logs](docs/logcat.png)                   |\n| Terminal                                               | Logs                                                  |\n\n## ⚙️ Configuration\n\nThe `.env.sample` file in the root directory is a good starting point. This is the full list of all env variables currently supported:\n\n| Variable                              | Description                                                                       | Default     |\n| ------------------------------------- | --------------------------------------------------------------------------------- | ----------- |\n| `DROIDGROUND_BASE_PATH`               | Path of the webapp (useful for hosting on subpaths)                               | -           |\n| `DROIDGROUND_APP_PACKAGE_NAME`        | Package name of target app                                                        | -           |\n| `DROIDGROUND_ADB_HOST`                | ADB host                                                                          | `localhost` |\n| `DROIDGROUND_ADB_PORT`                | ADB port                                                                          | `5037`      |\n| `DROIDGROUND_DEVICE_TYPE`             | `usb` or `network`                                                                | `usb`       |\n| `DROIDGROUND_DEVICE_HOST`             | IP of Android device (`adb`) (network mode only)                                  | -           |\n| `DROIDGROUND_DEVICE_PORT`             | port of Android device (`adb`) (network mode only)                                | -           |\n| `DROIDGROUND_INIT_SCRIPTS_FOLDER`     | Folder containing `setup.sh` and `reset.sh`                                       | `/init.d`   |\n| `DROIDGROUND_HOST`                    | Bind address                                                                      | `0.0.0.0`   |\n| `DROIDGROUND_PORT`                    | Bind port                                                                         | `4242`      |\n| `DROIDGROUND_RESTART_APP_DISABLED`    | Disable app restart                                                               | `false`     |\n| `DROIDGROUND_APP_MANAGER_DISABLED`    | Disable app manager                                                               | `false`     |\n| `DROIDGROUND_BUG_REPORT_DISABLED`     | Disable bugreport                                                                 | `false`     |\n| `DROIDGROUND_FILE_BROWSER_DISABLED`   | Disable file browser                                                              | `false`     |\n| `DROIDGROUND_FRIDA_DISABLED`          | Disable Frida support                                                             | `false`     |\n| `DROIDGROUND_FRIDA_TYPE`              | `jail` or `full`                                                                  | `jail`      |\n| `DROIDGROUND_LOGCAT_DISABLED`         | Disable logcat                                                                    | `false`     |\n| `DROIDGROUND_REBOOT_ENABLED`          | Enable reboot                                                                     | `false`     |\n| `DROIDGROUND_SHUTDOWN_ENABLED`        | Enable shutdown                                                                   | `false`     |\n| `DROIDGROUND_START_ACTIVITY_DISABLED` | Disable startActivity                                                             | `false`     |\n| `DROIDGROUND_START_RECEIVER_DISABLED` | Disable broadcast                                                                 | `false`     |\n| `DROIDGROUND_START_SERVICE_DISABLED`  | Disable startService                                                              | `false`     |\n| `DROIDGROUND_TERMINAL_DISABLED`       | Disable terminal                                                                  | `false`     |\n| `DROIDGROUND_RESET_DISABLED`          | Disable reset                                                                     | `false`     |\n| `DROIDGROUND_EXPLOIT_APP_DURATION`    | The time (in seconds) the exploit app will be active                              | `10`        |\n| `DROIDGROUND_EXPLOIT_APP_MAX_SIZE`    | The max size (in MB) of the exploit app                                           | `50`        |\n| `DROIDGROUND_NUM_TEAMS`               | The number of teams playing simultaneously                                        | -           |\n| `DROIDGROUND_TEAM_TOKEN_\u003cN\u003e`          | The token for the nth team. Auto-generated if missing                             | -           |\n| `DROIDGROUND_IP_STATIC`               | The static IP address to display. It takes precedence over `DROIDGROUND_IP_IFACE` | -           |\n| `DROIDGROUND_IP_IFACE`                | The network interface for the displayed IP address                                | -           |\n| `DROIDGROUND_LOGO_LINK`               | Optionally set the logo click-through link (e.g., your CTF main page)             | -           |\n\nThe `DROIDGROUND_IP_IFACE` looks for an exact match first and fallbacks to the first interface that _starts with_ the provided value since Docker only allows to specify the network interface **prefix** within the container.\n\nThe usage of the `DROIDGROUND_NUM_TEAMS` variable slightly changes the behaviour of the application under the hood. If this option is set:\n\n1. The exploit server feature is enabled, allowing each team to be able to use their own (very simple) exploit server via their **_team token_**.\n2. The team token will be **required** to install and run exploit apps. Each installed app will be tied to a team and other teams won't be able to run it.\n\nThis allows to share the same DroidGround instance with multiple teams in challenges where the flag can be exfiltrated via a network request. This **massively reduces deploy costs of DroidGround** for CTF competitions.\n\nFurthermore, if the value is set to `-1` it will enable the so-called **Unlimited Teams** mode. In this mode a button to generate a new **_team token_** will be available in the _Overview_ page. All the `DROIDGROUND_TEAM_TOKEN_\u003cN\u003e` variables are ignored if this mode is enabled.\n\n## 🧩 Use Cases\n\nHere are some ways DroidGround can be used:\n\n1. **Hidden Activity**: Find and launch an unexposed activity to see the flag (player's app contains a dummy flag).\n2. **RCE**: The app is vulnerable to RCE and the flag is stored on the device.\n3. **Frida Instrumentation**: Overload a method and extract the flag from private memory using a script.\n\n## ⚙️ Usage\n\nA couple of sample _Docker Compose_ files are provided in the [examples](./examples/apps) folder. They use apps from the [DroidGround samples repo](https://github.com/SECFORCE/droidground-samples) which will progressively be enhanced to showcase all the key features. They are also a good starting point to understand how to setup your own CTF.\n\nOn boot _DroidGround_ does the following:\n\n1. Set up the connection with `adb`\n2. Run the `setup.sh` in the folder specified by `DROIDGROUND_INIT_SCRIPTS_FOLDER` if present. This script can be used to install the target app and do everything else that's needed to init the CTF (e.g. placing the flag in a known location)\n3. (if _Frida_ is enabled) Download the correct `frida-server` based on the version installed and the architecture of the device and start it\n4. Run the target app (the one specified through `DROIDGROUND_APP_PACKAGE_NAME`). If the app is not installed _DroidGround_ will exit.\n5. Setup the _REST APIs_, the _WebSocket_ servers and the display streaming\n\nHere is a sample `setup.sh` script:\n\n```sh\n#!/usr/bin/env bash\n\nadb shell pm uninstall com.example.app # To do some cleanup\necho \"Sleep for 2 seconds before installing app\"\nsleep 2\necho \"Installing app...\"\nadb install ./flag.apk # The cwd is set to the \"init.d\" folder, so the apk file can be accessed with the relative path\necho \"Install command executed\"\n```\n\nFor a production deploy (in a real CTF) you may want to provision a pre-defined number of DroidGround instances beforehand or you may want to allow the users to spawn instances (with a limitation or maybe associate each team/user with a specific instance). For this reason we also added a simple [spawner example](./examples/spawner).\n\nAlternatively, as previously mentioned, you can create a challenge where the flag can be exfiltrated via a network request and leverage the `DROIDGROUND_NUM_TEAMS` env variable to avoid spawning multiple instances (which could be expensive). The [net-multi-step](./examples/apps/net-multi-step/) folder provides a good example on how to deliver this type of challenges.\n\n## 💡 Tips\n\nHere are some suggestions for setting up your Android CTF:\n\n- Be careful when enabling **Frida Full Mode**, the player will have complete control over the device (that's why we made the **Frida Jail Mode** as detailed in [Frida Library](./library)).\n- Be careful when enabling the **Terminal**, the player will have complete control over the device.\n- Be careful when enabling the **Shutdown** feature.\n- If you plan to make the flag directly visible in the UI you may want to find a way to spawn different instances (one for each team/player)\n\nWhile testing the setup before going in production it could be useful to get the **attack surface** of the target app. This is something that players shouldn't see because it's part of their job to discover and analyze the attack surface!\n\nTherefore, a `GET` endpoint reachable at `/attackSurface` is provided and protected with a token (that needs to be used as the value of the `Authorization` header) that is randomly generated during the boot and printed in the logs (therefore accessible only by sysadmins).\n\nIf you want to use your own Frida scripts in jailed mode, you just need to bind-mount the folder that contains them into the Docker container:\n\n```yaml\nvolumes:\n  - \u003cFrida library folder\u003e:/droidground/library\n```\n\nA `library.json` file (like the one in the [library](./library/library.json)) is required to instruct the application on the list of available scripts.\n\n## 🛠 Development\n\nGetting it up \u0026 running shouldn't be too difficult, but before starting you should have the following tools installed:\n\n- `frida` (only if you enable _Frida_)\n- `node` (it's a Node app, you need to have it!)\n- `adb` (well, we rely on it to talk with the device)\n- _JDK_ (you need it to build the companion app)\n\nAfter that you may just run the following:\n\n```sh\ngit clone https://github.com/SECFORCE/droidground.git\ncd droidground\n\n# Install without running scripts\nnpm install --ignore-scripts\n# Rebuild frida to get the bindings\nnpm rebuild frida\n# Build companion app\nnpm run companion\n# Get scrcpy\nnpx fetch-scrcpy-server 3.1\nnpm run scrcpy\n```\n\nAfter that you just need to set the **env** variables and then run `npm run dev` and you'll be good to go. Happy dev mode!\n\n## 🤝 Contributing\n\nPull requests are welcome! Please open an issue first to discuss major changes. Ideas for new CTF workflows or challenge types are especially appreciated.\n\n## 📚 Credits\n\nDeveloped by [Angelo Delicato](https://github.com/thelicato) [@SECFORCE](https://www.secforce.com).\n\nThe _server_ section heavily relies on the amazing work done by [@yume-chan](https://github.com/yume-chan/ya-webadb), probably this app wouldn't exist if it wasn't for his amazing work.\n\nThe _companion_ app is heavily based on the [aya server](https://github.com/liriliri/aya/tree/master/server) which works the same way as the [scrcpy server](https://github.com/Genymobile/scrcpy). More details can be found in the specific [README](./companion/README.md).\n\n## 🪪 License\n\n_DroidGround_ is released under the [GPL-3.0 LICENSE](https://github.com/SECFORCE/droidground/blob/main/LICENSE)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsecforce%2Fdroidground","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsecforce%2Fdroidground","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsecforce%2Fdroidground/lists"}