この大会は2021/2/18 21:00(JST)~2021/2/22 21:00(JST)に開催されました。
今回もチームで参戦。結果は3925点で1762チーム中53位でした。
自分で解けた問題をWriteupとして書いておきます。
Intro 1 (_Introduction 25)
問題にフラグが書いてあった。
flag{some_clever_text}
Intro 2 (_Introduction 25)
Doscordに入ると、#generalチャネルのトピックにフラグが書いてあった。
flag{you_f0und_m3}
Esoteric (Misc 25)
Brainf*ck言語。https://sange.fi/esoteric/brainfuck/impl/interp/i.htmlで実行する。
flag{wtf_is_brainfuck}
Quit messing with my flags (Misc 25)
161EBD7D45089B3446EE4E0D86DBCF92をmd5の逆変換として検索する。
https://md5.gromweb.com/?md5=161ebd7d45089b3446ee4e0d86dbcf92 ⇒P@ssw0rd
flag{P@ssw0rd}
One Byte at a Time (Misc 50)
$ nc challenges.ctfd.io 30468 Show me how much of the flag you know and I'll help you with the rest.' [flag]>f You seem to know the first 1 characters of the flag! XORing the next flag character with a random octet taken from some unknown IPv4 address I have... 0x1b $ nc challenges.ctfd.io 30468 Show me how much of the flag you know and I'll help you with the rest.' [flag]>fl You seem to know the first 2 characters of the flag! XORing the next flag character with a random octet taken from some unknown IPv4 address I have... 0x16 >>> 0x1b ^ ord('l') 119 >>> 0x16 ^ ord('a') 119 $ nc challenges.ctfd.io 30468 Show me how much of the flag you know and I'll help you with the rest.' [flag]> You seem to know the first 0 characters of the flag! XORing the next flag character with a random octet taken from some unknown IPv4 address I have... 0x76 >>> 0x76 ^ ord('f') 16 $ nc challenges.ctfd.io 30468 Show me how much of the flag you know and I'll help you with the rest.' [flag]> You seem to know the first 0 characters of the flag! XORing the next flag character with a random octet taken from some unknown IPv4 address I have... 0x64 >>> 0x64 ^ ord('f') 2 $ nc challenges.ctfd.io 30468 Show me how much of the flag you know and I'll help you with the rest.' [flag]> You seem to know the first 0 characters of the flag! XORing the next flag character with a random octet taken from some unknown IPv4 address I have... 0x11 >>> 0x11 ^ ord('f') 119
とりあえずXOR鍵は、16, 2, 119のどれか。プログラムで119の1点張りで復号する。
import socket def recvuntil(s, tail): data = '' while True: if tail in data: return data data += s.recv(1) key = 119 flag = '' c = '' while True: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('challenges.ctfd.io', 30468)) data = recvuntil(s, '>') print data + flag + c s.sendall(flag + c + '\n') data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data if data.startswith('You seem'): flag += c if 'character' not in data: break data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data code = eval(data) c = chr(code ^ key) else: data = recvuntil(s, '\n').rstrip() print data c = '' print flag
実行結果は以下の通り。
: Show me how much of the flag you know and I'll help you with the rest.' [flag]>flag{f0ll0w_th3_whit3_r@bb1t You seem to know the first 28 characters of the flag! XORing the next flag character with a random octet taken from some unknown IPv4 address I have... 0x0a Show me how much of the flag you know and I'll help you with the rest.' [flag]>flag{f0ll0w_th3_whit3_r@bb1t} You seem to know it all. Can't help you anymore flag{f0ll0w_th3_whit3_r@bb1t}
flag{f0ll0w_th3_whit3_r@bb1t}
Forwards from Grandma (Misc 100)
メールのSubjectにこう書いてある。
FWD: FWD: RE: FWD: FWD: RE: FWD: FWD: FWD: RE: RE: RE: FWD: { FWD: FWD: FWD: FWD: RE: RE: FWD: RE: RE: RE: FWD: FWD: FWD: FWD: FWD: FWD: FWD: FWD: FWD: FWD: RE: RE: FWD: RE: FWD: RE: RE: RE: RE: FWD: RE: FWD: FWD: } THIS IS HILARIOUS AND SO TRUE
FWD: を "."、RE: を"-"にすると、" "区切りでモールス信号になっているので、デコードする。
..-. .-.. .- --. {.. ..--.- -- .. ... ... ..--.- .- --- .-..}
flag{i_miss_aol}
Parsey Mcparser (Code 50)
指定したグループ名のユーザ名をリストで返すようスクリプトを組む。以下のスクリプトで通った。
def ParseNamesByGroup(blob, group_name): uname = [] arr = blob.split('+++,')[1:] for a in arr: data = a.split(',', 1)[1] info = data[1:-1].split(',[') for i in info: info = eval('{' + i.rstrip(']') + '}') group = info['Group'] if group == group_name: uname.append(info['user_name']) return uname data = raw_input() group_name = data.split('|')[0] blob = data.split('|')[1] result_names_list = ParseNamesByGroup(blob, group_name) print result_names_list
Random Encryption (Code 100)
コードの中に記載されているフラグで通った。
flag{n0t_that_r4ndom}
Random Encryption Fixed (Code 100)
seedを使った乱数なので、XOR鍵を割り出せる。XOR鍵を割り出したら、その鍵で復号する。
#!/usr/bin/python3 import random rand_list = [[249, 182, 79], [136, 198, 95], [159, 167, 6], [223, 136, 101], [66, 27, 77], [213, 234, 239], [25, 36, 53], [89, 113, 149], [65, 127, 119], [50, 63, 147], [204, 189, 228], [228, 229, 4], [64, 12, 191], [65, 176, 96], [185, 52, 207], [37, 24, 110], [62, 213, 244], [141, 59, 81], [166, 50, 189], [228, 5, 16], [59, 42, 251], [180, 239, 144], [13, 209, 132]] res = [184, 161, 235, 97, 140, 111, 84, 182, 162, 135, 76, 10, 69, 246, 195, 152, 133, 88, 229, 104, 111, 22, 39] seeds = [9925, 8861, 5738, 1649, 2696, 6926, 1839, 7825, 6434, 9699, 227, 7379, 9024, 817, 4022, 7129, 1096, 4149, 6147, 2966, 1027, 4350, 4272] flag = '' for i in range(len(res)): random.seed(seeds[i]) rands = [] for j in range(4): rands.append(random.randint(0, 255)) key = rands[i % 4] del rands[i % 4] assert rands == rand_list[i] flag += chr(res[i] ^ key) print(flag)
flag{Oppsie_LULZ_fixed}
Find Largest Triangle (Code 125)
三角形の面積は以下の式で求められる。
S = |(B - A) × (C - A)| / 2 ※"×"は外積
これを使って、全組合せで面積の最大値を返すようスクリプトを組む。以下のスクリプトで通った。
import math import itertools def ext_prod(a, b): v_aa = a + a[0:2] v_bb = b + b[0:2] v_o =[] for i in range(len(a)): v_o.append(v_aa[i+1] * v_bb[i+2] - v_aa[i+2] * v_bb[i+1]) return v_o def calc_area(p1, p2, p3): pt1 = [e2 - e1 for e1, e2 in zip(p1, p2)] pt2 = [e3 - e1 for e1, e3 in zip(p1, p3)] prod = ext_prod(pt1, pt2) size = math.sqrt(sum([pow(x, 2) for x in prod])) area = size / 2 return area def FindLargestTriangleArea(points): max_area = 0 for p in itertools.combinations(points, 3): area = calc_area(p[0], p[1], p[2]) if max_area < area: max_area = area return max_area # Reading space delimited points from stdin # and building list of 3D points points_data = raw_input() points = [] for point in points_data.split(' '): point_xyz = point.split(',') points.append([int(point_xyz[0]), int(point_xyz[1]), int(point_xyz[2])]) # Compute Largest Triangle and Print Area rounded to nearest whole number area = FindLargestTriangleArea(points) print int(round(area))
We Need an Emulator (Code 150)
XOR, MOV, REVERSEのオペランドでレジスタもTRX, DRXしかないので、全パターンをプログラムで作成する。
def xor(a, b): if len(a) > len(b): s1 = a s2 = b + '\x00' * (len(a) - len(b)) else: s1 = a + '\x00' * (len(b) - len(a)) s2 = b return ''.join(chr(ord(x) ^ ord(y)) for x, y in zip(s1, s2)) with open('Crypto.asm', 'r') as f: lines = [line.rstrip() for line in f.readlines()] trx = 'GED\x03hG\x15&Ka =;\x0c\x1a31o*5M' drx = '' for line in lines: op = line.split(' ')[0] if op == 'XOR': arg1 = line.split(' ')[1] arg2 = line.split(' ')[2] if arg2 == 'TRX': arg2 = trx else: arg2 = drx if arg1 == 'TRX': trx = xor(trx, arg2) else: drx = xor(drx, arg2) elif op == 'MOV': arg1 = line.split(' ')[1] arg2 = line.split(' ')[2] if arg2 == 'TRX': arg2 = trx elif arg2 == 'DRX': arg2 = drx else: arg2 = eval(arg2) if arg1 == 'TRX': trx = arg2 else: drx = arg2 else: arg1 = line.split(' ')[1] if arg1 == 'TRX': trx = trx[::-1] else: drx = drx[::-1] print trx
flag{N1ce_Emul8tor!1}
The only tool you'll ever need (Reverse Engineering 25)
$ strings a.out | grep flag flag{str1ngs_FTW} Enter the password to get the flag:
flag{str1ngs_FTW}
Stay Away Creepy Crawlers (Web App 25)
http://167.71.246.232/robots.txtにアクセスすると、フラグが書いてあった。
User-agent: * Disallow: /admin/ # flag{mr_roboto}
flag{mr_roboto}
Source of All Evil (Web App 25)
HTMLソースを見ると、フラグが書いてあった。
flag{best_implants_ever}
Can't find it (Web App 25)
適当なパス(例:http://167.71.246.232/a)にアクセスすると、エラーページが表示され、フラグが書いてある。
flag{404_oh_no}
Show me what you got (Web App 25)
http://167.71.246.232/imagesにアクセスすると、indexが見えている。
http://167.71.246.232/images/aljdi3sd.txtにアクセスするとフラグが書いてあった。
flag{disable_directory_indexes}
Headers for you inspiration (Web App 25)
レスポンスヘッダにフラグがあった。
flag{headersftw}
Spring MVC 1 (Web App 25)
src/main/java/com/tenable/ctf/mvc/MainController.javaを見る。
@GetMapping("/main") public ModelAndView getMain() { ModelAndView modelAndView = new ModelAndView("flag"); modelAndView.addObject("flag", flags.getFlag("spring_mvc_1")); // get main return modelAndView; }
GETメソッドで/mainにアクセスすればよい。http://challenges.ctfd.io:30542/mainにアクセスすると、フラグが表示された。
flag{flag1_517d74}
Spring MVC 2 (Web App 25)
src/main/java/com/tenable/ctf/mvc/MainController.javaを見る。
@PostMapping("/main") public String postMain(@RequestParam(name="magicWord", required=false, defaultValue="") String magicWord, Model model) { if (magicWord.equals("please")) model.addAttribute("flag", flags.getFlag("spring_mvc_3")); // post main param else model.addAttribute("flag", flags.getFlag("spring_mvc_2")); // post main return "flag"; }
POSTメソッドでmagicWordに"please"以外の値をセットして/mainにアクセスすればよい。
$ curl http://challenges.ctfd.io:30542/main -d 'magicWord=a' <!DOCTYPE HTML> <html> <head> <title>Tenable CTF: Spring MVC</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <p >flag{flag2_de3981}</p> </body> </html>
flag{flag2_de3981}
Spring MVC 3 (Web App 25)
src/main/java/com/tenable/ctf/mvc/MainController.javaを見る。
@PostMapping("/main") public String postMain(@RequestParam(name="magicWord", required=false, defaultValue="") String magicWord, Model model) { if (magicWord.equals("please")) model.addAttribute("flag", flags.getFlag("spring_mvc_3")); // post main param else model.addAttribute("flag", flags.getFlag("spring_mvc_2")); // post main return "flag"; }
POSTメソッドでmagicWordに"please"をセットして/mainにアクセスすればよい。
$ curl http://challenges.ctfd.io:30542/main -d 'magicWord=please' <!DOCTYPE HTML> <html> <head> <title>Tenable CTF: Spring MVC</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <p >flag{flag3_0d431e}</p> </body> </html>
flag{flag3_0d431e}
Spring MVC 4 (Web App 25)
src/main/java/com/tenable/ctf/mvc/MainController.javaを見る。
@PostMapping(path = "/main", consumes = "application/json") public String postMainJson(Model model) { model.addAttribute("flag", flags.getFlag("spring_mvc_4")); // post main flag json return "flag"; }
POSTメソッドでapplication/jsonのコンテンツタイプを指定して/mainにアクセスすればよい。
$ curl http://challenges.ctfd.io:30542/main -H "Content-Type: application/json" -X POST <!DOCTYPE HTML> <html> <head> <title>Tenable CTF: Spring MVC</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <p >flag{flag4_695954}</p> </body> </html>
flag{flag4_695954}
Spring MVC 5 (Web App 25)
src/main/java/com/tenable/ctf/mvc/MainController.javaを見る。
@RequestMapping(path = "/main", method = RequestMethod.OPTIONS) public String optionsMain(Model model) { model.addAttribute("flag", flags.getFlag("spring_mvc_5")); // options main return "flag"; }
OPTIONSメソッドで/mainにアクセスすればよい。
$ curl http://challenges.ctfd.io:30542/main -X OPTIONS <!DOCTYPE HTML> <html> <head> <title>Tenable CTF: Spring MVC</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <p >flag{flag5_70102b}</p> </body> </html>
flag{flag5_70102b}
Spring MVC 6 (Web App 25)
src/main/java/com/tenable/ctf/mvc/MainController.javaを見る。
@RequestMapping(path = "/main", method = RequestMethod.GET, headers = "Magic-Word=please") public String headersMain(Model model) { model.addAttribute("flag", flags.getFlag("spring_mvc_6")); // headers main return "flag"; }
GETメソッドでHTTPヘッダに"Magic-Word: please"をセットして/mainにアクセスすればよい。
$ curl http://challenges.ctfd.io:30542/main -H 'Magic-Word: please' <!DOCTYPE HTML> <html> <head> <title>Tenable CTF: Spring MVC</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <p >flag{flag6_ca1ddf}</p> </body> </html>
flag{flag6_ca1ddf}
Ripper Doc (Web App 50)
Cookieのauthenticatedの値をfalseからtrueに変更して、http://167.71.246.232/certified_rippers.phpにアクセスすると、フラグが表示された。
flag{messing_with_cookies}
Protected Directory (Web App 50)
http://167.71.246.232/.htpasswdにアクセスする。
admin:$apr1$1U8G15kK$tr9xPqBn68moYoH4atbg20
パスワードクラックする。
$ john .htpasswd Warning: detected hash type "md5crypt", but the string is also recognized as "md5crypt-long" Use the "--format=md5crypt-long" option to force loading these as that type instead Using default input encoding: UTF-8 Loaded 1 password hash (md5crypt, crypt(3) $1$ (and variants) [MD5 256/256 AVX2 8x3]) Proceeding with single, rules:Single Press 'q' or Ctrl-C to abort, almost any other key for status Warning: Only 2 candidates buffered for the current salt, minimum 24 needed for performance. Warning: Only 20 candidates buffered for the current salt, minimum 24 needed for performance. Warning: Only 23 candidates buffered for the current salt, minimum 24 needed for performance. Warning: Only 21 candidates buffered for the current salt, minimum 24 needed for performance. Warning: Only 13 candidates buffered for the current salt, minimum 24 needed for performance. Almost done: Processing the remaining buffered candidate passwords, if any. Warning: Only 15 candidates buffered for the current salt, minimum 24 needed for performance. Proceeding with wordlist:/usr/share/john/password.lst, rules:Wordlist Warning: Only 4 candidates left, minimum 24 needed for performance. Proceeding with incremental:ASCII alesh16 (admin) 1g 0:00:00:11 DONE 3/3 (2021-02-19 11:29) 0.08532g/s 88568p/s 88568c/s 88568C/s alesio2..alemeis Use the "--show" option to display all of the cracked passwords reliably Session completed
http://167.71.246.232/adminにアクセスし、以下の認証情報で認証すると、フラグが表示された。
admin / alesh16
flag{cracked_the_password}
Hackerman (Stego 25)
$ strings silly_hacker.svg | grep flag style="font-size:1.25px;fill:#808080">flag{m1cr0dot}</tspan></text>
flag{m1cr0dot}
Secret Images (Stego 125)
各RGBのXORを取ってみると、フラグが現れた。
from PIL import Image o_img = Image.open('crypted1.png').convert('RGB') e_img = Image.open('crypted2.png').convert('RGB') w, h = o_img.size output_img = Image.new('RGB', (w, h), (255, 255, 255)) for y in range(h): for x in range(w): r1, g1, b1 = o_img.getpixel((x, y)) r2, g2, b2 = e_img.getpixel((x, y)) output_img.putpixel((x, y), (r1 ^ r2, g1 ^ g2, b1 ^ b2)) output_img.save('flag.png')
flag{otp_reuse_fail}
A3S Turtles (Stego 250)
$ fcrackzip -u -D -p dict/rockyou.txt turtles128.zip PASSWORD FOUND!!!!: pw == 0 $ unzip -P 0 turtles128.zip Archive: turtles128.zip inflating: turtles127.zip $ fcrackzip -u -D -p dict/rockyou.txt turtles127.zip PASSWORD FOUND!!!!: pw == 0 $ unzip -P 0 turtles127.zip Archive: turtles127.zip inflating: turtles126.zip $ fcrackzip -u -D -p dict/rockyou.txt turtles126.zip PASSWORD FOUND!!!!: pw == 1
パスワードは1桁の数字に決まっていそう。スクリプトを組んで試した結果はパスワードは0か1。turtle1.zipを解凍すると、key.pngが展開された。そこにはこう書いてある。
ed 57 0e 22 d4 58 e2 57 34 fc 08 d8 49 96 1d a9
問題のタイトルから考えると、AES暗号が関係しているのかもしれない。0, 1の128ビットのデータを暗号データとして、上記の鍵でAES-ECBの復号をしてみる。
import zipfile from Crypto.Cipher import AES def unzip_with_pwd(filename, path='.', pwd=''): with zipfile.ZipFile(filename, 'r') as zf: try: zf.extractall(path=path, pwd=pwd) return True except RuntimeError: return False def unpad(s): return s[:-ord(s[-1])] b_ct = '' for i in range(128, 0, -1): fname = 'turtles%d.zip' % i for j in [0, 1]: pwd = str(j) res = unzip_with_pwd(fname, pwd=pwd) if res: b_ct += str(j) break ct = ''.join([chr(int(b_ct[i:i+8], 2)) for i in range(0, len(b_ct), 8)]) key = 'ed570e22d458e25734fc08d849961da9'.decode('hex') cipher = AES.new(key, AES.MODE_ECB) flag = unpad(cipher.decrypt(ct)) print flag
flag{steg0_a3s}
Classic Crypto (Crypto 50)
Vigenere暗号。https://www.guballa.de/vigenere-solverで復号する。
There are many theories about us. That we’re anarchists, kids, crazy film-buffs that saw one too many superhero movies. The truth is, we are all these things. Anonymous is a symbol, like the flag a country flies. The flag is the symbol of the country. Our masks are our national identity. We are not Anonymous – we represent the ideals of Anonymous. Truth, freedom and the removal of censorship. Like any symbol, we affix it wherever we go, as you have seen from street protests. We have no leaders, civilians or soldiers. We are all one. We run operations because that is what the group decides to do. We choose targets because that is what the people who represent the ideals of Anonymous want to fight for. The world is in trouble. We see it every day – war, poverty, murder. Every day we are bombarded with news and images, as we sit at home safe in the knowledge that we are powerless, that “better” minds are dealing with the situation. But what if you could be the change you want to see? I’m twenty five years old. I went to school and college. I fought for my country then got a job and paid my taxes. If you met me on the street I wouldn’t even register on your radar. I am just another person in a sea of faces. But in cyberspace we are different. We helped free the people of Egypt. We helped fight against Israel as it attempted genocide. We exposed more than fifty thousand paedophiles around the world. We fought the drug cartels. We have taken to the streets to fight for the rights you are letting slip through your fingers. We are Anonymous. The flag is "flag{classicvigenere}" In today’s world we are seen as terrorists or at best dangerous anarchists. We’re called “cowards” and “posers” for hiding behind masks, but who is the real poser? We take away the face and leave only the message. Behind the mask we could be anyone, which is why we are judged by what we say and do, not who we are or what we have. We exist without nationality, skin colour or religious bias.
復号した文中にフラグがあった。
flag{classicvigenere}
Easy Peasy (Crypto 50)
base64デコード、hexデコード、rot13で復号すると、フラグになった。
enc = 'NzMgNzkgNmUgNzQgN2IgNzAgNjIgNjEgNzQgNjUgNmUgNjcgNjYgNWYgNmMgNjIgNjggNWYgNzQgNjIgNjcgNWYgN2EgNzIgN2Q=' dec = enc.decode('base64') print dec dec = dec.replace(' ', '').decode('hex') print dec flag = dec.decode('rot13') print flag
実行結果は以下の通り。
73 79 6e 74 7b 70 62 61 74 65 6e 67 66 5f 6c 62 68 5f 74 62 67 5f 7a 72 7d synt{pbatengf_lbh_tbg_zr} flag{congrats_you_got_me}
flag{congrats_you_got_me}
Netrunner Encryption (Crypto 200)
Source Codeをクリックすると、以下のようになっている。
<html> <body> <h1>Netrunner Encryption Tool</h1> <a href="netrun.txt">Source Code</a> <form method=post action="crypto.php"> <input type=text name="text_to_encrypt"> <input type="submit" name="do_encrypt" value="Encrypt"> </form> <?php function pad_data($data) { $flag = "flag{wouldnt_y0u_lik3_to_know}"; $pad_len = (16 - (strlen($data.$flag) % 16)); return $data . $flag . str_repeat(chr($pad_len), $pad_len); } if(isset($_POST["do_encrypt"])) { $cipher = "aes-128-ecb"; $iv = hex2bin('00000000000000000000000000000000'); $key = hex2bin('74657374696E676B6579313233343536'); echo "</br><br><h2>Encrypted Data:</h2>"; $ciphertext = openssl_encrypt(pad_data($_POST['text_to_encrypt']), $cipher, $key, 0, $iv); echo "<br/>"; echo "<b>$ciphertext</b>"; } ?> </body> </html>
<入力文字列><フラグ>
a 7ii97QpMPBNhknGD3igg4aKhzWdroplDGd0QSc/8pG5I/EjJ6OXYKXOVQPIDkUpB aa lCODIH4f3+rCvpYde8/oaAW0B7t/tMwBOtJadxcOFcZI/EjJ6OXYKXOVQPIDkUpB aaa HsoEuhN3CpdMSxbOv0MRcYHQ4lxtL157fCDm+Bq0zX5I/EjJ6OXYKXOVQPIDkUpB aaaa CdvUd/SwH2a6O4lgnmq5BeVLvLtBezfPf0Il0LGIZ+NI/EjJ6OXYKXOVQPIDkUpB aaaaa guFCH9dTJ1fBIZLA5iLGXZX8HlrVjaA+bkMjwPdrXXFI/EjJ6OXYKXOVQPIDkUpB aaaaaa zKMyyy5eAwCWk/Ui/PIlnmE0aprqGzDI8Ap/JyX8VY9I/EjJ6OXYKXOVQPIDkUpBSPxIyejl2ClzlUDyA5FKQQ==
0123456789abcdef aaaaaaFFFFFFFFFF FFFFFFFFFFFFFFFF PPPPPPPPPPPPPPPP 0123456789abcdef aaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaF FFFFFFFFFFFFFFFF FFFFFFFFFPPPPPPP
import requests import re url = 'http://167.71.246.232:8080/crypto.php' flag = '' for i in range(26): for code in range(32, 127): print '[+] try flag:', flag + chr(code) if i < 16: text = 'a' * (15 - i) + flag + chr(code) + 'a' * (63 - i) else: text = flag[-15:] + chr(code) + 'a' * (63 - i) payload = {'text_to_encrypt': text, 'do_encrypt': 'Encrypt'} r = requests.post(url, data=payload) body = r.text pattern = '<b>(.+)</b>' m = re.search(pattern, body) enc = m.group(1).decode('base64') ct0 = enc[16*0:16*1] ct4 = enc[16*4:16*5] if ct0 == ct4: flag += chr(code) break print '[*] flag:', flag
実行結果は以下の通り。
: [+] try flag: flag{b4d_bl0cks_for_g0nksv [+] try flag: flag{b4d_bl0cks_for_g0nksw [+] try flag: flag{b4d_bl0cks_for_g0nksx [+] try flag: flag{b4d_bl0cks_for_g0nksy [+] try flag: flag{b4d_bl0cks_for_g0nksz [+] try flag: flag{b4d_bl0cks_for_g0nks{ [+] try flag: flag{b4d_bl0cks_for_g0nks| [+] try flag: flag{b4d_bl0cks_for_g0nks} [*] flag: flag{b4d_bl0cks_for_g0nks}
flag{b4d_bl0cks_for_g0nks}
ECDSA Implementation Review (Crypto 225)
ECDSAのrが同じ場合の脆弱性の問題。kを計算できるので、それからsecretを計算できる。あとはそれを元にAESの復号をすればフラグになる。
#!/usr/bin/python3 from Crypto.Cipher import AES from Crypto.Util.number import * import binascii def unpad(s): return s[:-s[-1]] r = 50394691958404671760038142322836584427075094292966481588111912351250929073849 s1 = 26685296872928422980209331126861228951100823826633336689685109679472227918891 s2 = 40762052781056121604891649645502377037837029273276315084687606790921202237960 hash1 = 777971358777664237997807487843929900983351335441289679035928005996851307115 hash2 = 91840683637030200077344423945857298017410109326488651848157059631440788354195 order = 115792089210356248762697446949407573529996955224135760342422259061068512044369 k = int(((hash1 - hash2) % order) * inverse(((s1 - s2) % order), order)) secret = int((((((s1 * k) % order) - hash1) % order) * inverse(r, order)) % order) print('[+] secret =', secret) ct = b'f3ccfd5877ec7eb886d5f9372e97224c43f4412ca8eaeb567f9b20dd5e0aabd5' ctxt = binascii.unhexlify(ct) aes_key = secret.to_bytes(64, byteorder='little')[0:16] IV = b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' cipher = AES.new(aes_key, AES.MODE_CBC, IV) flag = unpad(cipher.decrypt(ctxt)) print('[*] flag =', flag)
実行結果は以下の通り。
[+] secret = 26924620604793025490002124762205825722410676804960639851404176074662508843402 [*] flag = b'flag{cRypt0_c4r3fully}'
flag{cRypt0_c4r3fully}