├── HTB ├── .obsidian │ ├── hotkeys.json │ ├── workspaces.json │ ├── app.json │ ├── templates.json │ ├── appearance.json │ ├── plugins │ │ ├── table-editor-obsidian │ │ │ ├── data.json │ │ │ ├── manifest.json │ │ │ └── styles.css │ │ ├── buttons │ │ │ ├── manifest.json │ │ │ └── styles.css │ │ ├── dataview │ │ │ ├── manifest.json │ │ │ ├── data.json │ │ │ └── styles.css │ │ ├── metadata-menu │ │ │ ├── manifest.json │ │ │ └── data.json │ │ ├── obsidian-chartsview-plugin │ │ │ ├── manifest.json │ │ │ ├── data.json │ │ │ └── styles.css │ │ └── obsidian-shellcommands │ │ │ ├── manifest.json │ │ │ ├── data.json │ │ │ └── styles.css │ ├── themes │ │ ├── Aqua │ │ │ ├── manifest.json │ │ │ └── theme.css │ │ ├── Minimal │ │ │ └── manifest.json │ │ └── Blue Topaz │ │ │ └── manifest.json │ ├── community-plugins.json │ ├── core-plugins.json │ ├── graph.json │ └── workspace.json ├── .res │ ├── Linux.png │ └── Windows.png ├── assets │ ├── machine_info_example.png │ ├── create_machine_example.png │ ├── update_machine_example.png │ └── index_machine_info_example.png ├── fileClass │ └── Machine.md ├── ⚡ Start Machine ⚡.md └── Index.md ├── Constants.py ├── SECURITY.md ├── .gitignore ├── hackthebox ├── __pycache__ │ ├── htb.cpython-310.pyc │ ├── solve.cpython-310.pyc │ ├── team.cpython-310.pyc │ ├── user.cpython-310.pyc │ ├── utils.cpython-310.pyc │ ├── vpn.cpython-310.pyc │ ├── content.cpython-310.pyc │ ├── endgame.cpython-310.pyc │ ├── errors.cpython-310.pyc │ ├── machine.cpython-310.pyc │ ├── search.cpython-310.pyc │ ├── __init__.cpython-310.pyc │ ├── challenge.cpython-310.pyc │ ├── constants.cpython-310.pyc │ └── fortress.cpython-310.pyc ├── constants.py ├── __init__.py ├── utils.py ├── content.py ├── fortress.py ├── errors.py ├── endgame.py ├── vpn.py ├── team.py ├── solve.py ├── search.py ├── leaderboard.py ├── user.py ├── challenge.py ├── machine.py └── htb.py ├── templates_md ├── __pycache__ │ ├── __init__.cpython-310.pyc │ ├── index_template.cpython-310.pyc │ ├── machine_template.cpython-310.pyc │ ├── template_chart_radar.cpython-310.pyc │ └── template_char_user_rating.cpython-310.pyc ├── post_exploitation_template.py ├── exploitation_template.py ├── __init__.py ├── index_template.py ├── recon_template.py ├── template_chart_radar.py ├── machine_template.py └── template_char_user_rating.py ├── requirements.txt ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── CODE_OF_CONDUCT.md ├── htb_api.py ├── README.md ├── CONTRIBUTING.md └── LICENSE /HTB/.obsidian/hotkeys.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /HTB/.obsidian/workspaces.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /Constants.py: -------------------------------------------------------------------------------- 1 | API_TOKEN = "" # YOUR HTB APP TOKEN 2 | -------------------------------------------------------------------------------- /HTB/.obsidian/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "promptDelete": false 3 | } -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | Linux 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | test.py 3 | HTB/Machines/* 4 | Constants.py 5 | TODO.md -------------------------------------------------------------------------------- /HTB/.res/Linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/HTB/.res/Linux.png -------------------------------------------------------------------------------- /HTB/.res/Windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/HTB/.res/Windows.png -------------------------------------------------------------------------------- /HTB/.obsidian/templates.json: -------------------------------------------------------------------------------- 1 | { 2 | "dateFormat": "DD-MM-YYYY", 3 | "folder": "templates" 4 | } -------------------------------------------------------------------------------- /HTB/assets/machine_info_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/HTB/assets/machine_info_example.png -------------------------------------------------------------------------------- /HTB/assets/create_machine_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/HTB/assets/create_machine_example.png -------------------------------------------------------------------------------- /HTB/assets/update_machine_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/HTB/assets/update_machine_example.png -------------------------------------------------------------------------------- /HTB/assets/index_machine_info_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/HTB/assets/index_machine_info_example.png -------------------------------------------------------------------------------- /hackthebox/__pycache__/htb.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/hackthebox/__pycache__/htb.cpython-310.pyc -------------------------------------------------------------------------------- /hackthebox/__pycache__/solve.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/hackthebox/__pycache__/solve.cpython-310.pyc -------------------------------------------------------------------------------- /hackthebox/__pycache__/team.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/hackthebox/__pycache__/team.cpython-310.pyc -------------------------------------------------------------------------------- /hackthebox/__pycache__/user.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/hackthebox/__pycache__/user.cpython-310.pyc -------------------------------------------------------------------------------- /hackthebox/__pycache__/utils.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/hackthebox/__pycache__/utils.cpython-310.pyc -------------------------------------------------------------------------------- /hackthebox/__pycache__/vpn.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/hackthebox/__pycache__/vpn.cpython-310.pyc -------------------------------------------------------------------------------- /hackthebox/constants.py: -------------------------------------------------------------------------------- 1 | API_BASE = "https://www.hackthebox.com/api/v4/" 2 | USER_AGENT = "htb-api/0.5.2" 3 | DOWNLOAD_COOLDOWN = 30 4 | -------------------------------------------------------------------------------- /hackthebox/__pycache__/content.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/hackthebox/__pycache__/content.cpython-310.pyc -------------------------------------------------------------------------------- /hackthebox/__pycache__/endgame.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/hackthebox/__pycache__/endgame.cpython-310.pyc -------------------------------------------------------------------------------- /hackthebox/__pycache__/errors.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/hackthebox/__pycache__/errors.cpython-310.pyc -------------------------------------------------------------------------------- /hackthebox/__pycache__/machine.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/hackthebox/__pycache__/machine.cpython-310.pyc -------------------------------------------------------------------------------- /hackthebox/__pycache__/search.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/hackthebox/__pycache__/search.cpython-310.pyc -------------------------------------------------------------------------------- /HTB/.obsidian/appearance.json: -------------------------------------------------------------------------------- 1 | { 2 | "accentColor": "", 3 | "cssTheme": "Blue Topaz", 4 | "theme": "obsidian", 5 | "baseFontSize": 14 6 | } -------------------------------------------------------------------------------- /hackthebox/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/hackthebox/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /hackthebox/__pycache__/challenge.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/hackthebox/__pycache__/challenge.cpython-310.pyc -------------------------------------------------------------------------------- /hackthebox/__pycache__/constants.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/hackthebox/__pycache__/constants.cpython-310.pyc -------------------------------------------------------------------------------- /hackthebox/__pycache__/fortress.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/hackthebox/__pycache__/fortress.cpython-310.pyc -------------------------------------------------------------------------------- /templates_md/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/templates_md/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /templates_md/__pycache__/index_template.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/templates_md/__pycache__/index_template.cpython-310.pyc -------------------------------------------------------------------------------- /HTB/.obsidian/plugins/table-editor-obsidian/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatType": "normal", 3 | "showRibbonIcon": true, 4 | "bindEnter": true, 5 | "bindTab": true 6 | } -------------------------------------------------------------------------------- /HTB/.obsidian/themes/Aqua/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Aqua", 3 | "version": "0.0.0", 4 | "minAppVersion": "0.16.0", 5 | "author": "sahilpatel09" 6 | } -------------------------------------------------------------------------------- /templates_md/__pycache__/machine_template.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/templates_md/__pycache__/machine_template.cpython-310.pyc -------------------------------------------------------------------------------- /templates_md/__pycache__/template_chart_radar.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/templates_md/__pycache__/template_chart_radar.cpython-310.pyc -------------------------------------------------------------------------------- /templates_md/__pycache__/template_char_user_rating.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x4xel/HTNotes/HEAD/templates_md/__pycache__/template_char_user_rating.cpython-310.pyc -------------------------------------------------------------------------------- /HTB/.obsidian/community-plugins.json: -------------------------------------------------------------------------------- 1 | [ 2 | "dataview", 3 | "metadata-menu", 4 | "buttons", 5 | "obsidian-shellcommands", 6 | "obsidian-chartsview-plugin", 7 | "table-editor-obsidian" 8 | ] -------------------------------------------------------------------------------- /HTB/.obsidian/themes/Minimal/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Minimal", 3 | "version": "6.1.9", 4 | "minAppVersion": "0.16.0", 5 | "author": "@kepano", 6 | "authorUrl": "https://twitter.com/kepano" 7 | } 8 | -------------------------------------------------------------------------------- /HTB/.obsidian/themes/Blue Topaz/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Blue Topaz", 3 | "version": "20221104", 4 | "minAppVersion": "1.0.0", 5 | "author": "WhyI", 6 | "authorUrl": "https://github.com/whyt-byte" 7 | } -------------------------------------------------------------------------------- /templates_md/post_exploitation_template.py: -------------------------------------------------------------------------------- 1 | 2 | def get_post_exploitation_template(): 3 | return '''### Local enumeration 4 | 5 | - 6 | 7 | ### Atack vector 8 | 9 | - 10 | 11 | --- 12 | 13 | 14 | 15 | ''' 16 | -------------------------------------------------------------------------------- /templates_md/exploitation_template.py: -------------------------------------------------------------------------------- 1 | 2 | def get_exploitation_template(): 3 | return '''## Payload / CVE used 4 | - 5 | - 6 | 7 | --- 8 | 9 | ## Lateral Movement 10 | 11 | ### Local enumeration 12 | 13 | - 14 | 15 | ### Atack vector 16 | 17 | - 18 | 19 | --- 20 | 21 | ''' 22 | -------------------------------------------------------------------------------- /HTB/.obsidian/plugins/buttons/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "buttons", 3 | "name": "Buttons", 4 | "description": "Create Buttons in your Obsidian notes to run commands, open links, and insert templates", 5 | "version": "0.4.19", 6 | "author": "shabegom", 7 | "authorUrl": "https://shbgm.ca", 8 | "isDesktopOnly": false, 9 | "minAppVersion": "0.12.8" 10 | } 11 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.27.1 2 | pytest==7.0.1 3 | python-dotenv==0.19.2 4 | pytest-cov==3.0.0 5 | pytest-xdist==2.5.0 6 | Sphinx==4.4.0 7 | sphinx-rtd-theme==1.0.0 8 | python-dateutil==2.8.2 9 | twine 10 | werkzeug==2.0.0 11 | pyhackthebox==0.5.6 12 | flask==2.0.3 13 | # Static type checking 14 | mypy 15 | types-requests 16 | types-python-dateutil 17 | 18 | 19 | -------------------------------------------------------------------------------- /HTB/.obsidian/plugins/dataview/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "dataview", 3 | "name": "Dataview", 4 | "version": "0.5.47", 5 | "minAppVersion": "0.13.11", 6 | "description": "Complex data views for the data-obsessed.", 7 | "author": "Michael Brenan ", 8 | "authorUrl": "https://github.com/blacksmithgu", 9 | "isDesktopOnly": false 10 | } 11 | -------------------------------------------------------------------------------- /hackthebox/__init__.py: -------------------------------------------------------------------------------- 1 | from .challenge import Challenge 2 | from .endgame import Endgame 3 | from .errors import * 4 | from .fortress import Fortress 5 | from .htb import HTBClient, HTBObject 6 | from .machine import Machine, MachineInstance 7 | from .search import Search 8 | from .solve import * 9 | from .team import Team 10 | from .user import User 11 | from .vpn import VPNServer 12 | -------------------------------------------------------------------------------- /HTB/.obsidian/plugins/metadata-menu/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "metadata-menu", 3 | "name": "Metadata Menu", 4 | "version": "0.3.4", 5 | "minAppVersion": "0.15.3", 6 | "description": "For data quality enthousiasts (and dataview lovers): manage the metadata of your notes.", 7 | "author": "mdelobelle", 8 | "authorUrl": "https://github.com/mdelobelle", 9 | "isDesktopOnly": false 10 | } -------------------------------------------------------------------------------- /HTB/.obsidian/core-plugins.json: -------------------------------------------------------------------------------- 1 | [ 2 | "file-explorer", 3 | "global-search", 4 | "switcher", 5 | "graph", 6 | "backlink", 7 | "outgoing-link", 8 | "tag-pane", 9 | "page-preview", 10 | "daily-notes", 11 | "templates", 12 | "note-composer", 13 | "command-palette", 14 | "editor-status", 15 | "starred", 16 | "outline", 17 | "word-count", 18 | "file-recovery" 19 | ] -------------------------------------------------------------------------------- /HTB/.obsidian/plugins/table-editor-obsidian/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "table-editor-obsidian", 3 | "name": "Advanced Tables", 4 | "author": "Tony Grosinger", 5 | "authorUrl": "https://grosinger.net", 6 | "description": "Improved table navigation, formatting, manipulation, and formulas", 7 | "isDesktopOnly": false, 8 | "minAppVersion": "0.13.8", 9 | "version": "0.17.3", 10 | "js": "main.js" 11 | } 12 | -------------------------------------------------------------------------------- /HTB/.obsidian/plugins/obsidian-chartsview-plugin/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "obsidian-chartsview-plugin", 3 | "name": "Charts View", 4 | "version": "1.1.9", 5 | "minAppVersion": "0.9.12", 6 | "description": "Data visualization solution in Obsidian based on Ant Design Charts.", 7 | "author": "caronchen", 8 | "authorUrl": "https://github.com/caronchen/obsidian-chartsview-plugin", 9 | "isDesktopOnly": false 10 | } 11 | -------------------------------------------------------------------------------- /HTB/.obsidian/plugins/obsidian-shellcommands/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "obsidian-shellcommands", 3 | "name": "Shell commands", 4 | "version": "0.16.0", 5 | "minAppVersion": "0.15.4", 6 | "description": "You can predefine system commands that you want to run frequently, and assign hotkeys for them. For example open external applications. Automatic execution is also supported, and execution via URI links.", 7 | "author": "Jarkko Linnanvirta", 8 | "authorUrl": "", 9 | "isDesktopOnly": true 10 | } 11 | -------------------------------------------------------------------------------- /templates_md/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | #imports templates 4 | from .index_template import get_index_template 5 | from .recon_template import get_recon_template 6 | from .exploitation_template import get_exploitation_template 7 | from .post_exploitation_template import get_post_exploitation_template 8 | 9 | 10 | #imports machine info 11 | from .template_char_user_rating import get_template_char_user_rating 12 | from .template_chart_radar import get_template_chart_radar 13 | from .machine_template import get_machine_template 14 | 15 | -------------------------------------------------------------------------------- /HTB/.obsidian/plugins/obsidian-chartsview-plugin/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "theme": "dark", 3 | "dataPath": "", 4 | "backgroundColor": "transparent", 5 | "paddingTop": 0, 6 | "paddingRight": 0, 7 | "paddingBottom": 0, 8 | "paddingLeft": 0, 9 | "showExportBtn": false, 10 | "wordCountFilter": "[A-z]{1,2}\n[0-9]+\n(?=[MDCLXVI])M*(C[MD]|D?C*)(X[CL]|L?X*)(I[XV]|V?I*)\nand\nare\nbut\ndid\nfor\nget\ngot\nhad\nhas\nher\nhim\nhis\nits\nnot\nour\nshe\nthe\nwas\nyou\nbeen\nfrom\nhave\ninto\nmine\nours\nthat\nthem\nthey\nthis\nwent\nwere\nwith\nthese\nthose" 11 | } -------------------------------------------------------------------------------- /HTB/.obsidian/plugins/table-editor-obsidian/styles.css: -------------------------------------------------------------------------------- 1 | .HyperMD-table-row span.cm-inline-code { 2 | font-size: 100%; 3 | } 4 | 5 | .widget-icon { 6 | width: 20px; 7 | height: 20px; 8 | fill: var(--text-muted); 9 | } 10 | 11 | .widget-icon:hover { 12 | fill: var(--text-normal); 13 | } 14 | 15 | .advanced-tables-csv-export textarea { 16 | height: 200px; 17 | width: 100%; 18 | } 19 | 20 | .advanced-tables-donation { 21 | width: 70%; 22 | margin: 0 auto; 23 | text-align: center; 24 | } 25 | 26 | .advanced-tables-donate-button { 27 | margin: 10px; 28 | } 29 | -------------------------------------------------------------------------------- /HTB/.obsidian/graph.json: -------------------------------------------------------------------------------- 1 | { 2 | "collapse-filter": false, 3 | "search": "", 4 | "showTags": true, 5 | "showAttachments": false, 6 | "hideUnresolved": false, 7 | "showOrphans": true, 8 | "collapse-color-groups": true, 9 | "colorGroups": [], 10 | "collapse-display": false, 11 | "showArrow": false, 12 | "textFadeMultiplier": 0, 13 | "nodeSizeMultiplier": 1, 14 | "lineSizeMultiplier": 1, 15 | "collapse-forces": true, 16 | "centerStrength": 0.518713248970312, 17 | "repelStrength": 10, 18 | "linkStrength": 1, 19 | "linkDistance": 250, 20 | "scale": 0.2972991379960998, 21 | "close": true 22 | } -------------------------------------------------------------------------------- /HTB/.obsidian/plugins/metadata-menu/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "presetFields": [], 3 | "fileClassQueries": [], 4 | "displayFieldsInContextMenu": true, 5 | "globallyIgnoredFields": [], 6 | "classFilesPath": "fileClass/", 7 | "isAutosuggestEnabled": true, 8 | "fileClassAlias": "fileClass", 9 | "settingsVersion": 3, 10 | "globalFileClass": "", 11 | "firstDayOfWeek": 1, 12 | "enableLinks": true, 13 | "enableTabHeader": true, 14 | "enableEditor": true, 15 | "enableBacklinks": true, 16 | "enableStarred": true, 17 | "enableFileExplorer": true, 18 | "enableSearch": true, 19 | "buttonIcon": "clipboard-list" 20 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /HTB/.obsidian/plugins/dataview/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "renderNullAs": "\\-", 3 | "taskCompletionTracking": false, 4 | "taskCompletionUseEmojiShorthand": false, 5 | "taskCompletionText": "completion", 6 | "taskCompletionDateFormat": "yyyy-MM-dd", 7 | "warnOnEmptyResult": true, 8 | "refreshEnabled": true, 9 | "refreshInterval": 2500, 10 | "defaultDateFormat": "MMMM dd, yyyy", 11 | "defaultDateTimeFormat": "h:mm a - MMMM dd, yyyy", 12 | "maxRecursiveRenderDepth": 4, 13 | "tableIdColumnName": "File", 14 | "tableGroupColumnName": "Group", 15 | "allowHtml": true, 16 | "inlineQueryPrefix": "=", 17 | "inlineJsQueryPrefix": "$=", 18 | "inlineQueriesInCodeblocks": true, 19 | "enableDataviewJs": true, 20 | "enableInlineDataviewJs": false, 21 | "prettyRenderInlineFields": true 22 | } -------------------------------------------------------------------------------- /templates_md/index_template.py: -------------------------------------------------------------------------------- 1 | 2 | def get_index_template(): 3 | return '''--- 4 | tags: 5 | - tag1 6 | - tag2 7 | --- 8 | 9 | *This is the structure of the machine folder* 10 | ```dataview 11 | list 12 | WHERE contains(file.folder, this.file.folder) 13 | ``` 14 | 15 | 16 | ## Workflow machine 17 | ```mermaid 18 | flowchart TB 19 | A["10.10.10.X"] -- 80 --> B["Web"] 20 | A -- 22 --> C["FTP"] 21 | A -- 445,139 --> D["SMB"] 22 | D --> K["creds.txt"] 23 | B --> E["Wordpress"] 24 | E --> J["Admin Pane"] 25 | K --> J 26 | J --> F["RCE CVE-X-X"] 27 | F --> G["www-data"] 28 | G --> H["gtfobins nmap"] 29 | H --> I["root"] 30 | 31 | 32 | ``` 33 | ## Skills Acquired 34 | 35 | - Text 36 | - Text 37 | 38 | ## Tools used 39 | 40 | - https://github.com/carlospolop/PEASS-ng/tree/master/linPEAS 41 | - https://github.com/carlospolop/PEASS-ng/tree/master/winPEAS 42 | - https://sqlmap.org/ 43 | - ... 44 | 45 | ''' 46 | -------------------------------------------------------------------------------- /HTB/fileClass/Machine.md: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | mapWithTag: true 4 | id:: {"type":"Input","options":{}} 5 | name:: {"type":"Input","options":{}} 6 | active:: {"type":"Boolean","options":{}} 7 | user_flag:: {"type":"Boolean","options":{}} 8 | root_flag:: {"type":"Boolean","options":{}} 9 | avatar:: {"type":"File","options":{}} 10 | difficulty_text:: {"type":"Select","options":{"valuesList":{"1":"Easy","2":"Medium","3":"Hard","4":"Insane"},"sourceType":"ValuesList","valuesListNotePath":"","valuesFromDVQuery":""}} 11 | os:: {"type":"Select","options":{"valuesList":{"1":"Linux","2":"Windows"},"sourceType":"ValuesList","valuesListNotePath":"","valuesFromDVQuery":""}} 12 | stars:: {"type":"Input","options":{}} 13 | tags:: {"type":"Input","options":{}} 14 | created:: {"type":"Date","options":{"dateFormat":"MM-DD-YYYY","defaultInsertAsLink":"false"}} 15 | published:: {"type":"Date","options":{"dateFormat":"MM-DD-YYYY","defaultInsertAsLink":"false"}} 16 | --- 17 | 18 | 19 | 20 | #machine -------------------------------------------------------------------------------- /templates_md/recon_template.py: -------------------------------------------------------------------------------- 1 | 2 | def get_recon_template(): 3 | return '''Template 4 | 5 | --- 6 | 7 | ## Nmap Summary 8 | | Port | Software | Version | Status | 9 | | ---- | ----------- | --------------------------------------- | ------- | 10 | | 53 | domain | Simple DNS | open | 11 | | 80 | http | Apache http 2.4.29 | open | 12 | | 139 | netbios-sec | Microsfot Windows netbios-ssn | open | 13 | | 389 | ldap | Microsoft Windows Active Directory LDAP | open | 14 | 15 | 16 | ## Information Recon 17 | 18 | Ports tcp open in nmap format 19 | 20 | ```bash 21 | 22 | ``` 23 | 24 | Ports services and versions nmap format 25 | 26 | ```bash 27 | 28 | ``` 29 | 30 | Ports UDP nmap format 31 | 32 | ```bash 33 | 34 | ``` 35 | 36 | --- 37 | 38 | ## Enumeration 39 | 40 | ## Port 80 - HTTP (Apache) 41 | 42 | 43 | 44 | --- 45 | 46 | ''' 47 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /hackthebox/utils.py: -------------------------------------------------------------------------------- 1 | import re 2 | from datetime import timedelta 3 | 4 | 5 | def parse_delta(time: str) -> timedelta: 6 | """Generates a timedelta from a string 7 | 8 | Args: 9 | time: The delta as a string 10 | 11 | Returns: 12 | A datetime.timedelta object 13 | 14 | """ 15 | regex = re.compile( 16 | r"((?P\d+?)[Yy])? ?((?P\d+?)M)? ?" 17 | r"((?P\d+?)[Ww])? ?" 18 | r"((?P\d+?)[Dd])? ?((?P\d+?)[Hh])? ?" 19 | r"((?P\d+?)m)? ?((?P\d+?)[Ss])?" 20 | ) 21 | parts = regex.match(time) 22 | if not parts or parts.group() == "": 23 | raise ValueError 24 | parts_dict = parts.groupdict() 25 | time_params = { 26 | "years": 0, 27 | "months": 0, 28 | "weeks": 0, 29 | "days": 0, 30 | "hours": 0, 31 | "minutes": 0, 32 | "seconds": 0, 33 | } 34 | for (name, param) in parts_dict.items(): 35 | if param: 36 | time_params[name] = int(param) 37 | # Remove unsupported params for timedelta 38 | time_params["days"] += time_params["years"] * 365 39 | del time_params["years"] 40 | time_params["days"] += time_params["months"] * 30 41 | del time_params["months"] 42 | return timedelta(**time_params) 43 | -------------------------------------------------------------------------------- /HTB/.obsidian/plugins/obsidian-chartsview-plugin/styles.css: -------------------------------------------------------------------------------- 1 | .chartsview-donation { 2 | width: fit-content; 3 | margin: 25px auto; 4 | text-align: center; 5 | position: absolute; 6 | left: 200px; 7 | right: 0; 8 | bottom: 0; 9 | } 10 | 11 | .chartsview-donation a.paypal { 12 | display: inline-block; 13 | } 14 | 15 | .chartsview-thumbnail-container.is-selected .chartsview-thumbnail { 16 | display: unset; 17 | position: fixed; 18 | top: 0; 19 | right: 0; 20 | } 21 | 22 | .chartsview-thumbnail-container .chartsview-thumbnail { 23 | display: none; 24 | } 25 | 26 | /* #### Desktops #### */ 27 | @media screen and (min-width: 1024px) { 28 | .chartsview-thumbnail-container .chartsview-thumbnail img { 29 | width: 500px; 30 | } 31 | } 32 | 33 | /* #### Mobile Phones Portrait or Landscape #### */ 34 | @media screen and (max-device-width: 640px) { 35 | .chartsview-thumbnail-container .chartsview-thumbnail img { 36 | width: 300px; 37 | } 38 | } 39 | 40 | .chartsview-export-button { 41 | padding: 5px; 42 | position: relative; 43 | width: fit-content; 44 | display: flex; 45 | opacity: 0; 46 | transition: opacity 500ms; 47 | background-color: var(--interactive-accent); 48 | color: var(--text-on-accent); 49 | border-radius: 5px; 50 | cursor: pointer; 51 | } 52 | 53 | .block-language-chartsview:hover .chartsview-export-button { 54 | opacity: 1; 55 | } -------------------------------------------------------------------------------- /templates_md/template_chart_radar.py: -------------------------------------------------------------------------------- 1 | def get_template_chart_radar(user_average,author): 2 | return f''' 3 | ```chartsview 4 | #-----------------# 5 | #- chart type -# 6 | #-----------------# 7 | type: Radar 8 | 9 | #-----------------# 10 | #- chart data -# 11 | #-----------------# 12 | data: 13 | - item: "ENUM" 14 | user: "user" 15 | score: {user_average["enum"]} 16 | - item: "REAL" 17 | user: "user" 18 | score: {user_average["real"]} 19 | - item: "CVE" 20 | user: "user" 21 | score: {user_average["cve"]} 22 | - item: "CUSTOM" 23 | user: "user" 24 | score: {user_average["custom"]} 25 | - item: "CTF" 26 | user: "user" 27 | score: {user_average["ctf"]} 28 | - item: "ENUM" 29 | user: "author" 30 | score: {author["enum"]} 31 | - item: "REAL" 32 | user: "author" 33 | score: {author["real"]} 34 | - item: "CVE" 35 | user: "author" 36 | score: {author["cve"]} 37 | - item: "CUSTOM" 38 | user: "author" 39 | score: {author["custom"]} 40 | - item: "CTF" 41 | user: "author" 42 | score: {author["ctf"]} 43 | 44 | #-----------------# 45 | #- chart options -# 46 | #-----------------# 47 | options: 48 | xField: "item" 49 | yField: "score" 50 | seriesField: "user" 51 | meta: 52 | score: 53 | alias: "Score" 54 | min: 0 55 | nice: true 56 | xAxis: 57 | line: null 58 | tickLine: null 59 | yAxis: 60 | label: false 61 | grid: 62 | alternateColor: "rgba(0, 0, 0, 0.04)" 63 | point: [] 64 | area: [] 65 | ``` 66 | ''' -------------------------------------------------------------------------------- /HTB/⚡ Start Machine ⚡.md: -------------------------------------------------------------------------------- 1 | 2 | ## Script Workflow for - Create Machine Note 3 | 4 | *This is simple button that will automate the creation of starting notes that will improve your quality of pwn. Just click on it and select the name of the machine you would like to compromise.* 5 | 6 | ```mermaid 7 | flowchart TB 8 | A["Button Action"] -- machine_name --> J["API Request"] 9 | J -- Not found --> N["Exit"] 10 | J -- Found Machine --> B["Search folder name"] 11 | B -- Found --> E["Just update the machine info"] 12 | B -- Not Found --> C["Create folder structure"] 13 | C --> L["assets"] 14 | C --> F["00-index"] 15 | C --> G["01-recon"] 16 | C --> H["02-exploitation"] 17 | C --> I["03-post-exploitation"] 18 | C -- HTB API request --> K["Create Machine file info"] 19 | 20 | ``` 21 | 22 | 23 | ```button 24 | name Create Machine Note 25 | type link 26 | action obsidian://shell-commands/?vault=HTB&execute=m6e17y5rts 27 | templater true 28 | color blue 29 | ``` 30 | 31 | 32 | --------------------- 33 | 34 | ## Script Workflow for - Update Machines info 35 | *As you can see in the diagram below, you can update the machine info of all the machines that has its folder name under /Machines folder* 36 | 37 | ```mermaid 38 | flowchart TB 39 | A["Button Action"] --> B["Search Markdown fileClass Machine"] 40 | B --> C["HTB API conn with file name as param"] 41 | C --> D["Create new file with updated attributes"] 42 | D --> C 43 | 44 | 45 | ``` 46 | 47 | 48 | ```button 49 | name Update Machines info 50 | type link 51 | action obsidian://shell-commands/?vault=HTB&execute=usnoddh2no 52 | templater true 53 | color blue 54 | Custom Class custom-buttom 55 | ``` 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /HTB/Index.md: -------------------------------------------------------------------------------- 1 | 2 | *Your vault summary has the following instances* 3 | 4 | ## Machines in vault 5 | 6 | ```dataviewjs 7 | const {fieldModifier: f} = this.app.plugins.plugins["metadata-menu"].api; 8 | const vault_path = this.app.vault.adapter.basePath 9 | dv.table(['Name', 'Avatar','Difficulty','Stars','OS'], 10 | dv.pages("#machine") 11 | .filter(p => p.file.path.includes("Machine")) 12 | .filter(p => !p.file.path.includes("fileClass")) 13 | .filter(p => !p.file.path.includes("templates")) 14 | .map(p => [ 15 | p.file.link, 16 | '', 17 | f(dv,p,"difficulty_text"), 18 | '

⭐️ '+ p.stars+'

', 19 | '' 20 | 21 | ]).sort(b => b.created ) 22 | ) 23 | ``` 24 | 25 | ---------------------- 26 | 27 | ## Machines in vault with USER but not ROOT 28 | 29 | 30 | ```dataviewjs 31 | const {fieldModifier: f} = this.app.plugins.plugins["metadata-menu"].api; 32 | 33 | dv.table(['Name', 'Avatar','Difficulty','Stars','OS'], 34 | dv.pages("#machine") 35 | .filter(p => p.file.path.includes("Machine")) 36 | .filter(p => !p.file.path.includes("fileClass")) 37 | .filter(p => !p.file.path.includes("templates")) 38 | .filter(p => !p.root_flag) 39 | .filter(p => p.user_flag) 40 | .map(p => [ 41 | p.file.link, 42 | '', 43 | f(dv,p,"difficulty_text"), 44 | '

⭐️ '+ p.stars+'

', 45 | '' 46 | 47 | ]).sort(b => b.created ) 48 | ) 49 | ``` 50 | 51 | ------------------- 52 | 53 | ## Machines in vault without USER and ROOT 54 | 55 | ```dataviewjs 56 | const {fieldModifier: f} = this.app.plugins.plugins["metadata-menu"].api; 57 | 58 | dv.table(['Name', 'Avatar','Difficulty','Stars','OS'], 59 | dv.pages("#machine") 60 | .filter(p => p.file.path.includes("Machine")) 61 | .filter(p => !p.file.path.includes("fileClass")) 62 | .filter(p => !p.file.path.includes("templates")) 63 | .filter(p => !p.root_flag) 64 | .filter(p => !p.user_flag) 65 | .map(p => [ 66 | p.file.link, 67 | '', 68 | f(dv,p,"difficulty_text"), 69 | '

⭐️ '+ p.stars+'

', 70 | '' 71 | 72 | ]).sort(b => b.created ) 73 | ) 74 | ``` 75 | -------------------------------------------------------------------------------- /hackthebox/content.py: -------------------------------------------------------------------------------- 1 | from typing import List, cast, Optional 2 | 3 | from . import htb 4 | from .challenge import Challenge 5 | from .errors import NotFoundException 6 | from .machine import Machine 7 | 8 | 9 | class Content: 10 | """Representation of a content fetch for a user on the platform 11 | 12 | Attributes: 13 | machines: Machines authored by the given user 14 | challenges: Challenges authored by the given user 15 | writeups: Currently not implemented; Write-ups associated with the given user 16 | items: A dict of all content items 17 | 18 | Args: 19 | userid: The user id 20 | """ 21 | 22 | _machines: Optional[List[Machine]] = None 23 | _challenges: Optional[List[Challenge]] = None 24 | 25 | _machine_ids: List[int] 26 | _challenge_ids: List[int] 27 | 28 | _is_resolved: bool = False 29 | _user_id: int 30 | 31 | @property 32 | def machines(self) -> List[Machine]: 33 | if self._machines is None: 34 | self._machines = [] 35 | for uid in self._machine_ids: 36 | try: 37 | self._machines.append(self._client.get_machine(uid)) 38 | except NotFoundException: 39 | pass 40 | return self._machines 41 | 42 | @property 43 | def challenges(self) -> List[Challenge]: 44 | if self._challenges is None: 45 | self._challenges = [] 46 | for uid in self._challenge_ids: 47 | try: 48 | self._challenges.append(self._client.get_challenge(uid)) 49 | except NotFoundException: 50 | pass 51 | return self._challenges 52 | 53 | @property 54 | def items(self) -> dict: 55 | self._is_resolved = True 56 | return {"machines": self.machines, "challenges": self.challenges} 57 | 58 | def __len__(self): 59 | return len(self._machine_ids) + len(self._challenge_ids) 60 | 61 | def __repr__(self): 62 | return f"" 63 | 64 | def __str__(self): 65 | return repr(self) 66 | 67 | def __init__(self, userid: int, client: htb.HTBClient): 68 | self._user_id = userid 69 | self._client = client 70 | data = cast(dict, self._client.do_request(f"user/profile/content/{userid}"))[ 71 | "profile" 72 | ]["content"] 73 | self._machine_ids = [x["id"] for x in data.get("machines", [])] 74 | self._challenge_ids = [x["id"] for x in data.get("challenges", [])] 75 | -------------------------------------------------------------------------------- /templates_md/machine_template.py: -------------------------------------------------------------------------------- 1 | from templates_md import get_template_char_user_rating, get_template_chart_radar 2 | import datetime 3 | 4 | 5 | 6 | 7 | def get_machine_template(VAULT_PATH,machine_data,user_average,author,user_rating,machine_tags): 8 | 9 | if machine_data.user_owned: 10 | user_owned = "✅" 11 | else: 12 | user_owned = "❌" 13 | machine_data.user_owned = False 14 | 15 | if machine_data.root_owned: 16 | root_owned = "✅" 17 | else: 18 | root_owned = "❌" 19 | machine_data.root_owned = False 20 | 21 | if machine_data.active: 22 | active = "✅" 23 | else: 24 | active = "❌" 25 | 26 | 27 | time_today = datetime.datetime.now() 28 | string_tags = "" 29 | for tag in machine_tags: 30 | tag_with_no_spaces = tag["name"].replace(" ", "_") 31 | string_tags = string_tags + "#" + tag_with_no_spaces + " " 32 | return f''' 33 | --- 34 | fileClass: Machine 35 | --- 36 | 37 |

38 | 39 | #machine 40 | 41 | ## Operation system - {machine_data.os} 42 | 43 | 44 | ## Metadata 45 | 46 | | | | 47 | | ---------------- | - | 48 | | ID |{machine_data.id} | 49 | | Name |{machine_data.name} | 50 | | Active |{active} | 51 | | User Flag |{user_owned} | 52 | | Root Flag |{root_owned}| 53 | | Difficulty Text |{machine_data.difficulty} | 54 | | Stars |⭐️ {machine_data.stars} | 55 | | Created Note |{time_today.strftime("%m/%d/%y")} | 56 | | Published |{machine_data.release_date.strftime("%m/%d/%y")} | 57 | | tags |{string_tags} | 58 | 59 |

60 | id:: {machine_data.id} 61 | active:: {machine_data.active} 62 | name:: {machine_data.name} 63 | os::{machine_data.os} 64 | user_flag:: {machine_data.user_owned} 65 | root_flag:: {machine_data.root_owned} 66 | difficulty_text:: {machine_data.difficulty} 67 | stars:: {machine_data.stars} 68 | created:: {time_today.strftime("%m/%d/%Y")} 69 | published:: {machine_data.release_date.strftime("%m/%d/%y")} 70 | avatar:: {machine_data.avatar} 71 | tags:: {string_tags} 72 |

73 | 74 | ## Statistics 75 | 76 | {get_template_chart_radar(user_average, author)} 77 | 78 | 79 | ### User rating 80 | 81 | {get_template_char_user_rating(user_rating)} 82 | 83 | 84 | ```button 85 | name Update this Machine info 86 | type link 87 | action obsidian://shell-commands/?vault=HTB&execute=g7sm2q030y 88 | templater true 89 | ``` 90 | 91 | ''' 92 | -------------------------------------------------------------------------------- /hackthebox/fortress.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from typing import List, cast 3 | 4 | from . import htb 5 | from .errors import IncorrectFlagException 6 | 7 | 8 | class Fortress(htb.HTBObject): 9 | """The class representing Hack The Box fortresses 10 | 11 | Attributes: 12 | id: The ID of the Fortress 13 | name: The name of the Fortress 14 | image: The relative URL of the Fortress' image 15 | num_flags: The number of available flags 16 | 17 | reset_votes: The number of votes to reset the Fortress 18 | progress: The active user's progress through the Fortress, out of 100 19 | flags: The list of flags available 20 | company: The Fortress' associated Company 21 | ip: IP address the Fortress can be contacted on 22 | 23 | """ 24 | 25 | name: str 26 | image: str 27 | num_flags: int 28 | 29 | _detailed_attributes = ("reset_votes", "progress", "flags", "company", "ip") 30 | reset_votes: int 31 | progress: int 32 | flags: List 33 | company: Company 34 | ip: str 35 | 36 | def submit(self, flag: str): 37 | """Submits a flag for an Fortress 38 | 39 | Args: 40 | flag: The flag for the Fortress 41 | 42 | """ 43 | submission = cast( 44 | dict, 45 | self._client.do_request( 46 | f"fortress/{self.id}/flag", 47 | json_data={ 48 | "flag": flag, 49 | }, 50 | ), 51 | ) 52 | if submission["message"] == "Wrong flag": 53 | raise IncorrectFlagException 54 | return True 55 | 56 | def __repr__(self): 57 | return f"" 58 | 59 | def __init__(self, data: dict, client: htb.HTBClient, summary=False): 60 | self._client = client 61 | self._detailed_func = client.get_fortress # type: ignore 62 | self.id = data["id"] 63 | self.name = data["name"] 64 | self.image = data["image"] 65 | if summary: 66 | self.num_flags = data["number_of_flags"] 67 | self._is_summary = True 68 | else: 69 | self.num_flags = len(data["flags"]) 70 | self.company = Company(data["company"]) 71 | self.reset_votes = data["reset_votes"] 72 | self.progress = data["progress_percent"] 73 | self.flags = data["flags"] 74 | self.ip = data["ip"] 75 | 76 | 77 | class Company: 78 | """Representation of a company registered on Hack The Box 79 | 80 | Attributes: 81 | id: The Company ID 82 | name: The Company name 83 | description: The Company description 84 | url: The Company website 85 | image: The Company logo 86 | 87 | """ 88 | 89 | id: int 90 | name: str 91 | description: str 92 | url: str 93 | image: str 94 | 95 | def __init__(self, data: dict): 96 | self.id = data["id"] 97 | self.name = data["name"] 98 | self.description = data["description"] 99 | self.url = data["url"] 100 | self.image = data["image"] 101 | -------------------------------------------------------------------------------- /HTB/.obsidian/themes/Aqua/theme.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --red: #b00; 3 | --blue: #00b; 4 | --navy: #0a192f; 5 | --light-navy: #112240; 6 | --lightest-navy: #233554; 7 | --slate: #8892b0; 8 | --light-slate: #a8b2d1; 9 | --lightest-slate: #ccd6f6; 10 | --white: #e6f1ff; 11 | --green: #64ffda; 12 | } 13 | 14 | :root 15 | { 16 | --font-size-normal: 18px; 17 | --font-size-code: 13.5px; 18 | --font-size-side-dock: 13.5px; 19 | --font-size-side-dock-title: 15px; 20 | --font-size-status-bar: 12px; 21 | --font-size-h1: 23px; 22 | --font-size-h2: 22px; 23 | --font-size-h3: 21px; 24 | --font-size-h4: 20px; 25 | --font-size-h5: 19px; 26 | --font-size-h6: 18px; 27 | --font-family-editor: Open Sans, Avenir Next, sans-serif; 28 | --font-family-preview: Gerogia, Avenir Next, sans-serif; 29 | --dark0: 30 | } 31 | 32 | .theme-light, 33 | .theme-dark { 34 | --navy-shadow: rgba(2,12,27,0.7); 35 | --navy-shadow1: var(--green); 36 | --back-shadow: 0 5px 30px 10px var(--navy-shadow);; 37 | --background-primary: var(--navy); 38 | --background-primary-alt: #1d2021; 39 | --background-secondary: var(--light-navy); 40 | --background-secondary-alt: var(--lightest-navy); 41 | --background-accent: #fff; 42 | --background-modifier-border: hsla(0,0%,100%,.16); 43 | --background-modifier-form-field: rgba(0, 0, 0, 0.2); 44 | --background-modifier-form-field-highlighted: rgba(0, 0, 0, 0.5); 45 | --background-modifier-box-shadow: rgba(0, 0, 0, 0.3); 46 | --background-modifier-success: #197300; /*no sure*/ 47 | --background-modifier-error: #3d0000; /*no sure*/ 48 | --background-modifier-error-rgb: 61, 0, 0; 49 | --background-modifier-error-hover: #470000; 50 | --background-modifier-cover: rgba(0, 0, 0, 0.6); 51 | --text-accent: #fe8019; 52 | --text-accent-hover: #83a598; 53 | --text-normal: #fbf1c7; 54 | --text-muted: #d5c4a1; 55 | --text-faint: #bdae93; 56 | --text-highlight-bg: #b57614; 57 | --text-error: var(--light-slate); 58 | --text-error-hover: #990000; 59 | --text-selection: rgba(65, 83, 88, 0.99); 60 | --text-on-accent: #fbf1c7; 61 | --interactive-normal: #b57614; 62 | --interactive-hover: #fe8019; 63 | --interactive-accent: var(--lightest-navy); 64 | --interactive-accent-rgb: 123, 108, 217; 65 | --interactive-accent-hover: #fe8019; 66 | --scrollbar-active-thumb-bg: rgba(0, 0, 0, 0.5); 67 | --scrollbar-bg: rgba(0, 0, 0, 0.05); 68 | --scrollbar-thumb-bg: rgba(0, 0, 0, 0.3); 69 | --text-title-h1: var(--lightest-slate); 70 | --text-title-h2: var(--light-slate); 71 | --text-title-h3: var(--slate); 72 | --text-title-h4: var(--slate); 73 | --text-title-h5: #458588; 74 | --text-title-h6: #b16286; 75 | --inline-code: #83a598; 76 | --code-block: #83a598; 77 | --pre-code: #3c3836; 78 | --blockquote-border: #b57614; 79 | --vim-cursor: #d65d0e; 80 | --border-color: #504945; 81 | } 82 | input[type='range']::-webkit-slider-thumb { 83 | background: var(--green); 84 | } 85 | -------------------------------------------------------------------------------- /hackthebox/errors.py: -------------------------------------------------------------------------------- 1 | class HtbException(Exception): 2 | """Base exception class for `hackthebox`""" 3 | 4 | pass 5 | 6 | 7 | class ApiError(HtbException): 8 | """The API responded in an unexpected way""" 9 | 10 | 11 | class AuthenticationException(HtbException): 12 | """An error authenticating to the API""" 13 | 14 | pass 15 | 16 | 17 | class NotFoundException(HtbException): 18 | """The API returned a 404 response for this request""" 19 | 20 | pass 21 | 22 | 23 | class MissingEmailException(AuthenticationException): 24 | """An email was not given where it was required""" 25 | 26 | pass 27 | 28 | 29 | class MissingPasswordException(AuthenticationException): 30 | """A password was not given where it was required""" 31 | 32 | pass 33 | 34 | 35 | class MissingOTPException(AuthenticationException): 36 | """An OTP was not given but 2FA is enabled""" 37 | 38 | pass 39 | 40 | 41 | class IncorrectOTPException(AuthenticationException): 42 | """An OTP was given but not accepted""" 43 | 44 | pass 45 | 46 | 47 | class VpnException(HtbException): 48 | """An error associated with the VPN""" 49 | 50 | pass 51 | 52 | 53 | class CannotSwitchWithActive(VpnException): 54 | """Failed to switch VPN because the user has an active machine""" 55 | 56 | pass 57 | 58 | 59 | class MachineException(HtbException): 60 | """An error associated with a machine""" 61 | 62 | pass 63 | 64 | 65 | class TooManyResetAttempts(MachineException): 66 | """Error for too many reset attempts""" 67 | 68 | pass 69 | 70 | 71 | class UnknownSolveException(HtbException): 72 | """An unknown solve type was passed""" 73 | 74 | pass 75 | 76 | 77 | class SolveError(HtbException): 78 | """Exceptions for solving""" 79 | 80 | 81 | class IncorrectFlagException(SolveError): 82 | """An incorrect flag was submitted""" 83 | 84 | pass 85 | 86 | 87 | class UserAlreadySubmitted(SolveError): 88 | """Player has already completed the user flag for this box""" 89 | 90 | pass 91 | 92 | 93 | class RootAlreadySubmitted(SolveError): 94 | """Player has already completed the root flag for this box""" 95 | 96 | 97 | class IncorrectArgumentException(HtbException): 98 | """An incorrectly formatted argument was passed""" 99 | 100 | reason: str 101 | 102 | def __str__(self): 103 | return repr(self) 104 | 105 | def __repr__(self): 106 | return f"IncorrectArgumentException(reason='{self.reason}')" 107 | 108 | def __init__(self, reason: str): 109 | self.reason = reason 110 | 111 | pass 112 | 113 | 114 | class NoDockerException(HtbException): 115 | """A challenge was 'started' when no Docker is available""" 116 | 117 | pass 118 | 119 | 120 | class NoDownloadException(HtbException): 121 | """A challenge was 'downloaded' when no download is available""" 122 | 123 | pass 124 | 125 | 126 | class RateLimitException(HtbException): 127 | """An internal ratelimit to prevent spam was violated""" 128 | 129 | def __init__(self, message): 130 | print(message) 131 | super().__init__(message) 132 | 133 | 134 | class CacheException(HtbException): 135 | """There was an issue with the token cache""" 136 | 137 | pass 138 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct - HTNotes 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to a positive environment for our 15 | community include: 16 | 17 | * Demonstrating empathy and kindness toward other people 18 | * Being respectful of differing opinions, viewpoints, and experiences 19 | * Giving and gracefully accepting constructive feedback 20 | * Accepting responsibility and apologizing to those affected by our mistakes, 21 | and learning from the experience 22 | * Focusing on what is best not just for us as individuals, but for the 23 | overall community 24 | 25 | Examples of unacceptable behavior include: 26 | 27 | * The use of sexualized language or imagery, and sexual attention or 28 | advances 29 | * Trolling, insulting or derogatory comments, and personal or political attacks 30 | * Public or private harassment 31 | * Publishing others' private information, such as a physical or email 32 | address, without their explicit permission 33 | * Other conduct which could reasonably be considered inappropriate in a 34 | professional setting 35 | 36 | ## Our Responsibilities 37 | 38 | Project maintainers are responsible for clarifying and enforcing our standards of 39 | acceptable behavior and will take appropriate and fair corrective action in 40 | response to any instances of unacceptable behavior. 41 | 42 | Project maintainers have the right and responsibility to remove, edit, or reject 43 | comments, commits, code, wiki edits, issues, and other contributions that are 44 | not aligned to this Code of Conduct, or to ban 45 | temporarily or permanently any contributor for other behaviors that they deem 46 | inappropriate, threatening, offensive, or harmful. 47 | 48 | ## Scope 49 | 50 | This Code of Conduct applies within all community spaces, and also applies when 51 | an individual is officially representing the community in public spaces. 52 | Examples of representing our community include using an official e-mail address, 53 | posting via an official social media account, or acting as an appointed 54 | representative at an online or offline event. 55 | 56 | ## Enforcement 57 | 58 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 59 | reported to the community leaders responsible for enforcement at <0x4xel@gmail.com>. 60 | All complaints will be reviewed and investigated promptly and fairly. 61 | 62 | All community leaders are obligated to respect the privacy and security of the 63 | reporter of any incident. 64 | 65 | ## Attribution 66 | 67 | This Code of Conduct is adapted from the [Contributor Covenant](https://contributor-covenant.org/), version 68 | [1.4](https://www.contributor-covenant.org/version/1/4/code-of-conduct/code_of_conduct.md) and 69 | [2.0](https://www.contributor-covenant.org/version/2/0/code_of_conduct/code_of_conduct.md), 70 | and was generated by [contributing-gen](https://github.com/bttger/contributing-gen). 71 | -------------------------------------------------------------------------------- /hackthebox/endgame.py: -------------------------------------------------------------------------------- 1 | from typing import List, cast, Optional 2 | 3 | from . import htb 4 | from .user import User 5 | from .errors import IncorrectFlagException 6 | 7 | 8 | class Endgame(htb.HTBObject): 9 | """The class representing Hack The Box endgames 10 | 11 | Attributes: 12 | name: The name of the Endgame 13 | avatar: The relative URL of the Endgame's avatar 14 | cover_image: The relative URL of the Endgame's cover image 15 | retired: Whether the Endgame is retired 16 | vip: Whether the Endgame requires VIP 17 | points: The points awarded by the Endgame 18 | completions: The number of players who have completed the Endgame 19 | reset_votes: The number of votes to reset the Endgame 20 | entry_points: IP addresses the Endgame can be contacted on 21 | description: The HTML description of the Endgame 22 | 23 | """ 24 | 25 | name: str 26 | avatar: str 27 | cover_image: str 28 | retired: bool 29 | vip: bool 30 | 31 | _detailed_attributes = ( 32 | "points", 33 | "completions", 34 | "reset_votes", 35 | "entry_points", 36 | "description", 37 | ) 38 | points: int 39 | completions: int 40 | reset_votes: int 41 | entry_points: List[str] 42 | description: str 43 | 44 | _authors: Optional[List[User]] = None 45 | _author_ids: Optional[List[int]] = None 46 | 47 | def submit(self, flag: str): 48 | """Submits a flag for an Endgame 49 | 50 | Args: 51 | flag: The flag for the Endgame 52 | 53 | """ 54 | submission = cast( 55 | dict, 56 | self._client.do_request( 57 | f"endgame/{self.id}/flag", 58 | json_data={ 59 | "flag": flag, 60 | }, 61 | ), 62 | ) 63 | if submission["message"] == "Wrong flag": 64 | raise IncorrectFlagException 65 | return True 66 | 67 | @property 68 | def authors(self): 69 | """ 70 | The creators of the Endgame 71 | Returns: A list of Users 72 | 73 | """ 74 | if self._authors is None: 75 | self._authors = [] 76 | for user_id in self._author_ids: 77 | self._authors.append(self._client.get_user(user_id)) 78 | return self._authors 79 | 80 | def __repr__(self): 81 | return f"" 82 | 83 | def __init__(self, data: dict, client: htb.HTBClient, summary=False): 84 | self._client = client 85 | self._detailed_func = client.get_endgame # type: ignore 86 | self.id = data["id"] 87 | self.name = data["name"] 88 | self.avatar = data["avatar_url"] 89 | self.cover_image = data["cover_image_url"] 90 | self.retired = data["retired"] 91 | self._author_ids = [] 92 | self.vip = data["vip"] 93 | for user in data["creators"]: 94 | self._author_ids.append(user["id"]) 95 | if not summary: 96 | self.points = int(data["points"]) 97 | self.entry_points = data["entry_points"] 98 | self.completions = data["players_completed"] 99 | self.reset_votes = data["endgame_reset_votes"] 100 | self.description = data["description"] 101 | else: 102 | self._is_summary = True 103 | -------------------------------------------------------------------------------- /hackthebox/vpn.py: -------------------------------------------------------------------------------- 1 | """ 2 | Examples: 3 | Retrieving the current VPN server:: 4 | 5 | print(client.get_current_vpn_server()) 6 | 7 | Switching to a given VPN server:: 8 | 9 | req = input("What server? ") 10 | server = next(filter(lambda x: x.friendly_name == req), client.get_all_vpn_servers())) 11 | server.switch() 12 | server.download(path="/tmp/out.ovpn") 13 | 14 | """ 15 | 16 | from __future__ import annotations 17 | 18 | import os 19 | 20 | from . import htb 21 | from .errors import VpnException, CannotSwitchWithActive 22 | 23 | from typing import TYPE_CHECKING, cast 24 | 25 | if TYPE_CHECKING: 26 | from .htb import HTBClient 27 | 28 | 29 | class VPNServer(htb.HTBObject): 30 | """Class representing individual VPN servers provided by Hack The Box 31 | 32 | Attributes: 33 | friendly_name: Friendly name of the server 34 | 35 | Example: ``'US Free 1'`` 36 | 37 | current_clients: The number of currently connected clients 38 | location: The physical location of the server 39 | 40 | Example: ``'US'`` 41 | 42 | """ 43 | 44 | friendly_name: str 45 | current_clients: int 46 | location: str 47 | _detailed_func = lambda x: None 48 | 49 | # noinspection PyUnresolvedReferences 50 | def __init__(self, data: dict, client: "HTBClient", summary=False): 51 | self._client = client 52 | self.id = data["id"] 53 | self.friendly_name = data["friendly_name"] 54 | self.current_clients = data["current_clients"] 55 | self.location = data["location"] 56 | self.summary = summary 57 | 58 | def __repr__(self): 59 | return f"" 60 | 61 | def __str__(self): 62 | return f"{self.friendly_name}" 63 | 64 | def switch(self) -> bool: 65 | """ 66 | Switches the client to use this VPN server 67 | 68 | Returns: Whether the switch was completed successfully 69 | """ 70 | # TODO: Throw exception on failure 71 | result = cast( 72 | dict, 73 | self._client.do_request(f"connections/servers/switch/{self.id}", post=True), 74 | ) 75 | if result["status"] is True: 76 | return True 77 | if ( 78 | result["message"] 79 | == "You must stop your active machine before switching VPN" 80 | ): 81 | raise CannotSwitchWithActive 82 | raise VpnException 83 | 84 | def download(self, path=None, tcp=False) -> str: 85 | """ 86 | 87 | Args: 88 | path: The name of the OVPN file to download to. If none is provided, it is saved to the current directory. 89 | tcp: Download TCP instead of UDP 90 | 91 | Returns: The path of the file 92 | 93 | """ 94 | if path is None: 95 | path = os.path.join(os.getcwd(), f"{self.friendly_name}.ovpn") 96 | url = f"access/ovpnfile/{self.id}/0" 97 | if tcp: 98 | # Funky URL 99 | url += "/1" 100 | data = self._client.do_request(url, download=True) 101 | # We can't download VPN packs for servers we're not assigned to 102 | if b"You are not assigned" in data: 103 | self.switch() 104 | data = cast(bytes, self._client.do_request(url, download=True)) 105 | with open(path, "wb") as f: 106 | f.write(data) 107 | return path 108 | -------------------------------------------------------------------------------- /htb_api.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import argparse 4 | import Constants 5 | 6 | from hackthebox import HTBClient 7 | from templates_md import get_machine_template, get_machine_template, get_index_template, get_recon_template, get_exploitation_template, get_post_exploitation_template 8 | 9 | # Params 10 | parser = argparse.ArgumentParser(description='A test program.') 11 | parser.add_argument("-m", "--machine_name", help="Input of the machine", default="",required=False) 12 | parser.add_argument("-v", "--vault_path", help="Path of obsidian vault", default="",required=True) 13 | args = parser.parse_args() 14 | 15 | 16 | machine_name = args.machine_name 17 | VAULT_PATH = args.vault_path 18 | 19 | client = HTBClient(app_token=Constants.API_TOKEN) 20 | try: 21 | if machine_name == "": #Recursively update of all machines 22 | folder = VAULT_PATH + "Machines" 23 | sub_folders = [name for name in os.listdir(folder) if os.path.isdir(os.path.join(folder, name))] 24 | for sub_folder in sub_folders: 25 | os.system("/usr/bin/python3 " + VAULT_PATH + "../htb_api.py -m " + sub_folder + ' -v "' + VAULT_PATH + '"') 26 | print("Finished execution of update " + sub_folder ) 27 | exit() 28 | else: 29 | machine_data = client.get_machine(machine_name) 30 | except: 31 | if machine_name != "": #Checks only if machine_name does not exist 32 | print(f"{machine_name} not found.") 33 | exit() 34 | 35 | try: 36 | machine_tags = client.get_tags_machine(int(machine_data.id)) 37 | except: 38 | machine_tags = [] 39 | 40 | try: 41 | matrix = client.get_matrix(int(machine_data.id)) 42 | except: 43 | matrix = [] 44 | 45 | try: 46 | user_rating = client.get_user_rating(int(machine_data.id)) 47 | except: 48 | user_rating = [] 49 | 50 | 51 | author = matrix["maker"] 52 | user_average = matrix["aggregate"] 53 | MACHINE_FOLDER_PATH = VAULT_PATH + "Machines/" + machine_data.name 54 | 55 | #Call templates 56 | machine_template = get_machine_template(VAULT_PATH,machine_data,user_average,author, user_rating, machine_tags) 57 | 58 | #In case of first execution, create Machine Folder 59 | if not os.path.exists(VAULT_PATH + "Machines/"): 60 | os.makedirs(VAULT_PATH + "Machines/") 61 | 62 | 63 | # You can change me to define your folder structure 64 | if not os.path.exists(MACHINE_FOLDER_PATH): 65 | os.makedirs(MACHINE_FOLDER_PATH) 66 | print("Created machine root folder") 67 | if not os.path.exists(MACHINE_FOLDER_PATH + "/assets"): 68 | os.makedirs(MACHINE_FOLDER_PATH + "/assets") 69 | print(("Created assets folder")) 70 | with open(os.path.join(MACHINE_FOLDER_PATH, "00-index.md"), 'w') as temp_file: 71 | temp_file.writelines(get_index_template()) 72 | print("Created 00-index.md") 73 | with open(os.path.join(MACHINE_FOLDER_PATH, "01-recon.md"), 'w') as temp_file: 74 | temp_file.writelines(get_recon_template()) 75 | print("Created 01-recon.md") 76 | with open(os.path.join(MACHINE_FOLDER_PATH, "02-exploitation.md"), 'w') as temp_file: 77 | temp_file.writelines(get_exploitation_template()) 78 | print("Created 02-exploitation.md") 79 | with open(os.path.join(MACHINE_FOLDER_PATH, "03-post-exploitation.md"), 'w') as temp_file: 80 | temp_file.writelines(get_post_exploitation_template()) 81 | print("Created 03-post-exploitation.md") 82 | 83 | 84 | # Create the file info machine 85 | with open(os.path.join(MACHINE_FOLDER_PATH, machine_data.name + ".md"), 'w') as temp_file: 86 | temp_file.writelines(machine_template) 87 | print("Created/Updated machine file ") 88 | 89 | 90 | print("Exiting...") -------------------------------------------------------------------------------- /HTB/.obsidian/plugins/buttons/styles.css: -------------------------------------------------------------------------------- 1 | /* @settings 2 | 3 | name: Buttons 4 | id: buttons-styles 5 | settings: 6 | - 7 | id: button-background 8 | title: Background 9 | type: variable-themed-color 10 | format: hex 11 | opacity: false 12 | default-light: '#f5f6f8' 13 | default-dark: '#1b1b1b' 14 | - 15 | id: button-text 16 | title: Text 17 | type: variable-themed-color 18 | format: hex 19 | opacity: false 20 | default-light: '#1b1b1b' 21 | default-dark: '#f5f6f8' 22 | - 23 | id: button-border 24 | title: Border 25 | type: variable-themed-color 26 | format: hex 27 | opacity: false 28 | default-light: '#7a9486' 29 | default-dark: '#84a83a' 30 | - 31 | id: button-box-shadow 32 | title: Box Shadow 33 | type: variable-themed-color 34 | format: rgb 35 | opacity: true 36 | default-light: '#1b1b1b' 37 | default-dark: '#f5f6f8' 38 | - 39 | id: button-border-radius 40 | title: Border Radius 41 | type: variable-number 42 | format: px 43 | default: 5 44 | - 45 | id: button-size 46 | title: Font Size 47 | type: variable-number 48 | format: em 49 | default: 1 50 | 51 | */ 52 | 53 | .block-language-button { 54 | padding: 5px; 55 | } 56 | 57 | button.button-default { 58 | border: 0.5px solid var(--button-border, #7a9486); 59 | border-radius: var(--button-border-radius, 5px); 60 | background-color: var(--button-background); 61 | padding: 10px 30px; 62 | color: var(--button-text); 63 | text-decoration: none; 64 | font-size: var(--button-size); 65 | margin: 0 5px; 66 | box-shadow: 0 1px 3px var(--button-box-shadow, rgba(0, 0, 0, 0.12)), 67 | 0 1px 2px var(--button-box-shadow, rgba(0, 0, 0, 0.24)); 68 | transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); 69 | } 70 | 71 | button.button-default:hover { 72 | z-index: 100; 73 | box-shadow: 0 4px 4px var(--button-box-shadow, rgba(0, 0, 0, 0.25)), 74 | 0 10px 10px var(--button-box-shadow, rgba(0, 0, 0, 0.22)); 75 | transform: translate3d(0px, -1.5px, 0px); 76 | background-color: var(--button-background); 77 | } 78 | 79 | .theme-dark button.button-default { 80 | border: 0.5px solid var(--button-border, #84a83a); 81 | } 82 | 83 | .theme-dark button.button-default:hover { 84 | z-index: 100; 85 | box-shadow: 0 4px 4px var(--button-box-shadow, rgba(210, 210, 210, 0.25)), 86 | 0 10px 10px var(--button-box-shadow, rgba(210, 210, 210, 0.22)); 87 | transform: translate3d(0px, -1.5px, 0px); 88 | } 89 | 90 | button.button-inline { 91 | width: unset; 92 | height: unset; 93 | padding: 0 8px; 94 | } 95 | 96 | button.blue { 97 | background: #76b3fa; 98 | color: black; 99 | } 100 | 101 | button.red { 102 | background-color: red; 103 | } 104 | 105 | button.green { 106 | background: green; 107 | } 108 | 109 | button.yellow { 110 | background: yellow; 111 | color: black; 112 | } 113 | 114 | button.purple { 115 | background: #725585; 116 | } 117 | 118 | button.blue:hover { 119 | background: #76b3fa; 120 | color: black; 121 | } 122 | 123 | button.red:hover { 124 | background: red; 125 | } 126 | 127 | button.green:hover { 128 | background: green; 129 | } 130 | 131 | button.yellow:hover { 132 | background: yellow; 133 | color: black; 134 | } 135 | 136 | button.purple:hover { 137 | background: #725585; 138 | } 139 | 140 | .button-maker { 141 | max-width: 35rem; 142 | width: 35rem; 143 | overflow-y: auto; 144 | max-height: 30rem; 145 | padding-left: 0.5rem; 146 | padding-right: 0.5rem; 147 | overflow-x: hidden; 148 | } 149 | -------------------------------------------------------------------------------- /HTB/.obsidian/plugins/dataview/styles.css: -------------------------------------------------------------------------------- 1 | /** Live Preview padding fixes, specifically for DataviewJS custom HTML elements. */ 2 | .is-live-preview .block-language-dataviewjs > p, .is-live-preview .block-language-dataviewjs > span { 3 | line-height: 1.0; 4 | } 5 | 6 | /*****************/ 7 | /** Table Views **/ 8 | /*****************/ 9 | 10 | /* List View Default Styling; rendered internally as a table. */ 11 | .table-view-table { 12 | width: 100%; 13 | } 14 | 15 | .table-view-table > thead > tr, .table-view-table > tbody > tr { 16 | margin-top: 1em; 17 | margin-bottom: 1em; 18 | text-align: left; 19 | } 20 | 21 | .table-view-table > tbody > tr:hover { 22 | background-color: var(--text-selection) !important; 23 | } 24 | 25 | .table-view-table > thead > tr > th { 26 | font-weight: 700; 27 | font-size: larger; 28 | border-top: none; 29 | border-left: none; 30 | border-right: none; 31 | border-bottom: solid; 32 | 33 | max-width: 100%; 34 | } 35 | 36 | .table-view-table > tbody > tr > td { 37 | text-align: left; 38 | border: none; 39 | font-weight: 400; 40 | max-width: 100%; 41 | } 42 | 43 | .table-view-table ul, .table-view-table ol { 44 | margin-block-start: 0.2em !important; 45 | margin-block-end: 0.2em !important; 46 | } 47 | 48 | /** Rendered value styling for any view. */ 49 | .dataview-result-list-root-ul { 50 | padding: 0em !important; 51 | margin: 0em !important; 52 | } 53 | 54 | .dataview-result-list-ul { 55 | margin-block-start: 0.2em !important; 56 | margin-block-end: 0.2em !important; 57 | } 58 | 59 | /** Generic grouping styling. */ 60 | .dataview.result-group { 61 | padding-left: 8px; 62 | } 63 | 64 | /*******************/ 65 | /** Inline Fields **/ 66 | /*******************/ 67 | 68 | .dataview.inline-field-key { 69 | padding-left: 8px; 70 | padding-right: 8px; 71 | font-family: var(--font-monospace); 72 | background-color: var(--background-primary-alt); 73 | color: var(--text-nav-selected); 74 | } 75 | 76 | .dataview.inline-field-value { 77 | padding-left: 8px; 78 | padding-right: 8px; 79 | font-family: var(--font-monospace); 80 | background-color: var(--background-secondary-alt); 81 | color: var(--text-nav-selected); 82 | } 83 | 84 | .dataview.inline-field-standalone-value { 85 | padding-left: 8px; 86 | padding-right: 8px; 87 | font-family: var(--font-monospace); 88 | background-color: var(--background-secondary-alt); 89 | color: var(--text-nav-selected); 90 | } 91 | 92 | /***************/ 93 | /** Task View **/ 94 | /***************/ 95 | 96 | .dataview.task-list-item, .dataview.task-list-basic-item { 97 | margin-top: 3px; 98 | margin-bottom: 3px; 99 | transition: 0.4s; 100 | } 101 | 102 | .dataview.task-list-item:hover, .dataview.task-list-basic-item:hover { 103 | background-color: var(--text-selection); 104 | box-shadow: -40px 0 0 var(--text-selection); 105 | cursor: pointer; 106 | } 107 | 108 | /*****************/ 109 | /** Error Views **/ 110 | /*****************/ 111 | 112 | div.dataview-error-box { 113 | width: 100%; 114 | min-height: 150px; 115 | display: flex; 116 | align-items: center; 117 | justify-content: center; 118 | border: 4px dashed var(--background-secondary); 119 | } 120 | 121 | .dataview-error-message { 122 | color: var(--text-muted); 123 | text-align: center; 124 | } 125 | 126 | /*************************/ 127 | /** Additional Metadata **/ 128 | /*************************/ 129 | 130 | .dataview.small-text { 131 | font-size: smaller; 132 | color: var(--text-muted); 133 | margin-left: 3px; 134 | } 135 | 136 | .dataview.small-text::before { 137 | content: "("; 138 | } 139 | 140 | .dataview.small-text::after { 141 | content: ")"; 142 | } 143 | -------------------------------------------------------------------------------- /hackthebox/team.py: -------------------------------------------------------------------------------- 1 | from . import htb 2 | 3 | from typing import TYPE_CHECKING, cast, Optional 4 | 5 | if TYPE_CHECKING: 6 | from .user import User 7 | 8 | 9 | class Team(htb.HTBObject): 10 | """The class representing Hack The Box teams 11 | 12 | Attributes: 13 | name: The name of the Team 14 | points: The Team's total points 15 | motto: The Team motto 16 | description: The Team description 17 | country_name: The name of the country the Team is based in 18 | avatar_url: The relative URL of the Tean's avatar 19 | twitter: The Team's Twitter account 20 | facebook: The Team's Facebook account 21 | discord: The Team's Discord 22 | public: Whether the Team is publicly visible 23 | can_delete_avatar: Whether the active User can delete the avatar 24 | is_respected: Whether the active User has respected the Team 25 | join_request_sent: Whether the active User has sent a request to join the Team 26 | 27 | """ 28 | 29 | name: str 30 | 31 | _detailed_attributes = ( 32 | "points", 33 | "motto", 34 | "description", 35 | "country_name", 36 | "avatar_url", 37 | "cover_image_url", 38 | "twitter", 39 | "facebook", 40 | "discord", 41 | "public", 42 | "can_delete_avatar", 43 | "captain", 44 | "is_respected", 45 | "join_request_sent", 46 | ) 47 | points: int 48 | motto: str 49 | description: str 50 | country_name: str 51 | avatar_url: str 52 | cover_image_url: str 53 | twitter: str 54 | facebook: str 55 | discord: str 56 | public: bool 57 | can_delete_avatar: bool 58 | # noinspection PyUnresolvedReferences 59 | _captain: Optional["User"] = None 60 | is_respected: Optional[bool] = None 61 | join_request_sent: Optional[bool] = None 62 | _ranking: Optional[int] = None 63 | _captain_id: int 64 | 65 | def __repr__(self): 66 | return f"" 67 | 68 | def __init__(self, data: dict, client: htb.HTBClient, summary: bool = False): 69 | self._client = client 70 | self._detailed_func = client.get_team # type: ignore 71 | self.id = data["id"] 72 | self.name = data["name"] 73 | if not summary: 74 | self.points = data["points"] 75 | self.motto = data["motto"] 76 | self.description = data["description"] 77 | self.country_name = data["country_name"] 78 | self.avatar_url = data["avatar_url"] 79 | self.cover_image_url = data["cover_image_url"] 80 | self.twitter = data["twitter"] 81 | self.facebook = data["facebook"] 82 | self.discord = data["facebook"] 83 | self.public = data["public"] 84 | self.can_delete_avatar = data["can_delete_avatar"] 85 | self._captain_id = data["captain"]["id"] 86 | self.is_respected = data["is_respected"] 87 | self.join_request_sent = data["join_request_sent"] 88 | else: 89 | self._is_summary = True 90 | 91 | @property 92 | def ranking(self) -> int: 93 | """Retrieve the global ranking of the team 94 | 95 | Returns: 96 | 97 | """ 98 | if not self._ranking: 99 | data = cast(dict, self._client.do_request(f"team/stats/owns/{self.id}")) 100 | self._ranking = data["rank"] 101 | return cast(int, self._ranking) 102 | 103 | # noinspection PyUnresolvedReferences 104 | @property 105 | def captain(self) -> "User": 106 | from .user import User 107 | 108 | if not self._captain: 109 | self._captain = self._client.get_user(self._captain_id) 110 | return cast(User, self._captain) 111 | -------------------------------------------------------------------------------- /hackthebox/solve.py: -------------------------------------------------------------------------------- 1 | from . import htb 2 | from datetime import datetime 3 | 4 | from typing import Optional, TYPE_CHECKING, cast 5 | 6 | if TYPE_CHECKING: 7 | from .htb import HTBClient, HTBObject 8 | from .machine import Machine 9 | 10 | 11 | class Solve: 12 | """Representation of completion of Hack The Box content 13 | 14 | Attributes: 15 | id: The ID of the solved item 16 | name: The name of the solved item 17 | date: The date of the solve 18 | blood: Whether the solve was a first blood 19 | points: The points awarded from the solve 20 | item (HTBObject): The solved item 21 | 22 | """ 23 | 24 | _client: "HTBClient" 25 | _item: Optional["HTBObject"] = None # The solved item 26 | id: int 27 | name: str 28 | date: datetime 29 | blood: bool 30 | points: int 31 | 32 | def __init__(self, data: dict, client: "HTBClient"): 33 | self._client = client 34 | self.date = data["date"] 35 | self.blood = data["first_blood"] 36 | self.id = data["id"] 37 | self.name = data["name"] 38 | 39 | 40 | class MachineSolve(Solve): 41 | """Representation of solving a Machine""" 42 | 43 | type: str # User/Root 44 | 45 | def __repr__(self): 46 | return f"" 47 | 48 | @property 49 | def item(self): 50 | return self.machine 51 | 52 | # noinspection PyUnresolvedReferences 53 | @property 54 | def machine(self) -> "Machine": 55 | """The solved Machine""" 56 | from .machine import Machine 57 | 58 | if not self._item: 59 | self._item = self._client.get_machine(self.id) 60 | return cast(Machine, self._item) 61 | 62 | def __init__(self, data: dict, client: "htb.HTBClient"): 63 | super().__init__(data, client) 64 | self.type = data["type"] 65 | 66 | 67 | class ChallengeSolve(Solve): 68 | """Representation of solving a Challenge""" 69 | 70 | category: str 71 | 72 | def __repr__(self): 73 | return f"" 74 | 75 | @property 76 | def item(self): 77 | return self.challenge 78 | 79 | @property 80 | def challenge(self): 81 | """The solved Challenge""" 82 | if not self._item: 83 | self._item = self._client.get_challenge(self.id) 84 | return self._item 85 | 86 | def __init__(self, data: dict, client: "htb.HTBClient"): 87 | super().__init__(data, client) 88 | self.category = data["challenge_category"] 89 | 90 | 91 | class EndgameSolve(Solve): 92 | """Representation of solving a Endgame""" 93 | 94 | flag_name: str 95 | 96 | def __repr__(self): 97 | return f"" 98 | 99 | @property 100 | def item(self): 101 | return self.endgame 102 | 103 | @property 104 | def endgame(self): 105 | """The solved Endgame""" 106 | if not self._item: 107 | self._item = self._client.get_endgame(self.id) 108 | return self._item 109 | 110 | def __init__(self, data: dict, client: "htb.HTBClient"): 111 | super().__init__(data, client) 112 | self.flag_name = data["flag_title"] 113 | 114 | 115 | class FortressSolve(Solve): 116 | """Representation of solving a Fortress""" 117 | 118 | flag_name: str 119 | 120 | def __repr__(self): 121 | return f"" 122 | 123 | @property 124 | def item(self): 125 | return self.fortress 126 | 127 | @property 128 | def fortress(self): 129 | """The solved Fortress""" 130 | if not self._item: 131 | self._item = self._client.get_fortress(self.id) 132 | return self._item 133 | 134 | def __init__(self, data: dict, client: "htb.HTBClient"): 135 | super().__init__(data, client) 136 | self.flag_name = data["flag_title"] 137 | -------------------------------------------------------------------------------- /templates_md/template_char_user_rating.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | def dos(): 5 | return f''' 6 | ```chartsview 7 | #-----------------# 8 | #- chart type -# 9 | #-----------------# 10 | type: Column 11 | 12 | #-----------------# 13 | #- chart data -# 14 | #-----------------# 15 | data: 16 | - folder: "PIECE OF CAKE" 17 | count: {int(user_rating["1"]["user"]) + int(user_rating["1"]["root"])} 18 | 19 | - folder: "VERY EASY" 20 | count: {int(user_rating["2"]["user"]) + int(user_rating["2"]["root"])} 21 | 22 | - folder: "EASY" 23 | count: {int(user_rating["3"]["user"]) + int(user_rating["3"]["root"])} 24 | 25 | - folder: "NOT TO EASY" 26 | count: {int(user_rating["4"]["user"]) + int(user_rating["4"]["root"])} 27 | 28 | - folder: "MEDIUM" 29 | count: {int(user_rating["5"]["user"]) + int(user_rating["5"]["root"])} 30 | 31 | - folder: "A BIT HARD" 32 | count: {int(user_rating["6"]["user"]) + int(user_rating["6"]["root"])} 33 | 34 | - folder: "HARD" 35 | count: {int(user_rating["7"]["user"]) + int(user_rating["7"]["root"])} 36 | 37 | - folder: "EXTREMELY HARD" 38 | count: {int(user_rating["8"]["user"]) + int(user_rating["8"]["root"])} 39 | 40 | - folder: "INSANE" 41 | count: {int(user_rating["9"]["user"]) + int(user_rating["9"]["root"])} 42 | 43 | - folder: "BRAINFUCK" 44 | count: {int(user_rating["10"]["user"]) + int(user_rating["10"]["root"])} 45 | 46 | #-----------------# 47 | #- chart options -# 48 | #-----------------# 49 | options: 50 | xField: "folder" 51 | yField: "count" 52 | padding: auto 53 | label: 54 | position: "middle" 55 | style: 56 | opacity: 0.6 57 | fontSize: 12 58 | columnStyle: 59 | fillOpacity: 0.5 60 | lineWidth: 1 61 | strokeOpacity: 0.7 62 | shadowColor: "grey" 63 | shadowBlur: 10 64 | shadowOffsetX: 5 65 | shadowOffsetY: 5 66 | xAxis: 67 | label: 68 | autoHide: false 69 | autoRotate: true 70 | meta: 71 | count: 72 | alias: "Votes" 73 | ``` 74 | ''' 75 | 76 | 77 | def get_template_char_user_rating(user_rating): 78 | return f''' 79 | ```chartsview 80 | #-----------------# 81 | #- chart type -# 82 | #-----------------# 83 | type: Column 84 | 85 | #-----------------# 86 | #- chart data -# 87 | #-----------------# 88 | data: 89 | - folder: "PIECE OF CAKE" 90 | count: {int(user_rating["1"]["user"]) + int(user_rating["1"]["root"])} 91 | 92 | - folder: "VERY EASY" 93 | count: {int(user_rating["2"]["user"]) + int(user_rating["2"]["root"])} 94 | 95 | - folder: "EASY" 96 | count: {int(user_rating["3"]["user"]) + int(user_rating["3"]["root"])} 97 | 98 | - folder: "NOT TO EASY" 99 | count: {int(user_rating["4"]["user"]) + int(user_rating["4"]["root"])} 100 | 101 | - folder: "MEDIUM" 102 | count: {int(user_rating["5"]["user"]) + int(user_rating["5"]["root"])} 103 | 104 | - folder: "A BIT HARD" 105 | count: {int(user_rating["6"]["user"]) + int(user_rating["6"]["root"])} 106 | 107 | - folder: "HARD" 108 | count: {int(user_rating["7"]["user"]) + int(user_rating["7"]["root"])} 109 | 110 | - folder: "EXTREMELY HARD" 111 | count: {int(user_rating["8"]["user"]) + int(user_rating["8"]["root"])} 112 | 113 | - folder: "INSANE" 114 | count: {int(user_rating["9"]["user"]) + int(user_rating["9"]["root"])} 115 | 116 | - folder: "BRAINFUCK" 117 | count: {int(user_rating["10"]["user"]) + int(user_rating["10"]["root"])} 118 | 119 | 120 | 121 | #-----------------# 122 | #- chart options -# 123 | #-----------------# 124 | options: 125 | xField: "folder" 126 | yField: "count" 127 | padding: auto 128 | label: 129 | position: "middle" 130 | style: 131 | opacity: 0.6 132 | fontSize: 12 133 | columnStyle: 134 | fillOpacity: 0.5 135 | lineWidth: 1 136 | strokeOpacity: 0.7 137 | shadowColor: "grey" 138 | shadowBlur: 10 139 | shadowOffsetX: 5 140 | shadowOffsetY: 5 141 | xAxis: 142 | label: 143 | autoHide: false 144 | autoRotate: true 145 | meta: 146 | count: 147 | alias: "Votes" 148 | ``` 149 | ''' -------------------------------------------------------------------------------- /hackthebox/search.py: -------------------------------------------------------------------------------- 1 | from typing import List, cast, Optional 2 | 3 | from .user import User 4 | from .machine import Machine 5 | from .team import Team 6 | from .challenge import Challenge 7 | from .errors import NotFoundException 8 | from . import htb 9 | 10 | 11 | class Search: 12 | """Representation of a search on the platform 13 | 14 | Attributes: 15 | users: Users returned by the Search 16 | machines: Machines returned by the Search 17 | teams: Teams returned by the Search 18 | challenges: Challenges returned by the Search 19 | items: A dict of all items returned by the Search 20 | 21 | Args: 22 | search: The term to search for 23 | _tags: The list of tags to filter by 24 | """ 25 | 26 | _users: Optional[List[User]] = None 27 | _machines: Optional[List[Machine]] = None 28 | _teams: Optional[List[Team]] = None 29 | _challenges: Optional[List[Challenge]] = None 30 | 31 | _user_ids: List[int] 32 | _machine_ids: List[int] 33 | _team_ids: List[int] 34 | _challenge_ids: List[int] 35 | 36 | _is_resolved: bool = False 37 | _term: str 38 | 39 | # The search API can return non-existent items (i.e. deleted). This should be handled and 40 | # not passed back to the user. 41 | 42 | @property 43 | def users(self) -> List[User]: 44 | if self._users is None: 45 | self._users = [] 46 | for uid in self._user_ids: 47 | try: 48 | self._users.append(self._client.get_user(uid)) 49 | except NotFoundException: 50 | pass 51 | return self._users 52 | 53 | @property 54 | def machines(self) -> List[Machine]: 55 | if self._machines is None: 56 | self._machines = [] 57 | for uid in self._machine_ids: 58 | try: 59 | self._machines.append(self._client.get_machine(uid)) 60 | except NotFoundException: 61 | pass 62 | return self._machines 63 | 64 | @property 65 | def teams(self) -> List[Team]: 66 | if self._teams is None: 67 | self._teams = [] 68 | try: 69 | for uid in self._team_ids: 70 | self._teams.append(self._client.get_team(uid)) 71 | except NotFoundException: 72 | pass 73 | return self._teams 74 | 75 | @property 76 | def challenges(self) -> List[Challenge]: 77 | if self._challenges is None: 78 | self._challenges = [] 79 | for uid in self._challenge_ids: 80 | try: 81 | self._challenges.append(self._client.get_challenge(uid)) 82 | except NotFoundException: 83 | pass 84 | return self._challenges 85 | 86 | @property 87 | def items(self) -> dict: 88 | self._is_resolved = True 89 | return { 90 | "users": self.users, 91 | "machines": self.machines, 92 | "teams": self.teams, 93 | "challenges": self.challenges, 94 | } 95 | 96 | # noinspection PyStatementEffect 97 | def __len__(self): 98 | return ( 99 | len(self._user_ids) 100 | + len(self._team_ids) 101 | + len(self._machine_ids) 102 | + len(self._challenge_ids) 103 | ) 104 | 105 | def __repr__(self): 106 | return f"" 107 | 108 | def __str__(self): 109 | return repr(self) 110 | 111 | def __init__(self, search: str, client: htb.HTBClient, _tags=None): 112 | if _tags is None: 113 | _tags = [] 114 | self._term = search 115 | self._client = client 116 | # Ignoring tags - they seem to currently not work on the API level 117 | search_data = cast( 118 | dict, self._client.do_request("search/fetch?query=" + search) 119 | ) 120 | self._user_ids = [x["id"] for x in search_data.get("users", [])] 121 | self._machine_ids = [x["id"] for x in search_data.get("machines", [])] 122 | self._team_ids = [x["id"] for x in search_data.get("teams", [])] 123 | self._challenge_ids = [x["id"] for x in search_data.get("challenges", [])] 124 | -------------------------------------------------------------------------------- /HTB/.obsidian/plugins/obsidian-shellcommands/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "settings_version": "0.16.0", 3 | "debug": false, 4 | "obsidian_command_palette_prefix": "Execute: ", 5 | "preview_variables_in_command_palette": true, 6 | "show_autocomplete_menu": true, 7 | "working_directory": "", 8 | "default_shells": {}, 9 | "environment_variable_path_augmentations": {}, 10 | "error_message_duration": 20, 11 | "notification_message_duration": 10, 12 | "execution_notification_mode": "disabled", 13 | "output_channel_clipboard_also_outputs_to_notification": true, 14 | "enable_events": true, 15 | "approve_modals_by_pressing_enter_key": true, 16 | "max_visible_lines_in_shell_command_fields": false, 17 | "shell_commands": [ 18 | { 19 | "id": "m6e17y5rts", 20 | "platform_specific_commands": { 21 | "default": "/usr/bin/python3 {{folder_path:absolute}}/../htb_api.py -m {{_machine_name}} -v {{folder_path:absolute}}/" 22 | }, 23 | "shells": {}, 24 | "alias": "New Machine Note", 25 | "icon": "activity", 26 | "confirm_execution": true, 27 | "ignore_error_codes": [], 28 | "output_channels": { 29 | "stdout": "modal", 30 | "stderr": "notification" 31 | }, 32 | "output_wrappers": { 33 | "stdout": null, 34 | "stderr": null 35 | }, 36 | "output_channel_order": "stdout-first", 37 | "events": {}, 38 | "command_palette_availability": "enabled", 39 | "preactions": [ 40 | { 41 | "type": "prompt", 42 | "enabled": true, 43 | "prompt_id": "j2fcw45kfm" 44 | } 45 | ], 46 | "variable_default_values": { 47 | "5a0vmo68z6": { 48 | "type": "show-errors", 49 | "value": "this.app.vault.adapter.basePath" 50 | } 51 | } 52 | }, 53 | { 54 | "id": "usnoddh2no", 55 | "platform_specific_commands": { 56 | "default": "/usr/bin/python3 {{folder_path:absolute}}/../htb_api.py -m \"\" -v {{folder_path:absolute}}/" 57 | }, 58 | "shells": {}, 59 | "alias": "Update Machine", 60 | "icon": null, 61 | "confirm_execution": true, 62 | "ignore_error_codes": [], 63 | "output_channels": { 64 | "stdout": "modal", 65 | "stderr": "notification" 66 | }, 67 | "output_wrappers": { 68 | "stdout": null, 69 | "stderr": null 70 | }, 71 | "output_channel_order": "stdout-first", 72 | "events": {}, 73 | "command_palette_availability": "enabled", 74 | "preactions": [ 75 | { 76 | "type": "prompt", 77 | "enabled": false, 78 | "prompt_id": "" 79 | } 80 | ], 81 | "variable_default_values": {} 82 | }, 83 | { 84 | "id": "g7sm2q030y", 85 | "platform_specific_commands": { 86 | "default": "python3 {{folder_path:absolute}}/../../../htb_api.py -m {{folder_name}} -v {{folder_path:absolute}}/../../" 87 | }, 88 | "shells": {}, 89 | "alias": "Update this Machine", 90 | "icon": null, 91 | "confirm_execution": true, 92 | "ignore_error_codes": [], 93 | "output_channels": { 94 | "stdout": "notification", 95 | "stderr": "notification" 96 | }, 97 | "output_wrappers": { 98 | "stdout": null, 99 | "stderr": null 100 | }, 101 | "output_channel_order": "stdout-first", 102 | "events": {}, 103 | "command_palette_availability": "enabled", 104 | "preactions": [ 105 | { 106 | "type": "prompt", 107 | "enabled": false, 108 | "prompt_id": "j2fcw45kfm" 109 | } 110 | ], 111 | "variable_default_values": {} 112 | } 113 | ], 114 | "prompts": [ 115 | { 116 | "id": "j2fcw45kfm", 117 | "title": "New Machine", 118 | "description": "Input text will search the name of the machine in the HTB API and return data. ", 119 | "preview_shell_command": true, 120 | "fields": [ 121 | { 122 | "label": "Select Machine", 123 | "description": "Select a machine name", 124 | "default_value": "", 125 | "target_variable_id": "qjtovztiv3", 126 | "required": true 127 | } 128 | ], 129 | "execute_button_text": "Find" 130 | } 131 | ], 132 | "custom_variables": [ 133 | { 134 | "id": "qjtovztiv3", 135 | "name": "machine_name", 136 | "description": "" 137 | }, 138 | { 139 | "id": "5a0vmo68z6", 140 | "name": "vault_folder", 141 | "description": "Default folder" 142 | } 143 | ], 144 | "output_wrappers": [] 145 | } -------------------------------------------------------------------------------- /hackthebox/leaderboard.py: -------------------------------------------------------------------------------- 1 | from typing import List, Iterator 2 | 3 | from . import htb 4 | from .team import Team 5 | from .user import User 6 | 7 | 8 | class Leaderboard(htb.HTBObject): 9 | """The class representing a Leaderboard 10 | 11 | Args: 12 | data: A list of Leaderboard entries 13 | leaderboard_type: The Type of entries in the Leaderboard 14 | 15 | """ 16 | 17 | _type: type 18 | _items: List[htb.HTBObject] 19 | _iter: Iterator[None] 20 | 21 | def __getitem__(self, key): 22 | return self._items[key] 23 | 24 | def __iter__(self): 25 | # noinspection PyTypeChecker 26 | self._iter = iter(self._items) 27 | return self 28 | 29 | def __next__(self): 30 | return next(self._iter) 31 | 32 | def __len__(self): 33 | return len(self._items) 34 | 35 | def __init__(self, data: List[dict], client: htb.HTBClient, leaderboard_type: type): 36 | self._type = leaderboard_type 37 | self._client = client 38 | if leaderboard_type == User: 39 | self._items = [User(usr, client, summary=True) for usr in data] 40 | elif leaderboard_type == Team: 41 | self._items = [Team(team, client, summary=True) for team in data] 42 | elif leaderboard_type == Country: 43 | self._items = [Country(country) for country in data] 44 | elif leaderboard_type == University: 45 | self._items = [University(university) for university in data] 46 | 47 | 48 | class Country(htb.HTBObject): 49 | """The class representing a Country 50 | 51 | Attributes: 52 | rank: The Country's global rank 53 | country_code: The Country's country code 54 | members: The number of members from the Country 55 | points: The Country's total points 56 | user_owns: The Country's total user owns 57 | root_owns: The Country's total root owns 58 | challenge_owns: The Country's total challenge owns 59 | user_bloods: The Country's total user bloods 60 | root_bloods: The Country's total root bloods 61 | fortress: The Country's total Fortress flags 62 | endgame: The Country's total Endgame flags 63 | name: The name of the Country 64 | Args: 65 | data: The data of the country 66 | 67 | """ 68 | 69 | # TODO: Move this into its own file, maybe `misc.py`? 70 | 71 | rank: int 72 | country_code: str 73 | members: int 74 | points: int 75 | user_owns: int 76 | root_owns: int 77 | challenge_owns: int 78 | user_bloods: int 79 | root_bloods: int 80 | fortress: int 81 | endgame: int 82 | name: str 83 | 84 | def __init__(self, data: dict): 85 | self.rank = data["rank"] 86 | self.country_code = data["country"] 87 | self.members = data["members"] 88 | self.points = data["points"] 89 | self.user_owns = data["user_owns"] 90 | self.root_owns = data["root_owns"] 91 | self.challenge_owns = data["challenge_owns"] 92 | self.user_bloods = data["user_bloods"] 93 | self.root_owns = data["root_bloods"] 94 | self.fortress = data["fortress"] 95 | self.endgame = data["endgame"] 96 | self.name = data["name"] 97 | 98 | 99 | class University(htb.HTBObject): 100 | """The class representing a University 101 | 102 | Attributes: 103 | rank: The University's global rank 104 | students: The number of students from the University 105 | points: The University's total points 106 | user_owns: The University's total user owns 107 | root_owns: The University's total root owns 108 | challenge_owns: The University's total challenge owns 109 | user_bloods: The University's total user bloods 110 | root_bloods: The University's total root bloods 111 | fortress: The University's total Fortress flags 112 | endgame: The University's total Endgame flags 113 | name: The name of the University 114 | Args: 115 | data: The data of the University 116 | 117 | """ 118 | 119 | rank: int 120 | students: int 121 | points: int 122 | user_owns: int 123 | root_owns: int 124 | challenge_owns: int 125 | user_bloods: int 126 | root_bloods: int 127 | fortress: int 128 | endgame: int 129 | name: str 130 | 131 | def __init__(self, data: dict): 132 | self.rank = data["rank"] 133 | self.students = data["students"] 134 | self.points = data["points"] 135 | self.user_owns = data["user_owns"] 136 | self.root_owns = data["root_owns"] 137 | self.challenge_owns = data["challenge_owns"] 138 | self.user_bloods = data["user_bloods"] 139 | self.root_owns = data["root_bloods"] 140 | self.fortress = data["fortress"] 141 | self.endgame = data["endgame"] 142 | self.name = data["name"] 143 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HTNotes 2 | 3 | HTNotes is a powerful automatic tool for `Linux` that integrates a Vault Workspace in `Obsidian`. It automates the first steps of taking Notes in a HTB machine by generating a folder structure given a machine name. It also provides integration with the HTB API that allows to perform requests and prints the info into markwdown files. 4 | 5 | As mention before, these actions can be performed with just a click of a button in Obsidian. 6 | 7 | 8 | ## Table of Contents 9 | 10 | - [Content](#Content) 11 | - [Setup](#Setup) 12 | - [Recommendations](#Recommendations) 13 | - [Usage](#Usage) 14 | - [Incoming](#Incoming) 15 | - [References](#References) 16 | 17 | ## Content 18 | 19 | `Nothing except your eyes could verify if this tool will help you in the future.` 20 | 21 | https://user-images.githubusercontent.com/48086822/201536167-b89ef695-8c6b-470b-9f94-d4e4637167ec.mp4 22 | 23 | If video is not displayed, you can also click on this link: 24 | https://youtu.be/tjHShmUzWM0 25 | 26 | ## Setup 27 | 28 | ### Install obsidian 29 | 30 | You need Obsidian to integrate the tool. 31 | 32 | Open your browser and go to [Download Obsidian](https://obsidian.md/download). 33 | Install .deb and execute the following command: 34 | 35 | ``` 36 | sudo dpkg -i <> 37 | ``` 38 | 39 | ### Clone the repo 40 | 41 | Now you need to clone this repository and install all the dependencies. 42 | 43 | ``` 44 | git clone https://github.com/0x4xel/HTNotes 45 | cd HTNotes 46 | pip install -r requirements.txt 47 | ``` 48 | 49 | ### Get App token in HackTheBox 50 | 51 | 1. You have to login in [HackTheBox](https://www.hackthebox.com/). 52 | 2. Explore under ```View Profile -> Profile Settings -> App Tokens -> Generate App token``` 53 | 3. Create a token that will be needed to make requests on HTB API. 54 | 55 | ### Put token in Constants.py 56 | 57 | Put you API key in `Constants.py` file and save. 58 | 59 | ### Open you Vault folder 60 | 61 | 1. Open Obsidian and select *open existing vault* 62 | 2. Select the folder HTB under HTNotes 63 | 3. IMPORTANT: When prompt open asking for permisions, press `enable`. This vault has preconfigured plugins that are needed to work properly 64 | 65 | Note: Consider that HTB folder will be the main vault folder, so scripts must be under HTNotes and will need `execution privileges` with current user to keep working. 66 | 67 | ## Recommendations 68 | 69 | Once you have configured your environment, you can remove `assets` folder that is only for `README` asset purposes. 70 | 71 | Now its yours. You can now change whatever you want. This is a pre-configured vault so you can change it, add or remove content, feel `free`. 72 | 73 | ## Usage 74 | 75 | When the script creates a machine folder, it also creates a fileClass call "machine" that receive the data from the API and print it into the {{machine_name}}.md. 76 | 77 | The profile of the machine will be displayed like: 78 | 79 | ![](HTB/assets/machine_info_example.png) 80 | 81 | #### Index 82 | 83 | There are three *dynamic tables* that will show you the diferent machines in the vault. Machines fileClass are defined by the tag "machine". 84 | 85 | This tables : 86 | 87 | 1. Show all the machines in the vault 88 | 2. Show the machines in the vault that have user flag but not root flag 89 | 3. Show the machines in the vault that have not started 90 | 91 | ![](HTB/assets/index_machine_info_example.png) 92 | 93 | #### Create Machine Note 94 | 95 | This is the main funtion of the integration. "Create Machine Note" button calls `htb_api.py` and makes all the implementation given below in the flowchart 96 | 97 | ![](HTB/assets/create_machine_example.png) 98 | 99 | #### Update Machine Info 100 | 101 | This button is usefull to trigger when you have completed a machine, or you didnt access to HTB in a while. It will `ONLY` update the machine info of all the machines you have in the vault, updating the state of flags and graphs. 102 | 103 | ![](HTB/assets/update_machine_example.png) 104 | 105 | ## Incoming 106 | 107 | As this is the first phase of the proyect, I would like to make some iterations over it and make this vault the main `brain` for training notes. 108 | 109 | - New Tool "HTTool" that automate the first steps of recognition in machine 110 | - Button on the machine info that uploads a Writeup to Github / Gitlab pages 111 | - Improve the machine info view 112 | - A button to setup up the HTB machine from API 113 | - Make some automate mermaid flowcharts about the machine workflow 114 | - ... 115 | 116 | If you have any new idea, please get in contact with me by my social media published on my Github profile. 117 | 118 | 119 | ## Changelog 120 | 121 | ##### 11/12/2022 122 | 123 | - New templates examples on folder machine 124 | - Fix broken attributes 125 | - Fix date format to mm/dd/yyyy 126 | - Fix internal script 127 | 128 | ## References 129 | 130 | https://github.com/clubby789/htb-api 131 | 132 | With the support of xerosec team https://discord.gg/cZPaRurR ❤️ 133 | -------------------------------------------------------------------------------- /HTB/.obsidian/workspace.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": { 3 | "id": "419b4bd71814993a", 4 | "type": "split", 5 | "children": [ 6 | { 7 | "id": "fc32798b129a248d", 8 | "type": "tabs", 9 | "children": [ 10 | { 11 | "id": "58b14016d8cd8da5", 12 | "type": "leaf", 13 | "state": { 14 | "type": "markdown", 15 | "state": { 16 | "file": "⚡ Start Machine ⚡.md", 17 | "mode": "source", 18 | "source": false 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | ], 25 | "direction": "vertical" 26 | }, 27 | "left": { 28 | "id": "b87917073de51c47", 29 | "type": "split", 30 | "children": [ 31 | { 32 | "id": "c6eb17fe4107d284", 33 | "type": "tabs", 34 | "dimension": 41.53846153846154, 35 | "children": [ 36 | { 37 | "id": "e08dbcc4709688c0", 38 | "type": "leaf", 39 | "state": { 40 | "type": "search", 41 | "state": { 42 | "query": "", 43 | "matchingCase": true, 44 | "explainSearch": false, 45 | "collapseAll": false, 46 | "extraContext": false, 47 | "sortOrder": "alphabetical" 48 | } 49 | } 50 | }, 51 | { 52 | "id": "d0290278b4441eac", 53 | "type": "leaf", 54 | "state": { 55 | "type": "starred", 56 | "state": {} 57 | } 58 | }, 59 | { 60 | "id": "1be54c2ba4f7f2d0", 61 | "type": "leaf", 62 | "state": { 63 | "type": "graph", 64 | "state": {} 65 | } 66 | }, 67 | { 68 | "id": "4db53388bd5bc1ad", 69 | "type": "leaf", 70 | "state": { 71 | "type": "graph", 72 | "state": {} 73 | } 74 | } 75 | ], 76 | "currentTab": 2 77 | }, 78 | { 79 | "id": "84608c2c80821b30", 80 | "type": "tabs", 81 | "dimension": 58.46153846153847, 82 | "children": [ 83 | { 84 | "id": "f9c3d4ffcaa05297", 85 | "type": "leaf", 86 | "state": { 87 | "type": "file-explorer", 88 | "state": { 89 | "sortOrder": "alphabetical" 90 | } 91 | } 92 | } 93 | ] 94 | } 95 | ], 96 | "direction": "horizontal", 97 | "width": 346.5 98 | }, 99 | "right": { 100 | "id": "897b955182786bfe", 101 | "type": "split", 102 | "children": [ 103 | { 104 | "id": "481bba2bbea7c737", 105 | "type": "tabs", 106 | "children": [ 107 | { 108 | "id": "3dea914a33867574", 109 | "type": "leaf", 110 | "state": { 111 | "type": "backlink", 112 | "state": { 113 | "file": "⚡ Start Machine ⚡.md", 114 | "collapseAll": false, 115 | "extraContext": false, 116 | "sortOrder": "alphabetical", 117 | "showSearch": false, 118 | "searchQuery": "", 119 | "backlinkCollapsed": false, 120 | "unlinkedCollapsed": true 121 | } 122 | } 123 | }, 124 | { 125 | "id": "708b4fd86a6d3873", 126 | "type": "leaf", 127 | "state": { 128 | "type": "outgoing-link", 129 | "state": { 130 | "file": "⚡ Start Machine ⚡.md", 131 | "linksCollapsed": false, 132 | "unlinkedCollapsed": true 133 | } 134 | } 135 | }, 136 | { 137 | "id": "dbb5525e5989ec3e", 138 | "type": "leaf", 139 | "state": { 140 | "type": "tag", 141 | "state": { 142 | "sortOrder": "frequency", 143 | "useHierarchy": true 144 | } 145 | } 146 | }, 147 | { 148 | "id": "4c5730635b959d8b", 149 | "type": "leaf", 150 | "state": { 151 | "type": "outline", 152 | "state": { 153 | "file": "⚡ Start Machine ⚡.md" 154 | } 155 | } 156 | }, 157 | { 158 | "id": "131801ce77a1335c", 159 | "type": "leaf", 160 | "state": { 161 | "type": "git-view", 162 | "state": {} 163 | } 164 | } 165 | ] 166 | } 167 | ], 168 | "direction": "horizontal", 169 | "width": 300 170 | }, 171 | "active": "58b14016d8cd8da5", 172 | "lastOpenFiles": [ 173 | "Index.md", 174 | "⚡ Start Machine ⚡.md", 175 | "Machines/Lame/Lame.md", 176 | "TODO.md", 177 | "fileClass/Machine.md", 178 | "Machines/Lame/00-index.md", 179 | "Machines/Lame/03-post-exploitation.md", 180 | "Machines/Trick/Trick.md", 181 | "Machines/Trick/01-recon.md", 182 | "Machines/Trick/00-index.md" 183 | ] 184 | } -------------------------------------------------------------------------------- /hackthebox/user.py: -------------------------------------------------------------------------------- 1 | from typing import List, Optional, TYPE_CHECKING 2 | 3 | from hackthebox.content import Content 4 | 5 | from . import htb 6 | from .solve import MachineSolve, ChallengeSolve, EndgameSolve, FortressSolve, Solve 7 | 8 | 9 | if TYPE_CHECKING: 10 | from .htb import HTBClient 11 | from .team import Team 12 | 13 | 14 | class User(htb.HTBObject): 15 | """The class representing Hack The Box Users 16 | 17 | Attributes: 18 | name: The username of the User 19 | avatar: The relative URL of the User's avatar 20 | ranking: The User's position on the Hall of Fame 21 | points: The User's current total points 22 | user_owns: The User's total Machine user owns 23 | root_owns: The User's total Machine root owns 24 | user_bloods: The User's total Machine user bloods 25 | root_bloods: The User's total Machine root bloods 26 | rank_name: The name of the User's current rank 27 | country_name: The name of the User's country 28 | team: The User's Team 29 | public: Whether the User's profile is publicly visible 30 | 31 | timezone: The User's timezone 32 | vip: Whether the User is VIP 33 | vip_plus: Whether the user is VIP+ 34 | respects: The number of respects the User has 35 | university: The User's University 36 | university_name: The User's university's name 37 | description: The User's description 38 | github: The User's Github profile 39 | linkedin: The User's LinkedIn profile 40 | twitter: The User's Twitter account 41 | website: The User's website 42 | respected: Whether the active User respects the User 43 | followed: Whether the active User follows the User 44 | rank_id: The ID of the User's rank 45 | rank_progress: The User's progress to the next rank 46 | next_rank: The next rank the User will reach 47 | next_rank_points: The points required to reach the next rank 48 | rank_requirement: The ownership required for the current Rank 49 | 50 | """ 51 | 52 | name: str 53 | avatar: str 54 | ranking: int 55 | points: int 56 | root_owns: int 57 | user_owns: int 58 | root_bloods: int 59 | user_bloods: int 60 | rank_name: str 61 | 62 | _detailed_attributes = ( 63 | "timezone", 64 | "vip", 65 | "vip_plus", 66 | "respects", 67 | "university", 68 | "university_name", 69 | "description", 70 | "github", 71 | "linkedin", 72 | "twitter", 73 | "website", 74 | "respected", 75 | "followed", 76 | "rank_id", 77 | "rank_progress", 78 | "next_rank", 79 | "next_rank_points", 80 | "rank_ownership", 81 | "rank_requirement", 82 | "country_name", 83 | "team", 84 | "public", 85 | ) 86 | timezone: str 87 | vip: bool 88 | vip_plus: bool 89 | respects: int 90 | # TODO: University object 91 | university = None 92 | university_name: str 93 | description: str 94 | github: str 95 | linkedin: str 96 | twitter: str 97 | website: str 98 | respected: bool 99 | followed: bool 100 | rank_id: int 101 | rank_progress: int 102 | next_rank: str 103 | next_rank_points: int 104 | rank_ownership: float 105 | rank_requirement: int 106 | country_name: str 107 | # noinspection PyUnresolvedReferences 108 | team: "Team" 109 | public: bool 110 | 111 | _activity: Optional[List[Solve]] = None 112 | 113 | @property 114 | def activity(self): 115 | if not self._activity: 116 | self._activity = [] 117 | solve_list = (self._client.do_request(f"user/profile/activity/{self.id}"))[ 118 | "profile" 119 | ]["activity"] 120 | for solve_item in solve_list: 121 | solve_type = solve_item["object_type"] 122 | if solve_type == "machine": 123 | self._activity.append(MachineSolve(solve_item, self._client)) 124 | elif solve_type == "challenge": 125 | self._activity.append(ChallengeSolve(solve_item, self._client)) 126 | elif solve_type == "endgame": 127 | self._activity.append(EndgameSolve(solve_item, self._client)) 128 | elif solve_type == "fortress": 129 | self._activity.append(FortressSolve(solve_item, self._client)) 130 | 131 | return self._activity 132 | 133 | def __repr__(self): 134 | return f"" 135 | 136 | # noinspection PyUnresolvedReferences 137 | def __init__(self, data: dict, client: "HTBClient", summary: bool = False): 138 | """Initialise a `User` using API data""" 139 | self._client = client 140 | self._detailed_func = client.get_user # type: ignore 141 | self.id = data["id"] 142 | self.name = data["name"] 143 | self.user_owns = data["user_owns"] 144 | self.points = data["points"] 145 | 146 | if summary: 147 | self._is_summary = True 148 | self.ranking = data["rank"] 149 | self.root_owns = data["root_owns"] 150 | self.user_bloods = data.get("user_bloods_count") or 0 151 | self.root_bloods = data.get("root_bloods_count") or 0 152 | self.rank_name = data.get("rank_text") or "" 153 | else: 154 | self.ranking = data["ranking"] 155 | self.root_owns = data["system_owns"] 156 | self.user_bloods = data["user_bloods"] 157 | self.root_bloods = data["system_bloods"] 158 | self.rank_name = data["rank"] 159 | 160 | self.respects = data["respects"] 161 | self.university = data["university"] 162 | self.university_name = data["university_name"] 163 | self.description = data["description"] 164 | self.github = data["github"] 165 | self.linkedin = data["linkedin"] 166 | self.twitter = data["twitter"] 167 | self.website = data["website"] 168 | self.respected = data.get("isRespected", False) 169 | self.followed = data.get("isFollowed", False) 170 | self.rank_progress = data["current_rank_progress"] 171 | self.next_rank = data["next_rank"] 172 | self.next_rank_points = data["next_rank_points"] 173 | self.rank_ownership = float(data["rank_ownership"]) 174 | self.rank_requirement = data["rank_requirement"] 175 | self.country_name = data["country_name"] 176 | self.team = data["team"] 177 | self.public = bool(data["public"]) 178 | 179 | # noinspection PyUnresolvedReferences 180 | def get_content(self): 181 | return Content(self.id, self._client) 182 | 183 | # noinspection PyUnresolvedReferences 184 | def get_machines(self): 185 | return self.get_content().machines 186 | 187 | # noinspection PyUnresolvedReferences 188 | def get_challenges(self): 189 | return self.get_content().challenges 190 | -------------------------------------------------------------------------------- /HTB/.obsidian/plugins/obsidian-shellcommands/styles.css: -------------------------------------------------------------------------------- 1 | /* 2 | 'Shell commands' plugin for Obsidian. 3 | Copyright (C) 2021 - 2022 Jarkko Linnanvirta 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, version 3 of the License. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | 17 | Contact the author (Jarkko Linnanvirta): https://github.com/Taitava/ 18 | */ 19 | 20 | /* 21 | * COMMON 22 | */ 23 | 24 | .SC-hide { 25 | display: none; 26 | } 27 | 28 | .SC-scrollable { 29 | overflow-y: auto; 30 | } 31 | 32 | .SC-no-margin { 33 | margin: 0; 34 | } 35 | 36 | .SC-no-top-border { 37 | border-top: none; 38 | } 39 | 40 | .SC-wrappable { 41 | white-space: normal; 42 | } 43 | 44 | .SC-small-font { 45 | font-size: 80%; 46 | } 47 | 48 | /* 49 | * SETTING GROUPS 50 | * Related setting fields combined together. 51 | */ 52 | 53 | div.SC-setting-group div.setting-item:not(div.setting-item-heading+div.setting-item) { 54 | /* Remove top border from all settings in the group, except settings that come AFTER a heading setting, as it looks 55 | * good when there is a border line below a heading setting. 56 | */ 57 | border-top: none; 58 | } 59 | 60 | div.SC-setting-group div.setting-item { 61 | padding: 5px; /* Shrink the padding. */ 62 | } 63 | 64 | div.SC-setting-group div.setting-item div.setting-item-info, 65 | div.SC-setting-group div.setting-item div.setting-item-control { 66 | width: 50%; /* Make sure description does not take over 50% of the space. */ 67 | } 68 | 69 | div.SC-setting-group div.setting-item input[type=text], 70 | div.SC-setting-group div.setting-item input[type=search], 71 | div.SC-setting-group div.setting-item textarea, 72 | div.SC-setting-group div.setting-item select { 73 | width: 100%; /* Enlarge fields. */ 74 | max-width: 100%; /* Remove a maximum width limitation, at least