├── .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 |
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 |
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 |
26 |
27 |
28 |
29 |
30 | {% endblock %}
31 |
--------------------------------------------------------------------------------
/ctfhub/templates/ctfhub/ctfs/detail_export.html:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/ctfhub/templates/ctfhub/ctfs/detail_notes.html:
--------------------------------------------------------------------------------
1 |
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 |
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 |
15 |
16 |
17 |
18 |
19 |
20 | CTF
21 | Date (in UTC)
22 | Duration
23 | Weight
24 | Import
25 |
26 |
27 |
28 | {% for ctf in ctftime_ctfs %}
29 |
30 |
31 | {{ctf.title}}
32 |
33 |
34 | {{ctf.start | date:"Y/m/d H:i:s"}}
35 | →
36 | {{ctf.finish | date:"Y/m/d H:i:s"}}
37 |
38 |
39 | {{ ctf.duration|naturaltime}}
40 |
41 | {{ ctf.weight | floatformat:"-2" }}
42 |
43 |
44 | {% endfor %}
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
58 |
59 |
60 | CTFTime ID
61 |
62 |
63 |
64 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/ctfhub/templates/ctfhub/dashboard/status.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
10 |
11 |
{{members | length}}
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
{{nb_ctf_played}}
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
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 |
5 |
6 |
7 |
11 |
12 |
13 |
14 |
15 | Shortcut
16 | Description
17 |
18 |
19 |
20 |
21 | Control+Alt+h
22 | Show this help menu.
23 |
24 |
25 | Control+Alt+s
26 | Focus on the search input.
27 |
28 |
29 | Control+Alt+f
30 | Focus on the challenge filter input.
31 |
32 |
33 | Control+Alt+g
34 | Jump to your current CTF.
35 |
36 |
37 | Control+Alt+t
38 | Jump to the team info page.
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
69 |
70 |
--------------------------------------------------------------------------------
/ctfhub/templates/ctfhub/stats/ctf_stats.html:
--------------------------------------------------------------------------------
1 | {% load humanize %}
2 | {% load ctfhub_filters %}
3 |
47 |
--------------------------------------------------------------------------------
/ctfhub/templates/ctfhub/stats/timeline.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/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 |
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 |
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 |
18 |
19 |
20 |
21 | {% for result in page_obj %}
22 |
23 |
24 |
25 |
26 |
27 |
28 | {{result.description}}
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | {% endfor %}
37 |
38 |
39 |
40 |
53 |
54 |
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 |
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 |
19 |
20 |
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 |
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 |
16 |
17 |
18 |
36 |
37 |
38 |
39 | Don't have an account?
Sign Up
40 |
41 |
42 | Lost your password,
Click here
43 |
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 |
17 |
18 |
19 |
37 |
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 |
18 |
19 |
20 |
33 |
34 |
35 | Back to login,
Click here
36 |
37 |
38 | If you've forgotten your email address, see your admin for a manual reset
39 |
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 |
15 |
16 |
44 | {% include 'snippets/formerror.html' %}
45 |
46 | {% if not request.user.is_authenticated %}
47 |
48 | Already have an account?
Log in here
49 |
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 | 
6 |
7 | ## View CTF
8 |
9 | 
10 | 
11 |
12 | ## Import CTFs from CTFtime
13 |
14 | 
15 |
16 |
17 | ## Challenge
18 |
19 | 
20 |
21 |
22 | ## Statistics
23 |
24 | 
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
--------------------------------------------------------------------------------