diff --git a/bbot/modules/apkpure.py b/bbot/modules/apkpure.py new file mode 100644 index 000000000..210dfef0e --- /dev/null +++ b/bbot/modules/apkpure.py @@ -0,0 +1,53 @@ +from pathlib import Path +from bbot.modules.base import BaseModule + + +class apkpure(BaseModule): + watched_events = ["MOBILE_APP"] + produced_events = ["FILESYSTEM"] + flags = ["passive", "safe", "code-enum"] + meta = { + "description": "Download android applications from apkpure.com", + "created_date": "2024-10-11", + "author": "@domwhewell-sage", + } + options = {"output_folder": ""} + options_desc = {"output_folder": "Folder to download apk's to"} + + async def setup(self): + output_folder = self.config.get("output_folder") + if output_folder: + self.output_dir = Path(output_folder) / "apk_files" + else: + self.output_dir = self.scan.home / "apk_files" + self.helpers.mkdir(self.output_dir) + return await super().setup() + + async def filter_event(self, event): + if event.type == "MOBILE_APP": + if "android" not in event.tags: + return False, "event is not an android app" + return True + + async def handle_event(self, event): + app_id = event.data.get("id", "") + path = await self.download_apk(app_id) + if path: + await self.emit_event( + {"path": str(path)}, + "FILESYSTEM", + tags=["apk", "file"], + parent=event, + context=f'{{module}} downloaded the apk "{app_id}" to: {path}', + ) + + async def download_apk(self, app_id): + path = None + url = f"https://d.apkpure.com/b/XAPK/{app_id}?version=latest" + self.helpers.mkdir(self.output_dir / app_id) + file_destination = self.output_dir / app_id / f"{app_id}.xapk" + result = await self.helpers.download(url, warn=False, filename=file_destination) + if result: + self.info(f'Downloaded "{app_id}" from "{url}", saved to {file_destination}') + path = file_destination + return path diff --git a/bbot/test/bbot_fixtures.py b/bbot/test/bbot_fixtures.py index abad144d1..0b2a0ec57 100644 --- a/bbot/test/bbot_fixtures.py +++ b/bbot/test/bbot_fixtures.py @@ -42,6 +42,13 @@ def tempwordlist(content): return filename +def tempapkfile(): + current_dir = Path(__file__).parent + with open(current_dir / "owasp_mastg.apk", "rb") as f: + apk_file = f.read() + return apk_file + + @pytest.fixture def clean_default_config(monkeypatch): clean_config = OmegaConf.merge( diff --git a/bbot/test/owasp_mastg.apk b/bbot/test/owasp_mastg.apk new file mode 100644 index 000000000..9a4f638f1 Binary files /dev/null and b/bbot/test/owasp_mastg.apk differ diff --git a/bbot/test/test_step_2/module_tests/test_module_apkpure.py b/bbot/test/test_step_2/module_tests/test_module_apkpure.py new file mode 100644 index 000000000..17b8e0685 --- /dev/null +++ b/bbot/test/test_step_2/module_tests/test_module_apkpure.py @@ -0,0 +1,69 @@ +from pathlib import Path +from .base import ModuleTestBase, tempapkfile + + +class TestAPKPure(ModuleTestBase): + modules_overrides = ["apkpure", "google_playstore", "speculate"] + apk_file = tempapkfile() + + async def setup_after_prep(self, module_test): + await module_test.mock_dns({"blacklanternsecurity.com": {"A": ["127.0.0.99"]}}) + module_test.httpx_mock.add_response( + url="https://play.google.com/store/search?q=blacklanternsecurity&c=apps", + text=""" + + + "blacklanternsecurity" - Android Apps on Google Play + + + + + """, + ) + module_test.httpx_mock.add_response( + url="https://play.google.com/store/apps/details?id=com.bbot.test", + text=""" + + + BBOT + + + + + + + """, + ) + module_test.httpx_mock.add_response( + url="https://d.apkpure.com/b/XAPK/com.bbot.test?version=latest", + content=self.apk_file, + ) + + def check(self, module_test, events): + assert len(events) == 6 + assert 1 == len( + [ + e + for e in events + if e.type == "DNS_NAME" and e.data == "blacklanternsecurity.com" and e.scope_distance == 0 + ] + ), "Failed to emit target DNS_NAME" + assert 1 == len( + [e for e in events if e.type == "ORG_STUB" and e.data == "blacklanternsecurity" and e.scope_distance == 0] + ), "Failed to find ORG_STUB" + assert 1 == len( + [ + e + for e in events + if e.type == "MOBILE_APP" + and "android" in e.tags + and e.data["id"] == "com.bbot.test" + and e.data["url"] == "https://play.google.com/store/apps/details?id=com.bbot.test" + ] + ), "Failed to find bbot android app" + filesystem_event = [ + e for e in events if e.type == "FILESYSTEM" and "com.bbot.test.xapk" in e.data["path"] and "apk" in e.tags + ] + assert 1 == len(filesystem_event), "Failed to download apk" + file = Path(filesystem_event[0].data["path"]) + assert file.is_file(), "Destination xapk doesn't exist"