この大会は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}
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鍵を割り出したら、その鍵で復号する。
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
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])])
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}
Source of All Evil (Web App 25)
HTMLソースを見ると、フラグが書いてあった。
flag{best_implants_ever}
Certificate of Authenticity (Web App 25)
httpsでアクセスし、証明書を見ると、フラグが書いてあった。
flag{selfsignedcert}
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"));
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"));
else
model.addAttribute("flag", flags.getFlag("spring_mvc_2"));
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"));
else
model.addAttribute("flag", flags.getFlag("spring_mvc_2"));
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"));
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"));
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"));
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}
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>
<入力文字列><フラグ>をAES-ECBで暗号化している。ソースコード中のflagやiv, keyは正しくないので、フラグを1文字ずつはみ出させながら、暗号を比較し平文を求める。
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の復号をすればフラグになる。
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}