├── 2017 ├── README.md └── Transformer │ ├── README.md │ ├── ciphertext.zip.enc │ ├── ida.png │ └── transformer └── README.md /2017/README.md: -------------------------------------------------------------------------------- 1 | ## 2017 Rust CTF Writeup 2 | 3 | 4 | ## writeups 5 | 6 | - [VolgaCTF 2017 Quals: Transformer](./Transformer) 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /2017/Transformer/README.md: -------------------------------------------------------------------------------- 1 | ## transformer 2 | 3 | We've got a file that was processed with a binary called transformer. We really need the contents of this file. Can you help? 4 | 5 | ## 探索 6 | 7 | 在Mac上执行程序: 8 | 9 | [Transformer] ./transformer 10 | zsh: exec format error: ./transformer 11 | 12 | 使用file 查看 文件属性 13 | 14 | [Transformer] file transformer 15 | transformer: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=3ba9baed5d1738eef040f333ec9c4707c3aeabf4, with debug_info, not stripped 16 | 17 | 发现是 64 位 Linux 二进制文件,拿到Linux系统下运行 18 | 19 | ➜ Transformer git:(main) ✗ ./transformer 20 | Usage: transformer 21 | 22 | 23 | 使用ldd 看看调用的库 24 | 25 | ➜ Transformer git:(main) ✗ ldd ./transformer 26 | linux-vdso.so.1 (0x00007ffc0c1ed000) 27 | libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fdccaf82000) 28 | librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007fdccaf77000) 29 | libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fdccaf55000) 30 | libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fdccaf3b000) 31 | libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fdccad76000) 32 | /lib64/ld-linux-x86-64.so.2 (0x00007fdccb1f5000) 33 | 34 | 35 | 我们尝试调用一下这个程序,构造一个输入文件: 36 | 37 | ➜ Transformer git:(main) ✗ ./transformer input.txt output.txt 38 | ➜ Transformer git:(main) ✗ cat input.txt output.txt 39 | 123456 40 | uV���7�# 41 | 42 | 123456 输入,输出的内容看不懂,有乱码, 文件类型为data,文件大小由7 bit变成了12 bit 43 | 44 | 用IDA打开二进制文件,发现使用了RC5库 45 | 46 | ![](./ida.png) 47 | 48 | 49 | 50 | We have an unstripped ELF 64-bit Linux binary of something that looks like an encryption application, along with an encrypted file named ciphertext.zip.enc. 51 | 52 | After loading the binary in IDA, we immediately see it's written in Rust. We also have plenty debug info, which makes it more bearable. Before jumping to reversing, let's play around with it a bit to get a feel for it and maybe make our lives easier. 53 | 54 | Executing the program without arguments prints: 55 | 56 | Usage: transformer 57 | We try out different input sizes and find a pattern: 58 | 59 | Input size(s) Output size 60 | 0...7 12 61 | 8...15 20 62 | 16...23 28 63 | We suspect a 64-bit block cipher (with always at least one byte of padding) with 4 extra bytes somewhere. 64 | 65 | When trying longer inputs we notice part of the plaintext appears unencrypted in the output. Take for example the following input: 66 | 67 | ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz9876543210 68 | Which produces this output: 69 | 70 | 00000000: 37f8 9c55 79c7 e40d ec3f 5c05 e27c e952 7..Uy....?\..|.R 71 | 00000010: c564 1e78 1f84 f8d5 2551 4e41 595a 3031 .d.x....%QNAYZ01 72 | 00000020: 3233 3435 03bb 2823 aca5 1d78 23f8 4363 2345..(#...x#.Cc 73 | 00000030: 8dd7 a1cf c4b8 c116 a26b 35c2 7576 7778 .........k5.uvwx 74 | 00000040: 797a 3938 1126 fefd 0e22 5b1a f58c 38cf yz98.&..."[...8. 75 | 00000050: 550c ab30 U..0 76 | Notice YZ012345 at offset 0x1C and uvwxyz98 at offset 0x3C. Those are 64-bit blocks straight from the plaintext! The offset between those two substrings in the plaintext is 32 bytes, and sure enough 0x3C - 0x1C = 32. However, the offset of YZ012345 from the start of the plaintext is 24 bytes, while the offset of that same block from the start of the ciphertext is 28 bytes, i.e. it looks like those extra 4 bytes might be at the beginning of the ciphertext. 77 | 78 | We also notice that multiple runs with the same input produce different outputs each time. There's some randomization involved, and most likely the 4 extra bytes have something to do with it (this later turned out to be true). 79 | 80 | To recap, our best guess is: 4 extra bytes at the beginning, 64-bit block cipher, every fourth block is left unencrypted, there's some kind of randomization. 81 | 82 | Now that we have some idea of what we're dealing with, let's get to reversing. The actually interesting functions seem to be: 83 | 84 | transformer::main::he40168f1b93e998b 85 | transformer::mode::encrypt_crt_ecb::hd9b87ece0c780db7 86 | transformer::mode::encrypt_thread::h3e003e4395b60fbd 87 | transformer::rc5::Rc5::encrypt_block::hc307a8b30c5c81e1 88 | transformer::rc5::Rc5::new::h30d6eb4c1bfeb265 89 | transformer::rc5::data_to_s::he50325ddf278b94d 90 | RC5 is a block cipher with variable key size, block size and number of rounds, all of which are unknown to us. The block size is twice the word size, which is a tweakable parameter. In our case, with 64-bit blocks, we'd have a 32-bit word size. Also, encrypt_crt_ecb could be a misspelling of encrypt_ctr_ecb, i.e. counter mode encryption. 91 | 92 | Looking at Rc5::new we notice some interesting constants: 93 | 94 | .text:7AD0 mov dword ptr [r13+0], 0B7E15163h 95 | ... 96 | .text:7B01 add eax, 9E3779B9h 97 | Those are the RC5 key expansion magic constants for a 32-bit word size, indeed. By comparing to the RC5 algorithm, we reverse Rc5::new and Rc5::encrypt_block to gain more information about how they're called and on the structures used to maintain context. 98 | 99 | Rc5::new takes (in order) an RC5 context struct, the number of rounds and the key (in a wrapper struct) as arguments. It initializes the RC5 context through the key expansion algorithm. Due to all the wrappers it wasn't obvious where things came from. We breakpointed on the function and saw that the number of rounds was always 16. We also noticed that two different keys were being used. 100 | 101 | Rc5::encrypt_block takes (in order) an RC5 context struct (initialized by Rc5::new), the higher 32 bits of the plaintext block and the lower 32 bits of the plaintext block. It returns the encrypted 64-bit block. 102 | 103 | Those functions are called from mode::encrypt_thread. It accepts (amongst other things) two keys and two 32-bit block halves. It creates two RC5 ciphers, one for each key. The first key is used to encrypt the block passed in the arguments. The second key is used to encrypt a block read from an input buffer. Then, the two encrypted blocks are XORed and the result is written to an output buffer. 104 | 105 | Helping ourselves with a debugger, we see that the input blocks are our input data and the output blocks are written to the output file. Also, the blocks passed as arguments look like this: 106 | 107 | pt[0] = 0x798d5d19, pt[1] = 0x0 108 | pt[0] = 0x798d5d19, pt[1] = 0x1 109 | pt[0] = 0x798d5d19, pt[1] = 0x2 110 | pt[0] = 0x798d5d19, pt[1] = 0x4 111 | pt[0] = 0x798d5d19, pt[1] = 0x5 112 | pt[0] = 0x798d5d19, pt[1] = 0x6 113 | pt[0] = 0x798d5d19, pt[1] = 0x8 114 | ... 115 | That's a counter mode! The high 32-bit half is fixed, while the lower 32-bit half is a counter starting from zero. Notice how every fourth block is skipped, but the counter is still incremented. 116 | 117 | The fixed high half matches the first 4 bytes of the output file. It's generated randomly in mode::encrypt_crt_ecb. 118 | 119 | The only missing piece of the puzzle are the encryption keys. Those are passed in a pretty obvious way from main to mode::encrypt_crt_ecb (which then hands them over to mode::encrypt_thread). They are: 120 | 121 | 1st key: A0 93 91 A8 CA 87 39 5C 122 | 2nd key: 86 5F AF 32 60 95 71 74 123 | We can finally write a decryption tool (RC5 implementation from here): 124 | 125 | #!/usr/bin/python3 126 | 127 | from RC5 import RC5 128 | import struct 129 | import sys 130 | 131 | KEY_1 = bytes.fromhex('A09391A8CA87395C') 132 | KEY_2 = bytes.fromhex('865FAF3260957174') 133 | 134 | with open(sys.argv[1], 'rb') as f: 135 | data = f.read() 136 | 137 | ctr_seed = data[:4] 138 | blocks = [data[i:i+8] for i in range(4, len(data), 8)] 139 | 140 | # 64bit blocks, 16 rounds 141 | rc5_1 = RC5(32, 16, KEY_1) 142 | rc5_2 = RC5(32, 16, KEY_2) 143 | 144 | pt = b'' 145 | for i in range(len(blocks)): 146 | block = blocks[i] 147 | if i % 4 == 3: 148 | # every fourth block is not encrypted 149 | # counter is still advanced 150 | block_pt = block 151 | else: 152 | ctr_pt = ctr_seed + struct.pack(' plaintext.zip 162 | 163 | $ file plaintext.zip 164 | plaintext.zip: Zip archive data, at least v2.0 to extract 165 | 166 | $ unzip plaintext.zip -d plaintext 167 | Archive: plaintext.zip 168 | inflating: plaintext/plaintext.txt 169 | 170 | $ cat plaintext/plaintext.txt 171 | This document defines four ciphers with enough detail to ensure interoperability between different implementations. The first cipher is the raw RC5 block cipher. The RC5 cipher takes a fixed size input block and produces a fixed sized output block using a transformation that depends on a key. The second cipher, RC5-CBC, is the Cipher Block Chaining (CBC) mode for RC5. It can process messages whose length is a multiple of the RC5 block size. The third cipher, RC5- CBC-Pad, handles plaintext of any length, though the ciphertext will be longer than the plaintext by at most the size of a single RC5 block. The RC5-CTS cipher is the Cipher Text Stealing mode of RC5, which handles plaintext of any length and the ciphertext length matches the plaintext length. 172 | 173 | In the meantime your flag is VolgaCTF{Wh1te_b0x_crypto_i$_not_crYpto}. 174 | 175 | The RC5 cipher was invented by Professor Ronald L. Rivest of the Massachusetts Institute of Technology in 1994. It is a very fast and simple algorithm that is parameterized by the block size, the number of rounds, and key length. These parameters can be adjusted to meet different goals for security, performance, and exportability. 176 | 177 | RSA Data Security Incorporated has filed a patent application on the RC5 cipher and for trademark protection for RC5, RC5-CBC, RC5-CBC-Pad, RC5-CTS and assorted variations. -------------------------------------------------------------------------------- /2017/Transformer/ciphertext.zip.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xxg1413/rust-ctf/b9096736bdbfca12c6919c82e30873fa8d72a45b/2017/Transformer/ciphertext.zip.enc -------------------------------------------------------------------------------- /2017/Transformer/ida.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xxg1413/rust-ctf/b9096736bdbfca12c6919c82e30873fa8d72a45b/2017/Transformer/ida.png -------------------------------------------------------------------------------- /2017/Transformer/transformer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xxg1413/rust-ctf/b9096736bdbfca12c6919c82e30873fa8d72a45b/2017/Transformer/transformer -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rust ctfs & writeups 2 | 3 | rust ctf writeups 4 | 5 |
6 | 7 | more: [Rust CVEs](https://github.com/xxg1413/rust-security) 8 | 9 | ## tasks & writeups 10 | 11 | - VolgaCTF 2017 Quals: [Transformer](https://ctftime.org/task/3695) 12 |
13 | 14 | - Hack.lu CTF 2018:[Rusty CodePad](https://ctftime.org/task/6866) 15 | - Hack.lu CTF 2018: [FluxDOS](https://ctftime.org/writeup/11876) 16 | - N1CTF 2018: [memsafety](https://ctftime.org/task/5494) 17 | - Blaze CTF 2018: [vectors](https://ctftime.org/task/6001) 18 | - Google Capture The Flag 2018 (Quals): [DogeStore](https://ctftime.org/task/6226) 19 | - RuCTFE 2018:[radiowave](https://ctftime.org/task/7014) 20 | - 35C3 CTF 2018: [JuniorCTF - ultra secret](https://ctftime.org/task/7429) 21 |
22 | 23 | - hack.lu-ctf-2019: [Gambling Future](https://ctftime.org/task/9607) 24 | - De1CTF 2019:[BabyRust](https://ctftime.org/task/8920) 25 | - Google Capture The Flag 2019 (Quals):[Sandstone](https://ctftime.org/task/8800) 26 | - CSAW CTF Final Round 2019:[macrypto](https://ctftime.org/task/9840) 27 | - OpenCTF 2019:[rusty](https://ctftime.org/task/8985) 28 | - Facebook CTF 2019:[rusty_shop](https://ctftime.org/task/8663) 29 | - Insomni'hack teaser 2019:[1118daysober](https://ctftime.org/task/7459) 30 | - Insomni'hack teaser 2019:[beginner_reverse](https://ctftime.org/task/7455) 31 | - DEF CON CTF Qualifier 2019: [gloryhost](https://ctftime.org/task/8550) 32 |
33 | 34 | - BSidesSF 2020 CTF:[config-me](https://ctftime.org/task/10528) 35 | - Byte Bandits CTF 2020: [baby-rust](https://ctftime.org/task/11175) 36 | - CONFidence CTF 2020 Teaser: [crsty sndbx](https://ctftime.org/task/10688) 37 | - ångstromCTF 2020:[Just Rust](https://ctftime.org/task/10765) 38 | - Codegate CTF 2020 Preliminary:[RS](https://ctftime.org/task/10399) 39 | - redpwnCTF 2020: [tetanus](https://ctftime.org/task/12143) 40 | - RCTF 2020 : [rust-flag](https://ctftime.org/task/11738) 41 | - UIUCTF 2020: [CalATMity](https://ctftime.org/task/12409) 42 | - InCTF 2020: [jazz](https://ctftime.org/task/12629) 43 | - TAMUctf 2020: [LeJIT](https://ctftime.org/task/10870) 44 |
45 | 46 | 47 | 48 | --------------------------------------------------------------------------------