├── .cloud └── .azure │ ├── compute.json │ ├── registermodel.json │ ├── run.json │ └── workspace.json ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── custom.md │ └── feature_request.md └── workflows │ ├── deploy.yml │ ├── slash_command.yml │ └── train.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── General └── picoCTF │ ├── static.md │ └── strings_static.png ├── LICENSE ├── README.md ├── SECURITY.md ├── certs.go ├── check.go ├── client.go ├── code ├── deploy │ ├── environment.yml │ └── score.py ├── test │ └── test.py └── train │ ├── environment.yaml │ ├── run_config.py │ └── train.py ├── crypt ├── choreography │ ├── choreography.409bac9b3fc5cd73a5d46b0f2bcf51f4331bdb6094c8d47b39c4d4ea32ad2028.tgz │ ├── choreography.md │ ├── cipher.py │ ├── pow.py │ ├── run.sh │ └── server.py ├── crauthzv2.md └── pressure │ ├── pressure.f3ac8045c9aad3972c42b098f5890ecf8e2398cf36993a7fbf197ad52725fca8.tgz │ └── pressure.md ├── docs └── images │ ├── ML Ops Workflow (2).png │ ├── actions.png │ ├── actions_tab.png │ ├── aml.png │ ├── ml-lifecycle.png │ └── secrets.png ├── game.go ├── go.mod ├── go.sum ├── level_server.go ├── misc ├── README ├── but I plaidiversed that already │ └── but I plaidiversed that already.md ├── generate.py ├── misc.md └── song-01.wav ├── poodle └── poodle.md ├── pppordle.b2672f7de0bd2cc6ff8a68e6c978f37d8470c02248847a36e1b5cf91c25beb7e.tgz ├── pwn ├── homium │ ├── Dockerfile │ ├── build-docker.sh │ ├── docker-main │ ├── holmium │ ├── holmium.443c089776cd6bcb2e5102e84b09285ecbf133b09f18f699cd6d8e6b15be7821.tgz │ └── parallelism-markdown.md ├── pppdddbbb │ ├── Cargo.lock │ ├── Cargo.toml │ ├── Plaidiverse.md │ ├── cerebrum-boggled.tar.xz │ ├── chal │ └── main.rs └── tinebpf │ └── tinebpf.md ├── server.go ├── session_server.go ├── state.go ├── total ├── Amongstourselves-shipmate.md ├── BasicController.ts ├── Body.ts ├── ConspiracyAction.ts ├── ConspiracyController.ts ├── Dockerfile ├── Dropship.ts ├── EmergencyButtonAction.ts ├── EmergencyButtonController.ts ├── FileTransferAction.ts ├── FileTransferController.ts ├── Game.ts ├── HoldController.ts ├── Level.ts ├── Makefile ├── MeetingController.ts ├── MovementController.ts ├── Player.ts ├── ProcessSampleController.ts ├── ProvideCredentialsController.ts ├── PurchaseSnackController.ts ├── README.md ├── RecalibrateEngineController.ts ├── ResetController.ts ├── SatelliteController.ts ├── Self.ts ├── SettingsController.ts ├── TheShelld.ts ├── VentController.ts ├── access-point-spritesheet.json ├── access-point.png ├── arrow-spritesheet.json ├── arrow.png ├── button-down.png ├── button-up.png ├── centrifuge-closed.png ├── centrifuge-loaded.png ├── centrifuge-open.png ├── centrifuge-overworld-spritesheet.json ├── centrifuge-overworld.png ├── close-button.png ├── conspiracy-board-overworld-spritesheet.json ├── conspiracy-board-overworld.png ├── conspiracy-board.png ├── default.conf ├── docker-compose.yaml ├── dropship.png ├── emergency-button-panel-button.png ├── emergency-button-panel.png ├── emergency-button-spritesheet.json ├── emergency-button.png ├── emergency-meeting.png ├── engine ├── file-transfer-panel.png ├── folder-closed.png ├── folder-open.png ├── index.ts ├── initdb.sql ├── keypad-overworld-spritesheet.json ├── keypad-overworld.png ├── keypad.png ├── login-button-down.png ├── login-button-hover.png ├── login-button-up.png ├── login-screen.png ├── monitor-spritesheet.json ├── monitor.png ├── package.json ├── post-it.png ├── satellite-spritesheet.json ├── satellite.png ├── shadow.png ├── shelld-overlay.png ├── shelld.png ├── shipmate-spritesheet.json ├── shipmate-spritesheet.png ├── snacks.png ├── start-panel-spritesheet.json ├── start-panel.png ├── test-tube.png ├── tsconfig.json ├── vending ├── vending-bottom-layer.png ├── vending-button.png ├── vending-machine-spritesheet.json ├── vending-machine.png ├── vending-top-layer.png ├── vent-spritesheet.json ├── vent.png ├── voting-screen.png ├── xinetd.conf └── yarn.lock ├── ui.go └── web ├── Amongst Ourselves: Hoaxer.md ├── Dockerfile ├── Makefile ├── Vax Register └── writeup.md ├── docker-compose.yaml ├── package.json ├── tsconfig.json └── yarn.lock /.cloud/.azure/compute.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "githubcluster", 3 | "compute_type": "amlcluster" 4 | } 5 | -------------------------------------------------------------------------------- /.cloud/.azure/registermodel.json: -------------------------------------------------------------------------------- 1 | { 2 | "model_file_name": "model.pkl" 3 | } -------------------------------------------------------------------------------- /.cloud/.azure/run.json: -------------------------------------------------------------------------------- 1 | { 2 | "experiment_name": "GitHubActionExperiment" 3 | } 4 | -------------------------------------------------------------------------------- /.cloud/.azure/workspace.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "same-demo-wus2", 3 | "resource_group": "gha_and_aml_rg" 4 | } 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.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/deploy.yml: -------------------------------------------------------------------------------- 1 | name: aml-deploy 2 | 3 | on: 4 | repository_dispatch: 5 | types: [deploy-command] 6 | 7 | jobs: 8 | deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Check variables 12 | env: 13 | PAYLOAD_CONTEXT: ${{ toJson(github.event.client_payload.slash_command) }} 14 | run: | 15 | if [ -z "${{ github.event.client_payload.slash_command.model_name }}"]; then echo "model_name is NULL"; exit 1; else echo "model_name is ${{ toJson(github.event.client_payload.slash_command.model_name) }}"; fi; 16 | if [ -z "${{ github.event.client_payload.slash_command.model_version }}"]; then echo "model_version is NULL"; exit 1; else echo "model_version is ${{ toJson(github.event.client_payload.slash_command.model_version) }}"; fi; 17 | 18 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 19 | - name: Check Out Repository 20 | id: checkout_repository 21 | uses: actions/checkout@v2 22 | 23 | - name: Create URL to the run output 24 | id: vars 25 | run: echo ::set-output name=run-url::https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID 26 | 27 | - name: Create comment 28 | uses: peter-evans/create-or-update-comment@v1 29 | with: 30 | token: ${{ secrets.GITHUB_TOKEN }} 31 | issue-number: ${{ github.event.client_payload.github.payload.issue.number }} 32 | body: | 33 | @${{ github.event.client_payload.github.actor }} Just submit a deploy request. 34 | [Click here to see the deploy run output](${{ steps.vars.outputs.run-url }}) 35 | 36 | # Connect or Create the Azure Machine Learning Workspace 37 | - name: Connect/Create Azure Machine Learning Workspace 38 | id: aml_workspace 39 | uses: Azure/aml-workspace@v1 40 | with: 41 | azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} 42 | 43 | # # Profile your model (TODO) 44 | # - name: Profile Model 45 | # if: github.event.client_payload.slash_command.profile == 'true' 46 | # id: aml_profile 47 | # uses: Azure/aml-profile@master 48 | # with: 49 | # azureCredentials: ${{ secrets.AZURE_CREDENTIALS }} 50 | 51 | # Test your model (this is optional) 52 | - name: Test Model 53 | if: github.event.client_payload.slash_command.test == 'true' 54 | id: aml_test 55 | uses: Azure/aml-deploy@v1 56 | with: 57 | azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} 58 | model_name: ${{ github.event.client_payload.slash_command.model_name }} 59 | model_version: ${{ github.event.client_payload.slash_command.model_version }} 60 | 61 | # Deploy your model to dev (this is optional) 62 | - name: Dev Deploy 63 | if: github.event.client_payload.slash_command.dev == 'true' 64 | id: aml_dev_deploy 65 | uses: Azure/aml-deploy@v1 66 | with: 67 | azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} 68 | model_name: ${{ github.event.client_payload.slash_command.model_name }} 69 | model_version: ${{ github.event.client_payload.slash_command.model_version }} 70 | parameters_file: "dev_aks.json" 71 | 72 | # Deploy your model to production 73 | - name: Prod Deploy 74 | if: github.event.client_payload.slash_command.prod == 'true' 75 | id: aml_prod_deploy 76 | uses: Azure/aml-deploy@v1 77 | with: 78 | azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} 79 | model_name: ${{ github.event.client_payload.slash_command.model_name }} 80 | model_version: ${{ github.event.client_payload.slash_command.model_version }} 81 | parameters_file: "prod_aks.json" 82 | 83 | - name: Create comment 84 | uses: peter-evans/create-or-update-comment@v1 85 | with: 86 | token: ${{ secrets.GITHUB_TOKEN }} 87 | issue-number: ${{ github.event.client_payload.github.payload.issue.number }} 88 | body: | 89 | scoring-endpoint: ${{ steps.aml_prod_deploy.outputs.service_scoring_uri}} 90 | swagger-uri: ${{ steps.aml_prod_deploy.outputs.service_swagger_uri}} 91 | -------------------------------------------------------------------------------- /.github/workflows/slash_command.yml: -------------------------------------------------------------------------------- 1 | name: slash-command 2 | 3 | on: 4 | issue_comment: 5 | types: [created] 6 | 7 | jobs: 8 | # This is the dispatch for slash commands in the repo 9 | slash_command: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Slash Command Dispatch 13 | id: slash_command 14 | uses: peter-evans/slash-command-dispatch@v1.1.2 15 | with: 16 | token: ${{ secrets.REPO_ACCESS_TOKEN }} 17 | reaction-token: ${{ secrets.GITHUB_TOKEN }} 18 | commands: deploy 19 | reaction-type: hooray 20 | permission: write 21 | issue-type: pull-request 22 | named-args: true -------------------------------------------------------------------------------- /.github/workflows/train.yml: -------------------------------------------------------------------------------- 1 | # Actions train a model on Azure Machine Learning 2 | name: aml-train 3 | on: [pull_request] 4 | 5 | jobs: 6 | train: 7 | runs-on: ubuntu-latest 8 | steps: 9 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 10 | - name: Check Out Repository 11 | id: checkout_repository 12 | uses: actions/checkout@v2 13 | 14 | # Connect or Create the Azure Machine Learning Workspace 15 | - name: Connect/Create Azure Machine Learning Workspace 16 | id: aml_workspace 17 | uses: Azure/aml-workspace@v1 18 | with: 19 | azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} 20 | 21 | # Connect or Create a Compute Target in Azure Machine Learning 22 | - name: Connect/Create Azure Machine Learning Compute Target 23 | id: aml_compute_training 24 | uses: Azure/aml-compute@v1 25 | with: 26 | azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} 27 | 28 | # Submit a training run to the Azure Machine Learning 29 | - name: Submit training run 30 | id: aml_run 31 | uses: Azure/aml-run@v1 32 | with: 33 | azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} 34 | 35 | - name: comment PR 36 | uses: unsplash/comment-on-pr@master 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | with: 40 | msg: "${{ steps.aml_run.outputs.run_metrics }}" 41 | 42 | # Register model in Azure Machine Learning model registry 43 | - name: Register model 44 | id: aml_registermodel 45 | uses: Azure/aml-registermodel@v1 46 | with: 47 | azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} 48 | run_id: ${{ steps.aml_run.outputs.run_id }} 49 | experiment_name: ${{ steps.aml_run.outputs.experiment_name }} 50 | 51 | - name: comment PR 52 | uses: unsplash/comment-on-pr@master 53 | env: 54 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 55 | with: 56 | msg: "${{ steps.aml_registermodel.outputs.model_id }}" 57 | 58 | # - uses: actions/upload-artifact@v1 59 | # with: 60 | # name: ${{ steps.aml_run.outputs.model }} 61 | # path: SpatialSimOutputs 62 | 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | [Aa][Rr][Mm]/ 24 | [Aa][Rr][Mm]64/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015/2017 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # Visual Studio 2017 auto generated files 36 | Generated\ Files/ 37 | 38 | # MSTest test Results 39 | [Tt]est[Rr]esult*/ 40 | [Bb]uild[Ll]og.* 41 | 42 | # NUNIT 43 | *.VisualState.xml 44 | TestResult.xml 45 | 46 | # Build Results of an ATL Project 47 | [Dd]ebugPS/ 48 | [Rr]eleasePS/ 49 | dlldata.c 50 | 51 | # Benchmark Results 52 | BenchmarkDotNet.Artifacts/ 53 | 54 | # .NET Core 55 | project.lock.json 56 | project.fragment.lock.json 57 | artifacts/ 58 | 59 | # StyleCop 60 | StyleCopReport.xml 61 | 62 | # Files built by Visual Studio 63 | *_i.c 64 | *_p.c 65 | *_h.h 66 | *.ilk 67 | *.meta 68 | *.obj 69 | *.iobj 70 | *.pch 71 | *.pdb 72 | *.ipdb 73 | *.pgc 74 | *.pgd 75 | *.rsp 76 | *.sbr 77 | *.tlb 78 | *.tli 79 | *.tlh 80 | *.tmp 81 | *.tmp_proj 82 | *_wpftmp.csproj 83 | *.log 84 | *.vspscc 85 | *.vssscc 86 | .builds 87 | *.pidb 88 | *.svclog 89 | *.scc 90 | 91 | # Chutzpah Test files 92 | _Chutzpah* 93 | 94 | # Visual C++ cache files 95 | ipch/ 96 | *.aps 97 | *.ncb 98 | *.opendb 99 | *.opensdf 100 | *.sdf 101 | *.cachefile 102 | *.VC.db 103 | *.VC.VC.opendb 104 | 105 | # Visual Studio profiler 106 | *.psess 107 | *.vsp 108 | *.vspx 109 | *.sap 110 | 111 | # Visual Studio Trace Files 112 | *.e2e 113 | 114 | # TFS 2012 Local Workspace 115 | $tf/ 116 | 117 | # Guidance Automation Toolkit 118 | *.gpState 119 | 120 | # ReSharper is a .NET coding add-in 121 | _ReSharper*/ 122 | *.[Rr]e[Ss]harper 123 | *.DotSettings.user 124 | 125 | # JustCode is a .NET coding add-in 126 | .JustCode 127 | 128 | # TeamCity is a build add-in 129 | _TeamCity* 130 | 131 | # DotCover is a Code Coverage Tool 132 | *.dotCover 133 | 134 | # AxoCover is a Code Coverage Tool 135 | .axoCover/* 136 | !.axoCover/settings.json 137 | 138 | # Visual Studio code coverage results 139 | *.coverage 140 | *.coveragexml 141 | 142 | # NCrunch 143 | _NCrunch_* 144 | .*crunch*.local.xml 145 | nCrunchTemp_* 146 | 147 | # MightyMoose 148 | *.mm.* 149 | AutoTest.Net/ 150 | 151 | # Web workbench (sass) 152 | .sass-cache/ 153 | 154 | # Installshield output folder 155 | [Ee]xpress/ 156 | 157 | # DocProject is a documentation generator add-in 158 | DocProject/buildhelp/ 159 | DocProject/Help/*.HxT 160 | DocProject/Help/*.HxC 161 | DocProject/Help/*.hhc 162 | DocProject/Help/*.hhk 163 | DocProject/Help/*.hhp 164 | DocProject/Help/Html2 165 | DocProject/Help/html 166 | 167 | # Click-Once directory 168 | publish/ 169 | 170 | # Publish Web Output 171 | *.[Pp]ublish.xml 172 | *.azurePubxml 173 | # Note: Comment the next line if you want to checkin your web deploy settings, 174 | # but database connection strings (with potential passwords) will be unencrypted 175 | *.pubxml 176 | *.publishproj 177 | 178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 179 | # checkin your Azure Web App publish settings, but sensitive information contained 180 | # in these scripts will be unencrypted 181 | PublishScripts/ 182 | 183 | # NuGet Packages 184 | *.nupkg 185 | # The packages folder can be ignored because of Package Restore 186 | **/[Pp]ackages/* 187 | # except build/, which is used as an MSBuild target. 188 | !**/[Pp]ackages/build/ 189 | # Uncomment if necessary however generally it will be regenerated when needed 190 | #!**/[Pp]ackages/repositories.config 191 | # NuGet v3's project.json files produces more ignorable files 192 | *.nuget.props 193 | *.nuget.targets 194 | 195 | # Microsoft Azure Build Output 196 | csx/ 197 | *.build.csdef 198 | 199 | # Microsoft Azure Emulator 200 | ecf/ 201 | rcf/ 202 | 203 | # Windows Store app package directories and files 204 | AppPackages/ 205 | BundleArtifacts/ 206 | Package.StoreAssociation.xml 207 | _pkginfo.txt 208 | *.appx 209 | 210 | # Visual Studio cache files 211 | # files ending in .cache can be ignored 212 | *.[Cc]ache 213 | # but keep track of directories ending in .cache 214 | !?*.[Cc]ache/ 215 | 216 | # Others 217 | ClientBin/ 218 | ~$* 219 | *~ 220 | *.dbmdl 221 | *.dbproj.schemaview 222 | *.jfm 223 | *.pfx 224 | *.publishsettings 225 | orleans.codegen.cs 226 | 227 | # Including strong name files can present a security risk 228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 229 | #*.snk 230 | 231 | # Since there are multiple workflows, uncomment next line to ignore bower_components 232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 233 | #bower_components/ 234 | 235 | # RIA/Silverlight projects 236 | Generated_Code/ 237 | 238 | # Backup & report files from converting an old project file 239 | # to a newer Visual Studio version. Backup files are not needed, 240 | # because we have git ;-) 241 | _UpgradeReport_Files/ 242 | Backup*/ 243 | UpgradeLog*.XML 244 | UpgradeLog*.htm 245 | ServiceFabricBackup/ 246 | *.rptproj.bak 247 | 248 | # SQL Server files 249 | *.mdf 250 | *.ldf 251 | *.ndf 252 | 253 | # Business Intelligence projects 254 | *.rdl.data 255 | *.bim.layout 256 | *.bim_*.settings 257 | *.rptproj.rsuser 258 | *- Backup*.rdl 259 | 260 | # Microsoft Fakes 261 | FakesAssemblies/ 262 | 263 | # GhostDoc plugin setting file 264 | *.GhostDoc.xml 265 | 266 | # Node.js Tools for Visual Studio 267 | .ntvs_analysis.dat 268 | node_modules/ 269 | 270 | # Visual Studio 6 build log 271 | *.plg 272 | 273 | # Visual Studio 6 workspace options file 274 | *.opt 275 | 276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 277 | *.vbw 278 | 279 | # Visual Studio LightSwitch build output 280 | **/*.HTMLClient/GeneratedArtifacts 281 | **/*.DesktopClient/GeneratedArtifacts 282 | **/*.DesktopClient/ModelManifest.xml 283 | **/*.Server/GeneratedArtifacts 284 | **/*.Server/ModelManifest.xml 285 | _Pvt_Extensions 286 | 287 | # Paket dependency manager 288 | .paket/paket.exe 289 | paket-files/ 290 | 291 | # FAKE - F# Make 292 | .fake/ 293 | 294 | # JetBrains Rider 295 | .idea/ 296 | *.sln.iml 297 | 298 | # CodeRush personal settings 299 | .cr/personal 300 | 301 | # Python Tools for Visual Studio (PTVS) 302 | __pycache__/ 303 | *.pyc 304 | 305 | # Cake - Uncomment if you are using it 306 | # tools/** 307 | # !tools/packages.config 308 | 309 | # Tabs Studio 310 | *.tss 311 | 312 | # Telerik's JustMock configuration file 313 | *.jmconfig 314 | 315 | # BizTalk build output 316 | *.btp.cs 317 | *.btm.cs 318 | *.odx.cs 319 | *.xsd.cs 320 | 321 | # OpenCover UI analysis results 322 | OpenCover/ 323 | 324 | # Azure Stream Analytics local run output 325 | ASALocalRun/ 326 | 327 | # MSBuild Binary and Structured Log 328 | *.binlog 329 | 330 | # NVidia Nsight GPU debugger configuration file 331 | *.nvuser 332 | 333 | # MFractors (Xamarin productivity tool) working folder 334 | .mfractor/ 335 | 336 | # Local History for Visual Studio 337 | .localhistory/ 338 | 339 | # BeatPulse healthcheck temp database 340 | healthchecksdb -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /General/picoCTF/static.md: -------------------------------------------------------------------------------- 1 | # Static ain't always noise 2 | This challenge was pretty simple, the players were given a bash script, which was hinting at the use of objdump to display defined text. 3 | By just running strings on the file, the flag was there:D 4 | 5 | 6 | `strings static` 7 | in terminal 8 | 9 | ``` 10 | ]A\A]A^A_ 11 | Oh hai! Wait what? A flag? Yes, it's around here somewhere! 12 | ;*3$" 13 | picoCTF{d15a5m_t34s3r_98d35619} 14 | GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 15 | crtstuff.c 16 | ``` 17 | 18 | ![](strings_static.png) 19 | -------------------------------------------------------------------------------- /General/picoCTF/strings_static.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/General/picoCTF/strings_static.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CTF Challenge 2 | 3 | 4 | 5 | 6 | # 🅿️🅿️🅿️ordle 7 | 8 | ### Requirements 9 | 10 | * Go 1.18: https://go.dev/doc/install 11 | 12 | ## Usage 13 | 14 | Navigate to the `client/` directory and run: 15 | ```bash 16 | go run . 17 | ``` 18 | 19 | Can you beat all 4 levels?! 20 | 21 | ### Troubleshooting 22 | 23 | * 🅿️🅿️🅿️ordle is best enjoyed on a screen with plenty of real estate, please consider using a monitor from one of our preferred partners (or try zooming in/out) 24 | * Colors seem off? To make sure you're getting the most out of our state-of-the-art color system, enable True Color™️ in your terminal (e.g., `export TERM=screen-256color`) 25 | 26 | ### Development Environment 27 | 28 | Set the `PPPORDLE_ENV` variable to `dev`: 29 | ```bash 30 | export PPPORDLE_ENV=dev 31 | ``` 32 | or inline: 33 | ```bash 34 | PPPORDLE_ENV=dev go run . 35 | ``` 36 | 37 | The server and client can be run locally from the `server/` and `client/` directories respectively with: 38 | 39 | ```bash 40 | go run . 41 | ``` 42 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) of a security vulnerability, please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /certs.go: -------------------------------------------------------------------------------- 1 | package cert 2 | 3 | import ( 4 | "crypto/ed25519" 5 | "crypto/rand" 6 | "crypto/x509" 7 | "crypto/x509/pkix" 8 | "encoding/pem" 9 | "errors" 10 | "math/big" 11 | "time" 12 | ) 13 | 14 | type PemCertPair struct { 15 | Cert []byte 16 | Key []byte 17 | } 18 | 19 | type CertConfig struct { 20 | Parent *PemCertPair 21 | IsServer bool 22 | IsClient bool 23 | Serial *big.Int 24 | CommonName string 25 | DNSNames []string 26 | SecsValid uint 27 | } 28 | 29 | // Reference: https://shaneutt.com/blog/golang-ca-and-signed-cert-go/ 30 | func MakeCerts(config CertConfig) (*PemCertPair, error) { 31 | var extKeyUsage []x509.ExtKeyUsage 32 | var keyUsage x509.KeyUsage 33 | var ok bool 34 | 35 | if config.IsClient { 36 | extKeyUsage = append(extKeyUsage, x509.ExtKeyUsageClientAuth) 37 | keyUsage |= x509.KeyUsageDigitalSignature 38 | } 39 | 40 | if config.IsServer { 41 | extKeyUsage = append(extKeyUsage, x509.ExtKeyUsageServerAuth) 42 | keyUsage |= x509.KeyUsageDigitalSignature 43 | } 44 | 45 | if config.Parent == nil { 46 | extKeyUsage = append(extKeyUsage, x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth) 47 | keyUsage |= x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature | x509.KeyUsageCRLSign 48 | } 49 | 50 | cert := &x509.Certificate{ 51 | SerialNumber: config.Serial, 52 | Subject: pkix.Name{ 53 | Country: []string{"🇺🇸"}, 54 | Province: []string{"Pennsylvania"}, 55 | Locality: []string{"Pittsburgh"}, 56 | Organization: []string{"PlaidCTF"}, 57 | CommonName: config.CommonName, 58 | }, 59 | DNSNames: config.DNSNames, 60 | NotBefore: time.Now(), 61 | NotAfter: time.Now().Add(time.Second * time.Duration(config.SecsValid)), 62 | IsCA: config.Parent == nil, 63 | KeyUsage: keyUsage, 64 | ExtKeyUsage: extKeyUsage, 65 | BasicConstraintsValid: true, 66 | } 67 | 68 | pubKey, privKey, err := ed25519.GenerateKey(rand.Reader) 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | parent := cert 74 | parentKey := privKey 75 | if config.Parent != nil { 76 | block, _ := pem.Decode(config.Parent.Cert) 77 | parent, err = x509.ParseCertificate(block.Bytes) 78 | if err != nil { 79 | return nil, err 80 | } 81 | 82 | block, _ = pem.Decode(config.Parent.Key) 83 | parsedKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) 84 | if err != nil { 85 | return nil, err 86 | } 87 | parentKey, ok = parsedKey.(ed25519.PrivateKey) 88 | if !ok { 89 | return nil, errors.New("key of signer is incorrect type") 90 | } 91 | } 92 | rawCert, err := x509.CreateCertificate(rand.Reader, cert, parent, pubKey, parentKey) 93 | if err != nil { 94 | return nil, err 95 | } 96 | 97 | pemCert := pem.EncodeToMemory(&pem.Block{ 98 | Type: "CERTIFICATE", 99 | Bytes: rawCert, 100 | }) 101 | 102 | rawPrivKey, err := x509.MarshalPKCS8PrivateKey(privKey) 103 | if err != nil { 104 | return nil, err 105 | } 106 | 107 | pemKey := pem.EncodeToMemory(&pem.Block{ 108 | Type: "PRIVATE KEY", 109 | Bytes: rawPrivKey, 110 | }) 111 | 112 | return &PemCertPair{ 113 | Cert: pemCert, 114 | Key: pemKey, 115 | }, nil 116 | } 117 | -------------------------------------------------------------------------------- /check.go: -------------------------------------------------------------------------------- 1 | package check 2 | 3 | import "log" 4 | 5 | func Fatal(message string, err error) { 6 | if err != nil { 7 | log.Fatalf("%s: %v", message, err) 8 | } 9 | } 10 | 11 | func Print(message string, err error) { 12 | if err != nil { 13 | log.Printf("%s: %v", message, err) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/tls" 5 | "crypto/x509" 6 | "encoding/json" 7 | "time" 8 | "errors" 9 | "fmt" 10 | "io" 11 | "log" 12 | "net" 13 | "os" 14 | 15 | "github.com/rivo/tview" 16 | 17 | "pppordle/game" 18 | ) 19 | 20 | const ( 21 | sessionPort = 1337 22 | ) 23 | 24 | var ( 25 | domain = "pppordle.chal.pwni.ng" 26 | dev = os.Getenv("PPPORDLE_ENV") 27 | ) 28 | 29 | func main() { 30 | log.SetOutput(io.Discard) 31 | 32 | if dev == "dev" { 33 | domain = "localhost" 34 | } 35 | 36 | startUI() 37 | } 38 | 39 | func startSession(level int, loadingText *tview.TextView) (net.Conn, error) { 40 | sessionServer := net.JoinHostPort(domain, fmt.Sprint(sessionPort)) 41 | levelServer := net.JoinHostPort(domain, fmt.Sprint(sessionPort+level)) 42 | 43 | certName := "certs/ca.pem" 44 | if dev == "dev" { 45 | certName = "certs/dev_ca.pem" 46 | } 47 | 48 | ca, err := os.ReadFile(certName) 49 | if err != nil { 50 | return nil, fmt.Errorf("failed to open CA cert file: %w", err) 51 | } 52 | caCertPool := x509.NewCertPool() 53 | ok := caCertPool.AppendCertsFromPEM(ca) 54 | if !ok { 55 | return nil, errors.New("Failed to add ca cert to pool") 56 | } 57 | 58 | tlsConfig := &tls.Config{ 59 | RootCAs: caCertPool, 60 | } 61 | 62 | conn, err := tls.Dial("tcp", sessionServer, tlsConfig) 63 | if err != nil { 64 | return nil, fmt.Errorf("failed to connect to session server: %w", err) 65 | } 66 | 67 | initResult, err := makeRequest[*game.InitResult](conn, game.Request{Type: game.RequestInit}) 68 | if err != nil { 69 | return nil, fmt.Errorf("failed to get session init result: %w", err) 70 | } 71 | log.Printf("received init result: %+v", initResult) 72 | 73 | sessionID := initResult.SessionID 74 | tlsConfig.ServerName = fmt.Sprintf("%s.session", sessionID.String()) 75 | 76 | if level > 1 { 77 | clientPem, err := os.ReadFile(fmt.Sprintf("certs/level%d.pem", level)) 78 | if err != nil { 79 | return nil, fmt.Errorf("unable to read client cert for this level: %w", err) 80 | } 81 | clientKey, err := os.ReadFile(fmt.Sprintf("certs/level%d.key", level)) 82 | if err != nil { 83 | return nil, fmt.Errorf("unable to read client key for this level: %w", err) 84 | } 85 | 86 | clientCert, err := tls.X509KeyPair(clientPem, clientKey) 87 | if err != nil { 88 | return nil, fmt.Errorf("unable to load client certificate: %w", err) 89 | } 90 | tlsConfig.Certificates = []tls.Certificate{clientCert} 91 | } 92 | 93 | authConn, err := tls.Dial("tcp", levelServer, tlsConfig) 94 | if err != nil { 95 | conn.Close() 96 | return nil, fmt.Errorf("failed to connnect to level %d server: %w", level, err) 97 | } 98 | 99 | defer authConn.Close() 100 | 101 | _, err = io.Copy(loadingText, authConn) 102 | if err != nil { 103 | conn.Close() 104 | return nil, fmt.Errorf("failed to read authentication response: %w", err) 105 | } 106 | 107 | time.Sleep(time.Second) 108 | 109 | return conn, nil 110 | } 111 | 112 | func makeRequest[R game.Result](conn net.Conn, req game.Request) (res R, err error) { 113 | e := json.NewEncoder(conn) 114 | d := json.NewDecoder(conn) 115 | 116 | if req.Type == game.RequestGuess { 117 | err = e.Encode(req) 118 | if err != nil { 119 | return nil, err 120 | } 121 | } 122 | 123 | err = d.Decode(&res) 124 | if err != nil { 125 | return nil, err 126 | } 127 | 128 | return res, nil 129 | } 130 | -------------------------------------------------------------------------------- /code/deploy/environment.yml: -------------------------------------------------------------------------------- 1 | # Conda environment specification. Details about the Conda environment file format: 2 | # https://conda.io/docs/user-guide/tasks/manage-environments.html#create-env-file-manually 3 | 4 | name: deployment_env 5 | dependencies: 6 | - python=3.8.2 7 | - scikit-learn=0.22.2 8 | - numpy=1.18.1 9 | - pip: 10 | - azureml-defaults==1.1.5 11 | - azureml-monitoring==0.1.0a18 12 | - inference-schema==1.0.2 13 | - inference-schema[numpy-support]==1.0.2 14 | channels: 15 | - conda-forge 16 | -------------------------------------------------------------------------------- /code/deploy/score.py: -------------------------------------------------------------------------------- 1 | import os 2 | import joblib 3 | import numpy as np 4 | 5 | from sklearn.svm import SVC 6 | from azureml.core import Model 7 | from azureml.monitoring import ModelDataCollector 8 | from inference_schema.schema_decorators import input_schema, output_schema 9 | from inference_schema.parameter_types.numpy_parameter_type import NumpyParameterType 10 | from inference_schema.parameter_types.standard_py_parameter_type import StandardPythonParameterType 11 | 12 | 13 | # The init() method is called once, when the web service starts up. 14 | # Typically you would deserialize the model file, as shown here using joblib, 15 | # and store it in a global variable so your run() method can access it later. 16 | def init(): 17 | global model 18 | global inputs_dc, prediction_dc 19 | # The AZUREML_MODEL_DIR environment variable indicates 20 | # a directory containing the model file you registered. 21 | model_path = Model.get_model_path(model_name="mymodel") 22 | model = joblib.load(model_path) 23 | inputs_dc = ModelDataCollector("sample-model", designation="inputs", feature_names=["feat1", "feat2", "feat3", "feat4"]) 24 | prediction_dc = ModelDataCollector("sample-model", designation="predictions", feature_names=["prediction"]) 25 | 26 | 27 | # The run() method is called each time a request is made to the scoring API. 28 | # Shown here are the optional input_schema and output_schema decorators 29 | # from the inference-schema pip package. Using these decorators on your 30 | # run() method parses and validates the incoming payload against 31 | # the example input you provide here. This will also generate a Swagger 32 | # API document for your web service. 33 | @input_schema('data', NumpyParameterType(np.array([[0.1, 1.2, 2.3, 3.4]]))) 34 | @output_schema(StandardPythonParameterType({'predict': [['Iris-virginica']]})) 35 | def run(data): 36 | # Use the model object loaded by init(). 37 | result = model.predict(data) 38 | inputs_dc.collect(data) #this call is saving our input data into Azure Blob 39 | prediction_dc.collect(result) #this call is saving our input data into Azure Blob 40 | 41 | # You can return any JSON-serializable object. 42 | return { "predict": result.tolist() } 43 | -------------------------------------------------------------------------------- /code/test/test.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from azureml.core import Webservice 4 | 5 | 6 | def main(service): 7 | # Creating input data 8 | print("Creating input data") 9 | data = {"data": [[ 1,2,3,4 ], [ 10,9,8,7 ]]} 10 | input_data = json.dumps(data) 11 | 12 | # Calling webservice 13 | print("Calling webservice") 14 | output_data = service.run(input_data) 15 | predictions = output_data.get("predict") 16 | assert type(predictions) == list 17 | 18 | 19 | if __name__ == "__main__": 20 | main() 21 | -------------------------------------------------------------------------------- /code/train/environment.yaml: -------------------------------------------------------------------------------- 1 | # Conda environment specification. Details about the Conda environment file format: 2 | # https://conda.io/docs/user-guide/tasks/manage-environments.html#create-env-file-manually 3 | 4 | name: training_env 5 | dependencies: 6 | - python=3.6.2 7 | - scikit-learn=0.22.2 8 | - pandas=1.0.3 9 | - matplotlib=3.2.1 10 | - pip: 11 | - azureml-defaults==1.1.5 12 | - azureml-dataprep[pandas,fuse]==1.4.0 13 | channels: 14 | - conda-forge -------------------------------------------------------------------------------- /code/train/run_config.py: -------------------------------------------------------------------------------- 1 | from azureml.core import ComputeTarget 2 | from azureml.train.estimator import Estimator 3 | 4 | 5 | def main(workspace): 6 | # Load compute target 7 | print("Loading compute target") 8 | compute_target = ComputeTarget( 9 | workspace=workspace, 10 | name="githubcluster" 11 | ) 12 | 13 | # Load script parameters 14 | print("Loading script parameters") 15 | script_params = { 16 | "--kernel": "linear", 17 | "--penalty": 1.0 18 | } 19 | 20 | # Create experiment config 21 | print("Creating experiment config") 22 | estimator = Estimator( 23 | source_directory="code/train", 24 | entry_script="train.py", 25 | script_params=script_params, 26 | compute_target=compute_target, 27 | pip_packages=["azureml-dataprep[pandas,fuse]", "scikit-learn", "pandas", "matplotlib"] 28 | ) 29 | return estimator 30 | -------------------------------------------------------------------------------- /code/train/train.py: -------------------------------------------------------------------------------- 1 | import os 2 | import argparse 3 | import itertools 4 | import numpy as np 5 | import pandas as pd 6 | import joblib 7 | import matplotlib.pyplot as plt 8 | 9 | from sklearn import datasets 10 | from sklearn.svm import SVC 11 | from sklearn.metrics import confusion_matrix, precision_score, recall_score, f1_score 12 | from sklearn.model_selection import train_test_split 13 | 14 | from azureml.core import Dataset, Run 15 | run = Run.get_context() 16 | 17 | 18 | def log_confusion_matrix_image(cm, labels, normalize=False, log_name='confusion_matrix', title='Confusion matrix', cmap=plt.cm.Blues): 19 | ''' 20 | This function prints and plots the confusion matrix. 21 | Normalization can be applied by setting `normalize=True`. 22 | ''' 23 | if normalize: 24 | cm = cm.astype('float') / cm.sum(axis = 1)[:, np.newaxis] 25 | print('Normalized confusion matrix') 26 | else: 27 | print('Confusion matrix, without normalization') 28 | print(cm) 29 | 30 | plt.figure() 31 | plt.imshow(cm, interpolation = 'nearest', cmap = cmap) 32 | plt.title(title) 33 | plt.colorbar() 34 | tick_marks = np.arange(len(labels)) 35 | plt.xticks(tick_marks, labels, rotation = 45) 36 | plt.yticks(tick_marks, labels) 37 | 38 | fmt = '.2f' if normalize else 'd' 39 | thresh = cm.max() / 2. 40 | for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])): 41 | plt.text(j, i, format(cm[i, j], fmt), horizontalalignment = "center", color = 'white' if cm[i, j] > thresh else 'black') 42 | 43 | plt.ylabel('True label') 44 | plt.xlabel('Predicted label') 45 | plt.tight_layout() 46 | run.log_image(log_name, plot=plt) 47 | plt.savefig(os.path.join('outputs', '{0}.png'.format(log_name))) 48 | 49 | 50 | def log_confusion_matrix(cm, labels): 51 | # log confusion matrix as object 52 | cm_json = { 53 | 'schema_type': 'confusion_matrix', 54 | 'schema_version': 'v1', 55 | 'data': { 56 | 'class_labels': labels, 57 | 'matrix': cm.tolist() 58 | } 59 | } 60 | run.log_confusion_matrix('confusion_matrix', cm_json) 61 | 62 | # log confusion matrix as image 63 | log_confusion_matrix_image(cm, labels, normalize=False, log_name='confusion_matrix_unnormalized', title='Confusion matrix') 64 | 65 | # log normalized confusion matrix as image 66 | log_confusion_matrix_image(cm, labels, normalize=True, log_name='confusion_matrix_normalized', title='Normalized confusion matrix') 67 | 68 | 69 | def main(args): 70 | # create the outputs folder 71 | os.makedirs('outputs', exist_ok=True) 72 | 73 | # Log arguments 74 | run.log('Kernel type', np.str(args.kernel)) 75 | run.log('Penalty', np.float(args.penalty)) 76 | 77 | # Load iris dataset 78 | X, y = datasets.load_iris(return_X_y=True) 79 | 80 | #dividing X,y into train and test data 81 | x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=223) 82 | data = {'train': {'X': x_train, 'y': y_train}, 83 | 'test': {'X': x_test, 'y': y_test}} 84 | 85 | # train a SVM classifier 86 | svm_model = SVC(kernel=args.kernel, C=args.penalty, gamma='scale').fit(data['train']['X'], data['train']['y']) 87 | svm_predictions = svm_model.predict(data['test']['X']) 88 | 89 | # accuracy for X_test 90 | accuracy = svm_model.score(data['test']['X'], data['test']['y']) 91 | print('Accuracy of SVM classifier on test set: {:.2f}'.format(accuracy)) 92 | run.log('Accuracy', np.float(accuracy)) 93 | 94 | # precision for X_test 95 | precision = precision_score(svm_predictions, data["test"]["y"], average='weighted') 96 | print('Precision of SVM classifier on test set: {:.2f}'.format(precision)) 97 | run.log('precision', precision) 98 | 99 | # recall for X_test 100 | recall = recall_score(svm_predictions, data["test"]["y"], average='weighted') 101 | print('Recall of SVM classifier on test set: {:.2f}'.format(recall)) 102 | run.log('recall', recall) 103 | 104 | # f1-score for X_test 105 | f1 = f1_score(svm_predictions, data["test"]["y"], average='weighted') 106 | print('F1-Score of SVM classifier on test set: {:.2f}'.format(f1)) 107 | run.log('f1-score', f1) 108 | 109 | # create a confusion matrix 110 | labels = ['Iris-setosa', 'Iris-versicolor', 'Iris-virginica'] 111 | labels_numbers = [0, 1, 2] 112 | cm = confusion_matrix(y_test, svm_predictions, labels_numbers) 113 | log_confusion_matrix(cm, labels) 114 | 115 | # files saved in the "outputs" folder are automatically uploaded into run history 116 | joblib.dump(svm_model, os.path.join('outputs', args.modelname)) 117 | run.log('Model Name', np.str(args.modelname)) 118 | 119 | 120 | def parse_args(): 121 | parser = argparse.ArgumentParser() 122 | parser.add_argument('--kernel', type=str, default='rbf', help='Kernel type to be used in the algorithm') 123 | parser.add_argument('--penalty', type=float, default=1.0, help='Penalty parameter of the error term') 124 | parser.add_argument('--modelname', type=str, default='model.pkl', help='Name of the model file') 125 | args = parser.parse_args() 126 | return args 127 | 128 | 129 | if __name__ == '__main__': 130 | args = parse_args() 131 | main(args=args) 132 | -------------------------------------------------------------------------------- /crypt/choreography/choreography.409bac9b3fc5cd73a5d46b0f2bcf51f4331bdb6094c8d47b39c4d4ea32ad2028.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/crypt/choreography/choreography.409bac9b3fc5cd73a5d46b0f2bcf51f4331bdb6094c8d47b39c4d4ea32ad2028.tgz -------------------------------------------------------------------------------- /crypt/choreography/choreography.md: -------------------------------------------------------------------------------- 1 | # choreography 2 | crypto 3 | Let's dance. nc choreography.chal.pwni.ng 1337 4 | handout 5 | Designed by ubuntor 6 | -------------------------------------------------------------------------------- /crypt/choreography/cipher.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import signal 4 | import os 5 | 6 | ROUNDS = 2**22 + 2 7 | QUERIES = 500 8 | 9 | sbox = [109, 86, 136, 240, 199, 237, 30, 94, 134, 162, 49, 78, 111, 172, 214, 117, 90, 226, 171, 105, 248, 216, 48, 196, 130, 203, 179, 223, 12, 123, 228, 96, 225, 113, 168, 5, 208, 124, 146, 184, 206, 77, 72, 155, 191, 83, 142, 197, 144, 218, 255, 39, 236, 221, 251, 102, 207, 57, 15, 159, 98, 80, 145, 22, 235, 63, 125, 120, 245, 198, 10, 233, 56, 92, 99, 55, 187, 43, 25, 210, 153, 101, 44, 252, 93, 82, 182, 9, 36, 247, 129, 3, 84, 74, 128, 69, 20, 246, 141, 2, 41, 169, 59, 217, 137, 95, 189, 138, 116, 7, 180, 60, 18, 238, 73, 133, 121, 62, 87, 40, 213, 37, 33, 122, 200, 192, 118, 205, 135, 53, 58, 89, 201, 21, 193, 149, 8, 112, 81, 243, 131, 158, 188, 154, 211, 147, 164, 195, 181, 222, 178, 67, 76, 115, 150, 127, 103, 254, 1, 249, 186, 88, 177, 61, 14, 152, 106, 161, 229, 70, 160, 175, 29, 224, 66, 38, 91, 79, 185, 114, 190, 6, 110, 194, 250, 119, 0, 230, 176, 51, 104, 219, 215, 151, 75, 13, 23, 165, 11, 139, 42, 167, 52, 85, 156, 253, 163, 19, 35, 140, 107, 31, 143, 166, 32, 47, 132, 239, 234, 71, 241, 157, 170, 64, 100, 16, 97, 227, 204, 34, 4, 50, 126, 209, 174, 46, 45, 28, 232, 24, 212, 244, 220, 173, 17, 54, 231, 108, 65, 202, 27, 68, 26, 183, 148, 242] 10 | 11 | def encrypt1(k, plaintext): 12 | a,b,c,d = plaintext 13 | for i in range(ROUNDS): 14 | a ^= sbox[b ^ k[(2*i)&3]] 15 | c ^= sbox[d ^ k[(2*i+1)&3]] 16 | a,b,c,d = b,c,d,a 17 | return bytes([a,b,c,d]) 18 | 19 | def encrypt2(k, plaintext): 20 | a,b,c,d = plaintext 21 | for i in range(ROUNDS)[::-1]: 22 | b,c,d,a = a,b,c,d 23 | c ^= sbox[d ^ k[(2*i)&3]] 24 | a ^= sbox[b ^ k[(2*i+1)&3]] 25 | return bytes([a,b,c,d]) 26 | 27 | key = os.urandom(4) 28 | result = b"" 29 | 30 | def handle_queries(f): 31 | global result 32 | num_queries = 0 33 | while True: 34 | query = bytes.fromhex(input("input (hex): ")) 35 | assert len(query) % 4 == 0 36 | assert len(query) > 0 37 | for i in range(0, len(query), 4): 38 | result += f(key, query[i:i+4]) 39 | num_queries += 1 40 | if num_queries >= QUERIES: 41 | return 42 | 43 | print("ENCRYPT 1") 44 | handle_queries(encrypt1) 45 | 46 | print("ENCRYPT 2") 47 | handle_queries(encrypt2) 48 | 49 | print("result:", result.hex()) 50 | signal.alarm(30) 51 | 52 | guess = bytes.fromhex(input("key guess (hex): ")) 53 | if guess == key: 54 | print("Congrats!") 55 | with open("flag", "r") as f: 56 | print(f.read().strip()) 57 | else: 58 | print("Wrong key.") 59 | print("Expected:", key.hex()) 60 | -------------------------------------------------------------------------------- /crypt/choreography/pow.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import string 3 | import random 4 | import hashlib 5 | 6 | # proof of work 7 | prefix = ''.join(random.choice(string.ascii_letters + string.digits) for i in range(10)) 8 | print("Give me a string starting with {} of length {} so its sha256sum ends in ffffff.".format(prefix, len(prefix)+8)) 9 | l = input().strip() 10 | if len(l) != len(prefix)+8 or not l.startswith(prefix) or hashlib.sha256(l.encode('ascii')).hexdigest()[-6:] != "ffffff": 11 | print("Nope.") 12 | sys.exit(1) 13 | -------------------------------------------------------------------------------- /crypt/choreography/run.sh: -------------------------------------------------------------------------------- 1 | python3 pow.py && pypy3 cipher.py 2 | -------------------------------------------------------------------------------- /crypt/choreography/server.py: -------------------------------------------------------------------------------- 1 | from nacl.bindings.crypto_scalarmult import ( 2 | crypto_scalarmult_ed25519_noclamp, 3 | crypto_scalarmult_ed25519_base_noclamp, 4 | ) 5 | from nacl.bindings.crypto_core import ( 6 | crypto_core_ed25519_scalar_mul, 7 | crypto_core_ed25519_scalar_reduce, 8 | crypto_core_ed25519_is_valid_point, 9 | crypto_core_ed25519_NONREDUCEDSCALARBYTES, 10 | crypto_core_ed25519_BYTES 11 | ) 12 | import struct 13 | import os 14 | import ast 15 | import hashlib 16 | import random 17 | 18 | def sha512(b): 19 | return hashlib.sha512(b).digest() 20 | 21 | CONST = 4096 22 | 23 | SECRET_LEN = int(random.randint(128, 256)) 24 | SECRET = [random.randint(1, 255) for i in range(SECRET_LEN)] 25 | 26 | with open('flag', 'r') as f: 27 | FLAG = f.read() 28 | 29 | def hsh(s): 30 | h = sha512(s) 31 | assert len(h) == crypto_core_ed25519_NONREDUCEDSCALARBYTES 32 | return crypto_scalarmult_ed25519_base_noclamp(crypto_core_ed25519_scalar_reduce(h)) 33 | 34 | 35 | def generate_secret_set(r): 36 | s = set() 37 | for (i, c) in enumerate(SECRET): 38 | s.add(hsh(bytes(str(i + 25037 * r * c).strip('L').encode('utf-8')))) 39 | return s 40 | 41 | 42 | def genr(): 43 | i = 0 44 | while i == 0: 45 | i, = struct.unpack(' 0 { 93 | runesLeft = removeFromRunesLeft(runesLeft, guessRune) 94 | indicators[i] = '🟨' 95 | } 96 | } 97 | 98 | return &GuessResult{ 99 | Error: "", 100 | Indicators: indicators, 101 | Complete: count == len(g.Word), 102 | } 103 | } 104 | 105 | func removeFromRunesLeft(runesLeft []rune, guessRune rune) []rune { 106 | for i, r := range runesLeft { 107 | if guessRune == r { 108 | return append(runesLeft[:i], runesLeft[i+1:]...) 109 | } 110 | } 111 | 112 | return runesLeft 113 | } 114 | 115 | func wordContainsRune(word []rune, guess rune) int { 116 | count := 0 117 | for _, r := range word { 118 | if guess == r { 119 | count++ 120 | } 121 | } 122 | 123 | return count 124 | } 125 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module pppordle 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/google/uuid v1.3.0 7 | github.com/jroimartin/gocui v0.5.0 8 | ) 9 | 10 | require ( 11 | github.com/gdamore/encoding v1.0.0 // indirect 12 | github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1 // indirect 13 | github.com/lucasb-eyer/go-colorful v1.2.0 // indirect 14 | github.com/mattn/go-runewidth v0.0.13 // indirect 15 | github.com/nsf/termbox-go v1.1.1 // indirect 16 | github.com/rivo/tview v0.0.0-20220307222120-9994674d60a8 // indirect 17 | github.com/rivo/uniseg v0.2.0 // indirect 18 | golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 // indirect 19 | golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect 20 | golang.org/x/text v0.3.6 // indirect 21 | ) 22 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= 2 | github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= 3 | github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1 h1:QqwPZCwh/k1uYqq6uXSb9TRDhTkfQbO80v8zhnIe5zM= 4 | github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1/go.mod h1:Az6Jt+M5idSED2YPGtwnfJV0kXohgdCBPmHGSYc1r04= 5 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 6 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 7 | github.com/jroimartin/gocui v0.5.0 h1:DCZc97zY9dMnHXJSJLLmx9VqiEnAj0yh0eTNpuEtG/4= 8 | github.com/jroimartin/gocui v0.5.0/go.mod h1:l7Hz8DoYoL6NoYnlnaX6XCNR62G7J5FfSW5jEogzaxE= 9 | github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= 10 | github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= 11 | github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= 12 | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 13 | github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= 14 | github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 15 | github.com/nsf/termbox-go v1.1.1 h1:nksUPLCb73Q++DwbYUBEglYBRPZyoXJdrj5L+TkjyZY= 16 | github.com/nsf/termbox-go v1.1.1/go.mod h1:T0cTdVuOwf7pHQNtfhnEbzHbcNyCEcVU4YPpouCbVxo= 17 | github.com/rivo/tview v0.0.0-20220307222120-9994674d60a8 h1:xe+mmCnDN82KhC010l3NfYlA8ZbOuzbXAzSYBa6wbMc= 18 | github.com/rivo/tview v0.0.0-20220307222120-9994674d60a8/go.mod h1:WIfMkQNY+oq/mWwtsjOYHIZBuwthioY2srOmljJkTnk= 19 | github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= 20 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 21 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 22 | golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY= 23 | golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 24 | golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 25 | golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= 26 | golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 27 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 28 | golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= 29 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 30 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 31 | -------------------------------------------------------------------------------- /level_server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/tls" 5 | "errors" 6 | "fmt" 7 | "net" 8 | "sync" 9 | "time" 10 | 11 | "pppordle/check" 12 | "pppordle/server/level" 13 | 14 | "github.com/google/uuid" 15 | ) 16 | 17 | var ( 18 | Requests = make(map[Conn]*uuid.UUID) 19 | RequestMutex sync.Mutex 20 | ) 21 | 22 | type LevelServer struct { 23 | Port int 24 | Config *tls.Config 25 | Level *level.Level 26 | Entrypoint bool 27 | } 28 | 29 | func (ls *LevelServer) Host() { 30 | listener, err := tls.Listen("tcp", fmt.Sprintf(":%d", ls.Port), ls.Config) 31 | check.Fatal(fmt.Sprintf("level %d listener failed", ls.Level.Number), err) 32 | 33 | for { 34 | conn, err := listener.Accept() 35 | if err != nil { 36 | check.Print("error accepting connection", err) 37 | continue 38 | } 39 | 40 | go ls.HandleAuthenticatedRequest(conn) 41 | } 42 | } 43 | 44 | func (ls *LevelServer) HandleAuthenticatedRequest(conn net.Conn) { 45 | sessionConn := Conn{ 46 | LocalAddr: conn.LocalAddr(), 47 | RemoteAddr: conn.RemoteAddr(), 48 | } 49 | 50 | defer func() { 51 | RequestMutex.Lock() 52 | delete(Requests, sessionConn) 53 | RequestMutex.Unlock() 54 | conn.Close() 55 | }() 56 | 57 | connErr := make(chan error, 1) 58 | sessionErr := make(chan error, 1) 59 | 60 | var wg sync.WaitGroup 61 | 62 | wg.Add(1) 63 | 64 | go func() { 65 | ls.sessionSearch(sessionConn, connErr, sessionErr) 66 | wg.Done() 67 | }() 68 | feedbackWriter(conn, connErr, sessionErr) 69 | 70 | wg.Wait() 71 | return 72 | } 73 | 74 | func feedbackWriter(conn net.Conn, connErr chan error, sessionErr chan error) { 75 | _, err := conn.Write([]byte("Searching for session...")) 76 | if err != nil { 77 | connErr <- err 78 | return 79 | } 80 | 81 | progressTimer := time.NewTicker(200 * time.Millisecond) 82 | 83 | sessionSearchLoop: 84 | for { 85 | select { 86 | case err = <-sessionErr: 87 | break sessionSearchLoop 88 | case <-progressTimer.C: 89 | _, err := conn.Write([]byte(".")) 90 | if err != nil { 91 | connErr <- err 92 | return 93 | } 94 | } 95 | } 96 | 97 | connErr <- nil 98 | 99 | if err != nil { 100 | conn.Write([]byte("\nError finding session:\n" + err.Error())) 101 | } else { 102 | conn.Write([]byte("\nSession found\n")) 103 | } 104 | } 105 | 106 | func (ls *LevelServer) sessionSearch(sessionConn Conn, connErr chan error, sessionErr chan error) { 107 | var err error 108 | bruteForcePrevention(1000) 109 | 110 | RequestMutex.Lock() 111 | sessionID, ok := Requests[sessionConn] 112 | RequestMutex.Unlock() 113 | if !ok { 114 | sessionErr <- errors.New("No session provided") 115 | return 116 | } 117 | 118 | session, ok := Sessions[*sessionID] 119 | if !ok { 120 | sessionErr <- errors.New("Could not find session") 121 | return 122 | } 123 | 124 | sessionErr <- nil 125 | select { 126 | case err = <-connErr: 127 | default: 128 | } 129 | if err != nil { 130 | return 131 | } 132 | 133 | session.GameChan <- ls.Level.GenerateGame() 134 | } 135 | 136 | func bruteForcePrevention(ms time.Duration) { 137 | time.Sleep(ms * time.Millisecond) 138 | } 139 | -------------------------------------------------------------------------------- /misc/README: -------------------------------------------------------------------------------- 1 | install Violet Aura Split VCCV.rar in OpenUtau 2 | run generate.py to generate song.ustx 3 | open song.ustx in OpenUtau and export song-01.wav 4 | 5 | (Violet Aura Split VCCV.rar from https://utau.fandom.com/wiki/Violet_Aura) 6 | -------------------------------------------------------------------------------- /misc/but I plaidiversed that already/but I plaidiversed that already.md: -------------------------------------------------------------------------------- 1 | We've enlisted you to make the plaidiverse inhabitable. The crowds will be arriving soon so we need to get all of these planets ready, otherwise people will not survive the trip. You are the only one left who can help. See the attached manual and start saving lives. 2 | 3 | -------------------------------------------------------------------------------- /misc/generate.py: -------------------------------------------------------------------------------- 1 | # made with OpenUtau v0.0.705.0 2 | 3 | from secret import FLAG, MELODY 4 | 5 | # X-SAMPA 6 | vowels = '3AIOao{}' 7 | consonants = '45DGLNSTfglprw' 8 | dummy_vowel = '@' 9 | dummy_consonant = 'h' 10 | 11 | assert(set(vowels+consonants) == set(FLAG)) 12 | assert(len(FLAG) == 29) 13 | 14 | diphones = [] 15 | 16 | # convert to CVCVCV... 17 | diphone = '' 18 | for i in FLAG: 19 | if len(diphone) == 0: 20 | if i in vowels: 21 | diphones.append(dummy_consonant + i) 22 | else: 23 | diphone = i 24 | else: 25 | if i in vowels: 26 | diphones.append(diphone + i) 27 | diphone = '' 28 | else: 29 | diphones.append(diphone + dummy_vowel) 30 | diphone = i 31 | if len(diphone) > 0: 32 | diphones.append(diphone + dummy_vowel) 33 | 34 | assert len(MELODY) == len(diphones) 35 | 36 | header = '''name: flagsong 37 | comment: '' 38 | output_dir: Vocal 39 | cache_dir: UCache 40 | ustx_version: 0.5 41 | bpm: 120 42 | beat_per_bar: 4 43 | beat_unit: 4 44 | resolution: 480 45 | expressions: 46 | dyn: 47 | name: dynamics (curve) 48 | abbr: dyn 49 | type: Curve 50 | min: -240 51 | max: 120 52 | default_value: 0 53 | is_flag: false 54 | flag: '' 55 | pitd: 56 | name: pitch deviation (curve) 57 | abbr: pitd 58 | type: Curve 59 | min: -1200 60 | max: 1200 61 | default_value: 0 62 | is_flag: false 63 | flag: '' 64 | clr: 65 | name: voice color 66 | abbr: clr 67 | type: Options 68 | min: 0 69 | max: -1 70 | default_value: 0 71 | is_flag: false 72 | options: [] 73 | eng: 74 | name: resampler engine 75 | abbr: eng 76 | type: Options 77 | min: 0 78 | max: 1 79 | default_value: 0 80 | is_flag: false 81 | options: 82 | - '' 83 | - worldline 84 | vel: 85 | name: velocity 86 | abbr: vel 87 | type: Numerical 88 | min: 0 89 | max: 200 90 | default_value: 100 91 | is_flag: false 92 | flag: '' 93 | vol: 94 | name: volume 95 | abbr: vol 96 | type: Numerical 97 | min: 0 98 | max: 200 99 | default_value: 100 100 | is_flag: false 101 | flag: '' 102 | atk: 103 | name: attack 104 | abbr: atk 105 | type: Numerical 106 | min: 0 107 | max: 200 108 | default_value: 100 109 | is_flag: false 110 | flag: '' 111 | dec: 112 | name: decay 113 | abbr: dec 114 | type: Numerical 115 | min: 0 116 | max: 100 117 | default_value: 0 118 | is_flag: false 119 | flag: '' 120 | gen: 121 | name: gender 122 | abbr: gen 123 | type: Numerical 124 | min: -100 125 | max: 100 126 | default_value: 0 127 | is_flag: true 128 | flag: g 129 | genc: 130 | name: gender (curve) 131 | abbr: genc 132 | type: Curve 133 | min: -100 134 | max: 100 135 | default_value: 0 136 | is_flag: false 137 | flag: '' 138 | bre: 139 | name: breath 140 | abbr: bre 141 | type: Numerical 142 | min: 0 143 | max: 100 144 | default_value: 0 145 | is_flag: true 146 | flag: B 147 | brec: 148 | name: breathiness (curve) 149 | abbr: brec 150 | type: Curve 151 | min: -100 152 | max: 100 153 | default_value: 0 154 | is_flag: false 155 | flag: '' 156 | lpf: 157 | name: lowpass 158 | abbr: lpf 159 | type: Numerical 160 | min: 0 161 | max: 100 162 | default_value: 0 163 | is_flag: true 164 | flag: H 165 | mod: 166 | name: modulation 167 | abbr: mod 168 | type: Numerical 169 | min: 0 170 | max: 100 171 | default_value: 0 172 | is_flag: false 173 | flag: '' 174 | alt: 175 | name: alternate 176 | abbr: alt 177 | type: Numerical 178 | min: 0 179 | max: 16 180 | default_value: 0 181 | is_flag: false 182 | flag: '' 183 | shft: 184 | name: tone shift 185 | abbr: shft 186 | type: Numerical 187 | min: -36 188 | max: 36 189 | default_value: 0 190 | is_flag: false 191 | flag: '' 192 | shfc: 193 | name: tone shift (curve) 194 | abbr: shfc 195 | type: Curve 196 | min: -1200 197 | max: 1200 198 | default_value: 0 199 | is_flag: false 200 | flag: '' 201 | tenc: 202 | name: tension (curve) 203 | abbr: tenc 204 | type: Curve 205 | min: -100 206 | max: 100 207 | default_value: 0 208 | is_flag: false 209 | flag: '' 210 | voic: 211 | name: voicing (curve) 212 | abbr: voic 213 | type: Curve 214 | min: 0 215 | max: 100 216 | default_value: 100 217 | is_flag: false 218 | flag: '' 219 | tracks: 220 | - singer: Violet Aura Split VCCV 221 | phonemizer: OpenUtau.Core.DefaultPhonemizer 222 | mute: false 223 | solo: false 224 | volume: 0 225 | voice_parts: 226 | - name: New Part 227 | comment: '' 228 | track_no: 0 229 | position: 0 230 | notes: 231 | ''' 232 | 233 | footer = ''' curves: [] 234 | wave_parts: [] 235 | ''' 236 | 237 | note = ''' - position: {} 238 | duration: {} 239 | tone: {} 240 | lyric: {} 241 | pitch: 242 | data: 243 | - {{x: -40, y: {}, shape: io}} 244 | - {{x: 40, y: 0, shape: io}} 245 | snap_first: true 246 | vibrato: {{length: 75, period: 175, depth: 25, in: 10, out: 10, shift: 0, drift: 0}} 247 | note_expressions: [] 248 | phoneme_expressions: [] 249 | phoneme_overrides: [] 250 | ''' 251 | 252 | song = '' 253 | position = 0 254 | prev_tone = MELODY[0][0] 255 | for lyric, (tone, duration) in zip(diphones, MELODY): 256 | assert duration % 240 == 0 257 | song += note.format(position, duration, tone, lyric, (prev_tone-tone)*10) 258 | position += duration 259 | prev_tone = tone 260 | 261 | with open('song.ustx','w') as f: 262 | f.write(header + song + footer) 263 | -------------------------------------------------------------------------------- /misc/misc.md: -------------------------------------------------------------------------------- 1 | flagsong 2 | misc 3 | Sing, O Muse, of the flage of Achilles... Required reading: https://en.wikipedia.org/wiki/X-SAMPA Note: the flag is NOT in `PCTF{...}` format. 4 | -------------------------------------------------------------------------------- /misc/song-01.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/misc/song-01.wav -------------------------------------------------------------------------------- /poodle/poodle.md: -------------------------------------------------------------------------------- 1 | 🅿️🅿️🅿️ordle 2 | misc 3 | Web is out, retro is in. Play your favorite word game from the comfort of your terminal! Too hard? Easy-mode, themes, and so much more coming soon as a part of the 🅿️🅿️🅿️ordle Plaidiverse Plus subscription package. Please send buyout inquiries to @plaidctf. 4 | -------------------------------------------------------------------------------- /pppordle.b2672f7de0bd2cc6ff8a68e6c978f37d8470c02248847a36e1b5cf91c25beb7e.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/pppordle.b2672f7de0bd2cc6ff8a68e6c978f37d8470c02248847a36e1b5cf91c25beb7e.tgz -------------------------------------------------------------------------------- /pwn/homium/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:focal-20220316 2 | 3 | WORKDIR /pwn 4 | RUN apt-get update && apt-get install -y \ 5 | gcc \ 6 | && rm -rf /var/lib/apt/lists/* 7 | 8 | COPY [ "holmium", "flag.txt", "docker-main", "./" ] 9 | 10 | CMD [ "./docker-main" ] 11 | -------------------------------------------------------------------------------- /pwn/homium/build-docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker build -t holmium . -------------------------------------------------------------------------------- /pwn/homium/docker-main: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | cat << 'EOF' 6 | Holmium compiler explorer. Enter your program's length followed by your program. 7 | 8 | cat <(wc -c main.hvm) main.hvm - 9 | EOF 10 | 11 | read size rest 12 | 13 | if ! [ 0 -le "$size" -a "$size" -le "1000000" ]; then 14 | echo 'Too big to run' 15 | exit 1 16 | fi 17 | 18 | echo "Reading $size bytes into main.hvm" 19 | dd bs=1 count="$size" > main.hvm 2>/dev/null 20 | 21 | ./holmium c main.hvm 22 | gcc -no-pie -pthread -o main main.c 23 | echo "Running..." 24 | ./main -------------------------------------------------------------------------------- /pwn/homium/holmium: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/pwn/homium/holmium -------------------------------------------------------------------------------- /pwn/homium/holmium.443c089776cd6bcb2e5102e84b09285ecbf133b09f18f699cd6d8e6b15be7821.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/pwn/homium/holmium.443c089776cd6bcb2e5102e84b09285ecbf133b09f18f699cd6d8e6b15be7821.tgz -------------------------------------------------------------------------------- /pwn/homium/parallelism-markdown.md: -------------------------------------------------------------------------------- 1 | # pwn 2 | 3 | Question: 4 | Do you remember potassium? This is the hyperscale successor. We're running on two cores to let you test out the parallelism. Optimal reduction makes the plaidverse go even faster than ever before. holmium.chal.pwni.ng:1337 5 | 6 | 7 | Flag: 8 | -------------------------------------------------------------------------------- /pwn/pppdddbbb/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "bf-jit" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "dynasmrt", 10 | ] 11 | 12 | [[package]] 13 | name = "bitflags" 14 | version = "1.3.2" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 17 | 18 | [[package]] 19 | name = "byteorder" 20 | version = "1.4.3" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 23 | 24 | [[package]] 25 | name = "dynasm" 26 | version = "1.2.1" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "47b1801e630bd336d0bbbdbf814de6cc749c9a400c7e3d995e6adfd455d0c83c" 29 | dependencies = [ 30 | "bitflags", 31 | "byteorder", 32 | "lazy_static", 33 | "proc-macro-error", 34 | "proc-macro2", 35 | "quote", 36 | "syn", 37 | ] 38 | 39 | [[package]] 40 | name = "dynasmrt" 41 | version = "1.2.1" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "1d428afc93ad288f6dffc1fa5f4a78201ad2eec33c5a522e51c181009eb09061" 44 | dependencies = [ 45 | "byteorder", 46 | "dynasm", 47 | "memmap2", 48 | ] 49 | 50 | [[package]] 51 | name = "lazy_static" 52 | version = "1.4.0" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 55 | 56 | [[package]] 57 | name = "libc" 58 | version = "0.2.119" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" 61 | 62 | [[package]] 63 | name = "memmap2" 64 | version = "0.5.3" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "057a3db23999c867821a7a59feb06a578fcb03685e983dff90daf9e7d24ac08f" 67 | dependencies = [ 68 | "libc", 69 | ] 70 | 71 | [[package]] 72 | name = "proc-macro-error" 73 | version = "1.0.4" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 76 | dependencies = [ 77 | "proc-macro-error-attr", 78 | "proc-macro2", 79 | "quote", 80 | "syn", 81 | "version_check", 82 | ] 83 | 84 | [[package]] 85 | name = "proc-macro-error-attr" 86 | version = "1.0.4" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 89 | dependencies = [ 90 | "proc-macro2", 91 | "quote", 92 | "version_check", 93 | ] 94 | 95 | [[package]] 96 | name = "proc-macro2" 97 | version = "1.0.36" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" 100 | dependencies = [ 101 | "unicode-xid", 102 | ] 103 | 104 | [[package]] 105 | name = "quote" 106 | version = "1.0.15" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" 109 | dependencies = [ 110 | "proc-macro2", 111 | ] 112 | 113 | [[package]] 114 | name = "syn" 115 | version = "1.0.86" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" 118 | dependencies = [ 119 | "proc-macro2", 120 | "quote", 121 | "unicode-xid", 122 | ] 123 | 124 | [[package]] 125 | name = "unicode-xid" 126 | version = "0.2.2" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 129 | 130 | [[package]] 131 | name = "version_check" 132 | version = "0.9.4" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 135 | -------------------------------------------------------------------------------- /pwn/pppdddbbb/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bf-jit" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | dynasmrt = "1.2.1" -------------------------------------------------------------------------------- /pwn/pppdddbbb/Plaidiverse.md: -------------------------------------------------------------------------------- 1 | The Plaidiverse will be stored in a highly scalable, enterprise-grade database, making use of the latest in Lean Seamless Mesh technology! Check back next year for version 2.0, which will include additional features such as NFT minting and data durability! pppdddbbb.chal.pwni.ng:1337 2 | -------------------------------------------------------------------------------- /pwn/pppdddbbb/cerebrum-boggled.tar.xz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/pwn/pppdddbbb/cerebrum-boggled.tar.xz -------------------------------------------------------------------------------- /pwn/pppdddbbb/chal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/pwn/pppdddbbb/chal -------------------------------------------------------------------------------- /pwn/pppdddbbb/main.rs: -------------------------------------------------------------------------------- 1 | use dynasmrt::{ 2 | dynasm, 3 | x64::{Assembler, X64Relocation}, 4 | DynamicLabel, DynasmApi, DynasmError, DynasmLabelApi, 5 | }; 6 | 7 | use std::fs::File; 8 | use std::io::{BufRead, Read, Write}; 9 | use std::mem; 10 | 11 | #[derive(Clone, Copy, Debug)] 12 | #[repr(i8)] 13 | enum RunResult { 14 | Success, 15 | TapeOverflow, 16 | TapeUnderflow, 17 | } 18 | 19 | fn generate_forward_labels( 20 | ops: &mut Assembler, 21 | program: &[u8], 22 | ) -> Vec<(DynamicLabel, DynamicLabel)> { 23 | program 24 | .into_iter() 25 | .copied() 26 | .filter(|&c| c == b'[') 27 | .map(|_| { 28 | let start_label = ops.new_dynamic_label(); 29 | let end_label = ops.new_dynamic_label(); 30 | (start_label, end_label) 31 | }) 32 | .collect() 33 | } 34 | 35 | fn generate_jit>( 36 | ops: &mut R, 37 | program: &[u8], 38 | tape: &mut [u8], 39 | mut forward_labels: Vec<(DynamicLabel, DynamicLabel)>, 40 | backward_labels: &mut Vec<(DynamicLabel, DynamicLabel)>, 41 | ) { 42 | dynasm!(ops 43 | ; .arch x64 44 | ; push rbx 45 | ; mov rcx, QWORD tape.as_mut_ptr() as _ 46 | ; xor rbx, rbx 47 | ); 48 | 49 | for c in program { 50 | match c { 51 | b'+' => dynasm!(ops 52 | ; .arch x64 53 | ; inc BYTE rcx => u8[rbx] 54 | ), 55 | b'-' => dynasm!(ops 56 | ; .arch x64 57 | ; dec BYTE rcx => u8[rbx] 58 | ), 59 | b'>' => dynasm!(ops 60 | ; .arch x64 61 | ; inc rbx 62 | ; mov rdx, QWORD tape.len() as _ 63 | ; cmp rbx, rdx 64 | ; jl >no_overflow 65 | ; mov al, RunResult::TapeOverflow as i8 66 | ; jmp >exit 67 | ; no_overflow: 68 | ), 69 | b'<' => dynasm!(ops 70 | ; .arch x64 71 | ; dec rbx 72 | ; cmp rbx, 0 73 | ; jge >no_overflow 74 | ; mov al, RunResult::TapeUnderflow as i8 75 | ; jmp >exit 76 | ; no_overflow: 77 | ), 78 | b',' => dynasm!(ops 79 | ; .arch x64 80 | ; push rax 81 | ; push rcx 82 | ; push rdx 83 | ; push rdi 84 | ; push rsi 85 | 86 | ; xor rax, rax 87 | ; xor rdi, rdi 88 | ; lea rsi, rcx => u8[rbx] 89 | ; mov rdx, 1 90 | ; syscall 91 | 92 | ; pop rsi 93 | ; pop rdi 94 | ; pop rdx 95 | ; pop rcx 96 | ; pop rax 97 | ), 98 | b'.' => dynasm!(ops 99 | ; .arch x64 100 | ; push rax 101 | ; push rcx 102 | ; push rdx 103 | ; push rdi 104 | ; push rsi 105 | 106 | ; mov rax, 1 107 | ; mov rdi, 1 108 | ; lea rsi, rcx => u8[rbx] 109 | ; mov rdx, 1 110 | ; syscall 111 | 112 | ; pop rsi 113 | ; pop rdi 114 | ; pop rdx 115 | ; pop rcx 116 | ; pop rax 117 | ), 118 | b'[' => { 119 | let (forward_label, backward_label) = 120 | forward_labels.pop().expect("unexpected opening bracket"); 121 | dynasm!(ops 122 | ; .arch x64 123 | ; cmp BYTE rcx => u8[rbx], 0 124 | ; je DWORD =>forward_label 125 | ; =>backward_label 126 | ); 127 | backward_labels.push((forward_label, backward_label)); 128 | } 129 | b']' => { 130 | let (forward_label, backward_label) = 131 | backward_labels.pop().expect("unmatched closing bracket"); 132 | 133 | dynasm!(ops 134 | ; .arch x64 135 | ; cmp BYTE rcx => u8[rbx], 0 136 | ; jne DWORD =>backward_label 137 | ; =>forward_label 138 | ) 139 | } 140 | _ => dynasm!(ops 141 | ; .arch x64 142 | ; nop 143 | ), 144 | }; 145 | } 146 | 147 | dynasm!(ops 148 | ; .arch x64 149 | ; mov al, RunResult::Success as i8 150 | ; exit: 151 | ; pop rbx 152 | ; ret 153 | ); 154 | } 155 | 156 | fn read_program() -> Vec { 157 | let stdin = std::io::stdin(); 158 | 159 | print!("program length: "); 160 | std::io::stdout().flush().unwrap(); 161 | 162 | let mut length_string = String::new(); 163 | stdin 164 | .read_line(&mut length_string) 165 | .expect("failed to read length string"); 166 | 167 | let length = length_string 168 | .trim() 169 | .parse::() 170 | .expect("invalid length string"); 171 | let mut result = vec![0; length]; 172 | 173 | print!("program source: "); 174 | std::io::stdout().flush().unwrap(); 175 | 176 | stdin 177 | .lock() 178 | .read_exact(&mut result) 179 | .expect("failed to read program input"); 180 | 181 | result 182 | } 183 | 184 | fn main() { 185 | let program = read_program(); 186 | 187 | let mut ops = Assembler::new().unwrap(); 188 | let start = ops.offset(); 189 | 190 | let mut tape: [u8; 0x1000] = [0; 0x1000]; 191 | 192 | let mut backward_labels = Vec::new(); 193 | 194 | let program = program.clone(); 195 | let forward_labels = generate_forward_labels(&mut ops, &program); 196 | 197 | generate_jit( 198 | &mut ops, 199 | &program, 200 | &mut tape, 201 | forward_labels, 202 | &mut backward_labels, 203 | ); 204 | 205 | let mut result = ops.commit(); 206 | 207 | while result.is_err() { 208 | println!("error assembling: {:?}", result); 209 | println!("please provide another (shorter or equally long) program"); 210 | let program = read_program(); 211 | 212 | let forward_labels = generate_forward_labels(&mut ops, &program); 213 | 214 | result = ops.alter(|modifier| { 215 | generate_jit( 216 | modifier, 217 | &program, 218 | &mut tape, 219 | forward_labels, 220 | &mut backward_labels, 221 | ); 222 | }) 223 | } 224 | 225 | { 226 | let reader = ops.reader(); 227 | let buf = reader.lock(); 228 | let f: extern "sysv64" fn() -> RunResult = unsafe { mem::transmute(buf.ptr(start)) }; 229 | 230 | let result = f(); 231 | 232 | println!("{:?}", result); 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /pwn/tinebpf/tinebpf.md: -------------------------------------------------------------------------------- 1 | 2 | pwn 3 | Take a byte out of every pretty fun snack available here. We made these to help us improve our scrutiny of the messages flying around the Plaidiverse. tinebpf.chal.pwni.ng 1337 4 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/tls" 5 | "crypto/x509" 6 | "errors" 7 | "fmt" 8 | "log" 9 | "math/big" 10 | "os" 11 | "strings" 12 | "sync" 13 | 14 | "github.com/google/uuid" 15 | 16 | "pppordle/cert" 17 | "pppordle/check" 18 | "pppordle/server/level" 19 | ) 20 | 21 | const ( 22 | sessionPort = 1337 23 | ) 24 | 25 | var ( 26 | domain = "pppordle.chal.pwni.ng" 27 | dev = os.Getenv("PPPORDLE_ENV") 28 | pemCA *cert.PemCertPair 29 | ) 30 | 31 | func main() { 32 | f, err := os.OpenFile("pppordle.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 33 | check.Fatal("could not open log file", err) 34 | defer f.Close() 35 | log.SetOutput(f) 36 | 37 | certName := "ca" 38 | if dev == "dev" { 39 | log.Println("starting server in development mode") 40 | domain = "localhost" 41 | certName = "dev_ca" 42 | } 43 | caCert, err := os.ReadFile(fmt.Sprintf("certs/%s.pem", certName)) 44 | check.Fatal("unable to read ca cert", err) 45 | caKey, err := os.ReadFile(fmt.Sprintf("certs/%s.key", certName)) 46 | check.Fatal("unable to read ca key", err) 47 | pemCA = &cert.PemCertPair{ 48 | Cert: caCert, 49 | Key: caKey, 50 | } 51 | 52 | pemServer, err := cert.MakeCerts(cert.CertConfig{ 53 | Parent: pemCA, 54 | IsServer: true, 55 | IsClient: false, 56 | Serial: big.NewInt(1), 57 | CommonName: domain, 58 | DNSNames: []string{domain, "*.session"}, 59 | SecsValid: 60 * 60 * 24 * 365, 60 | }) 61 | check.Fatal("unable to generate server certificate pair", err) 62 | 63 | caCertPool := x509.NewCertPool() 64 | ok := caCertPool.AppendCertsFromPEM(pemCA.Cert) 65 | if !ok { 66 | log.Fatalf("failed to add ca cert to pool") 67 | } 68 | 69 | var levels []LevelServer 70 | 71 | levels = append(levels, LevelServer{ 72 | Level: level.Level1(), 73 | Entrypoint: true, 74 | }) 75 | 76 | levels = append(levels, LevelServer{ 77 | Level: level.Level2(), 78 | }) 79 | 80 | levels = append(levels, LevelServer{ 81 | Level: level.Level3(), 82 | }) 83 | 84 | levels = append(levels, LevelServer{ 85 | Level: level.Level4(), 86 | }) 87 | 88 | serverCert, err := tls.X509KeyPair(pemServer.Cert, pemServer.Key) 89 | check.Fatal("unable to load server certificate pair", err) 90 | 91 | var wg sync.WaitGroup 92 | wg.Add(len(levels)) 93 | for i, l := range levels { 94 | l.Port = sessionPort + i + 1 95 | l.Config = &tls.Config{ 96 | Certificates: []tls.Certificate{serverCert}, 97 | MinVersion: tls.VersionTLS13, 98 | GetConfigForClient: getSessionFromHello, 99 | } 100 | 101 | if !l.Entrypoint { 102 | l.Config.ClientAuth = tls.RequireAndVerifyClientCert 103 | l.Config.ClientCAs = caCertPool 104 | l.Config.VerifyPeerCertificate = getLevelValidator(caCertPool, l.Level.Number) 105 | } 106 | 107 | fmt.Printf("Starting level %d listener\n", l.Level.Number) 108 | go func(levelServer LevelServer) { 109 | levelServer.Host() 110 | wg.Done() 111 | }(l) 112 | } 113 | 114 | fmt.Println("Starting session listener") 115 | handleSessions(sessionPort, &tls.Config{ 116 | Certificates: []tls.Certificate{serverCert}, 117 | MinVersion: tls.VersionTLS13, 118 | }, len(levels)) 119 | 120 | wg.Wait() 121 | } 122 | 123 | func getLevelValidator(caCertPool *x509.CertPool, levelNumber int) func([][]byte, [][]*x509.Certificate) error { 124 | return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { 125 | if len(verifiedChains) == 0 || len(verifiedChains[0]) == 0 { 126 | return errors.New("No verified chains") 127 | } 128 | 129 | opts := x509.VerifyOptions{ 130 | DNSName: fmt.Sprint(levelNumber), 131 | Roots: caCertPool, 132 | KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, 133 | } 134 | _, err := verifiedChains[0][0].Verify(opts) 135 | return err 136 | } 137 | } 138 | 139 | func getSessionFromHello(hello *tls.ClientHelloInfo) (*tls.Config, error) { 140 | sessionID, err := parseSessionID(hello.ServerName) 141 | if err != nil { 142 | return nil, nil 143 | } 144 | 145 | sessionConn := Conn{ 146 | LocalAddr: hello.Conn.LocalAddr(), 147 | RemoteAddr: hello.Conn.RemoteAddr(), 148 | } 149 | 150 | RequestMutex.Lock() 151 | Requests[sessionConn] = sessionID 152 | RequestMutex.Unlock() 153 | 154 | return nil, nil 155 | } 156 | 157 | func parseSessionID(domain string) (*uuid.UUID, error) { 158 | splitString := strings.Split(domain, ".") 159 | 160 | if len(splitString) != 2 { 161 | return nil, fmt.Errorf("session parsing failed: %w", errors.New("improperly formatted server name")) 162 | } 163 | 164 | sessionID, err := uuid.Parse(splitString[0]) 165 | if err != nil { 166 | return nil, fmt.Errorf("session parsing failed: %w", err) 167 | } 168 | 169 | return &sessionID, nil 170 | } 171 | -------------------------------------------------------------------------------- /session_server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/rand" 5 | "crypto/tls" 6 | "encoding/json" 7 | "fmt" 8 | "log" 9 | "math/big" 10 | "net" 11 | "pppordle/cert" 12 | "pppordle/check" 13 | "pppordle/game" 14 | "sync" 15 | "time" 16 | 17 | "github.com/google/uuid" 18 | ) 19 | 20 | type Session struct { 21 | Conn Conn 22 | GameChan chan *game.Game 23 | } 24 | 25 | type Conn struct { 26 | LocalAddr net.Addr 27 | RemoteAddr net.Addr 28 | } 29 | 30 | var ( 31 | Sessions = make(map[uuid.UUID]Session) 32 | SessionMutex sync.Mutex 33 | ) 34 | 35 | var ( 36 | timeout = 1 * time.Minute 37 | authTimeout = 3 * time.Second 38 | ) 39 | 40 | func handleSessions(port int, tlsConfig *tls.Config, levelCount int) { 41 | listener, err := tls.Listen("tcp", fmt.Sprintf(":%d", port), tlsConfig) 42 | check.Fatal("session listener failed", err) 43 | 44 | for { 45 | conn, err := listener.Accept() 46 | if err != nil { 47 | log.Println("Error accepting connection:", err) 48 | continue 49 | } 50 | 51 | sessionId := uuid.New() 52 | session := Session{ 53 | Conn: Conn{ 54 | LocalAddr: conn.LocalAddr(), 55 | RemoteAddr: conn.RemoteAddr(), 56 | }, 57 | GameChan: make(chan *game.Game), 58 | } 59 | SessionMutex.Lock() 60 | Sessions[sessionId] = session 61 | SessionMutex.Unlock() 62 | log.Printf("new session from %v: %v", conn.RemoteAddr(), sessionId) 63 | 64 | go func() { 65 | handleSession(conn, session, sessionId, levelCount) 66 | SessionMutex.Lock() 67 | delete(Sessions, sessionId) 68 | SessionMutex.Unlock() 69 | }() 70 | } 71 | } 72 | 73 | func handleSession(conn net.Conn, session Session, id uuid.UUID, levelCount int) { 74 | var req game.Request 75 | var g *game.Game 76 | 77 | defer conn.Close() 78 | 79 | decoder := json.NewDecoder(conn) 80 | encoder := json.NewEncoder(conn) 81 | 82 | err := conn.SetDeadline(time.Now().Add(timeout)) 83 | if err != nil { 84 | log.Println("Failed to set deadline:", err) 85 | return 86 | } 87 | 88 | initMessage := &game.InitResult{ 89 | SessionID: id, 90 | LevelCount: levelCount, 91 | } 92 | err = encoder.Encode(initMessage) 93 | if err != nil { 94 | log.Println("Error sending result of game information request:", err) 95 | return 96 | } 97 | 98 | authTimer := time.NewTimer(authTimeout) 99 | select { 100 | case g = <-session.GameChan: 101 | log.Printf("session %v: level %d authentication successful", id, g.Level) 102 | log.Println(string(g.Word)) 103 | case <-authTimer.C: 104 | log.Printf("session %v: authentication timeout", id) 105 | return 106 | } 107 | 108 | guesses := g.Guesses 109 | 110 | infoMessage := &game.InfoResult{ 111 | Length: len(g.Word), 112 | Level: g.Level, 113 | Guesses: guesses, 114 | Candidates: g.Candidates, 115 | } 116 | err = encoder.Encode(infoMessage) 117 | if err != nil { 118 | log.Println("Error sending result of game information request:", err) 119 | return 120 | } 121 | 122 | for { 123 | err := decoder.Decode(&req) 124 | if err != nil { 125 | return 126 | } 127 | 128 | if req.Type != game.RequestGuess { 129 | return 130 | } 131 | 132 | result := g.ProcessGuess([]rune(req.Data)) 133 | if len(result.Indicators) > 0 { 134 | guesses -= 1 135 | } 136 | 137 | if result.Complete && g.Level < levelCount { 138 | completionCert, err := generateCompletionCert(g.Level + 1) 139 | if err != nil { 140 | log.Println("Failed to generate client certificate:", err) 141 | return 142 | } 143 | result.ClientCert = *completionCert 144 | result.CompleteMessage = g.CompleteMessage 145 | } 146 | 147 | result.RemainingGuesses = guesses 148 | 149 | err = encoder.Encode(result) 150 | if err != nil { 151 | return 152 | } 153 | 154 | if result.Complete { 155 | log.Printf("session %v: level %d complete", id, g.Level) 156 | return 157 | } 158 | 159 | if guesses == 0 { 160 | log.Printf("session %v: no more guesses", id) 161 | return 162 | } 163 | } 164 | } 165 | 166 | func generateCompletionCert(level int) (*cert.PemCertPair, error) { 167 | serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) 168 | serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) 169 | if err != nil { 170 | return nil, err 171 | } 172 | 173 | pemClient, err := cert.MakeCerts(cert.CertConfig{ 174 | Parent: pemCA, 175 | IsServer: false, 176 | IsClient: true, 177 | Serial: serialNumber, 178 | CommonName: fmt.Sprint(level), 179 | DNSNames: []string{fmt.Sprint(level)}, 180 | SecsValid: 60 * 60 * 24, 181 | }) 182 | if err != nil { 183 | return nil, err 184 | } 185 | 186 | return pemClient, nil 187 | } 188 | -------------------------------------------------------------------------------- /state.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net" 5 | "time" 6 | 7 | "github.com/gdamore/tcell/v2" 8 | "github.com/rivo/tview" 9 | ) 10 | 11 | type State struct { 12 | Guesses int 13 | WordLen int 14 | Conn net.Conn 15 | GuessIndex int 16 | LetterIndex int 17 | Letters []*tview.Button 18 | Level int 19 | Complete bool 20 | Message *tview.Button 21 | AlertChan chan bool 22 | Candidates map[rune]*tview.Button 23 | } 24 | 25 | func (state *State) CurrentLetters() []*tview.Button { 26 | start := state.GuessIndex * state.WordLen 27 | end := start + state.WordLen 28 | return state.Letters[start:end] 29 | } 30 | 31 | func (state *State) CurrentLetter() *tview.Button { 32 | return state.Letters[(state.GuessIndex*state.WordLen)+state.LetterIndex] 33 | } 34 | 35 | func (state *State) SetMessage(message string, fadeout bool) { 36 | state.Message.SetLabel("[::b]" + message) 37 | state.AlertChan <- fadeout 38 | } 39 | 40 | func (state *State) MessageAnimationHandler() { 41 | stopChan := make(chan struct{}) 42 | go state.MessageFadeOut(stopChan, false) 43 | for { 44 | fadeout := <-state.AlertChan 45 | stopChan <- struct{}{} 46 | app.QueueUpdateDraw(func() { 47 | state.Message.SetBackgroundColor(colorWhite) 48 | }) 49 | go state.MessageFadeOut(stopChan, fadeout) 50 | } 51 | } 52 | 53 | func (state *State) MessageFadeOut(stopChan chan struct{}, fadeout bool) { 54 | var now time.Time 55 | 56 | select { 57 | case <-stopChan: 58 | return 59 | case <-time.After(time.Second): 60 | } 61 | 62 | now = time.Now() 63 | if fadeout { 64 | var i int32 65 | for i = 255; i >= 0; i-- { 66 | select { 67 | case <-stopChan: 68 | return 69 | default: 70 | } 71 | 72 | now = now.Add(2 * time.Millisecond) 73 | <-time.After(time.Until(now)) 74 | app.QueueUpdateDraw(func() { 75 | state.Message.SetBackgroundColor(tcell.NewRGBColor(i, i, i)) 76 | }) 77 | } 78 | } 79 | 80 | <-stopChan 81 | } 82 | 83 | func (state *State) UpdateIndicators(indicators []rune) { 84 | letters := state.CurrentLetters() 85 | for i, indicator := range indicators { 86 | state.LetterIndex = i 87 | 88 | color := colorBlack 89 | switch indicator { 90 | case '🟩': 91 | color = colorGreen 92 | case '🟨': 93 | color = colorYellow 94 | case '⬛': 95 | color = colorLightGray 96 | } 97 | 98 | state.CurrentLetter().SetBackgroundColor(color) 99 | state.CurrentLetter().SetBackgroundColorActivated(color) 100 | state.CurrentLetter().SetLabelColor(colorBlack) 101 | state.CurrentLetter().SetLabelColorActivated(colorBlack) 102 | 103 | label := []rune(letters[i].GetLabel()) 104 | if len(label) == 6 { 105 | c, ok := state.Candidates[label[5]] 106 | if ok { 107 | prevColor := c.GetBackgroundColor() 108 | switch prevColor { 109 | case colorLightGray: 110 | if color == colorLightGray { 111 | color = colorGray 112 | } 113 | c.SetBackgroundColor(color) 114 | case colorYellow: 115 | if color == colorGreen { 116 | c.SetBackgroundColor(color) 117 | } 118 | } 119 | 120 | if color == colorYellow || color == colorGreen { 121 | c.SetLabelColor(colorBlack) 122 | } 123 | } 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /total/Amongstourselves-shipmate.md: -------------------------------------------------------------------------------- 1 | PPP is proud to announce a completely original, platform-exclusive game for Plaidiverse! With novel socially deceptive mechanics, Amongst Ourselves™ has been recognized with the "Best-In-Class"® and "Best Narrative Game"® awards at the 2022 Plaidies℗℗℗ Game Awards™. Available now at e-shops near you in the Plaidiverse!! 2 | 3 | 70 points 4 | 32 solves 5 | When Blue's body is found in the communications room, Brown thinks the file she was processing may contain a hint about who killed her. (Misc. Hint: take a look at FileTransferController.ts.) 6 | First solved byCornfield Computer Crew (in 1 hour), galhacktic trendsetters (in 2 hours), and 2n+1 (in 2 hours) 7 | Process Sample 8 | 70 points 9 | 40 solves 10 | After Orange is found dead in the cafeteria, Brown and Cyan investigate soil samples to determine a cause of death. (Crypto.) 11 | First solved bygalhacktic trendsetters (in 48 minutes), ⚔️.TSJ⚔️. (in 1 hour), and zwnj (in 1 hour) 12 | Provide Credentials 13 | 70 points 14 | 39 solves 15 | Green starts teleporting wildly around The Shelld before suddenly disappearing. Meanwhile, Brown inspects the access logs on the ship's computer to piece together a timeline of the murders. (Web.) 16 | First solved byDiceGang (in 1 hour), Straw Hat (in 1 hour), and organizers (in 2 hours) 17 | Recalibrate Engine 18 | 70 points 19 | 30 solves 20 | After the ship experiences some engine trouble, the crew turns on Pink and White and accuses them of the killings. Curiously, Green also reappears briefly, before vanishing again. (Rev.) 21 | First solved bySPRUSH (in 1 hour), galhacktic trendsetters (in 1 hour), and DiceGang (in 2 hours) 22 | Purchase Snack 23 | 70 points 24 | 32 solves 25 | In a moment of calm, the shipmates reminisce over a meal of potato chips and chocolate chip cookies about their lives on Earth and share the reasons they joined the mission in the first place. (Pwn. Hint: take a look at PurchaseSnackPanel.tsx.) 26 | First solved by侍 (in 59 minutes), ⚔️.TSJ⚔️. (in 1 hour), and DiceGang (in 1 hour) 27 | -------------------------------------------------------------------------------- /total/BasicController.ts: -------------------------------------------------------------------------------- 1 | import { Game } from "../Game.js"; 2 | import { Self } from "../Self.js"; 3 | 4 | export abstract class BasicController unknown }> { 5 | public queue: T[]; 6 | 7 | public constructor() { 8 | this.queue = []; 9 | } 10 | 11 | public attach(_game: Game, _self: Self) { 12 | this.queue = []; 13 | } 14 | 15 | public advance( 16 | game: Game, 17 | self: Self, 18 | amount: number 19 | ) { 20 | for (let i = Math.floor(game.self.tick + 1); i < game.self.tick + amount; i++) { 21 | this.beforeTick(game, self); 22 | const action = this.queue.shift(); 23 | 24 | if (action !== undefined) { 25 | self.socket.emit("action", { syncId: self.syncId, tick: i, action: action.toJson() }); 26 | } 27 | } 28 | 29 | game.self.tick += amount; 30 | } 31 | 32 | public detach(game: Game, self: Self) { 33 | // If the queue isn't empty, we need to send the remaining actions or else we might desync. 34 | // This might move the player further ahead of the server than usual, but they should recover pretty quickly. 35 | while (true) { 36 | const action = this.queue.shift(); 37 | 38 | if (action === undefined) { 39 | break; 40 | } 41 | 42 | game.self.tick = Math.floor(game.self.tick + 1); 43 | self.socket.emit("action", { syncId: self.syncId, tick: game.self.tick, action: action.toJson() }); 44 | } 45 | } 46 | 47 | protected beforeTick(_game: Game, _self: Self) { 48 | // Inheriting classes may use this to do something every tick. 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /total/Body.ts: -------------------------------------------------------------------------------- 1 | import { Color } from "@amongst/game-common"; 2 | import { Point } from "@amongst/geometry"; 3 | 4 | export class Body { 5 | public constructor( 6 | public readonly id: string, 7 | public readonly color: Color, 8 | public readonly location: Point 9 | ) {} 10 | } 11 | -------------------------------------------------------------------------------- /total/ConspiracyAction.ts: -------------------------------------------------------------------------------- 1 | import { M, marshal, Marshaller } from "@zensors/sheriff"; 2 | 3 | export class ConspiracyAction { 4 | public readonly action: "exit"; 5 | 6 | public constructor(args: ConspiracyAction.ConstructorArgs) { 7 | this.action = args.action; 8 | } 9 | 10 | public static fromJson(json: ConspiracyAction.AsJson) { 11 | return new ConspiracyAction({ 12 | action: json.action 13 | }); 14 | } 15 | 16 | public static fromUnknown(json: unknown): ConspiracyAction { 17 | marshal(json, ConspiracyAction.Marshaller); 18 | return ConspiracyAction.fromJson(json); 19 | } 20 | 21 | public toJson(): ConspiracyAction.AsJson { 22 | return { 23 | action: this.action 24 | }; 25 | } 26 | } 27 | 28 | export namespace ConspiracyAction { 29 | export interface ConstructorArgs { 30 | action: "exit"; 31 | } 32 | 33 | export interface AsJson { 34 | action: "exit"; 35 | } 36 | 37 | export const Marshaller: Marshaller = M.obj({ 38 | action: M.lit("exit") 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /total/ConspiracyController.ts: -------------------------------------------------------------------------------- 1 | import { ConspiracyAction, ProcessSampleAction, ProcessSampleTime, SystemKind, SystemState } from "@amongst/game-common"; 2 | import { GameUpdate } from "@amongst/messages"; 3 | 4 | import { Game } from "../Game.js"; 5 | import { Self } from "../Self.js"; 6 | import { BasicController } from "./BasicController.js"; 7 | import { MovementController } from "./MovementController.js"; 8 | 9 | export class ConspiracyController extends BasicController { 10 | public readonly kind = SystemKind.Conspiracy; 11 | public state: SystemState.Conspiracy; 12 | public flags?: string[]; 13 | 14 | public constructor(state: SystemState.Conspiracy) { 15 | super(); 16 | this.state = state; 17 | } 18 | 19 | public exit(game: Game, self: Self) { 20 | this.queue.push(new ConspiracyAction({ action: "exit" })); 21 | self.setController(game, new MovementController()); 22 | } 23 | 24 | public updateFlag(update: GameUpdate.ConspiracyUpdate): void { 25 | this.flags = update.flags; 26 | } 27 | 28 | public attach(game: Game, self: Self) { 29 | super.attach(game, self); 30 | } 31 | 32 | public detach(game: Game, self: Self): void { 33 | super.detach(game, self); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /total/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | RUN apt-get update && apt-get install -y xinetd 4 | RUN adduser --no-create-home --disabled-password --gecos "" engine 5 | WORKDIR /engine 6 | COPY engine . 7 | COPY xinetd.conf /etc/xinetd.d/engine 8 | CMD ["/usr/sbin/xinetd", "-dontfork"] 9 | -------------------------------------------------------------------------------- /total/Dropship.ts: -------------------------------------------------------------------------------- 1 | import { Color, LevelMap } from "@amongst/game-common"; 2 | 3 | export const Dropship: LevelMap.AsJson = { 4 | id: "dropship", 5 | walls: [ 6 | { 7 | start: [6, -4.5], 8 | end: [30, -4.5] 9 | }, 10 | { 11 | start: [30, -4.5], 12 | end: [30, 13.5] 13 | }, 14 | { 15 | start: [30, 13.5], 16 | end: [6, 13.5] 17 | }, 18 | { 19 | start: [6, 13.5], 20 | end: [6, -4.5] 21 | }, 22 | { 23 | start: [6, 0], 24 | end: [15, -4.5] 25 | }, 26 | { 27 | start: [21, -4.5], 28 | end: [30, 0] 29 | }, 30 | ], 31 | devices: [ 32 | { 33 | id: "computer", 34 | hitArea: [ 35 | [16.5, 3], 36 | [19.5, 3], 37 | [19.5, 6], 38 | [16.5, 6], 39 | ] 40 | }, 41 | { 42 | id: "button", 43 | hitArea: [ 44 | [15, -4.5], 45 | [20.5, -4.5], 46 | [20.5, -3], 47 | [15, -3], 48 | ], 49 | graphics: { 50 | type: "start-panel", 51 | location: [18, -7] 52 | } 53 | } 54 | ], 55 | spawnPoints: { 56 | [Color.Red]: [7.5, 0], 57 | [Color.Blue]: [28.5, 0], 58 | [Color.Green]: [9, -0.75], 59 | [Color.Pink]: [27, -0.75], 60 | [Color.Orange]: [10.5, -1.5], 61 | [Color.Yellow]: [25.5, -1.5], 62 | [Color.Black]: [12, -2.25], 63 | [Color.White]: [24, -2.25], 64 | [Color.Purple]: [13.5, -3], 65 | [Color.Brown]: [22.5, -3], 66 | [Color.Cyan]: [15, -3.75], 67 | [Color.Lime]: [21, -3.75], 68 | }, 69 | graphics: { 70 | id: "dropship", 71 | origin: [0, 450], 72 | scale: 2, 73 | visibility: 1000 74 | }, 75 | bounds: [ 76 | [0, -18], 77 | [36, 30] 78 | ] 79 | }; 80 | -------------------------------------------------------------------------------- /total/EmergencyButtonAction.ts: -------------------------------------------------------------------------------- 1 | import { M, marshal, Marshaller } from "@zensors/sheriff"; 2 | 3 | export class EmergencyButtonAction { 4 | public readonly action: "exit" | "press"; 5 | 6 | public constructor(args: EmergencyButtonAction.ConstructorArgs) { 7 | this.action = args.action; 8 | } 9 | 10 | public static fromJson(json: EmergencyButtonAction.AsJson) { 11 | return new EmergencyButtonAction({ 12 | action: json.action, 13 | }); 14 | } 15 | 16 | public static fromUnknown(json: unknown): EmergencyButtonAction { 17 | marshal(json, EmergencyButtonAction.Marshaller); 18 | return EmergencyButtonAction.fromJson(json); 19 | } 20 | 21 | public toJson(): EmergencyButtonAction.AsJson { 22 | return { 23 | action: this.action, 24 | }; 25 | } 26 | } 27 | 28 | export namespace EmergencyButtonAction { 29 | export interface ConstructorArgs { 30 | action: "exit" | "press"; 31 | } 32 | 33 | export interface AsJson { 34 | action: "exit" | "press"; 35 | } 36 | 37 | export const Marshaller: Marshaller = M.obj({ 38 | action: M.union(M.lit("exit"), M.lit("press")), 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /total/EmergencyButtonController.ts: -------------------------------------------------------------------------------- 1 | import { EmergencyButtonAction, SystemKind } from "@amongst/game-common"; 2 | 3 | import { Game } from "../Game.js"; 4 | import { Self } from "../Self.js"; 5 | import { MovementController } from "."; 6 | import { BasicController } from "./BasicController.js"; 7 | 8 | export class EmergencyButtonController extends BasicController { 9 | public readonly kind = SystemKind.EmergencyButton; 10 | 11 | public constructor() { 12 | super(); 13 | } 14 | 15 | public attach(game: Game, self: Self) { 16 | super.attach(game, self); 17 | } 18 | 19 | public press(): void { 20 | this.queue.push(new EmergencyButtonAction({ action: "press" })); 21 | // don't bother setting the controller, because we're going to desync in a moment to get it set for us 22 | } 23 | 24 | public exit(game: Game, self: Self): void { 25 | this.queue.push(new EmergencyButtonAction({ action: "exit" })); 26 | self.setController(game, new MovementController()); 27 | } 28 | 29 | public detach(game: Game, self: Self): void { 30 | super.detach(game, self); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /total/FileTransferAction.ts: -------------------------------------------------------------------------------- 1 | import { M, marshal, Marshaller } from "@zensors/sheriff"; 2 | 3 | export class FileTransferAction { 4 | public readonly action: "exit" | "checksum" | "upload" | "done"; 5 | public readonly body?: string; 6 | 7 | public constructor(args: FileTransferAction.ConstructorArgs) { 8 | this.action = args.action; 9 | this.body = args.body; 10 | } 11 | 12 | public static fromJson(json: FileTransferAction.AsJson) { 13 | return new FileTransferAction({ 14 | action: json.action, 15 | body: json.body, 16 | }); 17 | } 18 | 19 | public static fromUnknown(json: unknown): FileTransferAction { 20 | marshal(json, FileTransferAction.Marshaller); 21 | return FileTransferAction.fromJson(json); 22 | } 23 | 24 | public toJson(): FileTransferAction.AsJson { 25 | return { 26 | action: this.action, 27 | body: this.body, 28 | }; 29 | } 30 | } 31 | 32 | export namespace FileTransferAction { 33 | export interface ConstructorArgs { 34 | action: "exit" | "checksum" | "upload" | "done"; 35 | body?: string; 36 | } 37 | 38 | export interface AsJson { 39 | action: "exit" | "checksum" | "upload" | "done"; 40 | body?: string; 41 | } 42 | 43 | export const Marshaller: Marshaller = M.obj({ 44 | action: M.union(M.lit("exit"), M.lit("checksum"), M.lit("upload"), M.lit("done")), 45 | body: M.opt(M.str), 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /total/FileTransferController.ts: -------------------------------------------------------------------------------- 1 | import crc from "crc-32"; 2 | 3 | import { FileTransferAction, FileTransferRateLimit, SystemKind, SystemState } from "@amongst/game-common"; 4 | import { GameUpdate } from "@amongst/messages"; 5 | 6 | import { Game } from "../Game.js"; 7 | import { Self } from "../Self.js"; 8 | import { BasicController } from "./BasicController.js"; 9 | import { MovementController } from "./MovementController.js"; 10 | 11 | export class FileTransferController extends BasicController { 12 | public static downloadMap = new Map(); 13 | public readonly kind = SystemKind.FileTransfer; 14 | public state: SystemState.FileTransfer; 15 | public downloadPosition: number; 16 | public downloadTotal: number; 17 | public uploadPosition: number; 18 | public uploadTotal: number; 19 | 20 | public constructor(state: SystemState.FileTransfer) { 21 | super(); 22 | this.state = state; 23 | this.downloadPosition = 0; 24 | this.downloadTotal = 1; 25 | this.uploadPosition = 0; 26 | this.uploadTotal = 0; 27 | } 28 | 29 | public attach(game: Game, self: Self) { 30 | super.attach(game, self); 31 | this.downloadPosition = 0; 32 | this.downloadTotal = 1; 33 | this.uploadPosition = 0; 34 | this.uploadTotal = FileTransferController.downloadMap.get(this.state.id)?.length ?? 0; 35 | } 36 | 37 | public receive(game: Game, self: Self, packet: GameUpdate.FileDownloadPacket) { 38 | if (packet.position === 0) { 39 | FileTransferController.downloadMap.set(this.state.id, ""); 40 | } 41 | 42 | const data = (FileTransferController.downloadMap.get(this.state.id) ?? "") + atob(packet.data); 43 | FileTransferController.downloadMap.set(this.state.id, data); 44 | this.downloadPosition = data.length; 45 | this.downloadTotal = packet.totalSize; 46 | 47 | if (data.length === packet.totalSize) { 48 | this.queue.push( 49 | new FileTransferAction({ 50 | action: "checksum", 51 | body: crc.bstr(data, 0).toString() 52 | }) 53 | ); 54 | self.setController(game, new MovementController()); 55 | } 56 | } 57 | 58 | public exit(game: Game, self: Self): void { 59 | this.queue.push(new FileTransferAction({ action: "exit" })); 60 | self.setController(game, new MovementController()); 61 | } 62 | 63 | public detach(game: Game, self: Self): void { 64 | super.detach(game, self); 65 | } 66 | 67 | protected beforeTick(game: Game, self: Self) { 68 | if (this.state.downloadComplete && !this.state.uploadComplete) { 69 | const data = FileTransferController.downloadMap.get(this.state.id) ?? ""; 70 | 71 | if (this.uploadPosition < data.length) { 72 | this.queue.push( 73 | new FileTransferAction({ 74 | action: "upload", 75 | body: btoa(data.slice(this.uploadPosition, this.uploadPosition + FileTransferRateLimit)) 76 | }) 77 | ); 78 | this.uploadPosition += FileTransferRateLimit; 79 | } else { 80 | this.queue.push( 81 | new FileTransferAction({ 82 | action: "done" 83 | }) 84 | ); 85 | self.setController(game, new MovementController()); 86 | } 87 | } 88 | } 89 | } 90 | 91 | (window as any).FileTransferController = FileTransferController; // you're welcome 92 | -------------------------------------------------------------------------------- /total/Game.ts: -------------------------------------------------------------------------------- 1 | import { Socket } from "socket.io-client"; 2 | 3 | import { SystemKind } from "@amongst/game-common"; 4 | import { Point } from "@amongst/geometry"; 5 | import { ClientEvent, GameSync, GameUpdate, GameUpdateBundle, ServerEvent } from "@amongst/messages"; 6 | 7 | import { Body } from "./Body.js"; 8 | import { 9 | ConspiracyController, 10 | FileTransferController, 11 | getController, 12 | HoldController, 13 | ProcessSampleController, 14 | PurchaseSnackController, 15 | RecalibrateEngineController, 16 | SatelliteController 17 | } from "./controllers/index.js"; 18 | import { ProvideCredentialsController } from "./controllers/ProvideCredentialsController.js"; 19 | import { Level } from "./Level.js"; 20 | import { Player } from "./Player.js"; 21 | import { Self } from "./Self.js"; 22 | 23 | export class Game { 24 | public lastUpdate: number; 25 | public tick: number; 26 | public partialTick: number; 27 | public self: Self; 28 | public others: Map; 29 | public bodies: Map; 30 | public level: Level; 31 | 32 | public constructor( 33 | tick: number, 34 | partialTick: number, 35 | self: Self, 36 | others: Map, 37 | level: Level 38 | ) { 39 | this.lastUpdate = Date.now(); 40 | this.tick = tick; 41 | this.partialTick = partialTick; 42 | this.self = self; 43 | this.others = others; 44 | this.bodies = new Map(); 45 | this.level = level; 46 | } 47 | 48 | public static fromSync( 49 | sync: GameSync, 50 | socket: Socket 51 | ) { 52 | const level = Level.fromSync(sync.level); 53 | 54 | const game = new Game( 55 | sync.tick, 56 | sync.partialTick, 57 | new Self( 58 | sync.self.id, 59 | socket, 60 | sync.self.name, 61 | sync.self.color, 62 | Point.fromJson(sync.self.location), 63 | sync.self.visualState, 64 | sync.self.dead, 65 | sync.self.hoaxer, 66 | sync.self.emergencyMeetings, 67 | sync.id, 68 | sync.nextPlayableTick + 10, // TODO: move this to a constant or something 69 | getController(level.systems.get(sync.self.system)!) 70 | ), 71 | new Map(sync.others.map((player) => [player.id, new Player(player.id, player.name, player.color)])), 72 | level 73 | ); 74 | 75 | game.self.controller.attach(game, game.self); 76 | return game; 77 | } 78 | 79 | public applyUpdates(bundle: GameUpdateBundle) { 80 | if (bundle.tick !== this.tick + 1) { 81 | // Not fatal, as this sometimes happens under normal circumstances (e.g. after desync) 82 | // eslint-disable-next-line no-console 83 | console.warn(`Tick mismatch: expected ${this.tick + 1}, got ${bundle.tick}`); 84 | } 85 | 86 | this.tick = bundle.tick; 87 | this.partialTick = bundle.partialTick; 88 | this.lastUpdate = Date.now(); 89 | 90 | for (const updateJson of bundle.updates) { 91 | this.applyUpdate(GameUpdate.fromJson(updateJson)); 92 | } 93 | } 94 | 95 | public copyFrom(other: Game) { 96 | this.tick = other.tick; 97 | this.self = other.self; 98 | this.others = other.others; 99 | this.bodies = other.bodies; 100 | this.level = other.level; 101 | } 102 | 103 | private applyUpdate(update: GameUpdate) { 104 | switch (update.kind) { 105 | case GameUpdate.Kind.PlayerJoined: { 106 | const player = new Player(update.id, update.name, update.color); 107 | this.others.set(update.id, player); 108 | return; 109 | } 110 | 111 | case GameUpdate.Kind.PlayerLeft: { 112 | this.others.delete(update.id); 113 | return; 114 | } 115 | 116 | case GameUpdate.Kind.VisibilityUpdate: { 117 | // Reset everything 118 | for (const player of this.others.values()) { 119 | player.location = undefined; 120 | player.visualState = undefined; 121 | player.dead = false; 122 | player.hoaxer = false; 123 | } 124 | 125 | this.bodies.clear(); 126 | 127 | // Process the updates 128 | for (const playerUpdate of update.players) { 129 | const player = this.others.get(playerUpdate.id); 130 | 131 | if (player === undefined) { 132 | throw new Error(`Player ${playerUpdate.id} not found`); 133 | } 134 | 135 | player.location = playerUpdate.location; 136 | player.visualState = playerUpdate.visualState; 137 | player.dead = playerUpdate.dead; 138 | player.hoaxer = playerUpdate.hoaxer; 139 | } 140 | 141 | for (const bodyUpdate of update.bodies) { 142 | const body = new Body(bodyUpdate.id, bodyUpdate.color, bodyUpdate.location); 143 | this.bodies.set(body.id, body); 144 | } 145 | 146 | return; 147 | } 148 | 149 | case GameUpdate.Kind.PlayerChangedSettings: { 150 | const player = update.id === this.self.id ? this.self : this.others.get(update.id); 151 | 152 | if (player === undefined) { 153 | throw new Error(`Player ${update.id} not found`); 154 | } 155 | 156 | player.name = update.name; 157 | player.color = update.color; 158 | 159 | return; 160 | } 161 | 162 | case GameUpdate.Kind.SystemStateUpdate: { 163 | this.level.updateSystemState(update.state); 164 | 165 | // Also send the update to the controller, for controller types that support it 166 | if ( 167 | update.state.kind === SystemKind.ProcessSample 168 | && this.self.controller instanceof ProcessSampleController 169 | && this.self.controller.state.id === update.state.id 170 | ) { 171 | this.self.controller.updateState(update.state); 172 | } 173 | 174 | if ( 175 | update.state.kind === SystemKind.ProvideCredentials 176 | && this.self.controller instanceof ProvideCredentialsController 177 | && this.self.controller.state.id === update.state.id 178 | ) { 179 | this.self.controller.updateState(update.state); 180 | } 181 | 182 | if ( 183 | update.state.kind === SystemKind.PurchaseSnack 184 | && this.self.controller instanceof PurchaseSnackController 185 | && this.self.controller.state.id === update.state.id 186 | ) { 187 | this.self.controller.updateState(update.state); 188 | } 189 | 190 | if ( 191 | update.state.kind === SystemKind.RecalibrateEngine 192 | && this.self.controller instanceof RecalibrateEngineController 193 | && this.self.controller.state.id === update.state.id 194 | ) { 195 | this.self.controller.updateState(update.state); 196 | } 197 | 198 | if ( 199 | update.state.kind === SystemKind.Hold 200 | && this.self.controller instanceof HoldController 201 | && this.self.controller.state.id === update.state.id 202 | ) { 203 | this.self.controller.updateState(update.state); 204 | } 205 | 206 | return; 207 | } 208 | 209 | case GameUpdate.Kind.FileDownloadPacket: { 210 | if ( 211 | this.self.controller instanceof FileTransferController 212 | && this.self.controller.state.id === update.id 213 | ) { 214 | this.self.controller.receive(this, this.self, update); 215 | } 216 | return; 217 | } 218 | 219 | case GameUpdate.Kind.ProcessSampleDisplayUpdate: { 220 | if (this.self.controller instanceof ProcessSampleController) { 221 | this.self.controller.updateDisplay(update); 222 | } 223 | return; 224 | } 225 | 226 | case GameUpdate.Kind.ProvideCredentialsResponse: { 227 | if (this.self.controller instanceof ProvideCredentialsController) { 228 | this.self.controller.receive(update); 229 | } 230 | return; 231 | } 232 | 233 | case GameUpdate.Kind.PurchaseSnackLayoutReady: { 234 | if (this.self.controller instanceof PurchaseSnackController) { 235 | this.self.controller.receiveLayout(update); 236 | } 237 | return; 238 | } 239 | 240 | case GameUpdate.Kind.PurchaseSnackOutput: { 241 | if (this.self.controller instanceof PurchaseSnackController) { 242 | this.self.controller.receiveOutput(update); 243 | } 244 | return; 245 | } 246 | 247 | case GameUpdate.Kind.RecalibrateEngineUpdate: { 248 | if (this.self.controller instanceof RecalibrateEngineController) { 249 | this.self.controller.receive(this.self, update); 250 | } 251 | return; 252 | } 253 | 254 | case GameUpdate.Kind.ConspiracyUpdate: { 255 | if (this.self.controller instanceof ConspiracyController) { 256 | this.self.controller.updateFlag(update); 257 | } 258 | return; 259 | } 260 | 261 | case GameUpdate.Kind.SatelliteUpdate: { 262 | if (this.self.controller instanceof SatelliteController) { 263 | this.self.controller.updateFlag(update); 264 | } 265 | return; 266 | } 267 | } 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /total/HoldController.ts: -------------------------------------------------------------------------------- 1 | import { HoldPurpose, SystemKind, SystemState } from "@amongst/game-common"; 2 | 3 | import { Game } from "../Game"; 4 | import { Self } from "../Self"; 5 | import { MeetingController, MovementController } from "."; 6 | import { BasicController } from "./BasicController"; 7 | 8 | export class HoldController extends BasicController { 9 | public readonly kind = SystemKind.Hold; 10 | public state: SystemState.Hold; 11 | public purpose: HoldPurpose; 12 | 13 | public constructor(state: SystemState.Hold) { 14 | super(); 15 | this.state = state; 16 | this.purpose = HoldPurpose.fromJson(state.purpose); 17 | } 18 | 19 | public updateState(state: SystemState.Hold): void { 20 | this.state = state; 21 | this.purpose = HoldPurpose.fromJson(state.purpose); 22 | } 23 | 24 | protected beforeTick(game: Game, self: Self): void { 25 | if (game.tick >= this.state.until) { 26 | switch (this.purpose.kind) { 27 | case HoldPurpose.Kind.GameStart: 28 | case HoldPurpose.Kind.VoteComplete: { 29 | self.setController(game, new MovementController()); 30 | } 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /total/Level.ts: -------------------------------------------------------------------------------- 1 | import { Map } from "immutable"; 2 | 3 | import { getLevelMap } from "@amongst/data"; 4 | import { LevelMap, SystemState } from "@amongst/game-common"; 5 | import { LevelSync } from "@amongst/messages/src/ServerEvent"; 6 | 7 | export class Level { 8 | public map: LevelMap; 9 | public systems: Map; 10 | 11 | public constructor(map: LevelMap, systems: Map) { 12 | this.map = map; 13 | this.systems = systems; 14 | } 15 | 16 | public static fromSync(sync: LevelSync): Level { 17 | return new Level( 18 | getLevelMap(sync.map), 19 | Map(sync.systems.map((system) => [system.id, system] as [number, SystemState])), 20 | ); 21 | } 22 | 23 | public updateSystemState(state: SystemState) { 24 | this.systems = this.systems.set(state.id, state); 25 | } 26 | 27 | public getSystemForDevice(device: string): SystemState | undefined { 28 | for (const system of this.systems.values()) { 29 | if (system.devices.includes(device)) { 30 | return system; 31 | } 32 | } 33 | 34 | return undefined; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /total/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: binaries handout.tgz 2 | 3 | handout.tgz: binaries pcap 4 | TMPDIR=$$(mktemp -d) && \ 5 | tar --exclude-from .tarignore -czf handout-tmp.tgz . && \ 6 | mkdir -p "$$TMPDIR/amongst" && \ 7 | tar -xzf handout-tmp.tgz -C "$$TMPDIR/amongst" && \ 8 | echo 'The server has a different file here' > "$$TMPDIR/amongst/packages/game-server/assets/sus" && \ 9 | tar -czf handout.tgz -C "$$TMPDIR" . && \ 10 | rm -rf "$$TMPDIR" && \ 11 | rm -f handout-tmp.tgz 12 | 13 | binaries: misc/recalibrate-engine/engine misc/purchase-snack/vending 14 | 15 | pcap: misc/file-transfer/out.pcap 16 | 17 | misc/recalibrate-engine/engine: misc/recalibrate-engine/engine.c misc/recalibrate-engine/build.Dockerfile 18 | docker build misc/recalibrate-engine -f misc/recalibrate-engine/build.Dockerfile -t recalibrate-engine:build 19 | CONTAINER_ID=$$(docker create recalibrate-engine:build) ; \ 20 | docker cp $$CONTAINER_ID:/chal/engine misc/recalibrate-engine/engine ; \ 21 | docker rm $$CONTAINER_ID 22 | 23 | misc/purchase-snack/vending: misc/purchase-snack/vending.c misc/purchase-snack/build.Dockerfile 24 | docker build misc/purchase-snack -f misc/purchase-snack/build.Dockerfile -t purchase-snack:build 25 | CONTAINER_ID=$$(docker create purchase-snack:build) ; \ 26 | docker cp $$CONTAINER_ID:/chal/vending misc/purchase-snack/vending ; \ 27 | docker rm $$CONTAINER_ID 28 | 29 | misc/file-transfer/out.pcap: misc/file-transfer/build.Dockerfile misc/file-transfer/run.sh misc/file-transfer/sus.png 30 | docker build misc/file-transfer -f misc/file-transfer/build.Dockerfile -t file-transfer:build 31 | CONTAINER_ID=$$(docker run -d file-transfer:build) ; \ 32 | sleep 20 ; \ 33 | docker cp $$CONTAINER_ID:/out/tcpdump.pcap misc/file-transfer/out.pcap ; \ 34 | docker stop $$CONTAINER_ID ; \ 35 | docker rm $$CONTAINER_ID 36 | cp misc/file-transfer/out.pcap packages/game-server/assets/sus 37 | -------------------------------------------------------------------------------- /total/MeetingController.ts: -------------------------------------------------------------------------------- 1 | import { SystemKind, SystemState } from "@amongst/game-common"; 2 | import { MeetingAction } from "@amongst/game-common"; 3 | 4 | import { Game } from "../Game.js"; 5 | import { Self } from "../Self.js"; 6 | import { BasicController } from "./BasicController.js"; 7 | 8 | export class MeetingController extends BasicController { 9 | public readonly kind = SystemKind.Meeting; 10 | public state: SystemState.Meeting; 11 | public selfVoted: boolean; 12 | 13 | public constructor(state: SystemState.Meeting) { 14 | super(); 15 | this.state = state; 16 | this.selfVoted = false; 17 | } 18 | 19 | public attach(game: Game, self: Self) { 20 | super.attach(game, self); 21 | this.selfVoted = false; 22 | } 23 | 24 | public vote(player: string | undefined) { 25 | this.queue.push(new MeetingAction({ vote: player })); 26 | this.selfVoted = true; 27 | } 28 | 29 | public detach(game: Game, self: Self): void { 30 | super.detach(game, self); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /total/MovementController.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ClientKillRadius, 3 | Direction, 4 | MoveAction, 5 | PlayerSpeed, 6 | PlayerVisualState, 7 | ReportRadius, 8 | SystemKind, 9 | SystemState 10 | } from "@amongst/game-common"; 11 | import { Point } from "@amongst/geometry"; 12 | 13 | import { Game } from "../Game.js"; 14 | import { Self } from "../Self.js"; 15 | import { getController } from "./index.js"; 16 | 17 | export class MovementController { 18 | public readonly kind = SystemKind.Movement; 19 | public partialMovement: Point; 20 | public displayLocation: Point; 21 | public displayVisualState: PlayerVisualState; 22 | 23 | public constructor() { 24 | this.partialMovement = Point.Origin; 25 | 26 | // These don't matter, since we'll get attached before they get read 27 | this.displayLocation = Point.Origin; 28 | this.displayVisualState = { kind: PlayerVisualState.Kind.Idle, direction: Direction.Left }; 29 | } 30 | 31 | public attach(game: Game, self: Self) { 32 | this.partialMovement = Point.Origin; 33 | this.displayLocation = self.location; 34 | this.displayVisualState = self.visualState; 35 | } 36 | 37 | public advance( 38 | game: Game, 39 | self: Self, 40 | amount: number, 41 | movement: Point, 42 | interact: boolean, 43 | kill: boolean, 44 | report: boolean 45 | ) { 46 | while (amount > 0) { 47 | const tickFinishTime = Math.floor(self.tick + 1) - self.tick; 48 | const step = Math.min(amount, tickFinishTime); 49 | game.self.tick += step; 50 | const newSystem = this.partialAdvance( 51 | game, 52 | self, 53 | step, 54 | step === tickFinishTime, 55 | movement, 56 | interact, 57 | kill, 58 | report 59 | ); 60 | amount -= step; 61 | 62 | if (newSystem !== undefined) { 63 | self.setController(game, getController(newSystem)); 64 | break; 65 | } 66 | } 67 | } 68 | 69 | public partialAdvance( 70 | game: Game, 71 | self: Self, 72 | amount: number, 73 | finishTick: boolean, 74 | movement: Point, 75 | interact: boolean, 76 | kill: boolean, 77 | report: boolean 78 | ): SystemState | undefined { 79 | this.partialMovement = this.partialMovement.add(movement.scale(amount * PlayerSpeed)); 80 | 81 | this.displayLocation = self.location.add(this.partialMovement); 82 | 83 | if (!self.dead) { 84 | this.displayLocation = game.level.map.applyWalls(this.displayLocation); 85 | this.displayLocation = game.level.map.applyBounds(this.displayLocation); 86 | } 87 | 88 | this.displayVisualState = ( 89 | this.partialMovement.isOrigin() 90 | ? { kind: PlayerVisualState.Kind.Idle, direction: self.visualState.direction } 91 | : { 92 | kind: PlayerVisualState.Kind.Moving, 93 | direction: ( 94 | this.partialMovement.x < 0 ? Direction.Left : 95 | this.partialMovement.x > 0 ? Direction.Right : 96 | self.visualState.direction 97 | ), 98 | frame: ( 99 | self.visualState.kind === PlayerVisualState.Kind.Moving 100 | ? (self.visualState.frame + 1) % 12 101 | : 0 102 | ) 103 | } 104 | ); 105 | 106 | if (finishTick) { 107 | self.location = this.displayLocation; 108 | self.visualState = this.displayVisualState; 109 | 110 | const delta = this.partialMovement; 111 | 112 | 113 | this.partialMovement = Point.Origin; 114 | 115 | if (interact) { 116 | const device = game.level.map.getDeviceAtPoint(self.location); 117 | 118 | if (device !== undefined) { 119 | const system = game.level.getSystemForDevice(device.id); 120 | 121 | if (system !== undefined) { 122 | const action = new MoveAction({ 123 | delta, 124 | bonusAction: new MoveAction.BonusAction.Interact({ 125 | system: system.id, 126 | device: device.id 127 | }) 128 | }); 129 | 130 | self.socket.emit("action", { 131 | syncId: self.syncId, 132 | tick: Math.round(self.tick), 133 | action: action.toJson() 134 | }); 135 | 136 | return system; 137 | } 138 | } 139 | } else if (self.hoaxer && kill && !self.dead) { 140 | // Target the closest valid non-hoaxer 141 | const players = ( 142 | Array.from(game.others.values()) 143 | .filter((player) => player.location !== undefined) 144 | .filter((player) => player.hoaxer !== true) 145 | .filter((player) => !player.dead) 146 | .sort((a, b) => a.location!.dist(self.location) - b.location!.dist(self.location)) 147 | ); 148 | 149 | for (const target of players) { 150 | if (game.level.map.hasWallBetween(self.location, target.location!)) { 151 | continue; 152 | } 153 | 154 | if (target.location!.dist(self.location) <= ClientKillRadius) { 155 | const action = new MoveAction({ 156 | delta, 157 | bonusAction: new MoveAction.BonusAction.Kill({ 158 | target: target.id, 159 | at: target.location! 160 | }) 161 | }); 162 | 163 | self.location = target.location!; 164 | 165 | self.socket.emit("action", { 166 | syncId: self.syncId, 167 | tick: Math.round(self.tick), 168 | action: action.toJson() 169 | }); 170 | 171 | return undefined; 172 | } 173 | } 174 | } else if (report && !self.dead) { 175 | // Target the closest valid body 176 | const bodies = ( 177 | Array.from(game.bodies.values()) 178 | .sort((a, b) => a.location.dist(self.location) - b.location.dist(self.location)) 179 | .filter((body) => !game.level.map.hasWallBetween(self.location, body.location, true)) 180 | ); 181 | 182 | if (bodies.length > 0) { 183 | const target = bodies[0]; 184 | 185 | if (target.location.dist(self.location) <= ReportRadius) { 186 | const action = new MoveAction({ 187 | delta, 188 | bonusAction: new MoveAction.BonusAction.Report({ 189 | body: target.id 190 | }) 191 | }); 192 | 193 | self.socket.emit("action", { 194 | syncId: self.syncId, 195 | tick: Math.round(self.tick), 196 | action: action.toJson() 197 | }); 198 | 199 | return undefined; 200 | } 201 | } 202 | } 203 | 204 | if (!delta.isOrigin()) { 205 | const action = new MoveAction({ delta }); 206 | self.socket.emit("action", { 207 | syncId: self.syncId, 208 | tick: Math.round(self.tick), 209 | action: action.toJson() 210 | }); 211 | } 212 | } 213 | 214 | return undefined; 215 | } 216 | 217 | public detach(_game: Game, _self: Self) { 218 | // nothing to do 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /total/Player.ts: -------------------------------------------------------------------------------- 1 | import { Color, PlayerVisualState } from "@amongst/game-common"; 2 | import { Point } from "@amongst/geometry"; 3 | 4 | export class Player { 5 | public id: string; 6 | public name: string; 7 | public color: Color; 8 | public location?: Point; 9 | public visualState?: PlayerVisualState; 10 | public dead?: boolean; 11 | public hoaxer?: boolean; 12 | 13 | public constructor( 14 | id: string, 15 | name: string, 16 | color: Color 17 | ) { 18 | this.id = id; 19 | this.name = name; 20 | this.color = color; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /total/ProcessSampleController.ts: -------------------------------------------------------------------------------- 1 | import { ProcessSampleAction, ProcessSampleTime, SystemKind, SystemState } from "@amongst/game-common"; 2 | import { GameUpdate } from "@amongst/messages"; 3 | 4 | import { Game } from "../Game.js"; 5 | import { Self } from "../Self.js"; 6 | import { BasicController } from "./BasicController.js"; 7 | import { MovementController } from "./MovementController.js"; 8 | 9 | export class ProcessSampleController extends BasicController { 10 | public readonly kind = SystemKind.ProcessSample; 11 | public state: SystemState.ProcessSample; 12 | public display?: string; 13 | 14 | public constructor(state: SystemState.ProcessSample) { 15 | super(); 16 | this.state = state; 17 | } 18 | 19 | public begin(game: Game, self: Self) { 20 | this.queue.push(new ProcessSampleAction({ action: "begin" })); 21 | this.state.timerEndsAt = Math.floor(self.tick) + ProcessSampleTime; 22 | } 23 | 24 | public end(game: Game, self: Self) { 25 | this.queue.push(new ProcessSampleAction({ action: "end" })); 26 | } 27 | 28 | public exit(game: Game, self: Self) { 29 | this.queue.push(new ProcessSampleAction({ action: "exit" })); 30 | self.setController(game, new MovementController()); 31 | } 32 | 33 | public updateDisplay(update: GameUpdate.ProcessSampleDisplayUpdate): void { 34 | this.display = update.content; 35 | } 36 | 37 | public attach(game: Game, self: Self) { 38 | super.attach(game, self); 39 | } 40 | 41 | public detach(game: Game, self: Self): void { 42 | super.detach(game, self); 43 | } 44 | 45 | public updateState(state: SystemState.ProcessSample): void { 46 | this.state = state; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /total/ProvideCredentialsController.ts: -------------------------------------------------------------------------------- 1 | import { ProvideCredentialsAction, SystemKind, SystemState } from "@amongst/game-common"; 2 | import { GameUpdate } from "@amongst/messages"; 3 | 4 | import { Game } from "../Game.js"; 5 | import { Self } from "../Self.js"; 6 | import { BasicController } from "./BasicController.js"; 7 | import { MovementController } from "./MovementController.js"; 8 | 9 | export class ProvideCredentialsController extends BasicController { 10 | public readonly kind = SystemKind.ProvideCredentials; 11 | public state: SystemState.ProvideCredentials; 12 | public lastResponse?: GameUpdate.ProvideCredentialsResponse; 13 | 14 | public constructor(state: SystemState.ProvideCredentials) { 15 | super(); 16 | this.state = state; 17 | } 18 | 19 | public send(username: string, password: string) { 20 | this.queue.push(new ProvideCredentialsAction({ exit: false, username, password })); 21 | } 22 | 23 | public exit(game: Game, self: Self) { 24 | this.queue.push(new ProvideCredentialsAction({ exit: true, username: "", password: "" })); 25 | self.setController(game, new MovementController()); 26 | } 27 | 28 | public receive(update: GameUpdate.ProvideCredentialsResponse): void { 29 | this.lastResponse = update; 30 | } 31 | 32 | public attach(game: Game, self: Self) { 33 | super.attach(game, self); 34 | this.lastResponse = undefined; 35 | } 36 | 37 | public detach(game: Game, self: Self): void { 38 | super.detach(game, self); 39 | } 40 | 41 | public updateState(state: SystemState.ProvideCredentials): void { 42 | this.state = state; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /total/PurchaseSnackController.ts: -------------------------------------------------------------------------------- 1 | import { List, Map } from "immutable"; 2 | 3 | import { PurchaseSnackAction, SystemKind, SystemState } from "@amongst/game-common"; 4 | import { GameUpdate } from "@amongst/messages"; 5 | 6 | import { Game } from "../Game.js"; 7 | import { Self } from "../Self.js"; 8 | import { BasicController } from "./BasicController.js"; 9 | import { MovementController } from "./MovementController.js"; 10 | 11 | export class PurchaseSnackController extends BasicController { 12 | public readonly kind = SystemKind.PurchaseSnack; 13 | public state: SystemState.PurchaseSnack; 14 | public layout?: Map; 15 | public display?: string; 16 | public dispensedSnacks: List; 17 | public waiting: boolean; 18 | 19 | public constructor(state: SystemState.PurchaseSnack) { 20 | super(); 21 | this.state = state; 22 | this.dispensedSnacks = List(); 23 | this.waiting = true; 24 | } 25 | 26 | public send(selection: string) { 27 | this.queue.push(new PurchaseSnackAction({ exit: false, selection })); 28 | this.waiting = true; 29 | } 30 | 31 | public exit(game: Game, self: Self) { 32 | this.queue.push(new PurchaseSnackAction({ exit: true, selection: "" })); 33 | self.setController(game, new MovementController()); 34 | } 35 | 36 | public receiveLayout(update: GameUpdate.PurchaseSnackLayoutReady): void { 37 | this.layout = update.layout; 38 | this.waiting = false; 39 | } 40 | 41 | public receiveOutput(update: GameUpdate.PurchaseSnackOutput): void { 42 | switch (update.event) { 43 | case "display": { 44 | this.display = update.body; 45 | this.waiting = false; 46 | break; 47 | } 48 | 49 | case "dispense": { 50 | this.dispensedSnacks = this.dispensedSnacks.push(update.body); 51 | this.waiting = false; 52 | break; 53 | } 54 | } 55 | } 56 | 57 | public attach(game: Game, self: Self) { 58 | super.attach(game, self); 59 | this.display = undefined; 60 | this.dispensedSnacks = List(); 61 | this.waiting = true; 62 | } 63 | 64 | public detach(game: Game, self: Self): void { 65 | super.detach(game, self); 66 | } 67 | 68 | public updateState(state: SystemState.PurchaseSnack): void { 69 | this.state = state; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /total/README.md: -------------------------------------------------------------------------------- 1 | # game-common 2 | -------------------------------------------------------------------------------- /total/RecalibrateEngineController.ts: -------------------------------------------------------------------------------- 1 | import { RecalibrateEngineAction, SystemKind, SystemState } from "@amongst/game-common"; 2 | import { GameUpdate } from "@amongst/messages"; 3 | 4 | import { Game } from "../Game.js"; 5 | import { Self } from "../Self.js"; 6 | import { BasicController } from "./BasicController.js"; 7 | import { MovementController } from "./MovementController.js"; 8 | 9 | export class RecalibrateEngineController extends BasicController { 10 | public readonly kind = SystemKind.RecalibrateEngine; 11 | public state: SystemState.RecalibrateEngine; 12 | public display?: string; 13 | public flash?: { pattern: number[]; start: number }; 14 | 15 | public constructor(state: SystemState.RecalibrateEngine) { 16 | super(); 17 | this.state = state; 18 | } 19 | 20 | public send(button: number) { 21 | this.queue.push(new RecalibrateEngineAction({ button })); 22 | } 23 | 24 | public exit(game: Game, self: Self) { 25 | this.queue.push(new RecalibrateEngineAction({ button: 0 })); 26 | self.setController(game, new MovementController()); 27 | } 28 | 29 | public receive(self: Self, update: GameUpdate.RecalibrateEngineUpdate): void { 30 | switch (update.event) { 31 | case "display": { 32 | this.display = update.body; 33 | break; 34 | } 35 | 36 | case "flash": { 37 | this.flash = { pattern: update.body.split("").map(Number), start: self.tick }; 38 | break; 39 | } 40 | } 41 | } 42 | 43 | public attach(game: Game, self: Self) { 44 | super.attach(game, self); 45 | this.display = undefined; 46 | this.flash = undefined; 47 | } 48 | 49 | public detach(game: Game, self: Self): void { 50 | super.detach(game, self); 51 | } 52 | 53 | public updateState(state: SystemState.RecalibrateEngine): void { 54 | this.state = state; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /total/ResetController.ts: -------------------------------------------------------------------------------- 1 | import { ResetAction, SystemKind } from "@amongst/game-common"; 2 | 3 | import { Game } from "../Game.js"; 4 | import { Self } from "../Self.js"; 5 | import { MovementController } from "."; 6 | import { BasicController } from "./BasicController.js"; 7 | 8 | export class ResetController extends BasicController { 9 | public readonly kind = SystemKind.Reset; 10 | 11 | public constructor() { 12 | super(); 13 | } 14 | 15 | public attach(game: Game, self: Self) { 16 | super.attach(game, self); 17 | } 18 | 19 | public reset(game: Game, self: Self): void { 20 | // this.queue.push(new ResetAction({ action: "reset" })); 21 | // don't set controller, because it doesn't matter 22 | const action = new ResetAction({ action: "start" }); 23 | self.socket.emit("action", { syncId: self.syncId, tick: 995, action: action.toJson() }); 24 | } 25 | 26 | public exit(game: Game, self: Self): void { 27 | this.queue.push(new ResetAction({ action: "exit" })); 28 | self.setController(game, new MovementController()); 29 | } 30 | 31 | public start(): void { 32 | this.queue.push(new ResetAction({ action: "start" })); 33 | } 34 | 35 | public detach(game: Game, self: Self): void { 36 | super.detach(game, self); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /total/SatelliteController.ts: -------------------------------------------------------------------------------- 1 | import { SatelliteAction, SystemKind, SystemState } from "@amongst/game-common"; 2 | import { GameUpdate } from "@amongst/messages"; 3 | 4 | import { Game } from "../Game.js"; 5 | import { Self } from "../Self.js"; 6 | import { BasicController } from "./BasicController.js"; 7 | import { MovementController } from "./MovementController.js"; 8 | 9 | export class SatelliteController extends BasicController { 10 | public readonly kind = SystemKind.Satellite; 11 | public state: SystemState.Satellite; 12 | public flags?: string[]; 13 | 14 | public constructor(state: SystemState.Satellite) { 15 | super(); 16 | this.state = state; 17 | } 18 | 19 | public exit(game: Game, self: Self) { 20 | this.queue.push(new SatelliteAction({ action: "exit" })); 21 | self.setController(game, new MovementController()); 22 | } 23 | 24 | public updateFlag(update: GameUpdate.SatelliteUpdate): void { 25 | this.flags = update.flags; 26 | } 27 | 28 | public attach(game: Game, self: Self) { 29 | super.attach(game, self); 30 | } 31 | 32 | public detach(game: Game, self: Self): void { 33 | super.detach(game, self); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /total/Self.ts: -------------------------------------------------------------------------------- 1 | import { Socket } from "socket.io-client"; 2 | 3 | import { Color, PlayerVisualState } from "@amongst/game-common"; 4 | import { Point } from "@amongst/geometry"; 5 | import { ClientEvent, ServerEvent } from "@amongst/messages"; 6 | 7 | import { Controller } from "./controllers/index.js"; 8 | import { Game } from "./Game.js"; 9 | import { Player } from "./Player.js"; 10 | 11 | export class Self extends Player { 12 | public socket: Socket; 13 | public location: Point; 14 | public visualState: PlayerVisualState; 15 | public dead: boolean; 16 | public hoaxer: boolean; 17 | public emergencyMeetings: number; 18 | public syncId: number; 19 | public tick: number; 20 | public controller: Controller; 21 | 22 | public constructor( 23 | id: string, 24 | socket: Socket, 25 | name: string, 26 | color: Color, 27 | location: Point, 28 | visualState: PlayerVisualState, 29 | dead: boolean, 30 | hoaxer: boolean, 31 | emergencyMeetings: number, 32 | syncId: number, 33 | tick: number, 34 | controller: Controller, 35 | ) { 36 | super(id, name, color); 37 | this.socket = socket; 38 | this.location = location; 39 | this.visualState = visualState; 40 | this.dead = dead; 41 | this.hoaxer = hoaxer; 42 | this.emergencyMeetings = emergencyMeetings; 43 | this.syncId = syncId; 44 | this.tick = tick; 45 | this.controller = controller; 46 | } 47 | 48 | public setController(game: Game, controller: Controller) { 49 | this.controller.detach(game, this); 50 | this.controller = controller; 51 | this.controller.attach(game, this); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /total/SettingsController.ts: -------------------------------------------------------------------------------- 1 | import { Color, SystemKind } from "@amongst/game-common"; 2 | import { SettingsAction } from "@amongst/game-common"; 3 | 4 | import { Game } from "../Game.js"; 5 | import { Self } from "../Self.js"; 6 | import { MovementController } from "."; 7 | import { BasicController } from "./BasicController.js"; 8 | 9 | export class SettingsController extends BasicController { 10 | public readonly kind = SystemKind.Settings; 11 | 12 | public constructor() { 13 | super(); 14 | } 15 | 16 | public attach(game: Game, self: Self) { 17 | super.attach(game, self); 18 | } 19 | 20 | public update(color: Color, name: string): void { 21 | // Overwrite the entire queue, as this update will supercede all previous updates 22 | this.queue = [new SettingsAction({ exit: false, color, name })]; 23 | } 24 | 25 | public exit(game: Game, self: Self): void { 26 | this.queue.push(new SettingsAction({ exit: true })); 27 | self.setController(game, new MovementController()); 28 | } 29 | 30 | public detach(game: Game, self: Self): void { 31 | super.detach(game, self); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /total/TheShelld.ts: -------------------------------------------------------------------------------- 1 | import { Color, LevelMap } from "@amongst/game-common"; 2 | 3 | export const TheShelld: LevelMap.AsJson = { 4 | id: "shelld", 5 | walls: [ 6 | // Cafeteria 7 | { 8 | start: [0, 0], 9 | end: [25.5, 0] 10 | }, 11 | { 12 | start: [25.5, 0], 13 | end: [25.5, 9] 14 | }, 15 | { 16 | start: [25.5, 15], 17 | end: [25.5, 19.5] 18 | }, 19 | { 20 | start: [25.5, 19.5], 21 | end: [0, 19.5] 22 | }, 23 | { 24 | start: [0, 19.5], 25 | end: [0, 15] 26 | }, 27 | { 28 | start: [0, 9], 29 | end: [0, 0] 30 | }, 31 | { 32 | start: [21, 0], 33 | end: [25.5, 4.5] 34 | }, 35 | { 36 | start: [19.8, 2.4], 37 | end: [5.7, 2.4], 38 | transparent: true 39 | }, 40 | { 41 | start: [5.7, 2.4], 42 | end: [5.7, 6.6], 43 | transparent: true 44 | }, 45 | { 46 | start: [5.7, 6.6], 47 | end: [19.8, 6.6], 48 | transparent: true 49 | }, 50 | { 51 | start: [19.8, 6.6], 52 | end: [19.8, 2.4], 53 | transparent: true 54 | }, 55 | { 56 | start: [19.8, 8.4], 57 | end: [5.7, 8.4], 58 | transparent: true 59 | }, 60 | { 61 | start: [5.7, 8.4], 62 | end: [5.7, 12.6], 63 | transparent: true 64 | }, 65 | { 66 | start: [5.7, 12.6], 67 | end: [19.8, 12.6], 68 | transparent: true 69 | }, 70 | { 71 | start: [19.8, 12.6], 72 | end: [19.8, 8.4], 73 | transparent: true 74 | }, 75 | { 76 | start: [19.8, 14.4], 77 | end: [5.7, 14.4], 78 | transparent: true 79 | }, 80 | { 81 | start: [5.7, 14.4], 82 | end: [5.7, 18.6], 83 | transparent: true 84 | }, 85 | { 86 | start: [5.7, 18.6], 87 | end: [19.8, 18.6], 88 | transparent: true 89 | }, 90 | { 91 | start: [19.8, 18.6], 92 | end: [19.8, 14.4], 93 | transparent: true 94 | }, 95 | { 96 | start: [0, 1.5], 97 | end: [4, 1.5], 98 | transparent: true 99 | }, 100 | { 101 | start: [4, 1.5], 102 | end: [4, 0], 103 | transparent: true 104 | }, 105 | 106 | // Cafeteria-Admin Hallway 107 | { 108 | start: [25.5, 9], 109 | end: [34.5, 9] 110 | }, 111 | { 112 | start: [34.5, 15], 113 | end: [25.5, 15] 114 | }, 115 | 116 | // Admin 117 | { 118 | start: [34.5, 9], 119 | end: [34.5, -3] 120 | }, 121 | { 122 | start: [34.5, -3], 123 | end: [40.5, -3] 124 | }, 125 | { 126 | start: [46.5, -3], 127 | end: [49.5, -3] 128 | }, 129 | { 130 | start: [49.5, -3], 131 | end: [49.5, 19.5] 132 | }, 133 | { 134 | start: [49.5, 19.5], 135 | end: [34.5, 19.5] 136 | }, 137 | { 138 | start: [34.5, 19.5], 139 | end: [34.5, 15] 140 | }, 141 | { 142 | start: [34.5, 0], 143 | end: [37.5, -3] 144 | }, 145 | { 146 | start: [49.5, 13.5], 147 | end: [47.1, 13.5], 148 | transparent: true 149 | }, 150 | { 151 | start: [47.1, 13.5], 152 | end: [47.1, 18], 153 | transparent: true 154 | }, 155 | { 156 | start: [47.1, 18], 157 | end: [40.5, 18], 158 | transparent: true 159 | }, 160 | { 161 | start: [40.5, 18], 162 | end: [40.5, 19.5], 163 | transparent: true 164 | }, 165 | 166 | // Admin-Communications-Navigation Hallway 167 | { 168 | start: [40.5, -3], 169 | end: [40.5, -21] 170 | }, 171 | { 172 | start: [46.5, -21], 173 | end: [46.5, -15] 174 | }, 175 | { 176 | start: [46.5, -15], 177 | end: [57, -15] 178 | }, 179 | { 180 | start: [57, -9], 181 | end: [46.5, -9] 182 | }, 183 | { 184 | start: [46.5, -9], 185 | end: [46.5, -3] 186 | }, 187 | 188 | // Navigation 189 | { 190 | start: [57, -15], 191 | end: [57, -16.5] 192 | }, 193 | { 194 | start: [57, -16.5], 195 | end: [70.5, -16.5] 196 | }, 197 | { 198 | start: [70.5, -16.5], 199 | end: [70.5, -1.5] 200 | }, 201 | { 202 | start: [70.5, -1.5], 203 | end: [57, -1.5] 204 | }, 205 | { 206 | start: [57, -1.5], 207 | end: [57, -9] 208 | }, 209 | { 210 | start: [67.5, -16.5], 211 | end: [70.5, -13.5] 212 | }, 213 | { 214 | start: [70.5, -4.5], 215 | end: [67.5, -1.5] 216 | }, 217 | 218 | // Communications 219 | { 220 | start: [40.5, -15], 221 | end: [21, -15] 222 | }, 223 | { 224 | start: [25.5, -15], 225 | end: [25.5, -28.5], 226 | }, 227 | { 228 | start: [25.5, -33], 229 | end: [49.5, -33] 230 | }, 231 | { 232 | start: [49.5, -33], 233 | end: [49.5, -21] 234 | }, 235 | { 236 | start: [49.5, -21], 237 | end: [46.5, -21] 238 | }, 239 | { 240 | start: [40.5, -21], 241 | end: [37.5, -21] 242 | }, 243 | { 244 | start: [37.5, -21], 245 | end: [31.5, -15] 246 | }, 247 | 248 | // Communications-Medbay-UpperEngine Hallway 249 | { 250 | start: [25.5, -28.5], 251 | end: [15, -28.5] 252 | }, 253 | { 254 | start: [15, -28.5], 255 | end: [15, -24] 256 | }, 257 | { 258 | start: [7.5, -24], 259 | end: [7.5, -28.5] 260 | }, 261 | { 262 | start: [7.5, -28.5], 263 | end: [-4.5, -28.5], 264 | }, 265 | { 266 | start: [-4.5, -28.5], 267 | end: [-4.5, -22.5] 268 | }, 269 | { 270 | start: [-4.5, -22.5], 271 | end: [0, -22.5] 272 | }, 273 | { 274 | start: [0, -18], 275 | end: [-10.5, -18] 276 | }, 277 | { 278 | start: [-10.5, -12], 279 | end: [-10.5, -28.5] 280 | }, 281 | { 282 | start: [-10.5, -28.5], 283 | end: [-22.5, -28.5] 284 | }, 285 | { 286 | start: [-22.5, -33], 287 | end: [25.5, -33] 288 | }, 289 | 290 | // Medbay 291 | { 292 | start: [15, -24], 293 | end: [21, -24] 294 | }, 295 | { 296 | start: [21, -24], 297 | end: [21, 0] 298 | }, 299 | { 300 | start: [21, -4.5], 301 | end: [7.5, -4.5] 302 | }, 303 | { 304 | start: [7.5, -4.5], 305 | end: [7.5, -16.5] 306 | }, 307 | { 308 | start: [7.5, -16.5], 309 | end: [0, -16.5] 310 | }, 311 | { 312 | start: [0, -16.5], 313 | end: [0, -18] 314 | }, 315 | { 316 | start: [0, -22.5], 317 | end: [0, -24] 318 | }, 319 | { 320 | start: [0, -24], 321 | end: [7.5, -24] 322 | }, 323 | { 324 | start: [17.2, -4.5], 325 | end: [17.2, -6.9], 326 | transparent: true 327 | }, 328 | { 329 | start: [17.2, -6.9], 330 | end: [7.5, -6.9], 331 | transparent: true 332 | }, 333 | 334 | // UpperEngine 335 | { 336 | start: [-22.5, -28.5], 337 | end: [-22.5, -15] 338 | }, 339 | { 340 | start: [-22.5, -15], 341 | end: [-25.5, -15] 342 | }, 343 | { 344 | start: [-31.5, -15], 345 | end: [-34.5, -15] 346 | }, 347 | { 348 | start: [-34.5, -15], 349 | end: [-34.5, -33] 350 | }, 351 | { 352 | start: [-34.5, -33], 353 | end: [-22.5, -33] 354 | }, 355 | { 356 | start: [-34.5, -30], 357 | end: [-31.5, -33] 358 | }, 359 | 360 | // UpperEngine-Electrical-LowerEngine Hallway 361 | { 362 | start: [-25.5, -15], 363 | end: [-25.5, -9] 364 | }, 365 | { 366 | start: [-25.5, -9], 367 | end: [-19.5, -9] 368 | }, 369 | { 370 | start: [-19.5, -3], 371 | end: [-25.5, -3] 372 | }, 373 | { 374 | start: [-25.5, -3], 375 | end: [-25.5, 3] 376 | }, 377 | { 378 | start: [-31.5, 3], 379 | end: [-31.5, -15] 380 | }, 381 | 382 | // Electrical 383 | { 384 | start: [-19.5, -9], 385 | end: [-19.5, -12] 386 | }, 387 | { 388 | start: [-19.5, -12], 389 | end: [7.5, -12] 390 | }, 391 | { 392 | start: [4.5, -12], 393 | end: [4.5, -4.5] 394 | }, 395 | { 396 | start: [4.5, -4.5], 397 | end: [-4.5, -4.5] 398 | }, 399 | { 400 | start: [-4.5, -4.5], 401 | end: [-4.5, 9] 402 | }, 403 | { 404 | start: [-4.5, -0], 405 | end: [-19.5, -0] 406 | }, 407 | { 408 | start: [-19.5, -0], 409 | end: [-19.5, -3] 410 | }, 411 | { 412 | start: [-16.5, -12], 413 | end: [-16.5, -3] 414 | }, 415 | { 416 | start: [-16.5, -3], 417 | end: [-13.5, -3] 418 | }, 419 | { 420 | start: [-13.5, -3], 421 | end: [-13.5, -12] 422 | }, 423 | { 424 | start: [-7.5, 0], 425 | end: [-7.5, -9], 426 | }, 427 | { 428 | start: [-7.5, -9], 429 | end: [-10.5, -9], 430 | }, 431 | { 432 | start: [-10.5, -9], 433 | end: [-10.5, 0], 434 | }, 435 | 436 | // LowerEngine 437 | { 438 | start: [-25.5, 3], 439 | end: [-22.5, 3] 440 | }, 441 | { 442 | start: [-22.5, 3], 443 | end: [-22.5, 9] 444 | }, 445 | { 446 | start: [-22.5, 15], 447 | end: [-22.5, 19.5] 448 | }, 449 | { 450 | start: [-22.5, 19.5], 451 | end: [-34.5, 19.5] 452 | }, 453 | { 454 | start: [-34.5, 19.5], 455 | end: [-34.5, 3] 456 | }, 457 | { 458 | start: [-34.5, 3], 459 | end: [-31.5, 3] 460 | }, 461 | { 462 | start: [-31.5, 19.5], 463 | end: [-34.5, 16.5] 464 | }, 465 | 466 | // LowerEngine-Cafeteria Hallway 467 | { 468 | start: [-22.5, 9], 469 | end: [0, 9] 470 | }, 471 | { 472 | start: [0, 15], 473 | end: [-22.5, 15] 474 | }, 475 | 476 | // Secret 477 | { 478 | start: [22.5, -10.5], 479 | end: [33, -10.5] 480 | }, 481 | { 482 | start: [33, -10.5], 483 | end: [33, -6] 484 | }, 485 | { 486 | start: [33, -6], 487 | end: [22.5, -6] 488 | }, 489 | { 490 | start: [22.5, -6], 491 | end: [22.5, -10.5] 492 | }, 493 | ], 494 | devices: [ 495 | { 496 | id: "vending", 497 | hitArea: [ 498 | [0, 0], 499 | [4, 0], 500 | [4, 4], 501 | [0, 4] 502 | ], 503 | graphics: { 504 | type: "vending-machine", 505 | location: [2, 0] 506 | } 507 | }, 508 | { 509 | id: "login", 510 | hitArea: [ 511 | [43.5, 16.5], 512 | [49.5, 16.5], 513 | [49.5, 19.5], 514 | [43.5, 19.5] 515 | ], 516 | graphics: { 517 | type: "monitor", 518 | location: [47.4, 18] 519 | } 520 | }, 521 | { 522 | id: "data-upload", 523 | hitArea: [ 524 | [34.5, 16.5], 525 | [37.5, 16.5], 526 | [37.5, 19.5], 527 | [34.5, 19.5] 528 | ], 529 | graphics: { 530 | type: "access-point", 531 | location: [34.5, 18], 532 | layer: 1 533 | } 534 | }, 535 | { 536 | id: "data-download", 537 | hitArea: [ 538 | [25.5, -18], 539 | [28.5, -18], 540 | [28.5, -15], 541 | [25.5, -15] 542 | ], 543 | graphics: { 544 | type: "access-point", 545 | location: [25.5, -16.5], 546 | layer: 1 547 | } 548 | }, 549 | { 550 | id: "centrifuge", 551 | hitArea: [ 552 | [10.5, -7.5], 553 | [13.5, -7.5], 554 | [13.5, -4.5], 555 | [10.5, -4.5] 556 | ], 557 | graphics: { 558 | type: "centrifuge", 559 | location: [12, -5.7] 560 | } 561 | }, 562 | { 563 | id: "keypad", 564 | hitArea: [ 565 | [-34.5, 3], 566 | [-31.5, 3], 567 | [-31.5, 4.5], 568 | [-34.5, 4.5] 569 | ], 570 | graphics: { 571 | type: "keypad", 572 | location: [-33, 1] 573 | } 574 | }, 575 | { 576 | id: "vent-cafeteria", 577 | hitArea: [ 578 | [15, 0], 579 | [18, 0], 580 | [18, 1.5], 581 | [15, 1.5] 582 | ], 583 | graphics: { 584 | type: "vent", 585 | location: [16.5, 0.75], 586 | layer: -1 587 | } 588 | }, 589 | { 590 | id: "vent-navigation", 591 | hitArea: [ 592 | [64.5, -16.5], 593 | [67.5, -16.5], 594 | [67.5, -15], 595 | [64.5, -15] 596 | ], 597 | graphics: { 598 | type: "vent", 599 | location: [66, -15.75], 600 | layer: -1 601 | } 602 | }, 603 | { 604 | id: "vent-communications", 605 | hitArea: [ 606 | [46.5, -33], 607 | [49.5, -33], 608 | [49.5, -31.5], 609 | [46.5, -31.5] 610 | ], 611 | graphics: { 612 | type: "vent", 613 | location: [48, -32.25], 614 | layer: -1 615 | } 616 | }, 617 | { 618 | id: "vent-medbay", 619 | hitArea: [ 620 | [18, -6], 621 | [21, -6], 622 | [21, -4.5], 623 | [18, -4.5] 624 | ], 625 | graphics: { 626 | type: "vent", 627 | location: [19.5, -5.25], 628 | layer: -1 629 | } 630 | }, 631 | { 632 | id: "vent-electrical-west", 633 | hitArea: [ 634 | [-13.5, -12], 635 | [-10.5, -12], 636 | [-10.5, -10.5], 637 | [-13.5, -10.5] 638 | ], 639 | graphics: { 640 | type: "vent", 641 | location: [-12, -11.25], 642 | layer: -1 643 | } 644 | }, 645 | { 646 | id: "vent-electrical-east", 647 | hitArea: [ 648 | [1.5, -6], 649 | [4.5, -6], 650 | [4.5, -4.5], 651 | [1.5, -4.5] 652 | ], 653 | graphics: { 654 | type: "vent", 655 | location: [3, -5.25], 656 | layer: -1 657 | } 658 | }, 659 | { 660 | id: "vent-lower-engine", 661 | hitArea: [ 662 | [-25.5, 3], 663 | [-22.5, 3], 664 | [-22.5, 4.5], 665 | [-25.5, 4.5] 666 | ], 667 | graphics: { 668 | type: "vent", 669 | location: [-24, 3.75], 670 | layer: -1 671 | } 672 | }, 673 | { 674 | id: "vent-hallway", 675 | hitArea: [ 676 | [-10.5, -19.5], 677 | [-7.5, -19.5], 678 | [-7.5, -18], 679 | [-10.5, -18] 680 | ], 681 | graphics: { 682 | type: "vent", 683 | location: [-9, -18.75], 684 | layer: -1 685 | } 686 | }, 687 | { 688 | id: "conspiracy-board", 689 | hitArea: [ 690 | [24, -10.5], 691 | [31.5, -10.5], 692 | [31.5, -9], 693 | [24, -9] 694 | ], 695 | graphics: { 696 | type: "conspiracy-board", 697 | location: [27.75, -12.5], 698 | hideInDark: true 699 | } 700 | }, 701 | { 702 | id: "emergency-button", 703 | hitArea: [ 704 | [9, 7.5], 705 | [16.5, 7.5], 706 | [16.5, 13.5], 707 | [9, 13.5] 708 | ], 709 | graphics: { 710 | type: "emergency-button", 711 | location: [12.75, 9.875] 712 | } 713 | }, 714 | { 715 | id: "satellite", 716 | hitArea: [ 717 | [-40.5, 36], 718 | [-34.5, 36], 719 | [-34.5, 42], 720 | [-40.5, 42] 721 | ], 722 | graphics: { 723 | type: "satellite", 724 | location: [-37.5, 39], 725 | layer: -1 726 | } 727 | } 728 | ], 729 | spawnPoints: { 730 | [Color.Red]: [11.5, 8], 731 | [Color.Blue]: [11.5, 13], 732 | [Color.Green]: [9, 8], 733 | [Color.Pink]: [16.5, 13], 734 | [Color.Orange]: [14, 8], 735 | [Color.Yellow]: [6.5, 13], 736 | [Color.Black]: [19, 13], 737 | [Color.White]: [16.5, 8], 738 | [Color.Purple]: [14, 13], 739 | [Color.Brown]: [9, 13], 740 | [Color.Cyan]: [19, 8], 741 | [Color.Lime]: [6.5, 8], 742 | }, 743 | graphics: { 744 | id: "shelld", 745 | scale: 1, 746 | origin: [1012.5, 1125], 747 | visibility: 16 748 | }, 749 | bounds: [ 750 | [-40.5, -45], 751 | [79.5, 42] 752 | ] 753 | }; 754 | -------------------------------------------------------------------------------- /total/VentController.ts: -------------------------------------------------------------------------------- 1 | import { SystemKind, SystemState } from "@amongst/game-common"; 2 | import { VentAction } from "@amongst/game-common"; 3 | 4 | import { Game } from "../Game.js"; 5 | import { Self } from "../Self.js"; 6 | import { MovementController } from "."; 7 | import { BasicController } from "./BasicController.js"; 8 | 9 | export class VentController extends BasicController { 10 | public readonly kind = SystemKind.Vent; 11 | public state: SystemState.Vent; 12 | 13 | public constructor(state: SystemState.Vent) { 14 | super(); 15 | this.state = state; 16 | } 17 | 18 | public attach(game: Game, self: Self) { 19 | super.attach(game, self); 20 | } 21 | 22 | public moveTo(game: Game, self: Self, target: string): void { 23 | if (this.state.devices.includes(target)) { 24 | const device = game.level.map.devices.get(target); 25 | 26 | if (device !== undefined) { 27 | this.queue.push(new VentAction({ target })); 28 | self.location = device.getBoundingBox().getCenter(); 29 | } 30 | } 31 | } 32 | 33 | public exit(game: Game, self: Self): void { 34 | this.queue.push(new VentAction({ exit: true, target: "" })); 35 | self.setController(game, new MovementController()); 36 | } 37 | 38 | public detach(game: Game, self: Self): void { 39 | super.detach(game, self); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /total/access-point-spritesheet.json: -------------------------------------------------------------------------------- 1 | { 2 | "frames": { 3 | "normal": { 4 | "frame": { 5 | "x": 0, 6 | "y": 0, 7 | "w": 30, 8 | "h": 60 9 | }, 10 | "sourceSize": { 11 | "w": 30, 12 | "h": 60 13 | }, 14 | "anchor": { 15 | "x": 10, 16 | "y": 30 17 | } 18 | }, 19 | "highlighted": { 20 | "frame": { 21 | "x": 30, 22 | "y": 0, 23 | "w": 30, 24 | "h": 60 25 | }, 26 | "sourceSize": { 27 | "w": 30, 28 | "h": 60 29 | }, 30 | "anchor": { 31 | "x": 10, 32 | "y": 30 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /total/access-point.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/access-point.png -------------------------------------------------------------------------------- /total/arrow-spritesheet.json: -------------------------------------------------------------------------------- 1 | { 2 | "frames": { 3 | "normal": { 4 | "frame": { 5 | "x": 0, 6 | "y": 0, 7 | "w": 150, 8 | "h": 150 9 | }, 10 | "sourceSize": { 11 | "w": 150, 12 | "h": 150 13 | }, 14 | "anchor": { 15 | "x": 150, 16 | "y": 75 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /total/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/arrow.png -------------------------------------------------------------------------------- /total/button-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/button-down.png -------------------------------------------------------------------------------- /total/button-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/button-up.png -------------------------------------------------------------------------------- /total/centrifuge-closed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/centrifuge-closed.png -------------------------------------------------------------------------------- /total/centrifuge-loaded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/centrifuge-loaded.png -------------------------------------------------------------------------------- /total/centrifuge-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/centrifuge-open.png -------------------------------------------------------------------------------- /total/centrifuge-overworld-spritesheet.json: -------------------------------------------------------------------------------- 1 | { 2 | "frames": { 3 | "normal": { 4 | "frame": { 5 | "x": 0, 6 | "y": 0, 7 | "w": 75, 8 | "h": 75 9 | }, 10 | "sourceSize": { 11 | "w": 75, 12 | "h": 75 13 | }, 14 | "anchor": { 15 | "x": 37.5, 16 | "y": 54 17 | } 18 | }, 19 | "highlighted": { 20 | "frame": { 21 | "x": 75, 22 | "y": 0, 23 | "w": 75, 24 | "h": 75 25 | }, 26 | "sourceSize": { 27 | "w": 75, 28 | "h": 75 29 | }, 30 | "anchor": { 31 | "x": 37.5, 32 | "y": 54 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /total/centrifuge-overworld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/centrifuge-overworld.png -------------------------------------------------------------------------------- /total/close-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/close-button.png -------------------------------------------------------------------------------- /total/conspiracy-board-overworld-spritesheet.json: -------------------------------------------------------------------------------- 1 | { 2 | "frames": { 3 | "normal": { 4 | "frame": { 5 | "x": 0, 6 | "y": 0, 7 | "w": 150, 8 | "h": 90 9 | }, 10 | "sourceSize": { 11 | "w": 150, 12 | "h": 90 13 | }, 14 | "anchor": { 15 | "x": 75, 16 | "y": 45 17 | } 18 | }, 19 | "highlighted": { 20 | "frame": { 21 | "x": 150, 22 | "y": 0, 23 | "w": 150, 24 | "h": 90 25 | }, 26 | "sourceSize": { 27 | "w": 150, 28 | "h": 90 29 | }, 30 | "anchor": { 31 | "x": 75, 32 | "y": 45 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /total/conspiracy-board-overworld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/conspiracy-board-overworld.png -------------------------------------------------------------------------------- /total/conspiracy-board.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/conspiracy-board.png -------------------------------------------------------------------------------- /total/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | location /api { 3 | proxy_pass http://game:59999; 4 | } 5 | 6 | location / { 7 | root /amongst; 8 | } 9 | } -------------------------------------------------------------------------------- /total/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "3.1" 2 | services: 3 | provide-credentials: 4 | image: postgres:13.6 5 | restart: always 6 | environment: 7 | POSTGRES_USER: postgres 8 | POSTGRES_PASSWORD: postgres 9 | POSTGRES_DB: postgres 10 | ports: 11 | - "5432:5432" 12 | volumes: 13 | - ./misc/provide-credentials/initdb.sql:/docker-entrypoint-initdb.d/initdb.sql 14 | purchase-snack: 15 | build: 16 | context: ./misc/purchase-snack 17 | image: amongst-vending 18 | environment: 19 | FLAG: ${PWN_FLAG-you_didnt_fill_in_the_flag} 20 | ports: 21 | - "35360:35360" 22 | security_opt: 23 | - seccomp=unconfined 24 | recalibrate-engine: 25 | build: 26 | context: ./misc/recalibrate-engine 27 | image: amongst-engine 28 | environment: 29 | FLAG: ${REV_FLAG-you_didnt_fill_in_the_flag} 30 | ports: 31 | - "25581:25581" 32 | game: 33 | build: 34 | context: . 35 | image: amongst-game 36 | environment: 37 | PROVIDE_CREDENTIALS_CONNECTION_STRING: postgresql://amongst:amongst@provide-credentials:5432/postgres 38 | PURCHASE_SNACK_HOST: purchase-snack 39 | PURCHASE_SNACK_PORT: 35360 40 | RECALIBRATE_ENGINE_HOST: recalibrate-engine 41 | RECALIBRATE_ENGINE_PORT: 25581 42 | BIND_ADDRESS: 0.0.0.0 43 | HOST: ${AMONGST_HOST} 44 | CRYPTO_FLAG: ${CRYPTO_FLAG} 45 | CONSPIRACY_FLAG: ${CONSPIRACY_FLAG} 46 | SATELLITE_FLAG: ${SATELLITE_FLAG} 47 | EXPLORE_FLAG: ${EXPLORE_FLAG} 48 | ports: 49 | - "60000-60010:60000-60010" 50 | nginx: 51 | build: 52 | context: ./misc/nginx 53 | image: amongst-nginx 54 | ports: 55 | - "80:80" 56 | depends_on: 57 | - game 58 | restart: always 59 | -------------------------------------------------------------------------------- /total/dropship.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/dropship.png -------------------------------------------------------------------------------- /total/emergency-button-panel-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/emergency-button-panel-button.png -------------------------------------------------------------------------------- /total/emergency-button-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/emergency-button-panel.png -------------------------------------------------------------------------------- /total/emergency-button-spritesheet.json: -------------------------------------------------------------------------------- 1 | { 2 | "frames": { 3 | "normal": { 4 | "frame": { 5 | "x": 0, 6 | "y": 0, 7 | "w": 45, 8 | "h": 45 9 | }, 10 | "sourceSize": { 11 | "w": 45, 12 | "h": 45 13 | }, 14 | "anchor": { 15 | "x": 22.5, 16 | "y": 22.5 17 | } 18 | }, 19 | "highlighted": { 20 | "frame": { 21 | "x": 45, 22 | "y": 0, 23 | "w": 45, 24 | "h": 45 25 | }, 26 | "sourceSize": { 27 | "w": 45, 28 | "h": 45 29 | }, 30 | "anchor": { 31 | "x": 22.5, 32 | "y": 22.5 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /total/emergency-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/emergency-button.png -------------------------------------------------------------------------------- /total/emergency-meeting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/emergency-meeting.png -------------------------------------------------------------------------------- /total/engine: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/engine -------------------------------------------------------------------------------- /total/file-transfer-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/file-transfer-panel.png -------------------------------------------------------------------------------- /total/folder-closed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/folder-closed.png -------------------------------------------------------------------------------- /total/folder-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/folder-open.png -------------------------------------------------------------------------------- /total/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./controllers/index.js"; 2 | export { Game } from "./Game.js"; 3 | export { Level } from "./Level.js"; 4 | export { Player } from "./Player.js"; 5 | -------------------------------------------------------------------------------- /total/initdb.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE users (id serial primary key, username text, password text); 2 | INSERT INTO users (username, password) VALUES ('zuck', 'hunter2'); 3 | INSERT INTO users (username, password) VALUES ('randall', 'correcthorsebatterystaple'); 4 | INSERT INTO users (username, password) VALUES ('richard', 'cowabunga'); 5 | 6 | CREATE TABLE flag (flag text); 7 | INSERT INTO flag (flag) VALUES ('PCTF{SAMPLE_WEB_FLAG}'); 8 | 9 | CREATE USER amongst WITH PASSWORD 'amongst'; 10 | GRANT SELECT ON ALL TABLES IN SCHEMA public TO amongst; 11 | -------------------------------------------------------------------------------- /total/keypad-overworld-spritesheet.json: -------------------------------------------------------------------------------- 1 | { 2 | "frames": { 3 | "normal": { 4 | "frame": { 5 | "x": 0, 6 | "y": 0, 7 | "w": 30, 8 | "h": 45 9 | }, 10 | "sourceSize": { 11 | "w": 30, 12 | "h": 45 13 | }, 14 | "anchor": { 15 | "x": 15, 16 | "y": 22.5 17 | } 18 | }, 19 | "highlighted": { 20 | "frame": { 21 | "x": 30, 22 | "y": 0, 23 | "w": 30, 24 | "h": 45 25 | }, 26 | "sourceSize": { 27 | "w": 30, 28 | "h": 45 29 | }, 30 | "anchor": { 31 | "x": 15, 32 | "y": 22.5 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /total/keypad-overworld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/keypad-overworld.png -------------------------------------------------------------------------------- /total/keypad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/keypad.png -------------------------------------------------------------------------------- /total/login-button-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/login-button-down.png -------------------------------------------------------------------------------- /total/login-button-hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/login-button-hover.png -------------------------------------------------------------------------------- /total/login-button-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/login-button-up.png -------------------------------------------------------------------------------- /total/login-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/login-screen.png -------------------------------------------------------------------------------- /total/monitor-spritesheet.json: -------------------------------------------------------------------------------- 1 | { 2 | "frames": { 3 | "normal": { 4 | "frame": { 5 | "x": 0, 6 | "y": 0, 7 | "w": 75, 8 | "h": 120 9 | }, 10 | "sourceSize": { 11 | "w": 75, 12 | "h": 120 13 | }, 14 | "anchor": { 15 | "x": 20, 16 | "y": 80 17 | } 18 | }, 19 | "highlighted": { 20 | "frame": { 21 | "x": 75, 22 | "y": 0, 23 | "w": 75, 24 | "h": 120 25 | }, 26 | "sourceSize": { 27 | "w": 75, 28 | "h": 120 29 | }, 30 | "anchor": { 31 | "x": 20, 32 | "y": 80 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /total/monitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/monitor.png -------------------------------------------------------------------------------- /total/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@amongst/game-common", 3 | "packageManager": "yarn@3.1.1", 4 | "main": "dist/index.js", 5 | "types": "dist/index.d.ts", 6 | "type": "module", 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc --watch" 10 | }, 11 | "devDependencies": { 12 | "typescript": "^4.5.5" 13 | }, 14 | "dependencies": { 15 | "@amongst/geometry": "workspace:^", 16 | "@zensors/sheriff": "^2.1.1", 17 | "immutable": "^4.0.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /total/post-it.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/post-it.png -------------------------------------------------------------------------------- /total/satellite-spritesheet.json: -------------------------------------------------------------------------------- 1 | { 2 | "frames": { 3 | "normal": { 4 | "frame": { 5 | "x": 0, 6 | "y": 0, 7 | "w": 150, 8 | "h": 150 9 | }, 10 | "sourceSize": { 11 | "w": 150, 12 | "h": 150 13 | }, 14 | "anchor": { 15 | "x": 75, 16 | "y": 75 17 | } 18 | }, 19 | "highlighted": { 20 | "frame": { 21 | "x": 150, 22 | "y": 0, 23 | "w": 150, 24 | "h": 150 25 | }, 26 | "sourceSize": { 27 | "w": 150, 28 | "h": 150 29 | }, 30 | "anchor": { 31 | "x": 75, 32 | "y": 75 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /total/satellite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/satellite.png -------------------------------------------------------------------------------- /total/shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/shadow.png -------------------------------------------------------------------------------- /total/shelld-overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/shelld-overlay.png -------------------------------------------------------------------------------- /total/shelld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/shelld.png -------------------------------------------------------------------------------- /total/shipmate-spritesheet.json: -------------------------------------------------------------------------------- 1 | { 2 | "frames": { 3 | "idle": { 4 | "frame": { 5 | "x": 0, 6 | "y": 0, 7 | "w": 80, 8 | "h": 120 9 | }, 10 | "sourceSize": { 11 | "w": 80, 12 | "h": 120 13 | }, 14 | "anchor": { 15 | "x": 40, 16 | "y": 112 17 | } 18 | }, 19 | "walk01": { 20 | "frame": { 21 | "x": 80, 22 | "y": 0, 23 | "w": 80, 24 | "h": 120 25 | }, 26 | "sourceSize": { 27 | "w": 80, 28 | "h": 120 29 | }, 30 | "anchor": { 31 | "x": 40, 32 | "y": 112 33 | } 34 | }, 35 | "walk02": { 36 | "frame": { 37 | "x": 160, 38 | "y": 0, 39 | "w": 80, 40 | "h": 120 41 | }, 42 | "sourceSize": { 43 | "w": 80, 44 | "h": 120 45 | }, 46 | "anchor": { 47 | "x": 40, 48 | "y": 112 49 | } 50 | }, 51 | "walk03": { 52 | "frame": { 53 | "x": 240, 54 | "y": 0, 55 | "w": 80, 56 | "h": 120 57 | }, 58 | "sourceSize": { 59 | "w": 80, 60 | "h": 120 61 | }, 62 | "anchor": { 63 | "x": 40, 64 | "y": 112 65 | } 66 | }, 67 | "walk04": { 68 | "frame": { 69 | "x": 0, 70 | "y": 120, 71 | "w": 80, 72 | "h": 120 73 | }, 74 | "sourceSize": { 75 | "w": 80, 76 | "h": 120 77 | }, 78 | "anchor": { 79 | "x": 40, 80 | "y": 112 81 | } 82 | }, 83 | "walk05": { 84 | "frame": { 85 | "x": 80, 86 | "y": 120, 87 | "w": 80, 88 | "h": 120 89 | }, 90 | "sourceSize": { 91 | "w": 80, 92 | "h": 120 93 | }, 94 | "anchor": { 95 | "x": 40, 96 | "y": 112 97 | } 98 | }, 99 | "walk06": { 100 | "frame": { 101 | "x": 160, 102 | "y": 120, 103 | "w": 80, 104 | "h": 120 105 | }, 106 | "sourceSize": { 107 | "w": 80, 108 | "h": 120 109 | }, 110 | "anchor": { 111 | "x": 40, 112 | "y": 112 113 | } 114 | }, 115 | "walk07": { 116 | "frame": { 117 | "x": 240, 118 | "y": 120, 119 | "w": 80, 120 | "h": 120 121 | }, 122 | "sourceSize": { 123 | "w": 80, 124 | "h": 120 125 | }, 126 | "anchor": { 127 | "x": 40, 128 | "y": 112 129 | } 130 | }, 131 | "walk08": { 132 | "frame": { 133 | "x": 0, 134 | "y": 240, 135 | "w": 80, 136 | "h": 120 137 | }, 138 | "sourceSize": { 139 | "w": 80, 140 | "h": 120 141 | }, 142 | "anchor": { 143 | "x": 40, 144 | "y": 112 145 | } 146 | }, 147 | "walk09": { 148 | "frame": { 149 | "x": 80, 150 | "y": 240, 151 | "w": 80, 152 | "h": 120 153 | }, 154 | "sourceSize": { 155 | "w": 80, 156 | "h": 120 157 | }, 158 | "anchor": { 159 | "x": 40, 160 | "y": 112 161 | } 162 | }, 163 | "walk10": { 164 | "frame": { 165 | "x": 160, 166 | "y": 240, 167 | "w": 80, 168 | "h": 120 169 | }, 170 | "sourceSize": { 171 | "w": 80, 172 | "h": 120 173 | }, 174 | "anchor": { 175 | "x": 40, 176 | "y": 112 177 | } 178 | }, 179 | "walk11": { 180 | "frame": { 181 | "x": 240, 182 | "y": 240, 183 | "w": 80, 184 | "h": 120 185 | }, 186 | "sourceSize": { 187 | "w": 80, 188 | "h": 120 189 | }, 190 | "anchor": { 191 | "x": 40, 192 | "y": 112 193 | } 194 | }, 195 | "walk12": { 196 | "frame": { 197 | "x": 0, 198 | "y": 360, 199 | "w": 80, 200 | "h": 120 201 | }, 202 | "sourceSize": { 203 | "w": 80, 204 | "h": 120 205 | }, 206 | "anchor": { 207 | "x": 40, 208 | "y": 112 209 | } 210 | }, 211 | "vent01": { 212 | "frame": { 213 | "x": 80, 214 | "y": 360, 215 | "w": 80, 216 | "h": 120 217 | }, 218 | "sourceSize": { 219 | "w": 80, 220 | "h": 120 221 | }, 222 | "anchor": { 223 | "x": 40, 224 | "y": 112 225 | } 226 | }, 227 | "vent02": { 228 | "frame": { 229 | "x": 160, 230 | "y": 360, 231 | "w": 80, 232 | "h": 120 233 | }, 234 | "sourceSize": { 235 | "w": 80, 236 | "h": 120 237 | }, 238 | "anchor": { 239 | "x": 40, 240 | "y": 112 241 | } 242 | }, 243 | "vent03": { 244 | "frame": { 245 | "x": 240, 246 | "y": 360, 247 | "w": 80, 248 | "h": 120 249 | }, 250 | "sourceSize": { 251 | "w": 80, 252 | "h": 120 253 | }, 254 | "anchor": { 255 | "x": 40, 256 | "y": 112 257 | } 258 | }, 259 | "vent04": { 260 | "frame": { 261 | "x": 0, 262 | "y": 480, 263 | "w": 80, 264 | "h": 120 265 | }, 266 | "sourceSize": { 267 | "w": 80, 268 | "h": 120 269 | }, 270 | "anchor": { 271 | "x": 40, 272 | "y": 112 273 | } 274 | }, 275 | "vent05": { 276 | "frame": { 277 | "x": 80, 278 | "y": 480, 279 | "w": 80, 280 | "h": 120 281 | }, 282 | "sourceSize": { 283 | "w": 80, 284 | "h": 120 285 | }, 286 | "anchor": { 287 | "x": 40, 288 | "y": 112 289 | } 290 | }, 291 | "vent06": { 292 | "frame": { 293 | "x": 160, 294 | "y": 480, 295 | "w": 80, 296 | "h": 120 297 | }, 298 | "sourceSize": { 299 | "w": 80, 300 | "h": 120 301 | }, 302 | "anchor": { 303 | "x": 40, 304 | "y": 112 305 | } 306 | }, 307 | "blank": { 308 | "frame": { 309 | "x": 240, 310 | "y": 480, 311 | "w": 80, 312 | "h": 120 313 | }, 314 | "sourceSize": { 315 | "w": 80, 316 | "h": 120 317 | }, 318 | "anchor": { 319 | "x": 40, 320 | "y": 112 321 | } 322 | } 323 | }, 324 | "animations": { 325 | "idle": ["idle"], 326 | "walk": ["walk01", "walk02", "walk03", "walk04", "walk05", "walk06", "walk07", "walk08", "walk09", "walk10", "walk11", "walk12"], 327 | "vent_enter": ["vent01", "vent02", "vent03", "vent04", "vent05", "vent06", "blank"], 328 | "vent_exit": ["blank", "vent06", "vent05", "vent04", "vent03", "vent02", "vent01"] 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /total/shipmate-spritesheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/shipmate-spritesheet.png -------------------------------------------------------------------------------- /total/snacks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/snacks.png -------------------------------------------------------------------------------- /total/start-panel-spritesheet.json: -------------------------------------------------------------------------------- 1 | { 2 | "frames": { 3 | "normal": { 4 | "frame": { 5 | "x": 0, 6 | "y": 0, 7 | "w": 60, 8 | "h": 45 9 | }, 10 | "sourceSize": { 11 | "w": 60, 12 | "h": 45 13 | }, 14 | "anchor": { 15 | "x": 30, 16 | "y": 22.5 17 | } 18 | }, 19 | "highlighted": { 20 | "frame": { 21 | "x": 0, 22 | "y": 45, 23 | "w": 60, 24 | "h": 45 25 | }, 26 | "sourceSize": { 27 | "w": 60, 28 | "h": 45 29 | }, 30 | "anchor": { 31 | "x": 30, 32 | "y": 22.5 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /total/start-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/start-panel.png -------------------------------------------------------------------------------- /total/test-tube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/test-tube.png -------------------------------------------------------------------------------- /total/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "dist", 6 | "composite": true 7 | }, 8 | "include": [ 9 | "src" 10 | ], 11 | "references": [ 12 | { "path": "../geometry" } 13 | ] 14 | } -------------------------------------------------------------------------------- /total/vending: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/vending -------------------------------------------------------------------------------- /total/vending-bottom-layer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/vending-bottom-layer.png -------------------------------------------------------------------------------- /total/vending-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/vending-button.png -------------------------------------------------------------------------------- /total/vending-machine-spritesheet.json: -------------------------------------------------------------------------------- 1 | { 2 | "frames": { 3 | "normal": { 4 | "frame": { 5 | "x": 0, 6 | "y": 0, 7 | "w": 90, 8 | "h": 150 9 | }, 10 | "sourceSize": { 11 | "w": 90, 12 | "h": 150 13 | }, 14 | "anchor": { 15 | "x": 45, 16 | "y": 90 17 | } 18 | }, 19 | "highlighted": { 20 | "frame": { 21 | "x": 90, 22 | "y": 0, 23 | "w": 90, 24 | "h": 150 25 | }, 26 | "sourceSize": { 27 | "w": 90, 28 | "h": 150 29 | }, 30 | "anchor": { 31 | "x": 45, 32 | "y": 90 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /total/vending-machine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/vending-machine.png -------------------------------------------------------------------------------- /total/vending-top-layer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/vending-top-layer.png -------------------------------------------------------------------------------- /total/vent-spritesheet.json: -------------------------------------------------------------------------------- 1 | { 2 | "frames": { 3 | "normal": { 4 | "frame": { 5 | "x": 0, 6 | "y": 0, 7 | "w": 60, 8 | "h": 39 9 | }, 10 | "sourceSize": { 11 | "w": 60, 12 | "h": 39 13 | }, 14 | "anchor": { 15 | "x": 30, 16 | "y": 19.5 17 | } 18 | }, 19 | "highlighted": { 20 | "frame": { 21 | "x": 0, 22 | "y": 39, 23 | "w": 60, 24 | "h": 39 25 | }, 26 | "sourceSize": { 27 | "w": 60, 28 | "h": 30 29 | }, 30 | "anchor": { 31 | "x": 30, 32 | "y": 19.5 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /total/vent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/vent.png -------------------------------------------------------------------------------- /total/voting-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esthicodes/CTF_Challenge/78fb6d690397ab2a80bdbc64959150e221a29382/total/voting-screen.png -------------------------------------------------------------------------------- /total/xinetd.conf: -------------------------------------------------------------------------------- 1 | service problem 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = engine 8 | bind = 0.0.0.0 9 | server = /engine/engine 10 | type = UNLISTED 11 | port = 25581 12 | } 13 | -------------------------------------------------------------------------------- /ui.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "unicode" 8 | 9 | "github.com/gdamore/tcell/v2" 10 | "github.com/rivo/tview" 11 | 12 | "pppordle/check" 13 | "pppordle/game" 14 | ) 15 | 16 | var ( 17 | app = tview.NewApplication() 18 | pages = tview.NewPages() 19 | 20 | colorBlack = tcell.NewRGBColor(0x00, 0x00, 0x00) 21 | colorWhite = tcell.NewRGBColor(0xff, 0xff, 0xff) 22 | colorGreen = tcell.NewRGBColor(0x56, 0xb4, 0x4d) 23 | colorYellow = tcell.NewRGBColor(0xff, 0xd9, 0x67) 24 | colorLightGray = tcell.NewRGBColor(0xa8, 0xa8, 0xa8) 25 | colorGray = tcell.NewRGBColor(0x47, 0x47, 0x47) 26 | colorRed = tcell.NewRGBColor(0xff, 0x56, 0x56) 27 | ) 28 | 29 | func startUI() { 30 | pages.SetBackgroundColor(colorBlack) 31 | pages.AddPage("Level Selector", levelSelector(pages), true, true) 32 | 33 | app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { 34 | if event.Key() == tcell.KeyEscape { 35 | frontPage, _ := pages.GetFrontPage() 36 | if frontPage == "Level Selector" { 37 | app.Stop() 38 | return nil 39 | } else { 40 | pages.RemovePage("Level") 41 | pages.SwitchToPage("Level Selector") 42 | return nil 43 | } 44 | } 45 | 46 | return event 47 | }) 48 | 49 | err := app.SetRoot(pages, true). 50 | SetFocus(pages). 51 | Run() 52 | if err != nil { 53 | panic(err) 54 | } 55 | } 56 | 57 | func levelSelector(pages *tview.Pages) tview.Primitive { 58 | grid := tview.NewGrid(). 59 | SetRows(0, 5, 30, 0). 60 | SetColumns(0, 80, 0). 61 | SetBorders(false). 62 | SetGap(1, 1) 63 | 64 | selector := tview.NewModal(). 65 | SetText("Level Selector"). 66 | AddButtons([]string{"1", "2", "3", "4"}). 67 | SetDoneFunc(func(buttonIndex int, buttonLabel string) { 68 | var level tview.Primitive 69 | loading, loadingText := loading(buttonIndex + 1) 70 | go func() { 71 | level = generateLevel(buttonIndex+1, loadingText) 72 | app.QueueUpdateDraw(func() { 73 | pages.AddAndSwitchToPage("Level", level, true) 74 | }) 75 | }() 76 | pages.AddAndSwitchToPage("Loading", loading, true) 77 | }). 78 | SetBackgroundColor(colorGreen) 79 | 80 | grid.AddItem(title(), 1, 1, 1, 1, 0, 0, false) 81 | grid.AddItem(selector, 2, 1, 1, 1, 0, 0, true) 82 | 83 | return grid 84 | } 85 | 86 | func title() tview.Primitive { 87 | titleText := "PPPORDLE" 88 | grid := tview.NewGrid(). 89 | SetRows(0). 90 | SetColumns(0, 0, 0, 0, 0, 0, 0, 0). 91 | SetBorders(false). 92 | SetGap(1, 1) 93 | 94 | for i, l := range titleText { 95 | b := tview.NewButton("[::b]" + string(l)) 96 | b.SetLabelColor(colorBlack) 97 | switch i { 98 | case 0: 99 | b.SetBackgroundColor(colorGreen) 100 | case 1: 101 | b.SetBackgroundColor(colorYellow) 102 | case 2: 103 | b.SetBackgroundColor(colorLightGray) 104 | default: 105 | b.SetBackgroundColor(colorGray) 106 | b.SetLabelColor(colorWhite) 107 | } 108 | 109 | grid.AddItem(b, 0, i, 1, 1, 0, 0, false) 110 | } 111 | 112 | return grid 113 | } 114 | 115 | func loading(level int) (tview.Primitive, *tview.TextView) { 116 | modal := func(p tview.Primitive, width, height int) tview.Primitive { 117 | return tview.NewFlex(). 118 | AddItem(nil, 0, 1, false). 119 | AddItem(tview.NewFlex().SetDirection(tview.FlexRow). 120 | AddItem(nil, 0, 1, false). 121 | AddItem(p, height, 1, false). 122 | AddItem(nil, 0, 1, false), width, 1, false). 123 | AddItem(nil, 0, 1, false) 124 | } 125 | 126 | tv := tview.NewTextView().SetChangedFunc(func() { app.Draw() }) 127 | tv.SetBackgroundColor(colorGreen). 128 | SetTitle(fmt.Sprintf("[::b]Loading Level %d", level)). 129 | SetTitleColor(colorWhite). 130 | SetBorder(true) 131 | 132 | return modal(tv, 40, 5), tv 133 | } 134 | 135 | func generateLevel(level int, loadingText *tview.TextView) tview.Primitive { 136 | errorModal := tview.NewModal(). 137 | AddButtons([]string{"Ok"}). 138 | SetBackgroundColor(colorRed). 139 | SetDoneFunc(func(buttonIndex int, buttonLabel string) { 140 | pages.RemovePage("Level") 141 | pages.SwitchToPage("Level Selector") 142 | }) 143 | 144 | conn, err := startSession(level, loadingText) 145 | if err != nil { 146 | log.Println(err) 147 | errorModal.SetText(err.Error()) 148 | return errorModal 149 | } 150 | 151 | infoResult, err := makeRequest[*game.InfoResult](conn, game.Request{Type: game.RequestInfo}) 152 | if err != nil { 153 | log.Println(err) 154 | errorModal.SetText(err.Error()) 155 | return errorModal 156 | } 157 | log.Printf("received game info result: %+v", infoResult) 158 | 159 | candidateMap, candidateButtons := buildCandidates(infoResult.Candidates) 160 | state := State{ 161 | Guesses: infoResult.Guesses, 162 | WordLen: infoResult.Length, 163 | Conn: conn, 164 | GuessIndex: 0, 165 | LetterIndex: 0, 166 | Level: level, 167 | Candidates: candidateMap, 168 | } 169 | 170 | scale := 50 / state.WordLen 171 | 172 | var guessRows = make([]int, state.Guesses+6) 173 | guessRows[0] = 1 174 | guessRows[1] = 3 175 | guessRows[2] = 0 176 | guessRows[3] = 3 177 | for i := 0; i < state.Guesses; i++ { 178 | guessRows[i+4] = scale / 2 179 | } 180 | guessRows[len(guessRows)-2] = 0 181 | guessRows[len(guessRows)-1] = -5 182 | 183 | var guessCols = make([]int, state.WordLen+4) 184 | guessCols[0] = 0 185 | guessCols[1] = 0 186 | for i := 0; i < state.WordLen; i++ { 187 | guessCols[i+2] = scale 188 | } 189 | guessCols[len(guessCols)-2] = 0 190 | guessCols[len(guessCols)-1] = 0 191 | 192 | state.Message = messageBox() 193 | state.AlertChan = make(chan bool) 194 | go state.MessageAnimationHandler() 195 | 196 | grid := tview.NewGrid(). 197 | SetRows(guessRows...). 198 | SetColumns(guessCols...). 199 | SetBorders(false). 200 | SetGap(1, 1). 201 | AddItem(title(), 1, 2, 1, len(guessCols)-4, 0, 0, false). 202 | AddItem(candidateButtons, len(guessRows)-1, 1, 1, len(guessCols)-2, 0, 0, false). 203 | AddItem(state.Message, 3, 2, 1, len(guessCols)-4, 0, 0, false) 204 | 205 | for i := 0; i < state.Guesses; i++ { 206 | for j := 0; j < state.WordLen; j++ { 207 | inputLetter := tview.NewButton("") 208 | inputLetter.SetBackgroundColor(colorGray) 209 | inputLetter.SetBackgroundColorActivated(colorGray) 210 | inputLetter.SetLabelColorActivated(colorWhite) 211 | state.Letters = append(state.Letters, inputLetter) 212 | 213 | grid.AddItem(inputLetter, i+4, j+2, 1, 1, 0, 0, false) 214 | } 215 | } 216 | 217 | app.SetFocus(state.Letters[state.LetterIndex]) 218 | 219 | grid.SetInputCapture(gameboardInputHandler(&state)) 220 | 221 | return grid 222 | } 223 | 224 | func messageBox() *tview.Button { 225 | b := tview.NewButton("").SetLabelColor(colorBlack) 226 | b.SetBackgroundColor(colorBlack) 227 | return b 228 | } 229 | 230 | func buildCandidates(candidates []rune) (map[rune]*tview.Button, tview.Primitive) { 231 | candidateRange := 250 - 20 232 | rowRange := 28 - 8 233 | rowSize := ((len(candidates) * rowRange) / candidateRange) + 8 234 | log.Println("Row Size:", rowSize) 235 | 236 | grid := tview.NewGrid(). 237 | SetBorders(false). 238 | SetGap(1, 1) 239 | 240 | buttons := make(map[rune]*tview.Button) 241 | col := -1 242 | row := -1 243 | for i, c := range candidates { 244 | if i%rowSize == 0 { 245 | col = 0 246 | row += 1 247 | } 248 | 249 | newButton := tview.NewButton("[::b]" + string(c)) 250 | newButton.SetLabelColor(colorWhite) 251 | newButton.SetBackgroundColor(colorLightGray) 252 | buttons[c] = newButton 253 | grid.AddItem(newButton, row, col, 1, 1, 0, 0, false) 254 | col += 1 255 | } 256 | 257 | return buttons, grid 258 | } 259 | 260 | func gameboardInputHandler(state *State) func(event *tcell.EventKey) *tcell.EventKey { 261 | return func(event *tcell.EventKey) *tcell.EventKey { 262 | if state.Complete { 263 | pages.RemovePage("Level") 264 | pages.SwitchToPage("Level Selector") 265 | return nil 266 | } 267 | 268 | if event.Key() == tcell.KeyBackspace || event.Key() == tcell.KeyBackspace2 { 269 | removeLetter(state) 270 | return nil 271 | } 272 | 273 | if event.Key() == tcell.KeyCR || event.Key() == tcell.KeyEnter { 274 | sendGuess(state) 275 | return nil 276 | } 277 | 278 | if len(string(event.Rune())) > 0 && !unicode.IsSpace(event.Rune()) { 279 | addLetter(unicode.ToUpper(event.Rune()), state) 280 | return nil 281 | } 282 | 283 | return event 284 | } 285 | } 286 | 287 | func addLetter(letter rune, state *State) { 288 | state.CurrentLetter().SetLabel("[::b]" + string(letter)) 289 | 290 | if state.LetterIndex == state.WordLen-1 { 291 | return 292 | } 293 | state.LetterIndex = (state.LetterIndex + 1) % state.WordLen 294 | 295 | app.SetFocus(state.CurrentLetter()) 296 | } 297 | 298 | func removeLetter(state *State) { 299 | if state.LetterIndex == 0 { 300 | return 301 | } 302 | 303 | if state.LetterIndex == state.WordLen-1 && len(state.CurrentLetter().GetLabel()) != 0 { 304 | state.CurrentLetter().SetLabel("") 305 | return 306 | } 307 | 308 | state.LetterIndex = (state.LetterIndex - 1) % state.WordLen 309 | 310 | state.CurrentLetter().SetLabel("") 311 | app.SetFocus(state.CurrentLetter()) 312 | } 313 | 314 | func sendGuess(state *State) { 315 | guess := "" 316 | for _, l := range state.CurrentLetters() { 317 | styled := []rune(l.GetLabel()) 318 | if len(styled) != 6 { 319 | state.SetMessage("Not enough letters", true) 320 | return 321 | } 322 | guess += string(styled[len(styled)-1:]) 323 | } 324 | 325 | guessResult, err := makeRequest[*game.GuessResult](state.Conn, game.Request{ 326 | Type: game.RequestGuess, 327 | Data: guess, 328 | }) 329 | if err != nil { 330 | log.Println(err) 331 | state.SetMessage(err.Error(), true) 332 | return 333 | } 334 | 335 | if len(guessResult.Error) != 0 { 336 | log.Println(guessResult.Error) 337 | state.SetMessage(guessResult.Error, true) 338 | return 339 | } 340 | 341 | state.UpdateIndicators(guessResult.Indicators) 342 | 343 | if guessResult.Complete { 344 | state.Complete = true 345 | state.SetMessage(guessResult.CompleteMessage, false) 346 | log.Printf("level %d completed", state.Level) 347 | 348 | if state.Level < 4 { 349 | next := state.Level + 1 350 | err = os.WriteFile(fmt.Sprintf("certs/level%d.pem", next), guessResult.ClientCert.Cert, 0600) 351 | check.Fatal(fmt.Sprintf("failed to write level %d client certificate", next), err) 352 | err = os.WriteFile(fmt.Sprintf("certs/level%d.key", next), guessResult.ClientCert.Key, 0600) 353 | check.Fatal(fmt.Sprintf("failed to write level %d client key", next), err) 354 | } 355 | 356 | return 357 | } 358 | 359 | state.GuessIndex += 1 360 | state.LetterIndex = 0 361 | 362 | if state.GuessIndex >= state.Guesses { 363 | state.Complete = true 364 | state.SetMessage("Better luck next time", false) 365 | state.GuessIndex = state.Guesses - 1 366 | return 367 | } 368 | 369 | app.SetFocus(state.CurrentLetter()) 370 | log.Printf("received guess result: %+v", guessResult) 371 | } 372 | -------------------------------------------------------------------------------- /web/Amongst Ourselves: Hoaxer.md: -------------------------------------------------------------------------------- 1 | Amongst Ourselves: Hoaxer 2 | misc 3 | web 4 | PPP is proud to announce DLC for the Critically Acclaimed™ video game Amongst Ourselves™, only on Plaidiverse! Find out the true identity of the murderer, and explore never-before-seen areas of The Shelld and beyond! ...After you give us another $40, that is. (Note: handout and servers are identical to Amongst Ourselves: Shipmate.) 5 | -------------------------------------------------------------------------------- /web/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16.9.0 2 | 3 | WORKDIR /amongst 4 | ADD . . 5 | 6 | # Need to yarn install in order to have esbuild 7 | RUN yarn install --immutable 8 | 9 | RUN yarn build 10 | RUN yarn build-client 11 | WORKDIR /amongst/packages/server 12 | CMD ["yarn", "start"] 13 | -------------------------------------------------------------------------------- /web/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: binaries handout.tgz 2 | 3 | handout.tgz: binaries pcap 4 | TMPDIR=$$(mktemp -d) && \ 5 | tar --exclude-from .tarignore -czf handout-tmp.tgz . && \ 6 | mkdir -p "$$TMPDIR/amongst" && \ 7 | tar -xzf handout-tmp.tgz -C "$$TMPDIR/amongst" && \ 8 | echo 'The server has a different file here' > "$$TMPDIR/amongst/packages/game-server/assets/sus" && \ 9 | tar -czf handout.tgz -C "$$TMPDIR" . && \ 10 | rm -rf "$$TMPDIR" && \ 11 | rm -f handout-tmp.tgz 12 | 13 | binaries: misc/recalibrate-engine/engine misc/purchase-snack/vending 14 | 15 | pcap: misc/file-transfer/out.pcap 16 | 17 | misc/recalibrate-engine/engine: misc/recalibrate-engine/engine.c misc/recalibrate-engine/build.Dockerfile 18 | docker build misc/recalibrate-engine -f misc/recalibrate-engine/build.Dockerfile -t recalibrate-engine:build 19 | CONTAINER_ID=$$(docker create recalibrate-engine:build) ; \ 20 | docker cp $$CONTAINER_ID:/chal/engine misc/recalibrate-engine/engine ; \ 21 | docker rm $$CONTAINER_ID 22 | 23 | misc/purchase-snack/vending: misc/purchase-snack/vending.c misc/purchase-snack/build.Dockerfile 24 | docker build misc/purchase-snack -f misc/purchase-snack/build.Dockerfile -t purchase-snack:build 25 | CONTAINER_ID=$$(docker create purchase-snack:build) ; \ 26 | docker cp $$CONTAINER_ID:/chal/vending misc/purchase-snack/vending ; \ 27 | docker rm $$CONTAINER_ID 28 | 29 | misc/file-transfer/out.pcap: misc/file-transfer/build.Dockerfile misc/file-transfer/run.sh misc/file-transfer/sus.png 30 | docker build misc/file-transfer -f misc/file-transfer/build.Dockerfile -t file-transfer:build 31 | CONTAINER_ID=$$(docker run -d file-transfer:build) ; \ 32 | sleep 20 ; \ 33 | docker cp $$CONTAINER_ID:/out/tcpdump.pcap misc/file-transfer/out.pcap ; \ 34 | docker stop $$CONTAINER_ID ; \ 35 | docker rm $$CONTAINER_ID 36 | cp misc/file-transfer/out.pcap packages/game-server/assets/sus 37 | -------------------------------------------------------------------------------- /web/Vax Register/writeup.md: -------------------------------------------------------------------------------- 1 | Vax Register 2 | 3 | You've been tasked with a pentesting engagement on a COVID-19 vaccine pre-registration system, they've provided you with a mockup build of the website with source code and they've asked you to find a way to login as "admin". 4 | -------------------------------------------------------------------------------- /web/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "3.1" 2 | services: 3 | provide-credentials: 4 | image: postgres:13.6 5 | restart: always 6 | environment: 7 | POSTGRES_USER: postgres 8 | POSTGRES_PASSWORD: postgres 9 | POSTGRES_DB: postgres 10 | ports: 11 | - "5432:5432" 12 | volumes: 13 | - ./misc/provide-credentials/initdb.sql:/docker-entrypoint-initdb.d/initdb.sql 14 | purchase-snack: 15 | build: 16 | context: ./misc/purchase-snack 17 | image: amongst-vending 18 | environment: 19 | FLAG: ${PWN_FLAG-you_didnt_fill_in_the_flag} 20 | ports: 21 | - "35360:35360" 22 | security_opt: 23 | - seccomp=unconfined 24 | recalibrate-engine: 25 | build: 26 | context: ./misc/recalibrate-engine 27 | image: amongst-engine 28 | environment: 29 | FLAG: ${REV_FLAG-you_didnt_fill_in_the_flag} 30 | ports: 31 | - "25581:25581" 32 | game: 33 | build: 34 | context: . 35 | image: amongst-game 36 | environment: 37 | PROVIDE_CREDENTIALS_CONNECTION_STRING: postgresql://amongst:amongst@provide-credentials:5432/postgres 38 | PURCHASE_SNACK_HOST: purchase-snack 39 | PURCHASE_SNACK_PORT: 35360 40 | RECALIBRATE_ENGINE_HOST: recalibrate-engine 41 | RECALIBRATE_ENGINE_PORT: 25581 42 | BIND_ADDRESS: 0.0.0.0 43 | HOST: ${AMONGST_HOST} 44 | CRYPTO_FLAG: ${CRYPTO_FLAG} 45 | CONSPIRACY_FLAG: ${CONSPIRACY_FLAG} 46 | SATELLITE_FLAG: ${SATELLITE_FLAG} 47 | EXPLORE_FLAG: ${EXPLORE_FLAG} 48 | ports: 49 | - "60000-60010:60000-60010" 50 | nginx: 51 | build: 52 | context: ./misc/nginx 53 | image: amongst-nginx 54 | ports: 55 | - "80:80" 56 | depends_on: 57 | - game 58 | restart: always 59 | -------------------------------------------------------------------------------- /web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "amongst-ourselves", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "lint": "eslint packages", 7 | "watch": "tsc -b -w packages/data packages/game-client packages/game-common packages/game-server packages/geometry packages/messages packages/server", 8 | "build": "tsc -b packages/data packages/game-client packages/game-common packages/game-server packages/geometry packages/messages packages/server", 9 | "build-client": "cd packages/client && yarn build" 10 | }, 11 | "workspaces": [ 12 | "packages/*" 13 | ], 14 | "devDependencies": { 15 | "@types/eslint": "^8", 16 | "@typescript-eslint/eslint-plugin": "^5.12.0", 17 | "@typescript-eslint/parser": "^5.12.0", 18 | "eslint": "^8.9.0", 19 | "eslint-plugin-eslint-comments": "^3.2.0", 20 | "eslint-plugin-import": "^2.25.4", 21 | "eslint-plugin-jsdoc": "^37.9.4", 22 | "eslint-plugin-prefer-arrow": "^1.2.3", 23 | "eslint-plugin-simple-import-sort": "^7.0.0", 24 | "typescript": "^4.5.5" 25 | }, 26 | "packageManager": "yarn@3.1.1" 27 | } 28 | -------------------------------------------------------------------------------- /web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "es2020", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "esModuleInterop": true, 8 | "skipLibCheck": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "declaration": true 11 | } 12 | } 13 | --------------------------------------------------------------------------------