├── .dockerignore ├── .editorconfig ├── .env.example ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── format.yml │ ├── notify.yml │ └── test.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .pylintrc ├── Dockerfile ├── README.md ├── ctfhub ├── __init__.py ├── admin.py ├── apps.py ├── context_processors.py ├── decorators │ └── __init__.py ├── exceptions.py ├── forms.py ├── helpers.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_auto_20210102_1139.py │ ├── 0003_auto_20210113_1945.py │ ├── 0004_ctf_note_id.py │ ├── 0005_auto_20210204_1921.py │ ├── 0006_auto_20210329_1218.py │ ├── 0007_ctf_rating.py │ ├── 0008_remove_challenge_whiteboard_id_and_more.py │ ├── 0009_add_challenge_assigned_members.py │ ├── 0009_alter_member_user.py │ ├── 0010_alter_challengefile_file_and_more.py │ ├── 0010_remove_ctf_visibility.py │ ├── 0011_ctf_visibility.py │ ├── 0012_alter_member_status.py │ ├── 0013_alter_member_country.py │ ├── 0014_alter_member_timezone.py │ ├── 0015_alter_ctf_note_id_alter_member_timezone.py │ ├── 0016_alter_member_timezone.py │ ├── 0017_merge_20230629_1617.py │ ├── 0018_alter_member_status.py │ ├── 0019_alter_challenge_note_id_alter_ctf_note_id.py │ ├── 0020_alter_member_hedgedoc_password.py │ ├── 0021_alter_team_avatar.py │ └── __init__.py ├── mixins.py ├── models.py ├── signals.py ├── templates │ ├── ctfhub │ │ ├── categories │ │ │ └── create.html │ │ ├── challenges │ │ │ ├── confirm_delete.html │ │ │ ├── create.html │ │ │ ├── detail.html │ │ │ ├── files │ │ │ │ ├── confirm_delete.html │ │ │ │ └── create.html │ │ │ ├── import.html │ │ │ └── list.html │ │ ├── ctfs │ │ │ ├── confirm_delete.html │ │ │ ├── create.html │ │ │ ├── detail.html │ │ │ ├── detail_challenges.html │ │ │ ├── detail_export.html │ │ │ ├── detail_notes.html │ │ │ ├── list.html │ │ │ ├── list_ctfs.html │ │ │ ├── list_ctftime_ctfs.html │ │ │ └── status.html │ │ ├── dashboard │ │ │ ├── dashboard.html │ │ │ └── status.html │ │ ├── footer.html │ │ ├── main.html │ │ ├── navbar.html │ │ ├── shortcuts.html │ │ ├── stats │ │ │ ├── charts.html │ │ │ ├── ctf_stats.html │ │ │ ├── detail.html │ │ │ ├── rank.html │ │ │ ├── team.html │ │ │ └── timeline.html │ │ └── tags │ │ │ ├── confirm_delete.html │ │ │ ├── create.html │ │ │ └── list.html │ ├── search │ │ └── list.html │ ├── snippets │ │ ├── formerror.html │ │ ├── messages.html │ │ ├── quick_add_category.html │ │ ├── quick_add_file.html │ │ └── quick_add_tag.html │ ├── team │ │ ├── confirm_delete.html │ │ ├── create.html │ │ └── edit.html │ └── users │ │ ├── confirm_delete.html │ │ ├── detail.html │ │ ├── edit.html │ │ ├── edit_advanced.html │ │ ├── edit_advanced_change_password.html │ │ ├── list.html │ │ ├── login.html │ │ ├── password_change.html │ │ ├── password_reset.html │ │ ├── password_reset_email.txt │ │ ├── password_reset_subject.txt │ │ └── register.html ├── templatetags │ ├── __init__.py │ └── ctfhub_filters.py ├── tests │ ├── __init__.py │ ├── test_forms.py │ ├── test_helpers.py │ ├── test_models.py │ ├── test_urls.py │ ├── test_views.py │ └── utils.py ├── urls.py ├── validators.py └── views │ ├── __init__.py │ ├── categories.py │ ├── challenges.py │ ├── ctfs.py │ ├── files.py │ ├── tags.py │ ├── teams.py │ └── users.py ├── ctfhub_project ├── __init__.py ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py ├── docker-compose.yml ├── docker-entrypoint.sh ├── docs ├── build.md ├── gallery.md └── ssl-setup.md ├── manage.py ├── pyproject.toml ├── requirements.txt ├── scripts ├── excalidraw │ ├── .env.example │ ├── Makefile │ └── docker-compose.yml └── proxy │ ├── .env.example │ ├── conf │ └── nginx │ │ └── nginx.conf │ └── docker-compose.yml ├── static ├── css │ ├── chart.css │ ├── fontawesome │ │ ├── css │ │ │ ├── all.css │ │ │ ├── all.min.css │ │ │ ├── brands.css │ │ │ ├── brands.min.css │ │ │ ├── fontawesome.css │ │ │ ├── fontawesome.min.css │ │ │ ├── regular.css │ │ │ ├── regular.min.css │ │ │ ├── solid.css │ │ │ ├── solid.min.css │ │ │ ├── svg-with-js.css │ │ │ ├── svg-with-js.min.css │ │ │ ├── v4-shims.css │ │ │ └── v4-shims.min.css │ │ └── webfonts │ │ │ ├── fa-brands-400.eot │ │ │ ├── fa-brands-400.svg │ │ │ ├── fa-brands-400.ttf │ │ │ ├── fa-brands-400.woff │ │ │ ├── fa-brands-400.woff2 │ │ │ ├── fa-regular-400.eot │ │ │ ├── fa-regular-400.svg │ │ │ ├── fa-regular-400.ttf │ │ │ ├── fa-regular-400.woff │ │ │ ├── fa-regular-400.woff2 │ │ │ ├── fa-solid-900.eot │ │ │ ├── fa-solid-900.svg │ │ │ ├── fa-solid-900.ttf │ │ │ ├── fa-solid-900.woff │ │ │ └── fa-solid-900.woff2 │ ├── main.css │ └── podium.css ├── images │ ├── blank-avatar.png │ ├── blank-ctf.png │ ├── favicon.png │ ├── flags │ │ ├── afghanistan.png │ │ ├── aland-islands.png │ │ ├── albania.png │ │ ├── algeria.png │ │ ├── american-samoa.png │ │ ├── andorra.png │ │ ├── angola.png │ │ ├── anguilla.png │ │ ├── antarctica.png │ │ ├── antigua-and-barbuda.png │ │ ├── argentina.png │ │ ├── armenia.png │ │ ├── aruba.png │ │ ├── australia.png │ │ ├── austria.png │ │ ├── azerbaijan.png │ │ ├── bahamas.png │ │ ├── bahrain.png │ │ ├── bangladesh.png │ │ ├── barbados.png │ │ ├── belarus.png │ │ ├── belgium.png │ │ ├── belize.png │ │ ├── benin.png │ │ ├── bermuda.png │ │ ├── bhutan.png │ │ ├── blank.png │ │ ├── bolivia.png │ │ ├── bosnia-and-herzegovina.png │ │ ├── botswana.png │ │ ├── bouvet-island.png │ │ ├── brazil.png │ │ ├── british-indian-ocean-territory.png │ │ ├── british-virgin-islands.png │ │ ├── brunei.png │ │ ├── bulgaria.png │ │ ├── burkina-faso.png │ │ ├── burundi.png │ │ ├── cambodia.png │ │ ├── cameroon.png │ │ ├── canada.png │ │ ├── cape-verde.png │ │ ├── caribbean-netherlands.png │ │ ├── cayman-islands.png │ │ ├── central-african-republic.png │ │ ├── chad.png │ │ ├── chile.png │ │ ├── china.png │ │ ├── christmas-island.png │ │ ├── cocos-keeling-islands.png │ │ ├── colombia.png │ │ ├── comoros.png │ │ ├── cook-islands.png │ │ ├── costa-rica.png │ │ ├── cote-divoire-ivory-coast.png │ │ ├── croatia.png │ │ ├── cuba.png │ │ ├── curacao.png │ │ ├── cyprus.png │ │ ├── czechia.png │ │ ├── denmark.png │ │ ├── djibouti.png │ │ ├── dominica.png │ │ ├── dominican-republic.png │ │ ├── dr-congo.png │ │ ├── ecuador.png │ │ ├── egypt.png │ │ ├── el-salvador.png │ │ ├── england.png │ │ ├── equatorial-guinea.png │ │ ├── eritrea.png │ │ ├── estonia.png │ │ ├── eswatini-swaziland.png │ │ ├── ethiopia.png │ │ ├── falkland-islands.png │ │ ├── faroe-islands.png │ │ ├── fiji.png │ │ ├── finland.png │ │ ├── france.png │ │ ├── french-guiana.png │ │ ├── french-polynesia.png │ │ ├── french-southern-and-antarctic-lands.png │ │ ├── gabon.png │ │ ├── gambia.png │ │ ├── georgia.png │ │ ├── germany.png │ │ ├── ghana.png │ │ ├── gibraltar.png │ │ ├── greece.png │ │ ├── greenland.png │ │ ├── grenada.png │ │ ├── guadeloupe.png │ │ ├── guam.png │ │ ├── guatemala.png │ │ ├── guernsey.png │ │ ├── guinea-bissau.png │ │ ├── guinea.png │ │ ├── guyana.png │ │ ├── haiti.png │ │ ├── heard-island-and-mcdonald-islands.png │ │ ├── honduras.png │ │ ├── hong-kong.png │ │ ├── hungary.png │ │ ├── iceland.png │ │ ├── india.png │ │ ├── indonesia.png │ │ ├── iran.png │ │ ├── iraq.png │ │ ├── ireland.png │ │ ├── isle-of-man.png │ │ ├── israel.png │ │ ├── italy.png │ │ ├── jamaica.png │ │ ├── japan.png │ │ ├── jersey.png │ │ ├── jordan.png │ │ ├── kazakhstan.png │ │ ├── kenya.png │ │ ├── kiribati.png │ │ ├── kosovo.png │ │ ├── kuwait.png │ │ ├── kyrgyzstan.png │ │ ├── laos.png │ │ ├── latvia.png │ │ ├── lebanon.png │ │ ├── lesotho.png │ │ ├── liberia.png │ │ ├── libya.png │ │ ├── liechtenstein.png │ │ ├── lithuania.png │ │ ├── luxembourg.png │ │ ├── macau.png │ │ ├── madagascar.png │ │ ├── malawi.png │ │ ├── malaysia.png │ │ ├── maldives.png │ │ ├── mali.png │ │ ├── malta.png │ │ ├── marshall-islands.png │ │ ├── martinique.png │ │ ├── mauritania.png │ │ ├── mauritius.png │ │ ├── mayotte.png │ │ ├── mexico.png │ │ ├── micronesia.png │ │ ├── moldova.png │ │ ├── monaco.png │ │ ├── mongolia.png │ │ ├── montenegro.png │ │ ├── montserrat.png │ │ ├── morocco.png │ │ ├── mozambique.png │ │ ├── myanmar.png │ │ ├── namibia.png │ │ ├── nauru.png │ │ ├── nepal.png │ │ ├── netherlands.png │ │ ├── new-caledonia.png │ │ ├── new-zealand.png │ │ ├── nicaragua.png │ │ ├── niger.png │ │ ├── nigeria.png │ │ ├── niue.png │ │ ├── norfolk-island.png │ │ ├── north-korea.png │ │ ├── north-macedonia.png │ │ ├── northern-ireland.png │ │ ├── northern-mariana-islands.png │ │ ├── norway.png │ │ ├── oman.png │ │ ├── pakistan.png │ │ ├── palau.png │ │ ├── palestine.png │ │ ├── panama.png │ │ ├── papua-new-guinea.png │ │ ├── paraguay.png │ │ ├── peru.png │ │ ├── philippines.png │ │ ├── pitcairn-islands.png │ │ ├── poland.png │ │ ├── portugal.png │ │ ├── puerto-rico.png │ │ ├── qatar.png │ │ ├── republic-of-the-congo.png │ │ ├── reunion.png │ │ ├── romania.png │ │ ├── russia.png │ │ ├── rwanda.png │ │ ├── saint-barthelemy.png │ │ ├── saint-helena-ascension-and-tristan-da-cunha.png │ │ ├── saint-kitts-and-nevis.png │ │ ├── saint-lucia.png │ │ ├── saint-martin.png │ │ ├── saint-pierre-and-miquelon.png │ │ ├── saint-vincent-and-the-grenadines.png │ │ ├── samoa.png │ │ ├── san-marino.png │ │ ├── sao-tome-and-principe.png │ │ ├── saudi-arabia.png │ │ ├── scotland.png │ │ ├── senegal.png │ │ ├── serbia.png │ │ ├── seychelles.png │ │ ├── sierra-leone.png │ │ ├── singapore.png │ │ ├── sint-maarten.png │ │ ├── slovakia.png │ │ ├── slovenia.png │ │ ├── solomon-islands.png │ │ ├── somalia.png │ │ ├── south-africa.png │ │ ├── south-georgia.png │ │ ├── south-korea.png │ │ ├── south-sudan.png │ │ ├── spain.png │ │ ├── sri-lanka.png │ │ ├── sudan.png │ │ ├── suriname.png │ │ ├── svalbard-and-jan-mayen.png │ │ ├── sweden.png │ │ ├── switzerland.png │ │ ├── syria.png │ │ ├── taiwan.png │ │ ├── tajikistan.png │ │ ├── tanzania.png │ │ ├── thailand.png │ │ ├── timor-leste.png │ │ ├── togo.png │ │ ├── tokelau.png │ │ ├── tonga.png │ │ ├── trinidad-and-tobago.png │ │ ├── tunisia.png │ │ ├── turkey.png │ │ ├── turkmenistan.png │ │ ├── turks-and-caicos-islands.png │ │ ├── tuvalu.png │ │ ├── uganda.png │ │ ├── ukraine.png │ │ ├── united-arab-emirates.png │ │ ├── united-kingdom.png │ │ ├── united-nations.png │ │ ├── united-states-minor-outlying-islands.png │ │ ├── united-states-virgin-islands.png │ │ ├── united-states.png │ │ ├── uruguay.png │ │ ├── uzbekistan.png │ │ ├── vanuatu.png │ │ ├── vatican-city-holy-see.png │ │ ├── venezuela.png │ │ ├── vietnam.png │ │ ├── wales.png │ │ ├── wallis-and-futuna.png │ │ ├── western-sahara.png │ │ ├── yemen.png │ │ ├── zambia.png │ │ └── zimbabwe.png │ ├── logo.png │ ├── new_logo_circle.png │ └── new_logo_square.png └── js │ ├── challenge.js │ ├── shortcuts.js │ └── utils.js └── uploads └── .do_not_remove /.dockerignore: -------------------------------------------------------------------------------- 1 | external-repos 2 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties 2 | 3 | root = true 4 | 5 | [*] 6 | end_of_line = lf 7 | insert_final_newline = true 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.py] 13 | indent_style = space 14 | indent_size = 4 15 | 16 | [Makefile] 17 | indent_style = tab 18 | 19 | [*.yml] 20 | indent_style = space 21 | indent_size = 2 22 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # 2 | # CTFHub 3 | # 4 | CTFHUB_DEBUG=1 # Change here to disable debug mode 5 | CTFHUB_DOMAIN=localhost # Change here to your server public IP / FQDN 6 | CTFHUB_PORT=8000 # Change here to your server public port for CTFHub 7 | CTFHUB_PROTOCOL=http # Change here to 1 if your public server uses https 8 | CTFHUB_SECRET_KEY=74320c04549af3a5f9fd9bc007b2e20ced8 # Change here 9 | # CTFHUB_URL=${CTFHUB_PROTOCOL}://${CTFHUB_DOMAIN}:${CTFHUB_PORT} # You can override the URL to the app, usually unecessary 10 | 11 | CTFHUB_ALLOWED_HOSTS="localhost;127.0.0.1" # Update here with your FQDN 12 | CTFHUB_TRUSTED_ORIGINS="http://127.0.0.1;http://localhost" # Update here to whitelist domains for CSRF protection 13 | 14 | # 15 | # CTFHub Database 16 | # 17 | CTFHUB_DB_HOST=db 18 | CTFHUB_DB_PORT=5432 19 | CTFHUB_DB_NAME=ctfhub # Change here 20 | CTFHUB_DB_USER=ctfhub # Change here 21 | CTFHUB_DB_PASSWORD=1358127ce28271330b266cbf2ff556af13653fb5 # Change here 22 | 23 | 24 | # 25 | # Hedgedoc settings 26 | # 27 | ## The FQDN of how web browsers should reach hedgedoc 28 | CTFHUB_HEDGEDOC_USESSL=false 29 | CTFHUB_HEDGEDOC_DOMAIN=hedgedoc 30 | CTFHUB_HEDGEDOC_PORT=3000 31 | CTFHUB_HEDGEDOC_URL=http://${CTFHUB_HEDGEDOC_DOMAIN}:${CTFHUB_HEDGEDOC_PORT} 32 | 33 | # 34 | # CTFHub Email recovery feature 35 | # 36 | # Customize below to enable the password recovery feature by email 37 | CTFHUB_EMAIL_SERVER_HOST='' # smtp.gmail.com or mailgun, or sendgrid etc. 38 | CTFHUB_EMAIL_SERVER_PORT=0 39 | CTFHUB_EMAIL_USERNAME='' 40 | CTFHUB_EMAIL_PASSWORD='' 41 | 42 | 43 | # 44 | # Notification webhook URLs 45 | # Leave blank or customize below to disable 46 | # 47 | CTFHUB_DISCORD_WEBHOOK_URL= 48 | 49 | 50 | # 51 | # Jitsi settings 52 | # 53 | CTFHUB_JITSI_URL=https://meet.jit.si 54 | 55 | 56 | # 57 | # Excalidraw settings 58 | # 59 | # By default, use the public instance at excalidraw.com 60 | # To use your own instance, a docker script can be found in `scripts/excalidraw/docker-compose.yml` 61 | # 62 | CTFHUB_EXCALIDRAW_URL=https://excalidraw.com:443 63 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report 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 | **Additional context** 27 | Add any other context about the problem here (callstacks can be seen with DEBUG mode enabled) 28 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.github/workflows/format.yml: -------------------------------------------------------------------------------- 1 | name: Format Validation 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: [main] 7 | 8 | jobs: 9 | format-check: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - uses: actions/setup-python@v4 14 | with: 15 | python-version: '3.10' 16 | - uses: pre-commit/action@v3.0.0 17 | -------------------------------------------------------------------------------- /.github/workflows/notify.yml: -------------------------------------------------------------------------------- 1 | name: "Discord Notification" 2 | on: [pull_request, issues] 3 | env: 4 | DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} 5 | 6 | jobs: 7 | notify: 8 | runs-on: ubuntu-latest 9 | steps: 10 | 11 | - name: Triggering Push Discord Notification 12 | if: github.event_name == 'push' && github.repository_owner == 'hugsy' 13 | uses: sarisia/actions-status-discord@v1 14 | with: 15 | nodetail: true 16 | title: ${{ github.actor }} pushed to `${{ github.ref }}` 17 | description: | 18 | --- 19 | **Changes**: ${{ github.event.compare }} 20 | --- 21 | **Commits**: 22 | ● ${{ join(github.event.commits.*.message, ' 23 | ● ') }} 24 | 25 | color: 0x0000ff 26 | username: ${{ github.actor }} on CTFHub 27 | avatar_url: "https://github.com/hugsy/ctfhub/blob/refresh-readme/static/images/new_logo_circle.png?raw=true" 28 | 29 | - name: Triggering Pull Request Discord Notification 30 | if: github.event_name == 'pull_request' && github.event.action == 'opened' && github.repository_owner == 'hugsy' 31 | uses: sarisia/actions-status-discord@v1 32 | with: 33 | nodetail: true 34 | title: ${{ github.actor }} created a new Pull Request (`#${{ github.event.pull_request.number }}`) 35 | description: | 36 | **${{ github.event.pull_request.title }}** 37 | 38 | ${{ github.event.pull_request.body }} 39 | 40 | --- 41 | Link: ${{ github.event.pull_request.html_url }} 42 | color: 0xff0000 43 | username: ${{ github.actor }} on CTFHub 44 | avatar_url: "https://github.com/hugsy/ctfhub/blob/refresh-readme/static/images/new_logo_circle.png?raw=true" 45 | 46 | - name: Triggering Issue Discord Notification 47 | if: github.event_name == 'issues' && github.event.action == 'opened' && github.repository_owner == 'hugsy' 48 | uses: sarisia/actions-status-discord@v1 49 | with: 50 | nodetail: true 51 | title: ${{ github.actor }} created a new Issue (`#${{ github.event.issue.number }}`) 52 | description: | 53 | **${{ github.event.issue.title }}** 54 | 55 | ${{ github.event.issue.body }} 56 | 57 | --- 58 | Link: ${{ github.event.issue.html_url }} 59 | color: 0x00ff00 60 | username: ${{ github.actor }} on CTFHub 61 | avatar_url: "https://github.com/hugsy/ctfhub/blob/refresh-readme/static/images/new_logo_circle.png?raw=true" 62 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: "Run tests" 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | 7 | 8 | jobs: 9 | test: 10 | name: "Run tests" 11 | runs-on: ubuntu-latest 12 | services: 13 | postgres: 14 | image: postgres 15 | env: 16 | POSTGRES_DB: ctfhub 17 | POSTGRES_USER: ctfhub 18 | POSTGRES_PASSWORD: ctfhub 19 | options: >- 20 | --health-cmd pg_isready 21 | --health-interval 10s 22 | --health-timeout 5s 23 | --health-retries 5 24 | ports: 25 | - 5432:5432 26 | 27 | hedgedoc: 28 | image: quay.io/hedgedoc/hedgedoc:alpine 29 | env: 30 | CMD_DB_URL: postgres://ctfhub:ctfhub@postgres:5432/ctfhub 31 | CMD_ALLOW_ANONYMOUS: false 32 | CMD_ALLOW_FREEURL: true 33 | CMD_IMAGE_UPLOAD_TYPE: filesystem 34 | CMD_DOMAIN: localhost 35 | CMD_PORT: 3000 36 | CMD_URL_ADDPORT: true 37 | CMD_PROTOCOL_USESSL: false 38 | ports: 39 | - 3000:3000 40 | 41 | steps: 42 | - name: Checkout 43 | uses: actions/checkout@v3 44 | 45 | - name: Setup environment vars 46 | run: | 47 | echo 127.0.1.1 hedgedoc | sudo tee -a /etc/hosts 48 | 49 | echo CTFHUB_PROTOCOL="http" >> ${GITHUB_ENV} 50 | echo CTFHUB_DOMAIN="localhost" >> ${GITHUB_ENV} 51 | echo CTFHUB_PORT="8000" >> ${GITHUB_ENV} 52 | echo CTFHUB_DB_NAME="ctfhub" >> ${GITHUB_ENV} 53 | echo CTFHUB_DB_USER="ctfhub" >> ${GITHUB_ENV} 54 | echo CTFHUB_DB_PASSWORD="ctfhub" >> ${GITHUB_ENV} 55 | echo CTFHUB_DB_HOST="localhost" >> ${GITHUB_ENV} 56 | echo CTFHUB_DB_PORT="5432" >> ${GITHUB_ENV} 57 | echo CTFHUB_HEDGEDOC_USESSL="false" >> ${GITHUB_ENV} 58 | echo CTFHUB_HEDGEDOC_DOMAIN="hedgedoc" >> ${GITHUB_ENV} 59 | echo CTFHUB_HEDGEDOC_PORT="3000" >> ${GITHUB_ENV} 60 | echo CTFHUB_HEDGEDOC_URL="http://hedgedoc:3000" >> ${GITHUB_ENV} 61 | echo CTFHUB_ALLOWED_HOSTS="localhost;127.0.0.1" >> ${GITHUB_ENV} 62 | echo CTFHUB_TRUSTED_ORIGINS="http://127.0.0.1;http://localhost" >> ${GITHUB_ENV} 63 | 64 | - name: Setup Python 65 | uses: actions/setup-python@v4 66 | with: 67 | python-version: '3.10' 68 | 69 | - name: Python pre-requisites 70 | run: | 71 | python -m pip install pip --user --upgrade 72 | python -m pip install -r requirements.txt --user --upgrade 73 | 74 | - name: Migrate DB 75 | run: | 76 | python manage.py migrate 77 | 78 | - name: Execute Tests 79 | run: | 80 | pytest -vv 81 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | env/ 3 | **/__pycache__/ 4 | *.pyc 5 | *.pyo 6 | debug.log 7 | uploads/media/ 8 | uploads/files/ 9 | external-repos/ 10 | .env 11 | volumes 12 | .ruff_cache 13 | .idea/ 14 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/rtts/djhtml 3 | rev: '3.0.6' 4 | hooks: 5 | - id: djhtml 6 | files: ctfhub/templates/.*\.html$ 7 | 8 | - id: djcss 9 | files: ctfhub/static/css/.*\.css$ 10 | 11 | - id: djjs 12 | files: ctfhub/static/js/.*\.js$ 13 | 14 | - repo: https://github.com/psf/black 15 | rev: '23.3.0' 16 | hooks: 17 | - id: black 18 | language_version: python3.10 19 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # https://hub.docker.com/_/python?tab=description 2 | FROM python:3.10-buster 3 | 4 | ENV PYTHONUNBUFFERED 1 5 | ENV PYTHONDONTWRITEBYTECODE 1 6 | ENV DEBUG 0 7 | 8 | RUN \ 9 | apt-get update && apt-get upgrade -y && \ 10 | apt-get install -y libpq-dev python3-dev postgresql-client && \ 11 | apt-get autoclean && apt-get autoremove 12 | 13 | WORKDIR /code 14 | 15 | COPY requirements.txt . 16 | RUN \ 17 | python3 -m pip install --upgrade pip && \ 18 | python3 -m pip install -r requirements.txt --no-cache-dir 19 | 20 | ENTRYPOINT ["bash", "/code/docker-entrypoint.sh"] 21 | -------------------------------------------------------------------------------- /ctfhub/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = "ctfhub.apps.CtfhubConfig" # pylint: disable=invalid-name 2 | -------------------------------------------------------------------------------- /ctfhub/admin.py: -------------------------------------------------------------------------------- 1 | # Register your models here. 2 | -------------------------------------------------------------------------------- /ctfhub/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | from django.utils.translation import gettext_lazy as _ 3 | 4 | 5 | class CtfhubConfig(AppConfig): 6 | name = "ctfhub" 7 | verbose_name = _("ctfhub") 8 | 9 | def ready(self): 10 | pass 11 | -------------------------------------------------------------------------------- /ctfhub/context_processors.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from typing import Union 3 | import django.http 4 | from django.conf import settings 5 | 6 | from ctfhub.models import Member 7 | 8 | 9 | def add_debug_context(request: django.http.HttpRequest) -> dict[str, dict[str, str]]: 10 | """Adds some CTFHub environment information to every context 11 | 12 | Args: 13 | request (django.http.HttpRequest): _description_ 14 | 15 | Returns: 16 | dict[str, str]: _description_ 17 | """ 18 | return { 19 | "CTFHub": { 20 | "DEBUG": settings.DEBUG, 21 | "VERSION": settings.PROJECT_VERSION, 22 | "URL": settings.PROJECT_URL, 23 | } 24 | } 25 | 26 | 27 | def add_timezone_context( 28 | request: django.http.HttpRequest, 29 | ) -> dict[str, Union[str, datetime.datetime]]: 30 | """Add the client timezone information to the HTTP context 31 | 32 | Args: 33 | request (django.http.HttpRequest): _description_ 34 | 35 | Returns: 36 | dict: _description_ 37 | """ 38 | if request.user.is_anonymous: 39 | return {"TZ": "UTC", "NOW": datetime.datetime.now()} 40 | 41 | try: 42 | member = Member.objects.get(user=request.user) 43 | return {"TZ": member.timezone, "NOW": datetime.datetime.now()} 44 | except Member.DoesNotExist: 45 | return {"TZ": "UTC", "NOW": datetime.datetime.now()} 46 | -------------------------------------------------------------------------------- /ctfhub/decorators/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/ctfhub/decorators/__init__.py -------------------------------------------------------------------------------- /ctfhub/exceptions.py: -------------------------------------------------------------------------------- 1 | class ExternalError(Exception): 2 | """Used for reporting exceptions from non-builtin components, for instance hedgedoc 3 | 4 | Args: 5 | Exception (_type_): _description_ 6 | """ 7 | -------------------------------------------------------------------------------- /ctfhub/migrations/0002_auto_20210102_1139.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-01-02 11:39 2 | 3 | import model_utils.fields 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [ 9 | ("ctfhub", "0001_initial"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="ctf", 15 | name="weight", 16 | field=models.FloatField(default=1), 17 | ), 18 | migrations.AlterField( 19 | model_name="member", 20 | name="country", 21 | field=model_utils.fields.StatusField( 22 | choices=[(0, "dummy")], 23 | default="Afghanistan", 24 | max_length=100, 25 | no_check_for_status=True, 26 | ), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /ctfhub/migrations/0003_auto_20210113_1945.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.3 on 2021-01-13 19:45 2 | 3 | import uuid 4 | 5 | import ctfhub.helpers 6 | from django.db import migrations, models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | dependencies = [ 11 | ("ctfhub", "0002_auto_20210102_1139"), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name="challenge", 17 | name="whiteboard_id", 18 | field=models.UUIDField(default=uuid.uuid4), 19 | ), 20 | migrations.AddField( 21 | model_name="ctf", 22 | name="whiteboard_access_token", 23 | field=models.CharField( 24 | default=ctfhub.helpers.get_random_string_64, max_length=64 25 | ), 26 | ), 27 | migrations.AlterField( 28 | model_name="challenge", 29 | name="note_id", 30 | field=models.CharField( 31 | blank=True, default=ctfhub.helpers.create_new_note, max_length=38 32 | ), 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /ctfhub/migrations/0004_ctf_note_id.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.3 on 2021-01-13 20:21 2 | 3 | import ctfhub.helpers 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [ 9 | ("ctfhub", "0003_auto_20210113_1945"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="ctf", 15 | name="note_id", 16 | field=models.CharField( 17 | blank=True, default=ctfhub.helpers.create_new_note, max_length=38 18 | ), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /ctfhub/migrations/0005_auto_20210204_1921.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.3 on 2021-02-04 19:21 2 | 3 | import django.db.models.deletion 4 | import model_utils.fields 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | dependencies = [ 10 | ("ctfhub", "0004_ctf_note_id"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name="member", 16 | name="status", 17 | field=model_utils.fields.StatusField( 18 | choices=[(0, "dummy")], 19 | default="member", 20 | max_length=100, 21 | no_check_for_status=True, 22 | ), 23 | ), 24 | migrations.AlterField( 25 | model_name="member", 26 | name="selected_ctf", 27 | field=models.ForeignKey( 28 | blank=True, 29 | null=True, 30 | on_delete=django.db.models.deletion.SET_NULL, 31 | related_name="players", 32 | related_query_name="player", 33 | to="ctfhub.ctf", 34 | ), 35 | ), 36 | ] 37 | -------------------------------------------------------------------------------- /ctfhub/migrations/0006_auto_20210329_1218.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.7 on 2021-03-29 12:18 2 | 3 | import model_utils.fields 4 | from django.db import migrations 5 | 6 | 7 | def set_default_timezone(apps, schema_editor): 8 | Member = apps.get_model("ctfhub", "Member") 9 | for member in Member.objects.all(): 10 | member.timezone = "UTC" 11 | member.save() 12 | 13 | 14 | class Migration(migrations.Migration): 15 | dependencies = [ 16 | ("ctfhub", "0005_auto_20210204_1921"), 17 | ] 18 | 19 | operations = [ 20 | migrations.AlterField( 21 | model_name="member", 22 | name="timezone", 23 | field=model_utils.fields.StatusField( 24 | choices=[(0, "dummy")], 25 | default="UTC", 26 | max_length=100, 27 | no_check_for_status=True, 28 | ), 29 | ), 30 | migrations.RunPython(set_default_timezone), 31 | ] 32 | -------------------------------------------------------------------------------- /ctfhub/migrations/0007_ctf_rating.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.7 on 2021-04-11 19:07 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("ctfhub", "0006_auto_20210329_1218"), 9 | ] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name="ctf", 14 | name="rating", 15 | field=models.FloatField(default=0.0), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /ctfhub/migrations/0009_add_challenge_assigned_members.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.2 on 2023-06-28 22:26 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("ctfhub", "0008_remove_challenge_whiteboard_id_and_more"), 9 | ] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name="challenge", 14 | name="assigned_members", 15 | field=models.ManyToManyField( 16 | blank=True, related_name="assigned_challenges", to="ctfhub.member" 17 | ), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /ctfhub/migrations/0009_alter_member_user.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.2 on 2023-06-27 17:11 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | dependencies = [ 10 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 11 | ("ctfhub", "0008_remove_challenge_whiteboard_id_and_more"), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name="member", 17 | name="user", 18 | field=models.OneToOneField( 19 | on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL 20 | ), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /ctfhub/migrations/0010_alter_challengefile_file_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.2 on 2023-06-28 22:53 2 | 3 | import django.core.files.storage 4 | from django.db import migrations, models 5 | 6 | import ctfhub.helpers 7 | import ctfhub.validators 8 | 9 | 10 | class Migration(migrations.Migration): 11 | dependencies = [ 12 | ("ctfhub", "0009_add_challenge_assigned_members"), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name="challengefile", 18 | name="file", 19 | field=models.FileField( 20 | null=True, 21 | storage=django.core.files.storage.FileSystemStorage( 22 | base_url="/uploads/", location="/code/uploads" 23 | ), 24 | upload_to=ctfhub.helpers.get_challenge_upload_path, 25 | validators=[ctfhub.validators.challenge_file_max_size_validator], 26 | ), 27 | ), 28 | migrations.AlterField( 29 | model_name="member", 30 | name="avatar", 31 | field=models.ImageField( 32 | blank=True, 33 | storage=django.core.files.storage.FileSystemStorage( 34 | base_url="/uploads/", location="/code/uploads" 35 | ), 36 | upload_to="media/", 37 | ), 38 | ), 39 | ] 40 | -------------------------------------------------------------------------------- /ctfhub/migrations/0010_remove_ctf_visibility.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.2 on 2023-06-27 17:59 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("ctfhub", "0009_alter_member_user"), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveField( 13 | model_name="ctf", 14 | name="visibility", 15 | ), 16 | ] 17 | -------------------------------------------------------------------------------- /ctfhub/migrations/0011_ctf_visibility.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.2 on 2023-06-27 18:05 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("ctfhub", "0010_remove_ctf_visibility"), 9 | ] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name="ctf", 14 | name="visibility", 15 | field=models.CharField( 16 | choices=[("OPEN", "Public"), ("PRIV", "Private")], 17 | default="OPEN", 18 | max_length=4, 19 | ), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /ctfhub/migrations/0012_alter_member_status.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.2 on 2023-06-27 18:17 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | from ctfhub.models import Member 7 | 8 | 9 | def forwards_func(apps, schema_editor): 10 | MemberModel = apps.get_model("ctfhub", "Member") 11 | 12 | for member in MemberModel.objects.all(): 13 | member.status = ( 14 | Member.StatusType.MEMBER 15 | if member.old_status == "member" 16 | else Member.StatusType.GUEST 17 | ) 18 | member.save() 19 | 20 | 21 | def reverse_func(apps, schema_editor): 22 | pass 23 | 24 | 25 | class Migration(migrations.Migration): 26 | dependencies = [ 27 | ("ctfhub", "0011_ctf_visibility"), 28 | ] 29 | 30 | operations = [ 31 | migrations.RenameField( 32 | model_name="member", 33 | old_name="status", 34 | new_name="old_status", 35 | ), 36 | migrations.AddField( 37 | model_name="member", 38 | name="status", 39 | field=models.IntegerField(choices=[(0, "Member"), (1, "Guest")], default=0), 40 | ), 41 | migrations.RunPython(forwards_func, reverse_func), 42 | migrations.RemoveField( 43 | model_name="member", 44 | name="old_status", 45 | ), 46 | ] 47 | -------------------------------------------------------------------------------- /ctfhub/migrations/0017_merge_20230629_1617.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.2 on 2023-06-29 16:17 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("ctfhub", "0010_alter_challengefile_file_and_more"), 9 | ("ctfhub", "0016_alter_member_timezone"), 10 | ] 11 | 12 | operations = [] 13 | -------------------------------------------------------------------------------- /ctfhub/migrations/0018_alter_member_status.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.2 on 2023-06-30 17:51 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("ctfhub", "0017_merge_20230629_1617"), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="member", 14 | name="status", 15 | field=models.IntegerField( 16 | choices=[(0, "Member"), (1, "Guest"), (2, "Inactive")], default=0 17 | ), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /ctfhub/migrations/0019_alter_challenge_note_id_alter_ctf_note_id.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.2 on 2023-06-30 19:19 2 | 3 | from django.db import migrations, models 4 | import uuid 5 | 6 | 7 | def forwards_func(apps, schema_editor): 8 | for model_name in "Ctf", "Challenge": 9 | MemberModel = apps.get_model("ctfhub", model_name) 10 | for entry in MemberModel.objects.all(): 11 | if entry.note_id.startswith("/"): 12 | entry.note_id = entry.note_id[1:] 13 | entry.save() 14 | 15 | 16 | def reverse_func(apps, schema_editor): 17 | pass 18 | 19 | 20 | class Migration(migrations.Migration): 21 | dependencies = [ 22 | ("ctfhub", "0018_alter_member_status"), 23 | ] 24 | 25 | operations = [ 26 | migrations.RunPython(forwards_func, reverse_func), 27 | migrations.AlterField( 28 | model_name="challenge", 29 | name="note_id", 30 | field=models.UUIDField(default=uuid.uuid4), 31 | ), 32 | migrations.AlterField( 33 | model_name="ctf", 34 | name="note_id", 35 | field=models.UUIDField(default=uuid.uuid4), 36 | ), 37 | ] 38 | -------------------------------------------------------------------------------- /ctfhub/migrations/0020_alter_member_hedgedoc_password.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.2 on 2023-06-30 21:06 2 | 3 | import ctfhub.helpers 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [ 9 | ("ctfhub", "0019_alter_challenge_note_id_alter_ctf_note_id"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="member", 15 | name="hedgedoc_password", 16 | field=models.CharField( 17 | default=ctfhub.helpers.get_random_string_64, 18 | editable=False, 19 | max_length=64, 20 | ), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /ctfhub/migrations/0021_alter_team_avatar.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.2 on 2023-07-04 16:06 2 | 3 | import ctfhub.validators 4 | import django.core.files.storage 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | dependencies = [ 10 | ("ctfhub", "0020_alter_member_hedgedoc_password"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name="team", 16 | name="avatar", 17 | field=models.ImageField( 18 | blank=True, 19 | storage=django.core.files.storage.FileSystemStorage( 20 | base_url="/uploads/", location="/code/uploads" 21 | ), 22 | upload_to="media/", 23 | validators=[ctfhub.validators.challenge_file_max_size_validator], 24 | ), 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /ctfhub/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/ctfhub/migrations/__init__.py -------------------------------------------------------------------------------- /ctfhub/mixins.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.mixins import AccessMixin 2 | 3 | from ctfhub.models import Member 4 | 5 | 6 | class RequireSuperPowersMixin(AccessMixin): 7 | """Verify that the current user has super powers.""" 8 | 9 | def dispatch(self, request, *args, **kwargs): 10 | if not request.user.member.has_superpowers: 11 | return self.handle_no_permission() 12 | self.member: Member = request.user.member 13 | return super().dispatch(request, *args, **kwargs) # type: ignore 14 | 15 | 16 | class MembersOnlyMixin(AccessMixin): 17 | """Verify that the current user is a member.""" 18 | 19 | def dispatch(self, request, *args, **kwargs): 20 | if request.user.member.is_guest: 21 | return self.handle_no_permission() 22 | self.member: Member = request.user.member 23 | return super().dispatch(request, *args, **kwargs) # type: ignore 24 | -------------------------------------------------------------------------------- /ctfhub/templates/ctfhub/categories/create.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ctfhub/templates/ctfhub/challenges/confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends 'ctfhub/main.html' %} 2 | 3 | {% block content %} 4 | 5 |
6 | 7 |
8 |
9 |

Delete challenge '{{challenge.name}}' ?

10 |

11 | 12 | This action will also delete associated flag and files associated to the challenge. 13 | It cannot be recovered. 14 | 15 |

16 | Confirm deletion? 17 |

18 |
19 |
20 | {% csrf_token %} 21 | 25 |
26 |
27 |
28 |
29 | 30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /ctfhub/templates/ctfhub/challenges/files/confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends 'ctfhub/main.html' %} 2 | 3 | {% block content %} 4 | 5 |
6 | 7 |
8 |
9 |

Delete file '{{challengefile.name}}' ?

10 |

11 | 12 | It cannot be recovered. 13 | 14 |

15 | Confirm deletion? 16 |

17 |
18 |
19 | {% csrf_token %} 20 | 24 |
25 |
26 |
27 |
28 | 29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /ctfhub/templates/ctfhub/challenges/files/create.html: -------------------------------------------------------------------------------- 1 | {% include 'snippets/formerror.html' %} -------------------------------------------------------------------------------- /ctfhub/templates/ctfhub/challenges/list.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/ctfhub/templates/ctfhub/challenges/list.html -------------------------------------------------------------------------------- /ctfhub/templates/ctfhub/ctfs/confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends 'ctfhub/main.html' %} 2 | 3 | {% block content %} 4 | 5 |
6 | 7 |
8 |
9 |

Delete CTF '{{ctf.name}}' ?

10 |

11 | 12 | This action will delete the CTF and all associated resources recursively (i.e. challenges, flags, files, etc.). 13 | It cannot be recovered. 14 | 15 |

16 | Confirm deletion? 17 |

18 |
19 |
20 | {% csrf_token %} 21 | 25 |
26 |
27 |
28 |
29 | 30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /ctfhub/templates/ctfhub/ctfs/detail_export.html: -------------------------------------------------------------------------------- 1 |
2 | 7 |
8 | -------------------------------------------------------------------------------- /ctfhub/templates/ctfhub/ctfs/detail_notes.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 11 |
12 |
13 |
14 | {% if request.user.member.hedgedoc_password %} 15 | 27 | {% endif %} -------------------------------------------------------------------------------- /ctfhub/templates/ctfhub/ctfs/list.html: -------------------------------------------------------------------------------- 1 | {% extends 'ctfhub/main.html' %} 2 | 3 | {% block content %} 4 | 5 | 6 | {% for message in messages %} 7 |

{{ message }}

8 | {% endfor %} 9 | 10 | 11 |
12 |
13 |
14 |
15 |
16 |
17 |

CTFs

18 |

19 | Manage your CTF from here. 20 |

21 |
22 |
23 |
24 |
25 |
26 | 27 |
28 | 29 |
30 |
31 |
32 |
33 | 44 |
45 | 46 | 47 |
48 |
49 |
50 | {% include 'ctfhub/ctfs/list_ctfs.html' %} 51 |
52 |
53 |
55 |
56 | {% include 'ctfhub/ctfs/list_ctftime_ctfs.html' %} 57 |
58 |
59 |
60 | 61 | 62 | 73 | 74 |
75 |
76 |
77 | {% endblock %} 78 | -------------------------------------------------------------------------------- /ctfhub/templates/ctfhub/ctfs/list_ctftime_ctfs.html: -------------------------------------------------------------------------------- 1 | {% load ctfhub_filters %} 2 | {% load humanize %} 3 | {% load tz %} 4 | 5 |
6 |
7 |
Current and future CTFs from CTFTime
8 |
9 |
10 | * All times are in UTC (hover for local time) 11 |
12 |
13 | 14 |
15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | {% for ctf in ctftime_ctfs %} 29 | 30 | 33 | 38 | 41 | 42 | 43 | 44 | {% endfor %} 45 | 46 |
CTFDate (in UTC)DurationWeightImport
31 | {{ctf.title}} 32 | 34 | {{ctf.start | date:"Y/m/d H:i:s"}} 35 |  →  36 | {{ctf.finish | date:"Y/m/d H:i:s"}} 37 | 39 | {{ ctf.duration|naturaltime}} 40 | {{ ctf.weight | floatformat:"-2" }}
47 |
48 |
49 | 50 | 51 | 71 | -------------------------------------------------------------------------------- /ctfhub/templates/ctfhub/dashboard/status.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 |
7 |
8 |
  Members
9 |
10 |
11 |

{{members | length}}

12 |
13 |
14 |
15 |
16 | 17 |
18 |
19 |
20 |
21 |
  Played CTFs
22 |
23 |
24 |

{{nb_ctf_played}}

25 |
26 |
27 |
28 |
29 | 30 |
31 |
32 |
33 |
34 |
  Running CTF(s): {{temporary_running_ctfs | length}} {% if current_ctfs|length == 0 %}😴{% endif %}
35 |
36 |
37 |
    38 | {% for ctf in temporary_running_ctfs %} 39 |
  • 40 | {{ctf.name | upper}} 41 | (ends in {{ctf.end_date | timeuntil}}) 42 |
  • 43 | {% empty %} 44 |
  • 45 |
    No CTF 46 | {% if next_ctf %} 47 | (next up: {{next_ctf}}, in {{next_ctf.start_date|timeuntil}}) 48 | {% endif %} 49 |
    50 |
  • 51 | {% endfor %} 52 |
53 |
54 |
55 |
56 |
57 |
58 | -------------------------------------------------------------------------------- /ctfhub/templates/ctfhub/footer.html: -------------------------------------------------------------------------------- 1 | {% load ctfhub_filters %} 2 |
3 | 64 | -------------------------------------------------------------------------------- /ctfhub/templates/ctfhub/main.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load ctfhub_filters %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | CTFHub 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | {% include 'ctfhub/navbar.html' %} 24 | 25 |
26 |
27 | {% block content %} 28 | {% endblock %} 29 |
30 |
31 | 32 | {% if request.user.is_authenticated %} 33 | {% include 'ctfhub/shortcuts.html' %} 34 | {% include 'ctfhub/footer.html' %} 35 | {% endif %} 36 | 37 | 38 | 41 | 42 | 43 | 67 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /ctfhub/templates/ctfhub/shortcuts.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 46 | 47 | 48 | 69 | 70 | -------------------------------------------------------------------------------- /ctfhub/templates/ctfhub/stats/ctf_stats.html: -------------------------------------------------------------------------------- 1 | {% load humanize %} 2 | {% load ctfhub_filters %} 3 |
4 |
5 |
6 |
7 | 8 | 43 |
44 |
45 |
46 |
47 | -------------------------------------------------------------------------------- /ctfhub/templates/ctfhub/stats/timeline.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 5 | 31 |
32 |
-------------------------------------------------------------------------------- /ctfhub/templates/ctfhub/tags/confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends 'ctfhub/main.html' %} 2 | 3 | {% block content %} 4 | 5 |
6 | 7 |
8 |
9 |
10 |
11 |
12 |

Delete tag '{{tag.name}}' ?

13 |

14 | 15 | This action cannot be recovered. 16 | 17 |

18 | Confirm deletion? 19 |

20 |
21 |
22 | {% csrf_token %} 23 | 27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | 35 | 36 | 37 | 38 | {% endblock %} 39 | -------------------------------------------------------------------------------- /ctfhub/templates/ctfhub/tags/create.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/ctfhub/templates/ctfhub/tags/create.html -------------------------------------------------------------------------------- /ctfhub/templates/ctfhub/tags/list.html: -------------------------------------------------------------------------------- 1 | {% extends 'ctfhub/main.html' %} 2 | 3 | {% block content %} 4 | 5 |
6 | 7 | {% include 'snippets/messages.html' %} 8 | {% load ctfhub_filters %} 9 | 10 | {% include 'snippets/formerror.html' %} 11 | 12 |
13 |
14 |
15 |
16 |
17 |

Browse CTF challenges by Tags

18 |

19 | Find challenges from existing tags. 20 |

21 |
22 |
23 |
24 |
25 |
26 | 27 |
28 | 29 |
30 |
31 |
32 |
33 |
34 |
35 |
Existing tags
36 | 43 |
44 |
45 | 46 |
47 | {% for tag in object_list %} 48 | 59 |     60 | {% endfor %} 61 |
62 |
63 |
64 | 65 | 66 |
67 |
68 | 69 |
70 | 71 | 72 | {% include 'snippets/quick_add_tag.html' %} 73 | 74 | {% endblock %} 75 | -------------------------------------------------------------------------------- /ctfhub/templates/search/list.html: -------------------------------------------------------------------------------- 1 | {% extends 'ctfhub/main.html' %} 2 | 3 | {% block content %} 4 | 5 |
6 | 7 | {% include 'snippets/messages.html' %} 8 | 9 | 10 |
11 |
12 |
13 | 14 |
15 |
16 |

Showing matching result(s) for '{{q}}' in '{{selected_category}}' category: {{total_result}} found

17 |
18 | 19 |
20 |
21 | {% for result in page_obj %} 22 |
23 |
Category: {{result.category}}
24 |
25 | 33 |
34 |
35 |
36 | {% endfor %} 37 | 38 | 55 | 56 |
57 |
58 |
59 | 60 |
61 |
62 | 63 | 64 | 65 | {% endblock %} 66 | -------------------------------------------------------------------------------- /ctfhub/templates/snippets/formerror.html: -------------------------------------------------------------------------------- 1 | {% if form.errors %} 2 |
3 |
4 |
5 | ERROR   6 | {{ form.errors }} 7 |
8 |
9 |
10 | {% endif %} -------------------------------------------------------------------------------- /ctfhub/templates/snippets/messages.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | {% for message in messages %} 5 | {% if message.level == 20 %} 6 |
7 | INFO   8 | {{message}} 9 | 10 |
11 | 12 | {% elif message.level == 25%} 13 |
14 | SUCCESS   15 | {{message}} 16 | 17 |
18 | 19 | {% elif message.level == 30 %} 20 |
21 | WARNING   22 | {{message}} 23 | 24 |
25 | 26 | {% elif message.level == 40 %} 27 |
28 | ERROR   29 | {{message}} 30 | 31 |
32 | 33 | {% else %} 34 |
35 | {{message.tags|upper}}   36 | {{message}} 37 | 38 |
39 | {% endif %} 40 | {% endfor %} 41 |
42 |
43 |
44 | 45 | 54 | -------------------------------------------------------------------------------- /ctfhub/templates/snippets/quick_add_category.html: -------------------------------------------------------------------------------- 1 | 25 | -------------------------------------------------------------------------------- /ctfhub/templates/snippets/quick_add_file.html: -------------------------------------------------------------------------------- 1 | 29 | -------------------------------------------------------------------------------- /ctfhub/templates/snippets/quick_add_tag.html: -------------------------------------------------------------------------------- 1 | 25 | -------------------------------------------------------------------------------- /ctfhub/templates/team/confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends 'ctfhub/main.html' %} 2 | 3 | {% block content %} 4 | 5 |
6 | 7 |
8 |
9 |

Delete TEAM '{{team.name}}' ?

10 |

11 | 12 | This action will effectively purgely all stored data of the team, recursively deleting all associated resources (i.e. team members, CTFs, challenges, flags, files, etc.). 13 | It cannot be recovered. 14 | 15 |

16 | Confirm deletion? 17 |

18 |
19 |
20 | {% csrf_token %} 21 | 25 |
26 |
27 |
28 |
29 | 30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /ctfhub/templates/team/create.html: -------------------------------------------------------------------------------- 1 | {% extends 'ctfhub/main.html' %} 2 | 3 | {% block content %} 4 | 5 | 6 | {% include 'snippets/messages.html' %} 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 |
18 |

Create Team for CTFHub

19 |
20 |
21 |
22 | {% csrf_token %} 23 |
24 | 25 | {{form.name}} 26 |
27 |
28 | 29 | {{form.email}} 30 |
31 | 34 |
35 |
36 | {% for message in messages %} 37 |

{{message}}

38 | {% endfor %} 39 | {% include 'snippets/formerror.html' %} 40 |
41 |
42 |
43 | 44 | 52 | 53 | {% endblock %} 54 | -------------------------------------------------------------------------------- /ctfhub/templates/users/confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends 'ctfhub/main.html' %} 2 | 3 | {% block content %} 4 | 5 |
6 | 7 |
8 |
9 |

Delete team member '{{member.username}}' ?

10 |

11 | 12 | This action will effectively purgely all stored data of the member, recursively deleting all associated resources (i.e. challenges, flags, files, etc.). 13 | It cannot be recovered. 14 | 15 |

16 | Confirm deletion? 17 |

18 |
19 |
20 | {% csrf_token %} 21 | 25 |
26 |
27 |
28 |
29 | 30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /ctfhub/templates/users/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'ctfhub/main.html' %} 2 | 3 | {% block content %} 4 | 5 | {% include 'snippets/messages.html' %} 6 | {% include 'snippets/formerror.html' %} 7 | 8 |
9 | 10 |
11 |
12 |
13 |
14 |
15 |

CTFHub Login

16 |
17 | 18 |
19 |
20 | {% csrf_token %} 21 |
22 | 23 | 24 |
25 | 26 |
27 | 28 | 29 |
30 | 31 | 34 |
35 |
36 | 37 |
38 | 41 | 44 |
45 |
46 |
47 |
48 |
49 | 50 | {% endblock %} 51 | -------------------------------------------------------------------------------- /ctfhub/templates/users/password_change.html: -------------------------------------------------------------------------------- 1 | {% extends 'ctfhub/main.html' %} 2 | 3 | {% block content %} 4 | 5 | {% include 'snippets/messages.html' %} 6 | 7 |
8 | 9 | {% include 'snippets/formerror.html' %} 10 | 11 |
12 |
13 |
14 |
15 |
16 |

Password reset

17 |
18 | 19 |
20 |
21 | {% csrf_token %} 22 |
23 | 24 | 25 |
26 | 27 |
28 | 29 | 30 |
31 | 32 | 35 |
36 |
37 |
38 | 41 |
42 |
43 |
44 |
45 |
46 | 47 | {% endblock %} 48 | -------------------------------------------------------------------------------- /ctfhub/templates/users/password_reset.html: -------------------------------------------------------------------------------- 1 | {% extends 'ctfhub/main.html' %} 2 | 3 | {% block content %} 4 | 5 | {% include 'snippets/messages.html' %} 6 | 7 |
8 | 9 | {% include 'snippets/formerror.html' %} 10 | 11 | 12 |
13 |
14 |
15 |
16 |
17 |

Password reset

18 |
19 | 20 |
21 |
22 | {% csrf_token %} 23 |
24 | 25 | 26 |
27 | 28 | 31 |
32 |
33 |
34 | 37 | 40 |
41 |
42 |
43 |
44 |
45 | 46 | {% endblock %} 47 | -------------------------------------------------------------------------------- /ctfhub/templates/users/password_reset_email.txt: -------------------------------------------------------------------------------- 1 | Hi, 2 | 3 | We received a request to reset the password for your account for this email address. 4 | To initiate the password reset process for your account, click the link below. 5 | 6 | {{ protocol }}://{{ domain }}{% url 'ctfhub:user-password-change' uidb64=uid token=token %} 7 | 8 | 9 | This is a one-time link once. If you need to reset your password again, please 10 | visit {{ protocol }}://{{domain}} and request another reset. 11 | 12 | If you did not make this request, you can simply ignore this email. 13 | 14 | 15 | Sincerely, 16 | 17 | The CTFHub Team 18 | -------------------------------------------------------------------------------- /ctfhub/templates/users/password_reset_subject.txt: -------------------------------------------------------------------------------- 1 | Password Reset Request -------------------------------------------------------------------------------- /ctfhub/templates/users/register.html: -------------------------------------------------------------------------------- 1 | {% extends 'ctfhub/main.html' %} 2 | 3 | {% block content %} 4 | 5 |
6 | 7 | {% include 'snippets/messages.html' %} 8 | 9 | 10 |
11 |
12 |
13 |
14 |

Register a new member

15 |
16 |
17 |
18 | {% csrf_token %} 19 |
20 | 21 | {{form.username}} 22 |
23 |
24 | 25 | {{form.email}} 26 |
27 |
28 | 29 | {{form.password1}} 30 |
31 |
32 | 33 | {{form.password2}} 34 |
35 |
36 | 37 | {{form.api_key}} 38 |
39 | 42 |
43 |
44 | {% include 'snippets/formerror.html' %} 45 |
46 | {% if not request.user.is_authenticated %} 47 | 50 | 53 | {% endif %} 54 |
55 |
56 |
57 |
58 | 63 | 64 |
65 | 66 | {% endblock %} 67 | -------------------------------------------------------------------------------- /ctfhub/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/ctfhub/templatetags/__init__.py -------------------------------------------------------------------------------- /ctfhub/templatetags/ctfhub_filters.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | from typing import TYPE_CHECKING, Any 3 | 4 | import bleach 5 | from django import template 6 | from django.utils.safestring import mark_safe 7 | 8 | if TYPE_CHECKING: 9 | from ctfhub.models import Challenge 10 | 11 | register = template.Library() 12 | 13 | 14 | @register.simple_tag 15 | def best_category(member, year=None): 16 | return member.best_category(year) 17 | 18 | 19 | @register.filter 20 | def as_time_accumulator_graph(items: list["Challenge"]): 21 | Point = namedtuple("Point", "time accu") 22 | accu = 0 23 | res: list[Point] = [] 24 | for item in items: 25 | accu += item.points 26 | res.append(Point(item.solved_time, accu)) 27 | return res 28 | 29 | 30 | @register.simple_tag(takes_context=True) 31 | def theme_cookie(context: dict[str, Any]): 32 | request = context["request"] 33 | value = request.COOKIES.get("theme", "light") 34 | if value not in ("light", "dark"): 35 | value = "light" 36 | return value 37 | 38 | 39 | @register.filter 40 | def html_sanitize(html: str) -> str: 41 | """Only authorize links (a tags) html. Escape the rest 42 | 43 | Args: 44 | html ([type]): [description] 45 | 46 | Returns: 47 | [type]: [description] 48 | """ 49 | return bleach.linkify( 50 | bleach.clean( 51 | html, 52 | tags=["a", "br", "hr"], 53 | protocols=["http", "https"], 54 | strip=True, 55 | ) 56 | ) 57 | 58 | 59 | @register.filter(is_safe=True, needs_autoescape=False) 60 | def as_tick_or_cross(is_on: bool): 61 | if is_on: 62 | return mark_safe( 63 | """""" 64 | ) 65 | 66 | return mark_safe( 67 | """""" 68 | ) 69 | -------------------------------------------------------------------------------- /ctfhub/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/ctfhub/tests/__init__.py -------------------------------------------------------------------------------- /ctfhub/tests/test_forms.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | from django.test import SimpleTestCase 3 | from ctfhub.forms import ChallengeCreateForm, ChallengeUpdateForm 4 | 5 | 6 | class TestForms(SimpleTestCase): 7 | def test_challenge_create_required_fields(self): 8 | required_fields: dict[str, Union[str, int]] = { 9 | "name": "fake name", 10 | "points": 100, 11 | } 12 | 13 | optional_fields: dict[str, Union[str, int]] = { 14 | "description": "fake", 15 | "category": "fake", 16 | } 17 | 18 | for required_field, _ in required_fields.items(): 19 | data = optional_fields | required_fields 20 | del data[required_field] 21 | form = ChallengeCreateForm(data=data) 22 | self.assertFalse(form.is_valid()) 23 | 24 | def test_challenge_create_manual_has_no_note_id(self): 25 | """ 26 | @ref #91 27 | """ 28 | form = ChallengeCreateForm() 29 | self.assertNotIn("note_id", form.fields) 30 | 31 | def test_challenge_update_has_always_note_id(self): 32 | """ 33 | @ref #91 34 | """ 35 | form = ChallengeUpdateForm() 36 | self.assertIn("note_id", form.fields) 37 | -------------------------------------------------------------------------------- /ctfhub/tests/test_urls.py: -------------------------------------------------------------------------------- 1 | from django.test import SimpleTestCase 2 | from django.urls import resolve 3 | from ctfhub.views import ( 4 | index, 5 | teams, 6 | users, 7 | dashboard, 8 | search, 9 | generate_stats, 10 | ctfs, 11 | challenges, 12 | files, 13 | categories, 14 | tags, 15 | ) 16 | 17 | 18 | class UrlsTest(SimpleTestCase): 19 | def test_home_url_resolves(self): 20 | url = "/" 21 | self.assertEqual(resolve(url).func, index) 22 | 23 | def test_teams_register_url_resolves(self): 24 | url = "/teams/register/" 25 | self.assertEqual(resolve(url).func.view_class, teams.TeamCreateView) 26 | 27 | def test_teams_edit_url_resolves(self): 28 | url = "/teams/edit/1" 29 | self.assertEqual(resolve(url).func.view_class, teams.TeamUpdateView) 30 | 31 | def test_users_list_url_resolves(self): 32 | url = "/users/" 33 | self.assertEqual(resolve(url).func.view_class, users.MemberListView) 34 | 35 | def test_users_add_url_resolves(self): 36 | url = "/users/add/" 37 | self.assertEqual(resolve(url).func.view_class, users.MemberCreateView) 38 | 39 | def test_dashboard_url_resolves(self): 40 | url = "/dashboard/" 41 | self.assertEqual(resolve(url).func, dashboard) 42 | 43 | def test_search_url_resolves(self): 44 | url = "/search/" 45 | self.assertEqual(resolve(url).func, search) 46 | 47 | def test_stats_url_resolves(self): 48 | url = "/stats/" 49 | self.assertEqual(resolve(url).func, generate_stats) 50 | 51 | def test_ctfs_list_url_resolves(self): 52 | url = "/ctfs/" 53 | self.assertEqual(resolve(url).func.view_class, ctfs.CtfListView) 54 | 55 | def test_challenges_list_url_resolves(self): 56 | url = "/challenges/" 57 | self.assertEqual(resolve(url).func.view_class, challenges.ChallengeListView) 58 | 59 | def test_categories_create_url_resolves(self): 60 | url = "/categories/create/" 61 | self.assertEqual(resolve(url).func.view_class, categories.CategoryCreateView) 62 | 63 | def test_tags_list_url_resolves(self): 64 | url = "/tags/" 65 | self.assertEqual(resolve(url).func.view_class, tags.TagListView) 66 | -------------------------------------------------------------------------------- /ctfhub/validators.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING 2 | from django.core.exceptions import ValidationError 3 | 4 | from ctfhub_project.settings import CHALLENGE_FILE_MAX_SIZE 5 | 6 | if TYPE_CHECKING: 7 | from django.core import files 8 | 9 | 10 | def challenge_file_max_size_validator(value: "files.File"): 11 | if value.size > CHALLENGE_FILE_MAX_SIZE: 12 | raise ValidationError( 13 | f"File too large. Size should not exceed {CHALLENGE_FILE_MAX_SIZE}B." 14 | ) 15 | -------------------------------------------------------------------------------- /ctfhub/views/categories.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.mixins import LoginRequiredMixin 2 | from django.contrib.messages.views import SuccessMessageMixin 3 | from django.shortcuts import render 4 | from django.urls.base import reverse 5 | from django.views.generic.edit import CreateView 6 | 7 | from ctfhub.forms import CategoryCreateForm 8 | from ctfhub.mixins import MembersOnlyMixin 9 | from ctfhub.models import ChallengeCategory 10 | 11 | 12 | class CategoryCreateView( 13 | LoginRequiredMixin, MembersOnlyMixin, SuccessMessageMixin, CreateView 14 | ): 15 | model = ChallengeCategory 16 | template_name = "ctfhub/categories/create.html" 17 | login_url = "/users/login/" 18 | redirect_field_name = "redirect_to" 19 | form_class = CategoryCreateForm 20 | success_message = "Category '%(name)s' successfully was created!" 21 | initial = { 22 | "name": "", 23 | } 24 | 25 | def get(self, request, *args, **kwargs): 26 | assert self.form_class 27 | form = self.form_class(initial=self.initial) 28 | return render(request, self.template_name, {"form": form}) 29 | 30 | def form_valid(self, form: CategoryCreateForm): 31 | category_name = form.instance.name.strip().lower() 32 | form.cleaned_data["name"] = category_name 33 | return super().form_valid(form) 34 | 35 | def get_success_url(self): 36 | redirect_to = self.request.META.get("HTTP_REFERER") or reverse( 37 | "ctfhub:dashboard" 38 | ) 39 | return redirect_to 40 | -------------------------------------------------------------------------------- /ctfhub/views/files.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from django.contrib.auth.decorators import login_required 4 | from django.contrib.auth.mixins import LoginRequiredMixin 5 | from django.contrib.messages.views import SuccessMessageMixin 6 | from django.http import HttpRequest 7 | from django.shortcuts import get_object_or_404, render 8 | from django.urls import reverse 9 | from django.views.generic import CreateView, DeleteView, DetailView 10 | from django_sendfile import sendfile 11 | 12 | from ctfhub.forms import ChallengeFileCreateForm 13 | from ctfhub.models import Challenge, ChallengeFile 14 | 15 | 16 | class ChallengeFileCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): 17 | model = ChallengeFile 18 | template_name = "ctfhub/challenges/files/create.html" 19 | login_url = "/users/login/" 20 | redirect_field_name = "redirect_to" 21 | form_class = ChallengeFileCreateForm 22 | success_message = "File added!" 23 | 24 | def get_success_url(self): 25 | obj: ChallengeFile = self.object # type: ignore 26 | return reverse("ctfhub:challenges-detail", kwargs={"pk": obj.challenge.id}) 27 | 28 | 29 | class ChallengeFileDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView): 30 | model = ChallengeFile 31 | template_name = "ctfhub/challenges/files/confirm_delete.html" 32 | login_url = "/users/login/" 33 | redirect_field_name = "redirect_to" 34 | success_message = "File deleted!" 35 | 36 | def get_success_url(self): 37 | obj = self.get_object() 38 | assert isinstance(obj, ChallengeFile) 39 | return reverse("ctfhub:challenges-detail", kwargs={"pk": obj.challenge.id}) 40 | 41 | def post(self, request, *args, **kwargs): 42 | obj = self.get_object() 43 | assert isinstance(obj, ChallengeFile) 44 | fpath = Path(obj.file.path) 45 | res = self.delete(request, *args, **kwargs) 46 | # delete the file on-disk 47 | # https://docs.djangoproject.com/en/3.1/releases/1.3/#deleting-a-model-doesn-t-delete-associated-files 48 | if fpath.exists(): 49 | fpath.unlink() 50 | return res 51 | 52 | 53 | class ChallengeFileDetailView(LoginRequiredMixin, SuccessMessageMixin, DetailView): 54 | model = ChallengeFile 55 | template_name = "ctfhub/challenges/files/detail.html" 56 | login_url = "/users/login/" 57 | redirect_field_name = "redirect_to" 58 | 59 | def get(self, request, *args, **kwargs): 60 | return render(request, self.template_name, {"form": {}}) 61 | 62 | 63 | @login_required 64 | def challenge_file_download_view(request: HttpRequest, challenge_id: int, pk: int): 65 | """Download a file from its 66 | 67 | Args: 68 | request (HttpRequest): _description_ 69 | challenge_id (int): _description_ 70 | 71 | Raises: 72 | AssertionError: if there's a mismatch between the file challenge and the challenge associated to it 73 | (sanity check) 74 | 75 | Returns: 76 | _type_: _description_ 77 | """ 78 | challenge = get_object_or_404(Challenge, pk=challenge_id) 79 | challenge_file = get_object_or_404(ChallengeFile, pk=pk) 80 | assert challenge_file.challenge == challenge 81 | return sendfile(request, challenge_file.file.path, mimetype=challenge_file.mime) 82 | -------------------------------------------------------------------------------- /ctfhub/views/tags.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.mixins import LoginRequiredMixin 2 | from django.contrib.messages.views import SuccessMessageMixin 3 | from django.shortcuts import render 4 | from django.urls.base import reverse, reverse_lazy 5 | from django.views.generic import CreateView, DeleteView, ListView 6 | 7 | from ctfhub.forms import TagCreateForm 8 | from ctfhub.mixins import MembersOnlyMixin 9 | from ctfhub.models import Tag 10 | 11 | 12 | class TagCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): 13 | model = Tag 14 | template_name = "ctfhub/tags/create.html" 15 | login_url = "/users/login/" 16 | redirect_field_name = "redirect_to" 17 | form_class = TagCreateForm 18 | success_message = "Tag '%(name)s' added!" 19 | initial = { 20 | "name": "", 21 | } 22 | 23 | def get(self, request, *args, **kwargs): 24 | assert self.form_class 25 | form = self.form_class(initial=self.initial) 26 | return render(request, self.template_name, {"form": form}) 27 | 28 | def form_valid(self, form: TagCreateForm): 29 | form.cleaned_data["name"] = form.instance.name.strip().lower() 30 | return super().form_valid(form) 31 | 32 | def get_success_url(self): 33 | redirect_to = self.request.META.get("HTTP_REFERER") or reverse( 34 | "ctfhub:dashboard" 35 | ) 36 | return redirect_to 37 | 38 | 39 | class TagListView(LoginRequiredMixin, MembersOnlyMixin, ListView): 40 | model = Tag 41 | template_name = "ctfhub/tags/list.html" 42 | login_url = "/users/login/" 43 | redirect_field_name = "redirect_to" 44 | 45 | def get_context_data(self, **kwargs): 46 | ctx = super().get_context_data(**kwargs) 47 | ctx |= { 48 | "add_tag_form": TagCreateForm(), 49 | } 50 | return ctx 51 | 52 | 53 | class TagDeleteView( 54 | LoginRequiredMixin, MembersOnlyMixin, SuccessMessageMixin, DeleteView 55 | ): 56 | model = Tag 57 | template_name = "ctfhub/tags/confirm_delete.html" 58 | login_url = "/users/login/" 59 | redirect_field_name = "redirect_to" 60 | success_message = "Tag deleted successfully" 61 | success_url = reverse_lazy("ctfhub:tags-list") 62 | -------------------------------------------------------------------------------- /ctfhub/views/teams.py: -------------------------------------------------------------------------------- 1 | from django.contrib import messages 2 | from django.contrib.auth.mixins import LoginRequiredMixin 3 | from django.contrib.messages.views import SuccessMessageMixin 4 | from django.shortcuts import redirect 5 | from django.urls import reverse_lazy 6 | from django.views.generic import CreateView, DeleteView, UpdateView 7 | 8 | from ctfhub.forms import TeamCreateUpdateForm 9 | from ctfhub.mixins import RequireSuperPowersMixin 10 | from ctfhub.models import Team 11 | 12 | MESSAGE_SUCCESS_TEAM_CREATED: str = "Team successfully created" 13 | MESSAGE_ERROR_MULTIPLE_TEAM_CREATE: str = "Only one team can be created" 14 | 15 | 16 | class TeamCreateView(SuccessMessageMixin, CreateView): 17 | model = Team 18 | template_name = "team/create.html" 19 | form_class = TeamCreateUpdateForm 20 | success_url = reverse_lazy("ctfhub:dashboard") 21 | success_message = MESSAGE_SUCCESS_TEAM_CREATED 22 | 23 | def dispatch(self, request, *args, **kwargs): 24 | if Team.objects.all().count(): 25 | messages.error(self.request, MESSAGE_ERROR_MULTIPLE_TEAM_CREATE) 26 | return redirect("ctfhub:home") 27 | if request.method and request.method.lower() in self.http_method_names: 28 | handler = getattr( 29 | self, request.method.lower(), self.http_method_not_allowed 30 | ) 31 | else: 32 | handler = self.http_method_not_allowed 33 | return handler(request, *args, **kwargs) 34 | 35 | def get_success_message(self, cleaned_data) -> str: 36 | obj: Team = self.object # type: ignore 37 | msg = f"Team '{obj.name}' successfully created!" 38 | msg += f"Use the API key '{obj.api_key}' to register new members." 39 | return msg 40 | 41 | 42 | class TeamUpdateView( 43 | LoginRequiredMixin, RequireSuperPowersMixin, SuccessMessageMixin, UpdateView 44 | ): 45 | model = Team 46 | success_url = reverse_lazy("ctfhub:dashboard") 47 | template_name = "team/edit.html" 48 | login_url = reverse_lazy("ctfhub:user-login") 49 | form_class = TeamCreateUpdateForm 50 | redirect_field_name = "redirect_to" 51 | success_message = "Team successfully edited" 52 | 53 | 54 | class TeamDeleteView( 55 | LoginRequiredMixin, RequireSuperPowersMixin, SuccessMessageMixin, DeleteView 56 | ): 57 | model = Team 58 | success_url = reverse_lazy("ctfhub:team-register") 59 | template_name = "team/confirm_delete.html" 60 | login_url = reverse_lazy("ctfhub:user-login") 61 | redirect_field_name = "redirect_to" 62 | success_message = "Team successfully deleted" 63 | -------------------------------------------------------------------------------- /ctfhub_project/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/ctfhub_project/__init__.py -------------------------------------------------------------------------------- /ctfhub_project/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for ctfhub_project project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ctfhub_project.settings") 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /ctfhub_project/urls.py: -------------------------------------------------------------------------------- 1 | """ctfhub_project URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/3.1/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.urls import include, path 17 | 18 | urlpatterns = [ 19 | # path('admin/', admin.site.urls), 20 | path("", include("ctfhub.urls")), 21 | ] 22 | -------------------------------------------------------------------------------- /ctfhub_project/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for ctfhub_project project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ctfhub_project.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | 5 | postgres: 6 | image: postgres:alpine 7 | env_file: 8 | - .env 9 | environment: 10 | - POSTGRES_DB=${CTFHUB_DB_NAME} 11 | - POSTGRES_USER=${CTFHUB_DB_USER} 12 | - POSTGRES_PASSWORD=${CTFHUB_DB_PASSWORD} 13 | volumes: 14 | - postgres-vol:/var/lib/postgresql/data 15 | networks: 16 | - ctfhub 17 | restart: always 18 | 19 | hedgedoc: 20 | image: quay.io/hedgedoc/hedgedoc:alpine 21 | links: 22 | - "postgres:db" 23 | env_file: 24 | - .env 25 | environment: 26 | - CMD_DB_URL=postgres://${CTFHUB_DB_USER}:${CTFHUB_DB_PASSWORD}@${CTFHUB_DB_HOST}:${CTFHUB_DB_PORT}/${CTFHUB_DB_NAME} 27 | - CMD_ALLOW_ANONYMOUS=false 28 | - CMD_ALLOW_FREEURL=true 29 | - CMD_IMAGE_UPLOAD_TYPE=filesystem 30 | - CMD_DOMAIN=${CTFHUB_HEDGEDOC_DOMAIN} 31 | - CMD_PORT=${CTFHUB_HEDGEDOC_PORT} 32 | - CMD_URL_ADDPORT=true 33 | - CMD_PROTOCOL_USESSL=${CTFHUB_HEDGEDOC_USESSL} 34 | - CMD_REQUIRE_FREEURL_AUTHENTICATION=true 35 | networks: 36 | - ctfhub 37 | ports: 38 | - 3000:3000 39 | restart: always 40 | depends_on: 41 | - postgres 42 | volumes: 43 | - type: volume 44 | source: hedgedoc-vol 45 | target: /hedgedoc/public/uploads 46 | read_only: false 47 | 48 | ctfhub: 49 | # build: https://github.com/hugsy/ctfhub.git#master 50 | build: . 51 | command: python manage.py runserver 0.0.0.0:8000 52 | links: 53 | - "postgres:db" 54 | env_file: 55 | - .env 56 | # - .env.proxy # Uncomment if using a proxy 57 | depends_on: 58 | - postgres 59 | - hedgedoc 60 | ports: 61 | - 8000:8000 62 | networks: 63 | - ctfhub 64 | restart: always 65 | volumes: 66 | - type: volume 67 | source: ctfhub-vol 68 | target: /code/uploads 69 | read_only: false 70 | 71 | - type: bind 72 | source: ./ 73 | target: /code 74 | read_only: false 75 | 76 | networks: 77 | ctfhub: 78 | driver: bridge 79 | 80 | volumes: 81 | postgres-vol: 82 | hedgedoc-vol: 83 | ctfhub-vol: 84 | -------------------------------------------------------------------------------- /docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | cd /code 6 | 7 | echo "[CTFHub] Database initialization..." 8 | while psql -h db 2>&1 | grep -q 'could not connect to server'; do 9 | >&2 echo "Waiting for PostgreSQL to boot up" 10 | sleep 1 11 | done 12 | 13 | echo "[CTFHub] Database setup: makemigrations..." 14 | python3 manage.py makemigrations --noinput 15 | 16 | echo "[CTFHub] Database setup: migrate..." 17 | python3 manage.py migrate --noinput 18 | 19 | exec "$@" 20 | -------------------------------------------------------------------------------- /docs/build.md: -------------------------------------------------------------------------------- 1 | # Build an instance 2 | 3 | ## Basic setup 4 | 5 | ```bash 6 | $ git clone https://github.com/hugsy/ctfhub 7 | $ cd ctfhub 8 | $ cp .env.example .env 9 | ### CHANGE THE CREDENTIALS IN .env ### 10 | $ nano .env 11 | ### BUILD EXCALIDRAW USING .env VARIABLES ### 12 | $ docker compose up -d --build 13 | ``` 14 | 15 | ## SSL + nginx reverse-proxy on Docker 16 | 17 | A standard secure way to deploy an instance of CTFHub is to use it over an SSL layer, and behind a reverse proxy. 18 | Using [Let's Encrypt](https://letsencrypt.org/) you can easily generate a valid SSL certificate, which can be used with an nginx container acting as a reverse-proxy. A boilerplate template was provided to you in `scripts/nginx`, which you can use in combination of the [instructions to generate local SSL certificates](./ssl-setup.md). Then run `docker compose` with multiple files as such: 19 | 20 | ```bash 21 | $ cp scripts/proxy/.env.nginx-proxy.example scripts/proxy/.env 22 | $ nano scripts/proxy/.env 23 | ### Edit the file to your need 24 | $ docker compose -f ./docker-compose.yml -f scripts/proxy/docker-compose.yml up -d --build 25 | ``` 26 | 27 | Note that the example env file have default variables that may not suit your environment. Adjust them to your needs. 28 | 29 | ## Deploy your instance for Excalidraw 30 | 31 | In `scripts/excalidraw` : 32 | 33 | ```bash 34 | $ cd scripts/excalidraw 35 | $ cp .env.example .env 36 | $ nano .env 37 | ### edit .env to match your configuration 38 | $ cd ../.. 39 | $ nano .env 40 | ### update the CTFHUB_EXCALIDRAW_URL setting to point to your local excalidraw 41 | $ docker compose -f ./docker-compose.yml -f scripts/excalidraw/docker-compose.yml up -d --build 42 | ``` 43 | 44 | Note that the example env file have default variables that may not suit your environment. Adjust them to your needs. 45 | 46 | ## Receive Discord notifications 47 | 48 | Create a [HTTP Webhook](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) on your Discord and paste that link to the setting `CTFHUB_DISCORD_WEBHOOK_URL` in your `.env` file. 49 | 50 | ## Receive Email notifications 51 | 52 | Edit your `.env` and populate the following fields: 53 | 54 | ```conf 55 | CTFHUB_EMAIL_SERVER_HOST='' # smtp.gmail.com or mailgun, or sendgrid etc. 56 | CTFHUB_EMAIL_SERVER_PORT=0 57 | CTFHUB_EMAIL_USERNAME='' 58 | CTFHUB_EMAIL_PASSWORD='' 59 | ``` 60 | 61 | With the appropriate values 62 | 63 | 64 | ## Migration 65 | 66 | If you're migrating from the first versions called `ctpad`, check out see [PR #83](https://github.com/hugsy/ctfhub/pull/83) to migrate the data to the new environment, search the `Setup > Migration` part. 67 | 68 | -------------------------------------------------------------------------------- /docs/gallery.md: -------------------------------------------------------------------------------- 1 | # Gallery 2 | 3 | ## Dashboard 4 | 5 | ![dashboard](https://i.imgur.com/vWvgjQ1.png) 6 | 7 | ## View CTF 8 | 9 | ![ctf](https://i.imgur.com/kEJo9Jj.png) 10 | ![ctf2](https://i.imgur.com/fe3vvfC.png) 11 | 12 | ## Import CTFs from CTFtime 13 | 14 | ![ctftime](https://i.imgur.com/TnOupMe.png) 15 | 16 | 17 | ## Challenge 18 | 19 | ![challenge1](https://i.imgur.com/YRvXs3u.png) 20 | 21 | 22 | ## Statistics 23 | 24 | ![stats](https://i.imgur.com/PGsPztU.png) 25 | 26 | -------------------------------------------------------------------------------- /docs/ssl-setup.md: -------------------------------------------------------------------------------- 1 | # Using SSL Certificates for a reverse-proxy 2 | 3 | ## Development usage 4 | 5 | For local development, you can use [mkcert](https://github.com/FiloSottile/mkcert) to generate the certificates. 6 | 7 | Example with default settings working with the provided `nginx.conf`: 8 | 9 | 10 | ```bash 11 | # If this is the first time you are using mkcert, create a local CA: 12 | mkcert -install 13 | 14 | # Generate a new certificates with the different subdomains: 15 | mkcert ctfhub.mydomain.com hedgedoc.mydomain.com excalidraw.mydomain.com collab.excalidraw.mydomain.com 16 | 17 | # Move the generated certificates to the certs folder. Assuming you are at the repository root folder: 18 | mkdir -p ./conf/certs/ctfdad.mydomain.com 19 | mv ctfhub.mydomain.com*-key.pem ./conf/certs/ctfdad.mydomain.com/privkey.pem 20 | mv ctfhub.mydomain.com*.pem ./conf/certs/ctfdad.mydomain.com/fullchain.pem 21 | ``` 22 | 23 | If `ctfhub.mydomain.com` is in the `/etc/hosts` file of your machine, then go to `https://ctfhub.mydomain.com` and enjoy the app! 24 | 25 | 26 | ## Production usage 27 | 28 | ### Using Let's Encrypt 29 | 30 | Generate your LetsEncrypt keys and certificates 31 | 32 | ```bash 33 | sudo apt-get update && sudo apt-get install certbot # if not already installed 34 | sudo certbot certonly --manual --preferred-challenges=dns 35 | ``` 36 | 37 | Follow the prompt. Certificates will be generated in `/etc/letsencrypt/live/` in the containers, which can be found in `./certs` on your host. 38 | 39 | Use that path to update the nginx configuration file (in `../nginx/nginx.conf`) and run `docker compose up` including the docker-compose file from this folder. 40 | 41 | ### Using your own certificates 42 | 43 | Place your TLS certificate + private key(s) for your domain here like 44 | 45 | ``` 46 | .//fullchain.pem 47 | .//privkey.pem 48 | ``` 49 | 50 | Then edit `../nginx/nginx.conf` to update the path to the chain/key. 51 | 52 | 53 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ctfhub_project.settings") 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == "__main__": 22 | main() 23 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "CTFHub" 3 | version = "0.1" 4 | authors = [{ name = "hugsy", email = "hugsy@blah.cat" }] 5 | description = "CTFHub" 6 | readme = "README.md" 7 | requires-python = ">=3.10" 8 | classifiers = [ 9 | "Development Status :: 4 - Beta", 10 | "License :: OSI Approved :: MIT License", 11 | "Programming Language :: Python :: 3 :: Only", 12 | "Programming Language :: Python :: 3.10", 13 | "Natural Language :: English", 14 | ] 15 | 16 | keywords = ["ctf"] 17 | 18 | [project.urls] 19 | "Homepage" = "https://github.com/hugsy/ctfhub" 20 | "Bug Tracker" = "https://github.com/hugsy/ctfhub/issues" 21 | 22 | [tool.pytest.ini_options] 23 | log_cli = false 24 | log_cli_level = "INFO" 25 | log_cli_format = "%(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)" 26 | log_cli_date_format = "%Y-%m-%d %H:%M:%S" 27 | DJANGO_SETTINGS_MODULE = "ctfhub_project.settings" 28 | 29 | [tool.ruff] 30 | # Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default. 31 | select = ["E", "F"] 32 | ignore = [] 33 | 34 | # Allow autofix for all enabled rules (when `--fix`) is provided. 35 | fixable = [ 36 | "A", 37 | "B", 38 | "C", 39 | "D", 40 | "E", 41 | "F", 42 | "G", 43 | "I", 44 | "N", 45 | "Q", 46 | "S", 47 | "T", 48 | "W", 49 | "ANN", 50 | "ARG", 51 | "BLE", 52 | "COM", 53 | "DJ", 54 | "DTZ", 55 | "EM", 56 | "ERA", 57 | "EXE", 58 | "FBT", 59 | "ICN", 60 | "INP", 61 | "ISC", 62 | "NPY", 63 | "PD", 64 | "PGH", 65 | "PIE", 66 | "PL", 67 | "PT", 68 | "PTH", 69 | "PYI", 70 | "RET", 71 | "RSE", 72 | "RUF", 73 | "SIM", 74 | "SLF", 75 | "TCH", 76 | "TID", 77 | "TRY", 78 | "UP", 79 | "YTT", 80 | ] 81 | unfixable = [] 82 | 83 | # Exclude a variety of commonly ignored directories. 84 | exclude = [ 85 | ".bzr", 86 | ".direnv", 87 | ".eggs", 88 | ".git", 89 | ".git-rewrite", 90 | ".hg", 91 | ".mypy_cache", 92 | ".nox", 93 | ".pants.d", 94 | ".pytype", 95 | ".ruff_cache", 96 | ".svn", 97 | ".tox", 98 | ".venv", 99 | "__pypackages__", 100 | "_build", 101 | "buck-out", 102 | "build", 103 | "dist", 104 | "node_modules", 105 | "venv", 106 | ] 107 | 108 | # Same as Black. 109 | line-length = 120 110 | 111 | # Allow unused variables when underscore-prefixed. 112 | dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" 113 | 114 | # Assume Python 3.10. 115 | target-version = "py310" 116 | 117 | [tool.ruff.mccabe] 118 | # Unlike Flake8, default to a complexity level of 10. 119 | max-complexity = 10 120 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # Core packages 2 | django >= 4.2.2 3 | Pillow 4 | requests 5 | python-magic 6 | psycopg2-binary 7 | django-model-utils 8 | django-sendfile2 9 | bleach 10 | 11 | # Dev packages (install those if you intend to submit PRs) 12 | pre-commit 13 | black 14 | 15 | # Test packages (for running tests) 16 | pytest 17 | pytest-django 18 | -------------------------------------------------------------------------------- /scripts/excalidraw/.env.example: -------------------------------------------------------------------------------- 1 | # 2 | # Redis (required for Excalidraw) 3 | # 4 | REDIS_PASSWORD=123234345123123 # Change here 5 | REACT_APP_BACKEND_V2_GET_URL=http://localhost:3001/api/v2/scenes/ # to Excalidraw 6 | REACT_APP_BACKEND_V2_POST_URL=http://localhost:3003/api/v2/scenes/ # to Excalidraw-Storage-Backend 7 | REACT_APP_LIBRARY_URL=https://libraries.excalidraw.com 8 | REACT_APP_LIBRARY_BACKEND=https://us-central1-excalidraw-room-persistence.cloudfunctions.net/libraries/ 9 | REACT_APP_STORAGE_BACKEND=http 10 | REACT_APP_HTTP_STORAGE_BACKEND_URL=http://localhost:3003/api/v2 # to Excalidraw-Storage-Backend 11 | REACT_APP_WS_SERVER_URL=http://localhost:3002 # to Excalidraw-Room 12 | REACT_APP_PORTAL_URL='' # THIS NEED TO BE LEFT BLANK 13 | EXCALIDRAW_NODE_ENV=production 14 | -------------------------------------------------------------------------------- /scripts/excalidraw/Makefile: -------------------------------------------------------------------------------- 1 | EXCALIDRAW_VERSION ?= master 2 | 3 | ## Meta installation targets 4 | build: apps build-apps 5 | apps: clone-external-repos excalidraw-checkout 6 | 7 | clone-external-repos: 8 | mkdir -p external-repos 9 | @git clone https://github.com/b310-digital/excalidraw.git external-repos/excalidraw || echo "Already installed" 10 | cp .env external-repos/excalidraw 11 | 12 | checkout-version: 13 | cd external-repos/$(APP_FOLDER); git fetch; git checkout $(APP_VERSION); cp ../../.env . 14 | 15 | excalidraw-checkout: 16 | make checkout-version -e APP_FOLDER=excalidraw -e APP_VERSION=$(EXCALIDRAW_VERSION) 17 | 18 | build-apps: 19 | docker compose build 20 | -------------------------------------------------------------------------------- /scripts/excalidraw/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | 5 | excalidraw: 6 | # https://github.com/excalidraw/excalidraw/blob/master/docker-compose.yml 7 | build: 8 | # context: https://github.com/excalidraw/excalidraw.git#master 9 | context: https://github.com/b310-digital/excalidraw.git#master 10 | args: 11 | - NODE_ENV=${EXCALIDRAW_NODE_ENV} 12 | networks: 13 | - ctfhub 14 | - excalidraw 15 | ports: 16 | - 3001:80 17 | restart: always 18 | env_file: 19 | - .env 20 | environment: 21 | - NODE_ENV=${EXCALIDRAW_NODE_ENV} 22 | 23 | healthcheck: 24 | disable: true 25 | stdin_open: true 26 | 27 | excalidraw-room: 28 | image: excalidraw/excalidraw-room 29 | restart: always 30 | networks: 31 | - excalidraw 32 | ports: 33 | - 3002:80 34 | 35 | excalidraw-storage-backend: 36 | # repo https://gitlab.com/kiliandeca/excalidraw-storage-backend 37 | image: kiliandeca/excalidraw-storage-backend 38 | networks: 39 | - excalidraw 40 | ports: 41 | - 3003:8080 42 | restart: always 43 | env_file: 44 | - .env 45 | environment: 46 | STORAGE_URI: redis://:${REDIS_PASSWORD}@redis:6379 47 | STORAGE_TTL: 2592000000 48 | depends_on: 49 | - redis 50 | 51 | redis: 52 | image: redis 53 | networks: 54 | - excalidraw 55 | command: redis-server --requirepass ${REDIS_PASSWORD} 56 | restart: always 57 | volumes: 58 | - redis-vol:/data 59 | 60 | networks: 61 | excalidraw: 62 | driver: bridge 63 | 64 | volumes: 65 | redis-vol: 66 | -------------------------------------------------------------------------------- /scripts/proxy/.env.example: -------------------------------------------------------------------------------- 1 | # 2 | # Override settings here. 3 | # 4 | 5 | # 6 | # CTFHub 7 | # 8 | CTFHUB_DEBUG=0 # Change here 9 | CTFHUB_HOSTNAME=ctfhub.mydomain.com # Change here to your server public IP / FQDN 10 | CTFHUB_PORT=443 # Change here to your server public port for CTFHub 11 | CTFHUB_USE_SSL=1 # Change here to 1 if your public server uses https 12 | CTFHUB_SECRET_KEY=74320c04549af3a5f9fd9bc007b2e20ced8 # Change here 13 | HEDGEDOC_URL=https://hedgedoc.mydomain.com # or set this to use nginx as a HTTPS reverse proxy 14 | EXCALIDRAW_URL=https://excalidraw.mydomain.com # Change here 15 | # Left blank or customize below to enable the password recovery feature by email 16 | CTFHUB_EMAIL_SERVER_HOST= # smtp.gmail.com or mailgun, or sendgrid etc. 17 | CTFHUB_EMAIL_SERVER_PORT=587 18 | CTFHUB_EMAIL_USERNAME= 19 | CTFHUB_EMAIL_PASSWORD= 20 | # Left blanck or customize below to enable Discord webhook notifications 21 | CTFHUB_WEBHOOK_URL_DISCORD= 22 | 23 | # Postgres 24 | POSTGRES_DB=ctfhub # (opt.) Change here 25 | POSTGRES_USER=ctfhub # (opt.) Change here 26 | POSTGRES_PASSWORD=tookahlaiphee2KieTeeg5ooxutang4o # Change here 27 | 28 | # Hedgedoc 29 | CMD_DOMAIN=hedgedoc.mydomain.com 30 | CMD_URL_ADDPORT=false 31 | CMD_PORT= 32 | CMD_PROTOCOL_USESSL=true 33 | -------------------------------------------------------------------------------- /scripts/proxy/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | nginx: 4 | image: nginx:latest 5 | networks: 6 | - ctfhub 7 | ports: 8 | - 80:80 # change here the public port to your own setup 9 | - 443:443 # change here the public port to your own setup 10 | depends_on: 11 | - ctfhub 12 | - hedgedoc 13 | volumes: 14 | - ./conf/nginx/nginx.conf:/etc/nginx/nginx.conf 15 | - ./conf/certs:/etc/nginx/certs 16 | restart: always 17 | -------------------------------------------------------------------------------- /static/css/chart.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/css/chart.css -------------------------------------------------------------------------------- /static/css/fontawesome/css/brands.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.15.1 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @font-face { 6 | font-family: 'Font Awesome 5 Brands'; 7 | font-style: normal; 8 | font-weight: 400; 9 | font-display: block; 10 | src: url("../webfonts/fa-brands-400.eot"); 11 | src: url("../webfonts/fa-brands-400.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.woff") format("woff"), url("../webfonts/fa-brands-400.ttf") format("truetype"), url("../webfonts/fa-brands-400.svg#fontawesome") format("svg"); } 12 | 13 | .fab { 14 | font-family: 'Font Awesome 5 Brands'; 15 | font-weight: 400; } 16 | -------------------------------------------------------------------------------- /static/css/fontawesome/css/brands.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.15.1 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @font-face{font-family:"Font Awesome 5 Brands";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-brands-400.eot);src:url(../webfonts/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.woff) format("woff"),url(../webfonts/fa-brands-400.ttf) format("truetype"),url(../webfonts/fa-brands-400.svg#fontawesome) format("svg")}.fab{font-family:"Font Awesome 5 Brands";font-weight:400} -------------------------------------------------------------------------------- /static/css/fontawesome/css/regular.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.15.1 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @font-face { 6 | font-family: 'Font Awesome 5 Free'; 7 | font-style: normal; 8 | font-weight: 400; 9 | font-display: block; 10 | src: url("../webfonts/fa-regular-400.eot"); 11 | src: url("../webfonts/fa-regular-400.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.woff") format("woff"), url("../webfonts/fa-regular-400.ttf") format("truetype"), url("../webfonts/fa-regular-400.svg#fontawesome") format("svg"); } 12 | 13 | .far { 14 | font-family: 'Font Awesome 5 Free'; 15 | font-weight: 400; } 16 | -------------------------------------------------------------------------------- /static/css/fontawesome/css/regular.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.15.1 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.eot);src:url(../webfonts/fa-regular-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.woff) format("woff"),url(../webfonts/fa-regular-400.ttf) format("truetype"),url(../webfonts/fa-regular-400.svg#fontawesome) format("svg")}.far{font-family:"Font Awesome 5 Free";font-weight:400} -------------------------------------------------------------------------------- /static/css/fontawesome/css/solid.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.15.1 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @font-face { 6 | font-family: 'Font Awesome 5 Free'; 7 | font-style: normal; 8 | font-weight: 900; 9 | font-display: block; 10 | src: url("../webfonts/fa-solid-900.eot"); 11 | src: url("../webfonts/fa-solid-900.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.woff") format("woff"), url("../webfonts/fa-solid-900.ttf") format("truetype"), url("../webfonts/fa-solid-900.svg#fontawesome") format("svg"); } 12 | 13 | .fa, 14 | .fas { 15 | font-family: 'Font Awesome 5 Free'; 16 | font-weight: 900; } 17 | -------------------------------------------------------------------------------- /static/css/fontawesome/css/solid.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.15.1 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.eot);src:url(../webfonts/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.woff) format("woff"),url(../webfonts/fa-solid-900.ttf) format("truetype"),url(../webfonts/fa-solid-900.svg#fontawesome) format("svg")}.fa,.fas{font-family:"Font Awesome 5 Free";font-weight:900} -------------------------------------------------------------------------------- /static/css/fontawesome/webfonts/fa-brands-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/css/fontawesome/webfonts/fa-brands-400.eot -------------------------------------------------------------------------------- /static/css/fontawesome/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/css/fontawesome/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /static/css/fontawesome/webfonts/fa-brands-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/css/fontawesome/webfonts/fa-brands-400.woff -------------------------------------------------------------------------------- /static/css/fontawesome/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/css/fontawesome/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /static/css/fontawesome/webfonts/fa-regular-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/css/fontawesome/webfonts/fa-regular-400.eot -------------------------------------------------------------------------------- /static/css/fontawesome/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/css/fontawesome/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /static/css/fontawesome/webfonts/fa-regular-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/css/fontawesome/webfonts/fa-regular-400.woff -------------------------------------------------------------------------------- /static/css/fontawesome/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/css/fontawesome/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /static/css/fontawesome/webfonts/fa-solid-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/css/fontawesome/webfonts/fa-solid-900.eot -------------------------------------------------------------------------------- /static/css/fontawesome/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/css/fontawesome/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /static/css/fontawesome/webfonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/css/fontawesome/webfonts/fa-solid-900.woff -------------------------------------------------------------------------------- /static/css/fontawesome/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/css/fontawesome/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /static/css/main.css: -------------------------------------------------------------------------------- 1 | div #total-player-registered 2 | { 3 | background-color: #4cb4c7; 4 | } 5 | 6 | 7 | div #total-ctf-played 8 | { 9 | background-color: #7abecc; 10 | } 11 | 12 | div #current-ctf 13 | { 14 | background-color: #7CD1C0; 15 | } 16 | 17 | .user_card { 18 | width: 350px; 19 | margin-top: auto; 20 | margin-bottom: auto; 21 | background: #a2a5a5; 22 | position: relative; 23 | display: flex; 24 | justify-content: center; 25 | flex-direction: column; 26 | padding: 10px; 27 | box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); 28 | -webkit-box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); 29 | -moz-box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); 30 | border-radius: 5px; 31 | } 32 | 33 | .greet-user-msg 34 | { 35 | font-size: 18px; 36 | color: #fff; 37 | margin-right: 20px; 38 | } 39 | 40 | .form_container { 41 | margin-top: 20px; 42 | } 43 | 44 | #form-title{ 45 | color: #fff; 46 | } 47 | 48 | .login_btn { 49 | width: 100%; 50 | background: #144a9b !important; 51 | color: white !important; 52 | } 53 | 54 | .login_btn:focus { 55 | box-shadow: none !important; 56 | outline: 0px !important; 57 | } 58 | 59 | .login_container { 60 | padding: 0 2rem; 61 | } 62 | 63 | .input-group-text { 64 | background: #f7ba5b !important; 65 | color: white !important; 66 | border: 0 !important; 67 | } 68 | 69 | .input_user, 70 | .input_pass:focus { 71 | box-shadow: none !important; 72 | outline: 0px !important; 73 | } 74 | 75 | #messages{ 76 | background-color: grey; 77 | color: #fff; 78 | padding: 10px; 79 | margin-top: 10px; 80 | } 81 | 82 | .ctf-completed-challenge-row{ 83 | background-color: rgba(78, 161, 78, 0.527); 84 | font-style: italic; 85 | text-decoration: line-through; 86 | } 87 | 88 | .progress { 89 | height: 1.5rem; 90 | font-size: 1rem; 91 | } 92 | 93 | .ratio-custom { 94 | --bs-aspect-ratio: 47.75%; 95 | } 96 | -------------------------------------------------------------------------------- /static/css/podium.css: -------------------------------------------------------------------------------- 1 | #podium-box { 2 | margin: 0 auto; 3 | display: flex; 4 | } 5 | 6 | .podium-number { 7 | font-weight: bold; 8 | font-size: 4em; 9 | color: white; 10 | } 11 | 12 | .step-container { 13 | flex: 1; 14 | display: flex; 15 | flex-direction: column; 16 | } 17 | 18 | .step-container>div:first-child { 19 | margin-top: auto; 20 | text-align: center; 21 | } 22 | 23 | .step { 24 | text-align: center; 25 | } 26 | 27 | .bg-blue { 28 | background-color: #063b65; 29 | } 30 | 31 | #first-step { 32 | height: 50%; 33 | } 34 | 35 | #second-step { 36 | height: 35%; 37 | } 38 | 39 | #third-step { 40 | height: 30%; 41 | } -------------------------------------------------------------------------------- /static/images/blank-avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/blank-avatar.png -------------------------------------------------------------------------------- /static/images/blank-ctf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/blank-ctf.png -------------------------------------------------------------------------------- /static/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/favicon.png -------------------------------------------------------------------------------- /static/images/flags/afghanistan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/afghanistan.png -------------------------------------------------------------------------------- /static/images/flags/aland-islands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/aland-islands.png -------------------------------------------------------------------------------- /static/images/flags/albania.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/albania.png -------------------------------------------------------------------------------- /static/images/flags/algeria.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/algeria.png -------------------------------------------------------------------------------- /static/images/flags/american-samoa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/american-samoa.png -------------------------------------------------------------------------------- /static/images/flags/andorra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/andorra.png -------------------------------------------------------------------------------- /static/images/flags/angola.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/angola.png -------------------------------------------------------------------------------- /static/images/flags/anguilla.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/anguilla.png -------------------------------------------------------------------------------- /static/images/flags/antarctica.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/antarctica.png -------------------------------------------------------------------------------- /static/images/flags/antigua-and-barbuda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/antigua-and-barbuda.png -------------------------------------------------------------------------------- /static/images/flags/argentina.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/argentina.png -------------------------------------------------------------------------------- /static/images/flags/armenia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/armenia.png -------------------------------------------------------------------------------- /static/images/flags/aruba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/aruba.png -------------------------------------------------------------------------------- /static/images/flags/australia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/australia.png -------------------------------------------------------------------------------- /static/images/flags/austria.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/austria.png -------------------------------------------------------------------------------- /static/images/flags/azerbaijan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/azerbaijan.png -------------------------------------------------------------------------------- /static/images/flags/bahamas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/bahamas.png -------------------------------------------------------------------------------- /static/images/flags/bahrain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/bahrain.png -------------------------------------------------------------------------------- /static/images/flags/bangladesh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/bangladesh.png -------------------------------------------------------------------------------- /static/images/flags/barbados.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/barbados.png -------------------------------------------------------------------------------- /static/images/flags/belarus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/belarus.png -------------------------------------------------------------------------------- /static/images/flags/belgium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/belgium.png -------------------------------------------------------------------------------- /static/images/flags/belize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/belize.png -------------------------------------------------------------------------------- /static/images/flags/benin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/benin.png -------------------------------------------------------------------------------- /static/images/flags/bermuda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/bermuda.png -------------------------------------------------------------------------------- /static/images/flags/bhutan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/bhutan.png -------------------------------------------------------------------------------- /static/images/flags/blank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/blank.png -------------------------------------------------------------------------------- /static/images/flags/bolivia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/bolivia.png -------------------------------------------------------------------------------- /static/images/flags/bosnia-and-herzegovina.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/bosnia-and-herzegovina.png -------------------------------------------------------------------------------- /static/images/flags/botswana.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/botswana.png -------------------------------------------------------------------------------- /static/images/flags/bouvet-island.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/bouvet-island.png -------------------------------------------------------------------------------- /static/images/flags/brazil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/brazil.png -------------------------------------------------------------------------------- /static/images/flags/british-indian-ocean-territory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/british-indian-ocean-territory.png -------------------------------------------------------------------------------- /static/images/flags/british-virgin-islands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/british-virgin-islands.png -------------------------------------------------------------------------------- /static/images/flags/brunei.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/brunei.png -------------------------------------------------------------------------------- /static/images/flags/bulgaria.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/bulgaria.png -------------------------------------------------------------------------------- /static/images/flags/burkina-faso.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/burkina-faso.png -------------------------------------------------------------------------------- /static/images/flags/burundi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/burundi.png -------------------------------------------------------------------------------- /static/images/flags/cambodia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/cambodia.png -------------------------------------------------------------------------------- /static/images/flags/cameroon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/cameroon.png -------------------------------------------------------------------------------- /static/images/flags/canada.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/canada.png -------------------------------------------------------------------------------- /static/images/flags/cape-verde.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/cape-verde.png -------------------------------------------------------------------------------- /static/images/flags/caribbean-netherlands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/caribbean-netherlands.png -------------------------------------------------------------------------------- /static/images/flags/cayman-islands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/cayman-islands.png -------------------------------------------------------------------------------- /static/images/flags/central-african-republic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/central-african-republic.png -------------------------------------------------------------------------------- /static/images/flags/chad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/chad.png -------------------------------------------------------------------------------- /static/images/flags/chile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/chile.png -------------------------------------------------------------------------------- /static/images/flags/china.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/china.png -------------------------------------------------------------------------------- /static/images/flags/christmas-island.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/christmas-island.png -------------------------------------------------------------------------------- /static/images/flags/cocos-keeling-islands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/cocos-keeling-islands.png -------------------------------------------------------------------------------- /static/images/flags/colombia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/colombia.png -------------------------------------------------------------------------------- /static/images/flags/comoros.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/comoros.png -------------------------------------------------------------------------------- /static/images/flags/cook-islands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/cook-islands.png -------------------------------------------------------------------------------- /static/images/flags/costa-rica.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/costa-rica.png -------------------------------------------------------------------------------- /static/images/flags/cote-divoire-ivory-coast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/cote-divoire-ivory-coast.png -------------------------------------------------------------------------------- /static/images/flags/croatia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/croatia.png -------------------------------------------------------------------------------- /static/images/flags/cuba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/cuba.png -------------------------------------------------------------------------------- /static/images/flags/curacao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/curacao.png -------------------------------------------------------------------------------- /static/images/flags/cyprus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/cyprus.png -------------------------------------------------------------------------------- /static/images/flags/czechia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/czechia.png -------------------------------------------------------------------------------- /static/images/flags/denmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/denmark.png -------------------------------------------------------------------------------- /static/images/flags/djibouti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/djibouti.png -------------------------------------------------------------------------------- /static/images/flags/dominica.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/dominica.png -------------------------------------------------------------------------------- /static/images/flags/dominican-republic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/dominican-republic.png -------------------------------------------------------------------------------- /static/images/flags/dr-congo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/dr-congo.png -------------------------------------------------------------------------------- /static/images/flags/ecuador.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/ecuador.png -------------------------------------------------------------------------------- /static/images/flags/egypt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/egypt.png -------------------------------------------------------------------------------- /static/images/flags/el-salvador.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/el-salvador.png -------------------------------------------------------------------------------- /static/images/flags/england.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/england.png -------------------------------------------------------------------------------- /static/images/flags/equatorial-guinea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/equatorial-guinea.png -------------------------------------------------------------------------------- /static/images/flags/eritrea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/eritrea.png -------------------------------------------------------------------------------- /static/images/flags/estonia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/estonia.png -------------------------------------------------------------------------------- /static/images/flags/eswatini-swaziland.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/eswatini-swaziland.png -------------------------------------------------------------------------------- /static/images/flags/ethiopia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/ethiopia.png -------------------------------------------------------------------------------- /static/images/flags/falkland-islands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/falkland-islands.png -------------------------------------------------------------------------------- /static/images/flags/faroe-islands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/faroe-islands.png -------------------------------------------------------------------------------- /static/images/flags/fiji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/fiji.png -------------------------------------------------------------------------------- /static/images/flags/finland.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/finland.png -------------------------------------------------------------------------------- /static/images/flags/france.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/france.png -------------------------------------------------------------------------------- /static/images/flags/french-guiana.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/french-guiana.png -------------------------------------------------------------------------------- /static/images/flags/french-polynesia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/french-polynesia.png -------------------------------------------------------------------------------- /static/images/flags/french-southern-and-antarctic-lands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/french-southern-and-antarctic-lands.png -------------------------------------------------------------------------------- /static/images/flags/gabon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/gabon.png -------------------------------------------------------------------------------- /static/images/flags/gambia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/gambia.png -------------------------------------------------------------------------------- /static/images/flags/georgia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/georgia.png -------------------------------------------------------------------------------- /static/images/flags/germany.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/germany.png -------------------------------------------------------------------------------- /static/images/flags/ghana.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/ghana.png -------------------------------------------------------------------------------- /static/images/flags/gibraltar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/gibraltar.png -------------------------------------------------------------------------------- /static/images/flags/greece.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/greece.png -------------------------------------------------------------------------------- /static/images/flags/greenland.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/greenland.png -------------------------------------------------------------------------------- /static/images/flags/grenada.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/grenada.png -------------------------------------------------------------------------------- /static/images/flags/guadeloupe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/guadeloupe.png -------------------------------------------------------------------------------- /static/images/flags/guam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/guam.png -------------------------------------------------------------------------------- /static/images/flags/guatemala.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/guatemala.png -------------------------------------------------------------------------------- /static/images/flags/guernsey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/guernsey.png -------------------------------------------------------------------------------- /static/images/flags/guinea-bissau.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/guinea-bissau.png -------------------------------------------------------------------------------- /static/images/flags/guinea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/guinea.png -------------------------------------------------------------------------------- /static/images/flags/guyana.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/guyana.png -------------------------------------------------------------------------------- /static/images/flags/haiti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/haiti.png -------------------------------------------------------------------------------- /static/images/flags/heard-island-and-mcdonald-islands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/heard-island-and-mcdonald-islands.png -------------------------------------------------------------------------------- /static/images/flags/honduras.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/honduras.png -------------------------------------------------------------------------------- /static/images/flags/hong-kong.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/hong-kong.png -------------------------------------------------------------------------------- /static/images/flags/hungary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/hungary.png -------------------------------------------------------------------------------- /static/images/flags/iceland.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/iceland.png -------------------------------------------------------------------------------- /static/images/flags/india.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/india.png -------------------------------------------------------------------------------- /static/images/flags/indonesia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/indonesia.png -------------------------------------------------------------------------------- /static/images/flags/iran.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/iran.png -------------------------------------------------------------------------------- /static/images/flags/iraq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/iraq.png -------------------------------------------------------------------------------- /static/images/flags/ireland.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/ireland.png -------------------------------------------------------------------------------- /static/images/flags/isle-of-man.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/isle-of-man.png -------------------------------------------------------------------------------- /static/images/flags/israel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/israel.png -------------------------------------------------------------------------------- /static/images/flags/italy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/italy.png -------------------------------------------------------------------------------- /static/images/flags/jamaica.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/jamaica.png -------------------------------------------------------------------------------- /static/images/flags/japan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/japan.png -------------------------------------------------------------------------------- /static/images/flags/jersey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/jersey.png -------------------------------------------------------------------------------- /static/images/flags/jordan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/jordan.png -------------------------------------------------------------------------------- /static/images/flags/kazakhstan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/kazakhstan.png -------------------------------------------------------------------------------- /static/images/flags/kenya.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/kenya.png -------------------------------------------------------------------------------- /static/images/flags/kiribati.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/kiribati.png -------------------------------------------------------------------------------- /static/images/flags/kosovo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/kosovo.png -------------------------------------------------------------------------------- /static/images/flags/kuwait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/kuwait.png -------------------------------------------------------------------------------- /static/images/flags/kyrgyzstan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/kyrgyzstan.png -------------------------------------------------------------------------------- /static/images/flags/laos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/laos.png -------------------------------------------------------------------------------- /static/images/flags/latvia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/latvia.png -------------------------------------------------------------------------------- /static/images/flags/lebanon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/lebanon.png -------------------------------------------------------------------------------- /static/images/flags/lesotho.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/lesotho.png -------------------------------------------------------------------------------- /static/images/flags/liberia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/liberia.png -------------------------------------------------------------------------------- /static/images/flags/libya.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/libya.png -------------------------------------------------------------------------------- /static/images/flags/liechtenstein.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/liechtenstein.png -------------------------------------------------------------------------------- /static/images/flags/lithuania.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/lithuania.png -------------------------------------------------------------------------------- /static/images/flags/luxembourg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/luxembourg.png -------------------------------------------------------------------------------- /static/images/flags/macau.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/macau.png -------------------------------------------------------------------------------- /static/images/flags/madagascar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/madagascar.png -------------------------------------------------------------------------------- /static/images/flags/malawi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/malawi.png -------------------------------------------------------------------------------- /static/images/flags/malaysia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/malaysia.png -------------------------------------------------------------------------------- /static/images/flags/maldives.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/maldives.png -------------------------------------------------------------------------------- /static/images/flags/mali.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/mali.png -------------------------------------------------------------------------------- /static/images/flags/malta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/malta.png -------------------------------------------------------------------------------- /static/images/flags/marshall-islands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/marshall-islands.png -------------------------------------------------------------------------------- /static/images/flags/martinique.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/martinique.png -------------------------------------------------------------------------------- /static/images/flags/mauritania.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/mauritania.png -------------------------------------------------------------------------------- /static/images/flags/mauritius.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/mauritius.png -------------------------------------------------------------------------------- /static/images/flags/mayotte.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/mayotte.png -------------------------------------------------------------------------------- /static/images/flags/mexico.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/mexico.png -------------------------------------------------------------------------------- /static/images/flags/micronesia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/micronesia.png -------------------------------------------------------------------------------- /static/images/flags/moldova.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/moldova.png -------------------------------------------------------------------------------- /static/images/flags/monaco.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/monaco.png -------------------------------------------------------------------------------- /static/images/flags/mongolia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/mongolia.png -------------------------------------------------------------------------------- /static/images/flags/montenegro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/montenegro.png -------------------------------------------------------------------------------- /static/images/flags/montserrat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/montserrat.png -------------------------------------------------------------------------------- /static/images/flags/morocco.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/morocco.png -------------------------------------------------------------------------------- /static/images/flags/mozambique.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/mozambique.png -------------------------------------------------------------------------------- /static/images/flags/myanmar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/myanmar.png -------------------------------------------------------------------------------- /static/images/flags/namibia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/namibia.png -------------------------------------------------------------------------------- /static/images/flags/nauru.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/nauru.png -------------------------------------------------------------------------------- /static/images/flags/nepal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/nepal.png -------------------------------------------------------------------------------- /static/images/flags/netherlands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/netherlands.png -------------------------------------------------------------------------------- /static/images/flags/new-caledonia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/new-caledonia.png -------------------------------------------------------------------------------- /static/images/flags/new-zealand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/new-zealand.png -------------------------------------------------------------------------------- /static/images/flags/nicaragua.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/nicaragua.png -------------------------------------------------------------------------------- /static/images/flags/niger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/niger.png -------------------------------------------------------------------------------- /static/images/flags/nigeria.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/nigeria.png -------------------------------------------------------------------------------- /static/images/flags/niue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/niue.png -------------------------------------------------------------------------------- /static/images/flags/norfolk-island.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/norfolk-island.png -------------------------------------------------------------------------------- /static/images/flags/north-korea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/north-korea.png -------------------------------------------------------------------------------- /static/images/flags/north-macedonia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/north-macedonia.png -------------------------------------------------------------------------------- /static/images/flags/northern-ireland.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/northern-ireland.png -------------------------------------------------------------------------------- /static/images/flags/northern-mariana-islands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/northern-mariana-islands.png -------------------------------------------------------------------------------- /static/images/flags/norway.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/norway.png -------------------------------------------------------------------------------- /static/images/flags/oman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/oman.png -------------------------------------------------------------------------------- /static/images/flags/pakistan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/pakistan.png -------------------------------------------------------------------------------- /static/images/flags/palau.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/palau.png -------------------------------------------------------------------------------- /static/images/flags/palestine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/palestine.png -------------------------------------------------------------------------------- /static/images/flags/panama.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/panama.png -------------------------------------------------------------------------------- /static/images/flags/papua-new-guinea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/papua-new-guinea.png -------------------------------------------------------------------------------- /static/images/flags/paraguay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/paraguay.png -------------------------------------------------------------------------------- /static/images/flags/peru.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/peru.png -------------------------------------------------------------------------------- /static/images/flags/philippines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/philippines.png -------------------------------------------------------------------------------- /static/images/flags/pitcairn-islands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/pitcairn-islands.png -------------------------------------------------------------------------------- /static/images/flags/poland.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/poland.png -------------------------------------------------------------------------------- /static/images/flags/portugal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/portugal.png -------------------------------------------------------------------------------- /static/images/flags/puerto-rico.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/puerto-rico.png -------------------------------------------------------------------------------- /static/images/flags/qatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/qatar.png -------------------------------------------------------------------------------- /static/images/flags/republic-of-the-congo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/republic-of-the-congo.png -------------------------------------------------------------------------------- /static/images/flags/reunion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/reunion.png -------------------------------------------------------------------------------- /static/images/flags/romania.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/romania.png -------------------------------------------------------------------------------- /static/images/flags/russia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/russia.png -------------------------------------------------------------------------------- /static/images/flags/rwanda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/rwanda.png -------------------------------------------------------------------------------- /static/images/flags/saint-barthelemy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/saint-barthelemy.png -------------------------------------------------------------------------------- /static/images/flags/saint-helena-ascension-and-tristan-da-cunha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/saint-helena-ascension-and-tristan-da-cunha.png -------------------------------------------------------------------------------- /static/images/flags/saint-kitts-and-nevis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/saint-kitts-and-nevis.png -------------------------------------------------------------------------------- /static/images/flags/saint-lucia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/saint-lucia.png -------------------------------------------------------------------------------- /static/images/flags/saint-martin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/saint-martin.png -------------------------------------------------------------------------------- /static/images/flags/saint-pierre-and-miquelon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/saint-pierre-and-miquelon.png -------------------------------------------------------------------------------- /static/images/flags/saint-vincent-and-the-grenadines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/saint-vincent-and-the-grenadines.png -------------------------------------------------------------------------------- /static/images/flags/samoa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/samoa.png -------------------------------------------------------------------------------- /static/images/flags/san-marino.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/san-marino.png -------------------------------------------------------------------------------- /static/images/flags/sao-tome-and-principe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/sao-tome-and-principe.png -------------------------------------------------------------------------------- /static/images/flags/saudi-arabia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/saudi-arabia.png -------------------------------------------------------------------------------- /static/images/flags/scotland.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/scotland.png -------------------------------------------------------------------------------- /static/images/flags/senegal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/senegal.png -------------------------------------------------------------------------------- /static/images/flags/serbia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/serbia.png -------------------------------------------------------------------------------- /static/images/flags/seychelles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/seychelles.png -------------------------------------------------------------------------------- /static/images/flags/sierra-leone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/sierra-leone.png -------------------------------------------------------------------------------- /static/images/flags/singapore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/singapore.png -------------------------------------------------------------------------------- /static/images/flags/sint-maarten.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/sint-maarten.png -------------------------------------------------------------------------------- /static/images/flags/slovakia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/slovakia.png -------------------------------------------------------------------------------- /static/images/flags/slovenia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/slovenia.png -------------------------------------------------------------------------------- /static/images/flags/solomon-islands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/solomon-islands.png -------------------------------------------------------------------------------- /static/images/flags/somalia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/somalia.png -------------------------------------------------------------------------------- /static/images/flags/south-africa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/south-africa.png -------------------------------------------------------------------------------- /static/images/flags/south-georgia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/south-georgia.png -------------------------------------------------------------------------------- /static/images/flags/south-korea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/south-korea.png -------------------------------------------------------------------------------- /static/images/flags/south-sudan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/south-sudan.png -------------------------------------------------------------------------------- /static/images/flags/spain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/spain.png -------------------------------------------------------------------------------- /static/images/flags/sri-lanka.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/sri-lanka.png -------------------------------------------------------------------------------- /static/images/flags/sudan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/sudan.png -------------------------------------------------------------------------------- /static/images/flags/suriname.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/suriname.png -------------------------------------------------------------------------------- /static/images/flags/svalbard-and-jan-mayen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/svalbard-and-jan-mayen.png -------------------------------------------------------------------------------- /static/images/flags/sweden.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/sweden.png -------------------------------------------------------------------------------- /static/images/flags/switzerland.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/switzerland.png -------------------------------------------------------------------------------- /static/images/flags/syria.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/syria.png -------------------------------------------------------------------------------- /static/images/flags/taiwan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/taiwan.png -------------------------------------------------------------------------------- /static/images/flags/tajikistan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/tajikistan.png -------------------------------------------------------------------------------- /static/images/flags/tanzania.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/tanzania.png -------------------------------------------------------------------------------- /static/images/flags/thailand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/thailand.png -------------------------------------------------------------------------------- /static/images/flags/timor-leste.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/timor-leste.png -------------------------------------------------------------------------------- /static/images/flags/togo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/togo.png -------------------------------------------------------------------------------- /static/images/flags/tokelau.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/tokelau.png -------------------------------------------------------------------------------- /static/images/flags/tonga.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/tonga.png -------------------------------------------------------------------------------- /static/images/flags/trinidad-and-tobago.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/trinidad-and-tobago.png -------------------------------------------------------------------------------- /static/images/flags/tunisia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/tunisia.png -------------------------------------------------------------------------------- /static/images/flags/turkey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/turkey.png -------------------------------------------------------------------------------- /static/images/flags/turkmenistan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/turkmenistan.png -------------------------------------------------------------------------------- /static/images/flags/turks-and-caicos-islands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/turks-and-caicos-islands.png -------------------------------------------------------------------------------- /static/images/flags/tuvalu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/tuvalu.png -------------------------------------------------------------------------------- /static/images/flags/uganda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/uganda.png -------------------------------------------------------------------------------- /static/images/flags/ukraine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/ukraine.png -------------------------------------------------------------------------------- /static/images/flags/united-arab-emirates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/united-arab-emirates.png -------------------------------------------------------------------------------- /static/images/flags/united-kingdom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/united-kingdom.png -------------------------------------------------------------------------------- /static/images/flags/united-nations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/united-nations.png -------------------------------------------------------------------------------- /static/images/flags/united-states-minor-outlying-islands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/united-states-minor-outlying-islands.png -------------------------------------------------------------------------------- /static/images/flags/united-states-virgin-islands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/united-states-virgin-islands.png -------------------------------------------------------------------------------- /static/images/flags/united-states.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/united-states.png -------------------------------------------------------------------------------- /static/images/flags/uruguay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/uruguay.png -------------------------------------------------------------------------------- /static/images/flags/uzbekistan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/uzbekistan.png -------------------------------------------------------------------------------- /static/images/flags/vanuatu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/vanuatu.png -------------------------------------------------------------------------------- /static/images/flags/vatican-city-holy-see.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/vatican-city-holy-see.png -------------------------------------------------------------------------------- /static/images/flags/venezuela.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/venezuela.png -------------------------------------------------------------------------------- /static/images/flags/vietnam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/vietnam.png -------------------------------------------------------------------------------- /static/images/flags/wales.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/wales.png -------------------------------------------------------------------------------- /static/images/flags/wallis-and-futuna.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/wallis-and-futuna.png -------------------------------------------------------------------------------- /static/images/flags/western-sahara.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/western-sahara.png -------------------------------------------------------------------------------- /static/images/flags/yemen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/yemen.png -------------------------------------------------------------------------------- /static/images/flags/zambia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/zambia.png -------------------------------------------------------------------------------- /static/images/flags/zimbabwe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/flags/zimbabwe.png -------------------------------------------------------------------------------- /static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/logo.png -------------------------------------------------------------------------------- /static/images/new_logo_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/new_logo_circle.png -------------------------------------------------------------------------------- /static/images/new_logo_square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/static/images/new_logo_square.png -------------------------------------------------------------------------------- /static/js/challenge.js: -------------------------------------------------------------------------------- 1 | window.addEventListener('DOMContentLoaded', (event) => { 2 | var formatSelect = document.getElementById('id_format'); 3 | var dataTextArea = document.getElementById('id_data'); 4 | 5 | formatSelect.addEventListener('change', function () { 6 | var selectedFormat = this.value; 7 | var placeholderText; 8 | 9 | switch (selectedFormat) { 10 | case 'RAW': 11 | placeholderText = 'name | category'; 12 | break; 13 | case 'CTFd': 14 | placeholderText = 'paste CTFd JSON /api/v1/challenges'; 15 | break; 16 | case 'rCTF': 17 | placeholderText = 'paste rCTF JSON /api/v1/challs'; 18 | break; 19 | default: 20 | placeholderText = ''; 21 | } 22 | 23 | dataTextArea.setAttribute('placeholder', placeholderText); 24 | }); 25 | 26 | // Trigger the change event on page load to set initial placeholder 27 | formatSelect.dispatchEvent(new Event('change')); 28 | }); -------------------------------------------------------------------------------- /static/js/shortcuts.js: -------------------------------------------------------------------------------- 1 | var isKeyPressed = {}; 2 | 3 | 4 | 5 | 6 | 7 | function triggerShortcutEvent() 8 | { 9 | for(let key in shortcutFunctionTable) 10 | { 11 | let keys = key.split("+"); 12 | let executeEvent = true; 13 | for(let i=0; i 30 | { 31 | isKeyPressed[keyDownEvent.key] = true; 32 | triggerShortcutEvent(); 33 | }; 34 | 35 | 36 | document.onkeyup = (keyUpEvent) => 37 | { 38 | //keyUpEvent.preventDefault(); 39 | isKeyPressed[keyUpEvent.key] = false; 40 | }; 41 | -------------------------------------------------------------------------------- /static/js/utils.js: -------------------------------------------------------------------------------- 1 | function generate_random_string(length = 12) { 2 | let charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 3 | let str = ''; 4 | for (let i = 0; i < length; i++) 5 | str += charset.charAt(Math.floor(Math.random() * charset.length)); 6 | 7 | return str; 8 | } 9 | 10 | function toggle_input_password_visibility() { 11 | for (let r of document.getElementsByClassName("reveal")) { 12 | if (r.type === "password") 13 | r.type = "text"; 14 | 15 | else if (r.type === "text") 16 | r.type = "password"; 17 | } 18 | } 19 | 20 | function generate_random_color(number) { 21 | const hue = number * 137.508; // use golden angle approximation 22 | return `hsl(${hue},50%,75%)`; 23 | } 24 | 25 | 26 | function timeuntil(datetime) { 27 | const end = new Date(datetime).getTime(); 28 | const now = new Date().getTime(); 29 | 30 | const offset = end - now; 31 | if (offset < 0) 32 | return "Finished"; 33 | 34 | let days = Math.floor(offset / (1000 * 60 * 60 * 24)); 35 | let hours = Math.floor((offset % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); 36 | let minutes = Math.floor((offset % (1000 * 60 * 60)) / (1000 * 60)); 37 | let seconds = Math.floor((offset % (1000 * 60)) / 1000); 38 | 39 | var res = ""; 40 | if (days) 41 | res += `${days}d `; 42 | res += `${hours}h ${minutes}m ${seconds}s left`; 43 | return res; 44 | } 45 | 46 | function sendToClipboard(elementId, time) { 47 | var text = document.getElementById('api_to_clipboard').innerHTML; 48 | navigator.clipboard.writeText(document.getElementById('team_api_key')).then( 49 | () => { 50 | document.getElementById('api_to_clipboard').innerHTML = text + ' ✅'; 51 | }, 52 | () => { 53 | document.getElementById('api_to_clipboard').innerHTML = text + ' ❌'; 54 | } 55 | ); 56 | setTimeout(() => { 57 | document.getElementById('api_to_clipboard').innerHTML = text; 58 | }, 5000); 59 | } 60 | -------------------------------------------------------------------------------- /uploads/.do_not_remove: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/ctfhub/52d297e2efe8984edb845e2fbb994fed942cd85d/uploads/.do_not_remove --------------------------------------------------------------------------------