# πββοΈ SubSurfer



SubSurfer is a fast and efficient subdomain enumeration and web property identification tool.

<br>
## π Features
- **Red Team/Bug Bounty Support**: Useful for both red team operations and web bug bounty projects
- **High-Performance Scanning**: Fast subdomain enumeration using asynchronous and parallel processing
- **Port Scanning**: Expand asset scanning range with customizable port selection
- **Web Service Identification**: Gather environmental details such as web servers and technology stacks
- **Pipeline Integration**: Supports integration with other tools using `-pipeweb` and `-pipesub` options
- **Modular Design**: Can be imported and used as a Python module
- **Continuous Updates**: - **Continuous Updates**: New passive/active modules will continue to be added
<br>
## π Installation
<b>bash</b>
```bash
git clone https://github.com/arrester/subsurfer.git
cd subsurfer
```
or <br>
<b>Python</b>
```bash
pip install subsurfer
```
<br>
## π Usage
### CLI Mode
<b>Basic Scan</b><br>
`subsurfer -t vulnweb.com`
<b>Enable Active Scanning</b><br>
`subsurfer -t vulnweb.com -a`
<b>Include Port Scanning</b><br>
`subsurfer -t vulnweb.com -dp` # Default Port <br>
`subsurfer -t vulnweb.com -p 80,443,8080-8090` # Custom ports
<b>Pipeline Output</b><br>
`subsurfer -t vulnweb.com -pipeweb` # Output only web server <br>
`subsurfer -t vulnweb.com -pipesub` # Output only subdomain results
### Using as a Python Module
<b>Subdomain Scan</b><br>
```python
from subsurfer.core.controller.controller import SubSurferController
import asyncio
async def main():
controller = SubSurferController(
target="vulnweb.com",
verbose=1,
active=False # Active Scan Option
)
# Collect subdomains
subdomains = await controller.collect_subdomains()
# Print results
print(f"Discovered Subdomains: {len(subdomains)}κ°")
for subdomain in sorted(subdomains):
print(subdomain)
if __name__ == "__main__":
asyncio.run(main())
```
<br>
<b>Port Scan</b><br>
```python
from subsurfer.core.controller.controller import SubSurferController
import asyncio
async def main():
controller = SubSurferController(
target="vulnweb.com",
verbose=1
)
# Collect subdomains
subdomains = await controller.collect_subdomains()
# Default ports (80, 443)
ports = None
# Set port scan options
# ports = controller.parse_ports() # Default ports
# Or specify custom ports
# ports = controller.parse_ports("80,443,8080-8090")
# Web service scanning
web_services = await controller.scan_web_services(subdomains, ports)
# Print web servers
print("\nμΉ μλ²:")
for server in sorted(web_services['web_servers']):
print(f"https://{server}")
# Print active services
print("\nνμ±νλ μλΉμ€:")
for service in sorted(web_services['enabled_services']):
print(service)
# Print discovered URLs and ports
print("\nλ°κ²¬λ URL:")
for subdomain, urls in web_services['all_urls'].items():
for url, port in urls:
print(f"{url}:{port}")
if __name__ == "__main__":
asyncio.run(main())
```
<br>
<b>Result Save</b><br>
```python
from subsurfer.core.controller.controller import SubSurferController
import asyncio
async def main():
controller = SubSurferController("vulnweb.com")
# Collect subdomains and scan web services
subdomains = await controller.collect_subdomains()
web_services = await controller.scan_web_services(subdomains)
# Save results
results_dict = {
'subdomains': subdomains,
'web_services': web_services.get('web_services', {}),
'web_servers': web_services.get('web_servers', set()),
'enabled_services': web_services.get('enabled_services', set()),
'all_urls': web_services.get('all_urls', {}) # Includes URL and port information
}
# Generate default result file path (stored in the "results" directory)
output_path = controller.get_output_path()
controller.save_results(results_dict, output_path)
if __name__ == "__main__":
asyncio.run(main())
```
<br>
## π§ͺ Testing
### Passive Handler Test
`pytest tests/handlers/test_passive_handler.py -v`
<br>
### Active Handler Test
`pytest tests/handlers/test_active_handler.py -v`
<br>
## πΊοΈ To-Do List
### Version 0.3
- Add JSON output option
- Add new passive modules
- Additional etc feature updates
### Version 0.4
- Add new passive modules
- Implement subdomain takeover detection
### Version 0.5
- Add new passive modules
- Add new active modules
## π Requirements
- Recommended: Python 3.13.0 or later
- aiohttp
- rich
- pytest (for testing)
## π License
MIT License
## π€ Contributions
Bug Report, Feature Suggestions, Pull Request
Raw data
{
"_id": null,
"home_page": "https://github.com/arrester/subsurfer",
"name": "subsurfer",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": "security, subdomain enumeration, bug bounty, red team, web security",
"author": "arrester",
"author_email": "arresterloyal@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/06/7f/b40330a16c77c69c95d53c700f909391b690cfffb83669bd2e95de2e813b/subsurfer-0.2.4.tar.gz",
"platform": null,
"description": "# \ud83c\udfc4\u200d\u2642\ufe0f SubSurfer\n\n\n\n\n\nSubSurfer is a fast and efficient subdomain enumeration and web property identification tool.\n\n\n<br>\n\n## \ud83c\udf1f Features\n- **Red Team/Bug Bounty Support**: Useful for both red team operations and web bug bounty projects\n- **High-Performance Scanning**: Fast subdomain enumeration using asynchronous and parallel processing\n- **Port Scanning**: Expand asset scanning range with customizable port selection\n- **Web Service Identification**: Gather environmental details such as web servers and technology stacks\n- **Pipeline Integration**: Supports integration with other tools using `-pipeweb` and `-pipesub` options\n- **Modular Design**: Can be imported and used as a Python module\n- **Continuous Updates**: - **Continuous Updates**: New passive/active modules will continue to be added\n\n<br>\n\n## \ud83d\ude80 Installation\n<b>bash</b>\n```bash\ngit clone https://github.com/arrester/subsurfer.git\ncd subsurfer\n```\n\nor <br>\n\n<b>Python</b>\n```bash\npip install subsurfer\n```\n\n<br>\n\n## \ud83d\udcd6 Usage\n### CLI Mode\n<b>Basic Scan</b><br>\n`subsurfer -t vulnweb.com`\n\n<b>Enable Active Scanning</b><br>\n`subsurfer -t vulnweb.com -a`\n\n<b>Include Port Scanning</b><br>\n`subsurfer -t vulnweb.com -dp` # Default Port <br>\n`subsurfer -t vulnweb.com -p 80,443,8080-8090` # Custom ports\n\n<b>Pipeline Output</b><br>\n`subsurfer -t vulnweb.com -pipeweb` # Output only web server <br>\n`subsurfer -t vulnweb.com -pipesub` # Output only subdomain results\n\n### Using as a Python Module\n<b>Subdomain Scan</b><br>\n```python\nfrom subsurfer.core.controller.controller import SubSurferController\nimport asyncio\n\nasync def main():\n controller = SubSurferController(\n target=\"vulnweb.com\",\n verbose=1,\n active=False # Active Scan Option\n )\n \n # Collect subdomains\n subdomains = await controller.collect_subdomains()\n \n # Print results\n print(f\"Discovered Subdomains: {len(subdomains)}\uac1c\")\n for subdomain in sorted(subdomains):\n print(subdomain)\n\nif __name__ == \"__main__\":\n asyncio.run(main())\n```\n\n<br>\n\n<b>Port Scan</b><br>\n```python\nfrom subsurfer.core.controller.controller import SubSurferController\nimport asyncio\n\nasync def main():\n controller = SubSurferController(\n target=\"vulnweb.com\",\n verbose=1\n )\n \n # Collect subdomains\n subdomains = await controller.collect_subdomains()\n \n # Default ports (80, 443)\n ports = None\n\n # Set port scan options\n # ports = controller.parse_ports() # Default ports\n # Or specify custom ports\n # ports = controller.parse_ports(\"80,443,8080-8090\")\n \n # Web service scanning\n web_services = await controller.scan_web_services(subdomains, ports)\n \n # Print web servers\n print(\"\\n\uc6f9 \uc11c\ubc84:\")\n for server in sorted(web_services['web_servers']):\n print(f\"https://{server}\")\n \n # Print active services\n print(\"\\n\ud65c\uc131\ud654\ub41c \uc11c\ube44\uc2a4:\")\n for service in sorted(web_services['enabled_services']):\n print(service)\n \n # Print discovered URLs and ports\n print(\"\\n\ubc1c\uacac\ub41c URL:\")\n for subdomain, urls in web_services['all_urls'].items():\n for url, port in urls:\n print(f\"{url}:{port}\")\n\nif __name__ == \"__main__\":\n asyncio.run(main())\n```\n\n<br>\n\n<b>Result Save</b><br>\n```python\nfrom subsurfer.core.controller.controller import SubSurferController\nimport asyncio\n\nasync def main():\n controller = SubSurferController(\"vulnweb.com\")\n \n # Collect subdomains and scan web services\n subdomains = await controller.collect_subdomains()\n web_services = await controller.scan_web_services(subdomains)\n \n # Save results\n results_dict = {\n 'subdomains': subdomains,\n 'web_services': web_services.get('web_services', {}),\n 'web_servers': web_services.get('web_servers', set()),\n 'enabled_services': web_services.get('enabled_services', set()),\n 'all_urls': web_services.get('all_urls', {}) # Includes URL and port information\n }\n \n # Generate default result file path (stored in the \"results\" directory)\n output_path = controller.get_output_path()\n controller.save_results(results_dict, output_path)\n\nif __name__ == \"__main__\":\n asyncio.run(main())\n```\n\n<br>\n\n## \ud83e\uddea Testing\n### Passive Handler Test\n`pytest tests/handlers/test_passive_handler.py -v`\n\n<br>\n\n### Active Handler Test\n`pytest tests/handlers/test_active_handler.py -v`\n\n<br>\n\n## \ud83d\uddfa\ufe0f To-Do List\n### Version 0.3\n- Add JSON output option\n- Add new passive modules\n- Additional etc feature updates\n\n### Version 0.4\n- Add new passive modules\n- Implement subdomain takeover detection\n\n### Version 0.5\n- Add new passive modules\n- Add new active modules\n\n## \ud83d\udccb Requirements\n- Recommended: Python 3.13.0 or later\n- aiohttp\n- rich\n- pytest (for testing)\n\n## \ud83d\udcdd License\nMIT License\n\n## \ud83e\udd1d Contributions\nBug Report, Feature Suggestions, Pull Request\n",
"bugtrack_url": null,
"license": null,
"summary": "Red Teaming and Web Bug Bounty Fast Asset Identification Tool",
"version": "0.2.4",
"project_urls": {
"Bug Reports": "https://github.com/arrester/subsurfer/issues",
"Documentation": "https://github.com/arrester/subsurfer#readme",
"Homepage": "https://github.com/arrester/subsurfer",
"Source": "https://github.com/arrester/subsurfer"
},
"split_keywords": [
"security",
" subdomain enumeration",
" bug bounty",
" red team",
" web security"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "614c10a3ff3e565d7e206a42e910d8c9553e90ad32c3881466fd417f9cfd1ee8",
"md5": "d7dd47713453b377a3834d621177d2e4",
"sha256": "13dad6decc5c3dfc72da247c70b9ec6d64fd75544cab1c1ee625820b7693c129"
},
"downloads": -1,
"filename": "subsurfer-0.2.4-py3-none-any.whl",
"has_sig": false,
"md5_digest": "d7dd47713453b377a3834d621177d2e4",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 40208,
"upload_time": "2025-02-01T07:17:18",
"upload_time_iso_8601": "2025-02-01T07:17:18.343459Z",
"url": "https://files.pythonhosted.org/packages/61/4c/10a3ff3e565d7e206a42e910d8c9553e90ad32c3881466fd417f9cfd1ee8/subsurfer-0.2.4-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "067fb40330a16c77c69c95d53c700f909391b690cfffb83669bd2e95de2e813b",
"md5": "3c69f7eeeef5d423abb022019e1e0019",
"sha256": "293e2a99b2d44d93f539423d8e38193771f29c0b555e1818ea61c1352b33f624"
},
"downloads": -1,
"filename": "subsurfer-0.2.4.tar.gz",
"has_sig": false,
"md5_digest": "3c69f7eeeef5d423abb022019e1e0019",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 26784,
"upload_time": "2025-02-01T07:17:20",
"upload_time_iso_8601": "2025-02-01T07:17:20.442770Z",
"url": "https://files.pythonhosted.org/packages/06/7f/b40330a16c77c69c95d53c700f909391b690cfffb83669bd2e95de2e813b/subsurfer-0.2.4.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-02-01 07:17:20",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "arrester",
"github_project": "subsurfer",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [
{
"name": "aiohappyeyeballs",
"specs": [
[
"==",
"2.4.4"
]
]
},
{
"name": "aiohttp",
"specs": [
[
"==",
"3.11.11"
]
]
},
{
"name": "aioresponses",
"specs": [
[
"==",
"0.7.7"
]
]
},
{
"name": "aiosignal",
"specs": [
[
"==",
"1.3.2"
]
]
},
{
"name": "art",
"specs": [
[
"==",
"6.4"
]
]
},
{
"name": "asyncio",
"specs": [
[
"==",
"3.4.3"
]
]
},
{
"name": "attrs",
"specs": [
[
"==",
"24.3.0"
]
]
},
{
"name": "beautifulsoup4",
"specs": [
[
"==",
"4.12.3"
]
]
},
{
"name": "bs4",
"specs": [
[
"==",
"0.0.2"
]
]
},
{
"name": "certifi",
"specs": [
[
"==",
"2024.12.14"
]
]
},
{
"name": "charset-normalizer",
"specs": [
[
"==",
"3.4.0"
]
]
},
{
"name": "dnspython",
"specs": [
[
"==",
"2.7.0"
]
]
},
{
"name": "frozenlist",
"specs": [
[
"==",
"1.5.0"
]
]
},
{
"name": "httpretty",
"specs": [
[
"==",
"1.1.4"
]
]
},
{
"name": "idna",
"specs": [
[
"==",
"3.10"
]
]
},
{
"name": "iniconfig",
"specs": [
[
"==",
"2.0.0"
]
]
},
{
"name": "lxml",
"specs": [
[
"==",
"5.3.0"
]
]
},
{
"name": "markdown-it-py",
"specs": [
[
"==",
"3.0.0"
]
]
},
{
"name": "mdurl",
"specs": [
[
"==",
"0.1.2"
]
]
},
{
"name": "multidict",
"specs": [
[
"==",
"6.1.0"
]
]
},
{
"name": "packaging",
"specs": [
[
"==",
"24.2"
]
]
},
{
"name": "pluggy",
"specs": [
[
"==",
"1.5.0"
]
]
},
{
"name": "propcache",
"specs": [
[
"==",
"0.2.1"
]
]
},
{
"name": "Pygments",
"specs": [
[
"==",
"2.18.0"
]
]
},
{
"name": "pytest",
"specs": [
[
"==",
"8.3.4"
]
]
},
{
"name": "pytest-asyncio",
"specs": [
[
"==",
"0.25.0"
]
]
},
{
"name": "python-Wappalyzer",
"specs": [
[
"==",
"0.3.1"
]
]
},
{
"name": "PyYAML",
"specs": [
[
"==",
"6.0.2"
]
]
},
{
"name": "requests",
"specs": [
[
"==",
"2.32.3"
]
]
},
{
"name": "rich",
"specs": [
[
"==",
"13.9.4"
]
]
},
{
"name": "setuptools",
"specs": [
[
"==",
"75.6.0"
]
]
},
{
"name": "soupsieve",
"specs": [
[
"==",
"2.6"
]
]
},
{
"name": "urllib3",
"specs": [
[
"==",
"2.2.3"
]
]
},
{
"name": "yarl",
"specs": [
[
"==",
"1.18.3"
]
]
}
],
"lcname": "subsurfer"
}