
Android CTF 챌린지를 위한 유연한 플레이그라운드입니다.
<h1 align="center">
<br>
<img src="https://assets.kitploit.com/production/public/readmes/7301/75c22abcf52c94c756ce37d77db74df73c48b6846c3aede4c8971c38e2180f65.png" alt= "droidground" width="200px">
</h1>
<p align="center">
<b>DroidGround</b>
<p>
<p align="center">
<a href="https://github.com/SECFORCE/droidground/blob/main/README.md"><img src="https://img.shields.io/badge/Documentation-complete-green.svg?style=flat"></a>
<a href="https://github.com/SECFORCE/droidground/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-GPL3-blue.svg"></a>
<a href="https://blackhat.com/eu-25/arsenal/schedule/index.html#droidground-a-flexible-playground-for-android-ctf-challenges-47803"><img src="https://raw.githubusercontent.com/secforce/droidground/HEAD/docs/blackhat-2025.svg"></a>
<br />
<a href="https://droidground.com" target="_blank">Website</a> |
<a href="https://droidground.com/demo" target="_blank">Demo</a>
</p>
전통적인 **CTF 챌린지**에서는 시스템의 파일에 플래그를 숨겨두고, 공격자가 취약점을 익스플로잇하여 이를 획득하도록 하는 방식이 일반적입니다. 그러나 Android 세계에서는 이 접근 방식이 잘 통하지 않습니다. APK 파일은 쉽게 다운로드하고 리버싱할 수 있기 때문에, **기기에 플래그를 두는 것은 보통 정적 분석이나 에뮬레이터 트릭으로 너무나 쉽게** 추출됩니다. 이는 현실적이고 런타임 중심의 챌린지를 만드는 능력을 심각하게 제한합니다.
_DroidGround_ 는 이 문제를 해결하기 위해 설계되었습니다.
이는 Android 모바일 해킹 챌린지를 **통제된** 그리고 **현실적인** 환경에서 호스팅하기 위해 맞춤 제작된 플랫폼으로, **공격자가 의도한 방식으로만 챌린지를 해결하도록 충분히 제약**됩니다.
중요한 점은, 참가자가 **앱 환경 내부에 갇힐(jailed)** 수 있다는 것입니다. 이 도구의 모듈성 덕분에 사용자가 셸을 실행할 수 있는지, 임의의 파일을 읽을 수 있는지, 또는 도구를 사이드로드할 수 있는지를 설정할 수 있습니다. 플래그를 획득하는 유일한 방법이 앱 자체를 이해하고 익스플로잇하는 것이 되도록 모든 것을 설정할 수 있습니다.
## 📋 목차
- [🧭 개요](#-개요)
- [✨ 기능](#-기능)
- [📸 스크린샷](#-스크린샷)
- [⚙️ 구성](#️-구성)
- [🧩 사용 사례](#-사용-사례)
- [⚙️ 사용법](#️-사용법)
- [💡 팁](#-팁)
- [🛠 개발](#-개발)
- [🤝 기여](#-기여)
- [📚 크레딧](#-크레딧)
- [🪪 라이선스](#-라이선스)
## 🧭 개요
_DroidGround_ 는 전통적인 CTF 환경에서는 구현하기 어려운 다양한 _Android_ 챌린지를 가능하게 합니다. 예를 들어, 원격 코드 실행(RCE) 챌린지에서 플레이어는 로컬 분석을 위해 APK를 받을 수 있습니다. 취약점을 발견한 후, 플레이어는 Frida 스크립트를 개발하고 DroidGround를 통해 실제 대상 기기에서 실행하여 내부 저장소에서 플래그를 추출할 수 있습니다. 다른 챌린지 유형으로는 숨겨진 액티비티, 사용자 정의 브로드캐스트 인텐트, 서비스 익스플로잇, 또는 사전 로드된 도구를 사용한 동적 분석 등이 있습니다.
실시간 기기 스트리밍, 기능에 대한 세밀한 제어, Frida 통합, 그리고 사용자 정의 가능한 설정 및 초기화 스크립트를 통해 DroidGround는 CTF 주최자가 일반적으로 가능한 범위를 훨씬 뛰어넘는 안전하고 유연하며 현실적인 Android 챌린지를 구축할 수 있도록 지원합니다.
## ✨ 기능
DroidGround는 서버에서 제어되는 풍부한 기능 세트를 제공합니다.
- **실시간 기기 화면** (`scrcpy` 사용), 선택적 마우스, 터치, 키보드 제어 지원
- **챌린지 상태 초기화**
- **앱 재시작 / 액티비티 시작 / 서비스 시작**
- **브로드캐스트 인텐트 전송**
- **기기 종료 / 재부팅**
- **버그리포트 다운로드 (bugreportz)**
- **Frida 스크립팅**
- 사전 로드된 라이브러리에서 실행 (jailed 모드)
- 임의 스크립트 실행 (full 모드)
- **파일 브라우저**
- **터미널 접근**
- **APK 관리**
- **Logcat 뷰어**
- **익스플로잇 서버** (팀 모드가 활성화된 경우)
거의 모든 기능이 **모듈식**이며 환경 변수를 통해 정의되므로, 챌린지 범위를 정밀하게 제어할 수 있습니다.
## 📸 스크린샷
|  |  |
| ------------------------------------------------------ | ----------------------------------------------------- |
| 개요 | 액티비티 시작 |
|  |  |
| Frida Jailed 모드 | Frida Full 모드 |
|  |  |
| 파일 브라우저 | 앱 관리자 |
|  |  |
| 터미널 | 로그 |
## ⚙️ 구성
루트 디렉터리의 `.env.sample` 파일이 좋은 시작점입니다. 다음은 현재 지원되는 모든 환경 변수의 전체 목록입니다:
| 변수 | 설명 | 기본값 |
| ------------------------------------- | --------------------------------------------------------------------------------- | ----------- |
| `DROIDGROUND_BASE_PATH` | 웹앱의 경로 (하위 경로에 호스팅할 때 유용) | - |
| `DROIDGROUND_APP_PACKAGE_NAME` | 대상 앱의 패키지 이름 | - |
| `DROIDGROUND_ADB_HOST` | ADB 호스트 | `localhost` |
| `DROIDGROUND_ADB_PORT` | ADB 포트 | `5037` |
| `DROIDGROUND_ADB_SERIAL` | 연결할 선택적 ADB 기기 시리얼 | - |
| `DROIDGROUND_DEVICE_TYPE` | `usb` 또는 `network` | `usb` |
| `DROIDGROUND_DEVICE_HOST` | Android 기기(`adb`)의 IP (network 모드 전용) | - |
| `DROIDGROUND_DEVICE_PORT` | Android 기기(`adb`)의 포트 (network 모드 전용) | - |
| `DROIDGROUND_INIT_SCRIPTS_FOLDER` | `setup.sh`와 `reset.sh`가 포함된 폴더 | `/init.d` |
| `DROIDGROUND_HOST` | 바인드 주소 | `0.0.0.0` |
| `DROIDGROUND_PORT` | 바인드 포트 | `4242` |
| `DROIDGROUND_RESTART_APP_DISABLED` | 앱 재시작 비활성화 | `false` |
| `DROIDGROUND_APP_MANAGER_DISABLED` | 앱 관리자 비활성화 | `false` |
| `DROIDGROUND_BUG_REPORT_DISABLED` | 버그리포트 비활성화 | `false` |
| `DROIDGROUND_FILE_BROWSER_DISABLED` | 파일 브라우저 비활성화 | `false` |
| `DROIDGROUND_FRIDA_DISABLED` | Frida 지원 비활성화 | `false` |
| `DROIDGROUND_FRIDA_TYPE` | `jail` 또는 `full` | `jail` |
| `DROIDGROUND_FRIDA_INJECTION` | `server` 또는 `gadget` | `server` |
| `DROIDGROUND_LOGCAT_DISABLED` | logcat 비활성화 | `false` |
| `DROIDGROUND_REBOOT_ENABLED` | 재부팅 활성화 | `false` |
| `DROIDGROUND_SHUTDOWN_ENABLED` | 종료 활성화 | `false` |
| `DROIDGROUND_START_ACTIVITY_DISABLED` | startActivity 비활성화 | `false` |
| `DROIDGROUND_START_RECEIVER_DISABLED` | 브로드캐스트 비활성화 | `false` |
| `DROIDGROUND_START_SERVICE_DISABLED` | startService 비활성화 | `false` |
| `DROIDGROUND_TERMINAL_DISABLED` | 터미널 비활성화 | `false` |
| `DROIDGROUND_RESET_DISABLED` | 초기화 비활성화 | `false` |
| `DROIDGROUND_SCRCPY_CONTROL_ENABLED` | 기기 화면을 통한 마우스, 터치, 키보드 입력 활성화 | `false` |
| `DROIDGROUND_SCRCPY_MAX_SIZE` | 최대 비디오 크기(픽셀); `0`은 기기 전체 해상도 유지 | `1280` |
| `DROIDGROUND_SCRCPY_MAX_FPS` | 최대 비디오 프레임 레이트; `0`은 제한 없음 | `60` |
| `DROIDGROUND_SCRCPY_VIDEO_BIT_RATE` | 비디오 비트레이트(초당 비트) | `4000000` |
| `DROIDGROUND_EXPLOIT_APP_DURATION` | 익스플로잇 앱이 활성화되는 시간(초) | `10` |
| `DROIDGROUND_EXPLOIT_APP_MAX_SIZE` | 익스플로잇 앱의 최대 크기(MB) | `50` |
| `DROIDGROUND_NUM_TEAMS` | 동시에 플레이하는 팀 수 | - |
| `DROIDGROUND_TEAM_TOKEN_<N>` | n번째 팀의 토큰. 없으면 자동 생성됨 | - |
| `DROIDGROUND_IP_STATIC` | 표시할 정적 IP 주소. `DROIDGROUND_IP_IFACE`보다 우선함 | - |
| `DROIDGROUND_IP_IFACE` | 표시할 IP 주소의 네트워크 인터페이스 | - |
| `DROIDGROUND_LOGO_LINK` | 선택적으로 로고 클릭 시 이동할 링크 설정 (예: CTF 메인 페이지) | - |
`DROIDGROUND_SCRCPY_CONTROL_ENABLED=true`로 설정하고 DroidGround를 재시작하면 기기 화면이 상호작용 가능해집니다. 화면을 클릭하거나 탭하여 포커스를 맞춘 후, 클릭, 드래그, 스와이프, 멀티 터치, 마우스 휠 스크롤, 또는 타이핑을 할 수 있습니다. Android 뒤로 가기는 마우스 오른쪽 클릭 또는 Escape 키를 누르면 됩니다. Tab과 Shift+Tab은 화면 밖으로 포커스를 이동시킵니다. 입력은 팀 모드를 포함하여 기기를 보는 모든 사람과 공유됩니다. 기본값은 보기 전용 화면으로 유지됩니다. 텍스트 입력은 scrcpy의 Android 키 주입을 사용하므로, 문자 지원은 기기의 키보드 매핑에 따라 달라집니다.
`DROIDGROUND_IP_IFACE`는 먼저 정확히 일치하는 항목을 찾고, 없으면 제공된 값으로 _시작하는_ 첫 번째 인터페이스로 대체합니다. Docker는 컨테이너 내에서 네트워크 인터페이스 **접두사**만 지정할 수 있기 때문입니다.
화면 스트리밍은 기본적으로 최대 1280픽셀, 최대 60fps, 4Mbps로 설정되어 인코딩, 네트워크, 브라우저 작업을 줄입니다. 이는 내장된 기기 뷰에서 반응성을 위해 약간의 세부 사항을 희생합니다. 이전의 전체 해상도 품질 설정을 복원하려면 `DROIDGROUND_SCRCPY_MAX_SIZE=0`과 `DROIDGROUND_SCRCPY_VIDEO_BIT_RATE=10000000`으로 설정하세요. 느린 연결이나 브라우저의 경우 최대 크기 `1024`, `30` fps, 초당 `2000000` 비트를 시도해 보세요. 이 설정을 변경한 후에는 DroidGround를 재시작하세요.
브라우저는 사용 가능한 경우 WebCodecs를 사용하고, 그렇지 않으면 소프트웨어 H.264 디코더로 대체합니다. WebCodecs는 보안 컨텍스트가 필요합니다: 원격 배포에는 HTTPS를 사용하세요 (localhost도 작동합니다). 원격 IP 또는 호스트 이름에서의 일반 HTTP는 소프트웨어 디코딩을 사용합니다. 느린 뷰어는 비디오 백로그를 누적하는 대신 새로운 키프레임으로 건너뜁니다. 1.5초 이내에 키프레임이 도착하지 않으면 DroidGround는 비디오 새로고침을 요청합니다. 이는 ReDroid의 `OMX.google.h264.encoder`와 같이 주기적인 I-프레임이 반드시 독립적으로 디코딩 가능한 IDR 프레임이 아닌 인코더를 지원합니다. 새로고침 요청은 뷰어 간에 공유되며 모든 사람의 화면을 잠시 새로고침할 수 있습니다. 서버는 비디오 새로고침을 위해 내부 scrcpy 제어 채널을 유지합니다; 마우스, 터치, 키보드 입력, 자동 화면 깨우기는 `DROIDGROUND_SCRCPY_CONTROL_ENABLED`에 의해 제어됩니다. 기본 브라우저 및 비디오 설정에 대해서는 [WebCodecs 명세](https://www.w3.org/TR/webcodecs/#videodecoder-interface)와 [scrcpy 비디오 문서](https://github.com/Genymobile/scrcpy/blob/master/doc/video.md)를 참조하세요.
`DROIDGROUND_NUM_TEAMS` 변수의 사용은 내부적으로 애플리케이션의 동작을 약간 변경합니다. 이 옵션이 설정되면:
1. 익스플로잇 서버 기능이 활성화되어, 각 팀이 자신의 **_팀 토큰_**을 통해 자체 (매우 간단한) 익스플로잇 서버를 사용할 수 있습니다.
2. 익스플로잇 앱을 설치하고 실행하려면 팀 토큰이 **필수**입니다. 설치된 각 앱은 팀에 연결되며 다른 팀은 이를 실행할 수 없습니다.
이는 네트워크 요청을 통해 플래그를 유출할 수 있는 챌린지에서 여러 팀과 동일한 DroidGround 인스턴스를 공유할 수 있게 합니다. 이는 CTF 대회에서 **DroidGround의 배포 비용을 대폭 절감**합니다.
또한, 값이 `-1`로 설정되면 소위 **Unlimited Teams** 모드가 활성화됩니다. 이 모드에서는 _개요_ 페이지에서 새로운 **_팀 토큰_**을 생성하는 버튼이 제공됩니다. 이 모드가 활성화되면 모든 `DROIDGROUND_TEAM_TOKEN_<N>` 변수는 무시됩니다.
## 🧩 사용 사례
다음은 DroidGround를 사용할 수 있는 몇 가지 방법입니다:
1. **숨겨진 액티비티**: 노출되지 않은 액티비티를 찾아 실행하여 플래그를 확인합니다 (플레이어의 앱에는 더미 플래그가 포함됨).
2. **RCE**: 앱이 RCE에 취약하고 플래그가 기기에 저장되어 있습니다.
3. **Frida 계측**: 스크립트를 사용하여 메서드를 오버로드하고 프라이빗 메모리에서 플래그를 추출합니다.
## ⚙️ 사용법
[examples](https://github.com/secforce/droidground/blob/main/examples/apps) 폴더에 몇 가지 샘플 _Docker Compose_ 파일이 제공됩니다. 이들은 [DroidGround 샘플 저장소](https://github.com/SECFORCE/droidground-samples)의 앱을 사용하며, 모든 주요 기능을 보여주기 위해 점진적으로 개선될 예정입니다. 또한 자신만의 CTF를 설정하는 방법을 이해하는 좋은 시작점이기도 합니다.
부팅 시 _DroidGround_ 는 다음을 수행합니다:
1. `adb`와의 연결 설정
2. `DROIDGROUND_INIT_SCRIPTS_FOLDER`로 지정된 폴더에 `setup.sh`가 있으면 실행합니다. 이 스크립트는 대상 앱을 설치하고 CTF를 초기화하는 데 필요한 모든 작업(예: 알려진 위치에 플래그 배치)을 수행하는 데 사용할 수 있습니다.
3. (_Frida_ 가 활성화된 경우) 설치된 버전과 기기 아키텍처에 따라 올바른 `frida-server`를 다운로드하고 시작합니다.
4. 대상 앱(`DROIDGROUND_APP_PACKAGE_NAME`으로 지정된 앱)을 실행합니다. 앱이 설치되어 있지 않으면 _DroidGround_ 는 종료됩니다.
5. _REST API_, _WebSocket_ 서버, 디스플레이 스트리밍을 설정합니다.
다음은 샘플 `setup.sh` 스크립트입니다:
```sh
#!/usr/bin/env bash
adb shell pm uninstall com.example.app # To do some cleanup
echo "Sleep for 2 seconds before installing app"
sleep 2
echo "Installing app..."
adb install ./flag.apk # The cwd is set to the "init.d" folder, so the apk file can be accessed with the relative path
echo "Install command executed"
```
프로덕션 배포(실제 CTF)의 경우, 미리 정의된 수의 DroidGround 인스턴스를 사전에 프로비저닝하거나, 사용자가 인스턴스를 생성하도록 허용(제한을 두거나 각 팀/사용자를 특정 인스턴스에 연결)할 수 있습니다. 이를 위해 간단한 [spawner 예제](https://github.com/secforce/droidground/blob/main/examples/spawner)도 추가했습니다.
또는 앞서 언급한 대로, 네트워크 요청을 통해 플래그를 유출할 수 있는 챌린지를 만들고 `DROIDGROUND_NUM_TEAMS` 환경 변수를 활용하여 여러 인스턴스를 생성하는 것(비용이 많이 들 수 있음)을 피할 수 있습니다. [net-multi-step](https://github.com/secforce/droidground/blob/main/examples/apps/net-multi-step) 폴더는 이러한 유형의 챌린지를 제공하는 방법에 대한 좋은 예를 제공합니다.
## 💡 팁
다음은 Android CTF를 설정하기 위한 몇 가지 제안입니다:
- **Frida Full Mode**를 활성화할 때는 주의하세요. 플레이어가 기기를 완전히 제어할 수 있게 됩니다 (그래서 [Frida Library](https://github.com/secforce/droidground/blob/main/library)에 설명된 대로 **Frida Jail Mode**를 만들었습니다).
- **터미널**을 활성화할 때는 주의하세요. 플레이어가 기기를 완전히 제어할 수 있게 됩니다.
- **종료** 기능을 활성화할 때는 주의하세요.
- 플래그를 UI에 직접 표시하려는 경우, 서로 다른 인스턴스(팀/플레이어당 하나씩)를 생성하는 방법을 찾아야 할 수 있습니다.
프로덕션에 들어가기 전에 설정을 테스트하는 동안 대상 앱의 **공격 표면**을 확인하는 것이 유용할 수 있습니다. 이는 플레이어가 보아서는 안 되는 정보입니다. 공격 표면을 발견하고 분석하는 것은 플레이어의 몫이기 때문입니다!
따라서 `/attackSurface`에서 접근할 수 있는 `GET` 엔드포인트가 제공되며, 부팅 중에 무작위로 생성되어 로그에 출력되는 토큰(`Authorization` 헤더의 값으로 사용해야 함)으로 보호됩니다 (따라서 시스템 관리자만 접근 가능).
jailed 모드에서 자신만의 Frida 스크립트를 사용하려면, 해당 스크립트가 포함된 폴더를 Docker 컨테이너에 바인드 마운트하면 됩니다:
```yaml
volumes:
- <Frida library folder>:/droidground/library
```
사용 가능한 스크립트 목록을 애플리케이션에 알려주려면 `library.json` 파일([library](https://github.com/secforce/droidground/blob/main/library/library.json)에 있는 것과 같은)이 필요합니다.
## 🛠 개발
실행하는 것은 그리 어렵지 않지만, 시작하기 전에 다음 도구가 설치되어 있어야 합니다:
- `frida` (_Frida_ 를 활성화한 경우에만)
- `node` (Node 앱이므로 반드시 필요합니다!)
- `adb` (기기와 통신하기 위해 의존합니다)
- _JDK_ (컴패니언 앱을 빌드하는 데 필요합니다)
그런 다음 다음을 실행하면 됩니다:
```sh
git clone https://github.com/SECFORCE/droidground.git
cd droidground
# Install without running scripts
npm install --ignore-scripts
# Rebuild frida to get the bindings
npm rebuild frida
# Build companion app
npm run companion
# Get scrcpy
npm run scrcpy
```
그런 다음 **env** 변수를 설정하고 `npm run dev`를 실행하면 준비 완료입니다. 즐거운 개발 모드 되세요!
## 🤝 기여
Pull request를 환영합니다! 주요 변경 사항은 먼저 이슈를 열어 논의해 주세요. 새로운 CTF 워크플로우나 챌린지 유형에 대한 아이디어는 특히 환영합니다.
## 📚 크레딧
[Angelo Delicato](https://github.com/thelicato) [@SECFORCE](https://www.secforce.com)가 개발했습니다.
_server_ 섹션은 [@yume-chan](https://github.com/yume-chan/ya-webadb)의 놀라운 작업에 크게 의존합니다. 그의 놀라운 작업이 없었다면 이 앱은 존재하지 않았을 것입니다.
_companion_ 앱은 [scrcpy 서버](https://github.com/Genymobile/scrcpy)와 동일한 방식으로 작동하는 [aya 서버](https://github.com/liriliri/aya/tree/master/server)를 기반으로 합니다. 자세한 내용은 해당 [README](https://github.com/secforce/droidground/blob/main/companion/README.md)에서 확인할 수 있습니다.
## 🪪 라이선스
_DroidGround_ 는 [GPL-3.0 LICENSE](https://github.com/SECFORCE/droidground/blob/main/LICENSE)에 따라 배포됩니다.