AceBear Security Contest 2019 Writeup

この大会は2019/4/6 9:00(JST)~2019/4/7 9:00(JST)に開催されました。
今回もチームで参戦。結果は200点で142チーム中39位でした。
メインの問題は全く解けず、残念でした。
自分で解けた問題をWriteupとして書いておきます。

Welcome <3 (Misc)

問題文はFree Flagと書いてあり、スコアボードへのリンクになっているだけのように見える。そこで問題全体をコピーする。

Free Flag }galF_eerF_yllaeR{raeBecA

白字でフラグが逆に書かれているので、元に戻す。

>>> '}galF_eerF_yllaeR{raeBecA'[::-1]
'AceBear{Really_Free_Flag}'
AceBear{Really_Free_Flag}

FeedBack (Misc)

アンケートに答えたら、フラグが表示された。

AceBear{H0p3_to_s3e_y0u_n3xt_ye4r}

Midnight Sun CTF 2019 Quals Writeup

この大会は2019/4/6 1:00(JST)~2019/4/7 1:00(JST)に開催されました。
今回もチームで参戦。結果は608点で432チーム中70位でした。
自分で解けた問題をWriteupとして書いておきます。

Ezdsa (crypto)

$ nc ezdsa-01.play.midnightsunctf.se 31337
Welcome to Spooners' EZDSA
Options:
1. Sign protocol
2. Quit
1
Enter data:
YQo=
(197331920949914888652115219109088718770931603447L, 512962212774096285728517071662679127614634451886L)
Options:
1. Sign protocol
2. Quit

スクリプトの内容は以下のような感じ。

m: 入力
h: mのsha1の数値
u: ランダム20*8ビット値

k = pow(g, u*m, q)
r = pow(g, k, p) % q
s = pow(k, q - 2, q) * (h + flag * r) % q

以下の定理が成り立つ。

pow(k, q - 1, q) = 1 mod q

上記の定理から以下が成り立つ。

pow(k, q - 2, q) = inv(k, q)

このことからこの問題は一般的なDSAの問題。この問題の一番多いパターンは同じrで異なるsのペアを見つけると脆弱性が発生するということ。何とかしてこのデータを入手したい。
0やq-1の倍数は指定できないので、少し調整して、(q-1)/2とq*(q-1)/2の場合で試すと、rが同じになった。あとはkを算出した後、key(flag)を算出すればよい。

import socket
from base64 import b64encode
from hashlib import sha1
from Crypto.Util.number import *

def recvuntil(s, tail):
    data = ''
    while True:
        if tail in data:
            return data
        data += s.recv(1)

q = 0x926c99d24bd4d5b47adb75bd9933de8be5932f4bL

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('ezdsa-01.play.midnightsunctf.se', 31337))

#### 1st try ####
data = recvuntil(s, 'Quit\n')
print data + '1'
s.sendall('1\n')
send_str1 = long_to_bytes((q-1)/2)
send_data1 = b64encode(send_str1)
data = recvuntil(s, 'data:\n')
print data + send_data1
s.sendall(send_data1 + '\n')
data = recvuntil(s, '\n').strip()
print data
r1 = int(data[1:-1].split(', ')[0].rstrip('L'))
s1 = int(data[1:-1].split(', ')[1].rstrip('L'))

#### 2nd try ####
data = recvuntil(s, 'Quit\n')
print data + '1'
s.sendall('1\n')
send_str2 = long_to_bytes(q*(q-1)/2)
send_data2 = b64encode(send_str2)
data = recvuntil(s, 'data:\n')
print data + send_data2
s.sendall(send_data2 + '\n')
data = recvuntil(s, '\n').strip()
print data
r2 = int(data[1:-1].split(', ')[0].rstrip('L'))
s2 = int(data[1:-1].split(', ')[1].rstrip('L'))

data = recvuntil(s, 'Quit\n')
print data + '2'
s.sendall('2\n')
data = recvuntil(s, '\n').strip()
print data

assert r1 == r2
assert s1 != s2

#### calculate k ####
h1 = bytes_to_long(sha1(send_str1).digest())
h2 = bytes_to_long(sha1(send_str2).digest())
k = int(((h1 - h2) % q) * inverse(((s1 - s2) % q), q))

#### calculate secret (flag) ####
key = int((((((s1 * k) % q) - h1) % q) * inverse(r1, q)) % q)
flag = long_to_bytes(key)
print flag
th4t_w4s_e4sy_eh?

RADARCTF Writeup

この大会は2019/4/4 1:00(JST)~2019/4/5 1:00(JST)に開催されました。
今回もチームで参戦。結果は6752点で347チーム中11位でした。
自分で解けた問題をWriteupとして書いておきます。

Chars (Crypto 100)

大量のRの文字の中に斜めにBase64になりそうな文字が入っている。
取り出して、デコードする。

with open('flag.txt', 'r') as f:
    lines = f.readlines()

flag = ''
for i in range(len(lines)):
    flag += lines[i][i]

while True:
    if len(flag) % 8 == 0:
        break
    flag += '='

flag = flag.decode('base64')
print flag
radar{char_after_char_give_flag}

Random (Crypto, RE 400)

暗号の最後の数値から鍵を算出できる。それを使って復号する。

with open('flag.txt', 'r') as f:
    codes = map(int, f.read().split('.'))

key = int(chr(codes[-1] - 49))

flag = ''
for code in codes[:-1]:
    flag += chr(code + key)

print flag
radar{rand_is_not_good_idea_lol}

Unnatural (Crypto 50)

記号がたくさん入っているのを除くと、Base64になりそう。

with open('flag.txt', 'r') as f:
    enc = f.read()

b64_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

flag = ''
for i in range(len(enc)):
    if enc[i] in b64_chars:
        flag += enc[i]

while True:
    if len(flag) % 8 == 0:
        break
    flag += '='

flag = flag.decode('base64')
print flag
radar{unnatural_base64_bcz_bad_chars}

Logo (Misc 200)

TweakPNGで見ると、IHDRのチャンクがおかしいようだ。バイナリエディタで高さを高くすると、隠れていたところからフラグが現れた。
f:id:satou-y:20190410072036p:plain

radar{hidden_p4rt_looks_smart}

Ocean (Misc 200)

unknown.zipはパスワードがかかっているがわからないので、fcrackzipでクラックする。

$ fcrackzip -u -D -p dict/rockyou.txt unknown.zip 


PASSWORD FOUND!!!!: pw == loveyou
$ unzip -P loveyou unknown.zip 
Archive:  unknown.zip
  inflating: unknown

展開されたunknownは、KMLファイルだった。Google Mapにインポートすると、海上にフラグが浮かび上がった。
f:id:satou-y:20190410072211p:plain

RADAR{231337}

Scattered 1 (Programming 500)

1文字ずつの画像ファイルがたくさんあり、ファイル名に番号がついている。。base64になりそうなので、各文字の画像のmd5で判断し、プログラムする。その際、Iとlが区別付かないので調整する。

from hashlib import md5
import string

def get_hash(file):
    with open(file, 'rb') as f:
        data = f.read()
    return md5(data).hexdigest()

def replace_l(s, idx):
    return s[:idx] + 'l' + s[idx+1:]

id_list = [
    -1,  #A
    19,  #B
    18,  #C
    -1,  #D
    31,  #E
    51,  #F
    2,   #G
    114, #H
    27,  #I
    259, #J
    -1,  #K
    17,  #L
    499, #M
    59,  #N
    -1,  #O
    -1,  #P
    75,  #Q
    147, #R
    1,   #S
    378, #T
    23,  #U
    3,   #V
    10,  #W
    62,  #X
    9,   #Y
    25,  #Z
    13,  #a
    5,   #b
    185, #c
    11,  #d
    97,  #e
    -1,  #f
    8,   #g
    12,  #h
    56,  #i
    244, #j
    28,  #k
    -1,  #l
    94,  #m
    80,  #n
    496, #o
    87,  #p
    40,  #q
    364, #r
    4,   #s
    199, #t
    52,  #u
    36,  #v
    -1,  #w
    123, #x
    126, #y
    208, #z
    132, #0
    100, #1
    34,  #2
    22,  #3
    15,  #4
    20,  #5
    -1,  #6
    -1,  #7
    7,   #8
    35,  #9
]

chars = string.uppercase + string.lowercase + string.digits
char_hashes = {}
for i in range(len(id_list)):
    if id_list[i] != -1:
        file = 'Scattered/%d.jpg' % id_list[i]
        h = get_hash(file)
        char_hashes[h] = chars[i]

b64 = ''
for i in range(1, 577):
    file = 'Scattered/%d.jpg' % i
    h = get_hash(file)
    b64 += char_hashes[h]

b64 = replace_l(b64, 26)
b64 = replace_l(b64, 63)
b64 = replace_l(b64, 66)
b64 = replace_l(b64, 114)
b64 = replace_l(b64, 147)
b64 = replace_l(b64, 187)
b64 = replace_l(b64, 199)
b64 = replace_l(b64, 234)
b64 = replace_l(b64, 259)
b64 = replace_l(b64, 279)
b64 = replace_l(b64, 342)
b64 = replace_l(b64, 343)
b64 = replace_l(b64, 383)
b64 = replace_l(b64, 403)
b64 = replace_l(b64, 451)
b64 = replace_l(b64, 463)
b64 = replace_l(b64, 475)
b64 = replace_l(b64, 542)
b64 = replace_l(b64, 546)
b64 = replace_l(b64, 569)

flag = b64.decode('base64')
print flag

実行結果は以下の通り。

Hello again , you did a good job wow and because you did a good job and you can find your flag in the end of text , good luck and don't forget to take a rest and have a good night because you are good programmer and don't forget to submit the flag and yes yes I want to make the base64 very long because I want to create a lot of photos because the lazzy people finally this is your flag radar{programming_is_important_good_job_bro}
radar{programming_is_important_good_job_bro}

Puzzle (Web 250)

ソースを見ると、こう書いてある。

<!-- don't forget to remove /puzzle_code_file.zip -->

コードを入手する。

<html>
    <head>
        <title>Puzzle</title>
        <style>
            #main {
    height: 100vh;
}
        </style>
    </head>
    <body>
        <link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<?php
$puzzle = $_SERVER['HTTP_USER_AGENT'];
if (is_numeric($puzzle)){
      if (strlen($puzzle) < 4){
          if ($puzzle > 10000){
                 echo ('<div class="d-flex justify-content-center align-items-center" id="main">
    <h1 class="mr-3 pr-3 align-top border-right inline-block align-content-center">Correct</h1>
    <div class="inline-block align-middle">
    	<h2 class="font-weight-normal lead" id="desc">radar{}</h2>
    </div>
</div>');
          } else {
                 echo ('<div class="d-flex justify-content-center align-items-center" id="main">
    <h1 class="mr-3 pr-3 align-top border-right inline-block align-content-center">Wrong</h1>
    <div class="inline-block align-middle">
    	<h2 class="font-weight-normal lead" id="desc">you solved 2 out 3</h2>
    </div>
</div>');
          }
      } else {
             echo ('<div class="d-flex justify-content-center align-items-center" id="main">
    <h1 class="mr-3 pr-3 align-top border-right inline-block align-content-center">Wrong</h1>
    <div class="inline-block align-middle">
    	<h2 class="font-weight-normal lead" id="desc">you solved 1 out 3</h2>
    </div>
</div>');
      }
} else {
    echo ('<div class="d-flex justify-content-center align-items-center" id="main">
    <h1 class="mr-3 pr-3 align-top border-right inline-block align-content-center">Wrong</h1>
    <div class="inline-block align-middle">
    	<h2 class="font-weight-normal lead" id="desc">you solved 0 out 3</h2>
    </div>
</div>');
}
?>

<!-- don't forget to remove /administrator_files.zip -->
    </body>
</html>

以下の条件を満たす必要あり。

・UserAgentは数字
・UserAgentは3桁以下
・UserAgenは10000より大きい

1e9にしたら、フラグが表示された。

radar{tricky_character_make_big_thing5}

Hawking (Misc 200)

$ steghide extract -sf SH.jpg 
Enter passphrase: ★hawkingを指定
wrote extracted data to "radar.txt".
$ cat radar.txt 
radar{h4wking_revealed_a_lot_of_secrets}
radar{h4wking_revealed_a_lot_of_secrets}

RadarEncrypt (Crypto, RE 300)

$ file RadarEncrypt.exe 
RadarEncrypt.exe: PE32 executable (GUI) Intel 80386 Mono/.Net assembly, for MS Windows

ILSpyでデコンパイルする。

        :
		public object encode(string pattern, string plaintext)
		{
			checked
			{
				try
				{
					bool flag = Conversions.ToBoolean(Operators.NotObject(this.checkSame(pattern, plaintext)));
					if (flag)
					{
						Interaction.MsgBox("You should include all the your string characters in the pattern", MsgBoxStyle.OkOnly, null);
					}
					else
					{
						string text = plaintext;
						int arg_38_0 = 0;
						int num = plaintext.Length - 1;
						int num2 = arg_38_0;
						while (true)
						{
							int arg_79_0 = num2;
							int num3 = num;
							if (arg_79_0 > num3)
							{
								break;
							}
							text = Strings.Replace(text, Conversions.ToString(plaintext[num2]), Conversions.ToString(pattern.IndexOf(plaintext[num2])) + ":", 1, 1, CompareMethod.Binary);
							num2++;
						}
						this.TextBox3.Text = text;
					}
				}
				catch (Exception expr_8A)
				{
					ProjectData.SetProjectError(expr_8A);
					ProjectData.ClearProjectError();
				}
				object result;
				return result;
			}
		}
        :

暗号はpatternのインデックスを表していることを使って、復号する。

with open('flag.txt', 'r') as f:
    idxes = map(int, f.read().split(':')[:-1])

pattern = '{qwertyuiopas_dfghjklzxcvbnm}'

flag = ''
for idx in idxes:
    flag += pattern[idx]

print flag
radar{index_of_make_small_cryptor}

EasyZip (Misc 50)

$ zip2john EasyZip.zip > hash.txt
ver 2.0 efh 9901 EasyZip.zip/flag.txt PKZIP Encr: cmplen=56, decmplen=28, crc=94113DF
$ john --wordlist=dict/rockyou.txt hash.txt --rules
Using default input encoding: UTF-8
Loaded 1 password hash (ZIP, WinZip [PBKDF2-SHA1 128/128 AVX 4x])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
saudi            (EasyZip.zip/flag.txt)
1g 0:00:02:07 DONE (2019-04-04 20:12) 0.007841g/s 1686p/s 1686c/s 1686C/s sexysis..pakala
Use the "--show" option to display all of the cracked passwords reliably
Session completed
$ john --show hash.txt 
EasyZip.zip/flag.txt:saudi:flag.txt:EasyZip.zip:EasyZip.zip

1 password hash cracked, 0 left

パスワードsaudiで解凍する。展開されたflag.txtにフラグが書いてある。

radar{easy_fl4g_in_e4sy_zip}

EasyCrypto (Crypto 50)

スペース区切りで2進数が並んでいる。末尾が必ず0になっているので、逆になっていると推測。デコードする。

with open('EasyCrypto.txt', 'r') as f:
    enc = f.read()

enc = enc[::-1].split(' ')

flag = ''
for e in enc:
    flag += chr(int(e, 2))

print flag
radar{ea5y_binaries_giv3_easy_fl4g}

EasyWeb (Web 50)

ソースを見ると、次のようなコメントが書いてある。

<!-- index.php?secretword= -->

secretwordにいろいろ文字列を指定することを試してみる。
http://blackfoxs.org/radar/easyweb/index.php?secretword=radarにアクセスすると、フラグが表示された。

radar{e4zy_w3b_true_m0ve_on}

RSA (Crypto 700)

nをfactordbで素因数分解する。

7576962585305391589 = 2045145391 * 3704852779

各行について復号すると、6桁の数字が並んでいる。2桁ずつ切って文字にしていくと、Base32になるので、デコードする。ASCIIコードがスペース区切りに並んでいるので、文字にしていく。

from Crypto.Util.number import inverse
import base64

with open('RSA.txt', 'r') as f:
    lines = f.readlines()

n = int(lines[0].strip().split('=')[1])
p = 2045145391
q = 3704852779
assert n == p * q

e = int(lines[2].strip().split('=')[1])

c_list = map(int, lines[4:])

phi = (p - 1) * (q - 1)
d = inverse(e, phi)

b32 = ''
for c in c_list:
    m = pow(c, d, n)
    s = str(m)
    for i in range(0, len(s), 2):
        b32 += chr(int(s[i:i+2]))

codes = base64.b32decode(b32)
codes = map(int, codes.split(' '))

flag = ''
for code in codes:
    flag += chr(code)

print flag
radar{factor1ng_c@n_cr@ck_rsa}

EasyReverse (RE 80)

$ file EasyReverse.exe 
EasyReverse.exe: PE32 executable (GUI) Intel 80386 Mono/.Net assembly, for MS Windows

ILSpyで開く。

namespace WindowsApplication20
{

                 :

	public class Form1 : Form

                 :

		public object flag()
		{
			string text = "radar";
			string text2 = "{";
			string text3 = "}";
			string text4 = "_";
			string text5 = "flagino";
			string text6 = "flago";
			string text7 = string.Concat(new string[]
			{
				text,
				text2,
				text5,
				text4,
				text6,
				text4,
				text6,
				text3
			});
			return string.Concat(new string[]
			{
				text,
				text2,
				text5,
				text6,
				text3
			});
		}

text7がフラグだった。

radar{flagino_flago_flago}

Black (Misc 200)

Stegsolveで開き、Red plane 1を見ると、フラグが出てきた。
f:id:satou-y:20190410212225p:plain

radar{reverse_color_give_flag}

Unzip (Misc 250)

zipの先頭10バイトが2バイト単位で逆になっているので直してみるが、パスワードがかかっている。

$ zip2john flag_fix.zip > hash.txt
ver 2.0 efh 9901 flag_fix.zip/flag.txt PKZIP Encr: cmplen=62, decmplen=32, crc=985EE9BE
$ john --wordlist=dict/rockyou.txt hash.txt --rules
Using default input encoding: UTF-8
Loaded 1 password hash (ZIP, WinZip [PBKDF2-SHA1 128/128 AVX 4x])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
root             (flag_fix.zip/flag.txt)
1g 0:00:01:43 DONE (2019-04-04 22:31) 0.009633g/s 7773p/s 7773c/s 7773C/s ruru12..room1234
Use the "--show" option to display all of the cracked passwords reliably
Session completed
$ john --show hash.txt
flag_fix.zip/flag.txt:root:flag.txt:flag_fix.zip:flag_fix.zip

1 password hash cracked, 0 left

パスワードrootで解凍する。展開されたflag.txtにフラグが書いてある。

radar{offest_is_v3ry_imp0rtantx}

その他

Rulesページにフラグがあった。(50pt)

radar{i_love_read_rules_because_im_good}

Giftのページにフラグがあった。(60pt)

radar{3njoy}

Hintsに書いてあったメールアドレス(radarctf@gmail.com)にメール送ったら、返信メールにフラグが書いてあった。(75pt)

radar{thanks_for_messaging_me}

Hintsなどのページにフラグが分断されて書いてあった。(1337pt)

radar{the_big_secret_is_you}

ENCRYPT CTF Writeup

この大会は2019/4/2 14:00(JST)~2019/4/4 14:00(JST)に開催されました。
今回もチームで参戦。結果は3536点で681チーム中4位でした。
自分で解けた問題をWriteupとして書いておきます。

sanity check (Misc 1)

ルールのページにフラグが書いてあった。

Thanks for reading rules here's your free flag: encryptCTF{L3t5_H4CK}
encryptCTF{L3t5_H4CK}

ham-me-baby (Misc 100)

$ nc 104.154.106.182 6969

                        Welcome To 

	   ____                       __    _______________  
	  / __/__  __________ _____  / /_  / ___/_  __/ __/  
	 / _// _ \/ __/ __/ // / _ \/ __/ / /__  / / / _/    
	/___/_//_/\__/_/  \_, / .__/\__/  \___/ /_/ /_/      
	              ___/___/_/_____                        
	             |_  |/ _ <  / _ \                       
	            / __// // / /\_, /                       
	           /____/\___/_//___/                        
		                                                     

you will be receiving hamming(7,4) codes. your job is to send data bits
from a 7 bit hamming code. 
 ___________________________________________________________________
|                                                                   |
|   DO YOUR RESEARCH : https://en.wikipedia.org/wiki/Hamming(7,4)   |
|  FLAG WILL BE PRINTED AFTER YOU SEND CORRECT DATA BITS 100 TIMES  |
|___________________________________________________________________|

and come back here. remember somebits could be flipped. you need to send
correct data bits.


[*] CODE: 1011101
[*] DATA: 1101
[*] CODE: 1100010
[*] DATA: 0010
[*] CODE: 1011100
[*] DATA: 1100
[*] CODE: 1001110
[*] DATA: 

Hamming(7,4)の問題。訂正せずにCODEから一部を取り出せばよいだけ。

import socket

def recvuntil(s, tail):
    data = ''
    while True:
        if tail in data:
            return data
        data += s.recv(1)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('104.154.106.182', 6969))

data = recvuntil(s, 'bits.\n').strip()
print data
data = recvuntil(s, '\n').strip()
print data
data = recvuntil(s, '\n').strip()
print data

for i in range(100):
    print 'Round %d' % (i+1)
    data = recvuntil(s, '\n').strip()
    print data
    if data == 'CODE VALIDATED':
        data = recvuntil(s, '\n').strip()
        print data
    code = data.split(': ')[1]
    d = code[2] + code[-3:]
    data = recvuntil(s, ': ')
    print data + d
    s.sendall(d + '\n')

data = recvuntil(s, '\n').strip()
print data
encryptCTF{hummmmm_hummmmmm}

ham-me-baby-2 (Misc 100)

ham-me-babyの問題の訂正らしい。CODEから必要に応じて訂正し、データ部分のみ返す必要がある。

import socket

def recvuntil(s, tail):
    data = ''
    while True:
        if tail in data:
            return data
        data += s.recv(1)

def check_bit(code):
    d = map(int, [code[2], code[4], code[5], code[6]])
    p = map(int, [code[0], code[1], code[3]])
    if (d[0] + d[1] + d[3]) % 2 != p[0]:
        return False
    elif (d[0] + d[2] + d[3]) % 2 != p[1]:
        return False
    elif (d[1] + d[2] + d[3]) % 2 != p[2]:
        return False
    return True

def correct(code):
    if check_bit(code):
        try_code = code
    else:
        for i in range(len(code)):
            if code[i] == '0':
                try_code = code[:i] + '1' + code[i+1:]
            else:
                try_code = code[:i] + '0' + code[i+1:]
            if check_bit(try_code):
                break
    return try_code[2] + try_code[-3:]

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('104.154.106.182', 6969))

data = recvuntil(s, 'bits.\n').strip()
print data
data = recvuntil(s, '\n').strip()
print data

for i in range(100):
    print 'Round %d' % (i+1)
    data = recvuntil(s, '\n').strip()
    print data
    code = data.split(': ')[1]
    correct_data = correct(code)
    data = recvuntil(s, ': ')
    print data + correct_data
    s.sendall(correct_data + '\n')
    data = recvuntil(s, '\n').strip()
    print data

data = recvuntil(s, '\n').strip()
print data
encryptCTF{1t_w4s_h4rd3r_th4n_1_th0ught}

Sweeeeeet (Web 50)

CookieのFLAGに以下がセットされている。

encryptCTF%7By0u_c4nt_U53_m3%7D
→encryptCTF{y0u_c4nt_U53_m3}

これはフラグとして通らない。
CookieのUIDに以下がセットされている。

f899139df5e1059396431415e770c6dd

md5逆変換すると、"100"になる。
以下の"0"のmd5をセットしてみる。

cfcd208495d565ef66e7dff9f98764da

CookieのFLAGが以下に変わった。

encryptCTF%7B4lwa4y5_Ch3ck_7h3_c00ki3s%7D%0A
encryptCTF{4lwa4y5_Ch3ck_7h3_c00ki3s}

It's a WrEP (Forensics 50)

無線通信のcapファイルが添付されている。

$ aircrack-ng encryptCTFWEP.cap 
Opening encryptCTFWEP.cap
Read 546320 packets.

   #  BSSID              ESSID                     Encryption

   1  58:D7:59:79:61:34  MCSP                      No data - WEP or WPA
   2  40:31:3C:E6:CA:3C  Malik’s                 WPA (1 handshake)
   3  14:CC:20:F5:32:FE  encryptCTF                WEP (88648 IVs)
   4  0C:D2:B5:72:3C:D4  home                      WPA (0 handshake)
   5  E4:6F:13:80:82:99  prime2                    No data - WEP or WPA
   6  04:95:E6:05:50:60  Letzpay1                  WPA (0 handshake)
   7  C4:B8:B4:BF:5B:C8  foresightinn              No data - WEP or WPA
   8  B4:EF:FA:51:EC:53  Le 2                      No data - WEP or WPA
   9  72:B7:AA:33:56:23  vivo 1802                 None (0.0.0.0)
  10  78:D3:8D:E4:E5:8C  Sarovar                   None (172.16.168.188)
  11  50:5D:AC:94:3D:E4  Webnyxa_airtel_12G        No data - WEP or WPA
  12  C4:B8:B4:4D:93:74  Webnyxa_airtel_first      No data - WEP or WPA
  13  C8:D7:79:A7:E0:0A                            Unknown
  14  B0:C1:9E:A3:18:BA  Airtel-Hotspot-18BA       WPA (0 handshake)
  15  C4:B8:B4:A8:92:20  sanchez                   No data - WEP or WPA
  16  74:DA:DA:D8:86:77  Orium                     WPA (0 handshake)
  17  AC:EE:9E:91:D3:43  AndroidAP                 No data - WEP or WPA
  18  AE:56:2C:96:57:C9                            Unknown
  19  84:FE:DC:DC:8C:06  password                  No data - WEP or WPA
  20  04:B1:67:C2:3D:87  Redmiamit                 No data - WEP or WPA
  21  70:5A:AC:92:B9:BC  Ritesh mobile             No data - WEP or WPA

Index number of target network ? 3

Opening encryptCTFWEP.cap
Attack will be restarted every 5000 captured ivs.
Starting PTW attack with 88648 ivs.


                                 Aircrack-ng 1.2 beta3


                 [00:00:00] Tested 837 keys (got 85909 IVs)

   KB    depth   byte(vote)
    0    0/ 17   57(107520) 84(99840) 71(97536) B5(97536) D0(96512) 
    1    0/  1   C4(116992) DD(98816) 1A(97792) 3A(96768) 59(94720) 
    2    2/  2   3E(101632) 4D(98816) 9B(97536) 2B(96256) 5E(96000) 
    3   13/  3   C5(93952) B6(93440) 63(93184) 72(93184) B8(92928) 
    4    0/  1   98(121344) F8(98048) C2(97536) C1(97280) D4(96000) 

     KEY FOUND! [ 57:34:35:5F:31:37:5F:52:33:34:4C:3F:21 ] (ASCII: W45_17_R34L?! )
	Decrypted correctly: 100%

WEPキーが見つかった。

encryptCTF{W45_17_R34L?!}

Journey to the centre of the file 1 (Forensics 75)

zipとgzで何重にも圧縮しているので、スクリプトで解凍する。

import subprocess
import os

cmd_file = 'file flag'
cmd_zip = 'unzip flag.zip'
cmd_gzip = 'gzip -d flag.gz'

i = 1
while True:
    print 'Round %d' % i
    i += 1

    if os.path.exists('flag.zip'):
        cmd = cmd_zip
        ret = subprocess.check_output( cmd.split(" ") )
        os.remove('flag.zip')
    elif os.path.exists('flag.gz'):
        cmd = cmd_gzip
        ret = subprocess.check_output( cmd.split(" ") )
        ret = subprocess.check_output( cmd_file.split(" ") )
        if 'Zip archive' in ret:
            os.rename('flag', 'flag.zip')
        else:
            ret = subprocess.check_output( 'cat flag'.split(" ") )
            print ret
            break
    else:
        break
encryptCTF{w422up_b14tch3s}

Wi Will H4CK YOU!! (Forensics 100)

無線通信のcapファイルが添付されている。

$ aircrack-ng -w dict/rockyou.txt encryptCTFWPA.cap 
Opening encryptCTFWPA.cap
Read 4790 packets.

   #  BSSID              ESSID                     Encryption

   1  14:CC:20:F5:32:FE  encryptCTF                WPA (1 handshake)
   2  0C:D2:B5:72:3C:D4  home                      No data - WEP or WPA
   3  40:31:3C:E6:CA:3C  Malik’s                 WPA (0 handshake)
   4  00:00:00:00:00:00                            Unknown

Index number of target network ? q
Index number of target network ? 1

Opening encryptCTFWPA.cap
Reading packets, please wait...

                                 Aircrack-ng 1.2 beta3


                   [01:41:30] 7049164 keys tested (1210.81 k/s)


                           KEY FOUND! [ ThanckYou ]


      Master Key     : 48 89 DD 32 53 9E A7 49 1E 63 3D F7 06 CD CD 4F 
                       0E 1E 22 56 BD DA 8B 10 ED 80 6D 8E 20 3C 7B D3 

      Transient Key  : F0 F9 59 E0 EA 24 01 AC B4 B9 CE 01 F3 04 60 A9 
                       C9 84 6F 2A 59 0A 24 9E 3E 5E 87 36 64 EC D8 8D 
                       C3 2C AA FA E7 02 D0 D1 D2 64 96 2E A7 A8 D5 29 
                       4B FF A3 EB AB 16 D9 11 F7 2F F3 6B 2D F8 61 31 

      EAPOL HMAC     : EC F7 55 70 56 9E 7B AE 18 BB 39 6E 88 C6 F3 F2

WPAキーが見つかった。

encryptCTF{ThanckYou}

yhpargonagets (Steganography 25)

Pythonライブラリを使う。

from steganography.steganography import Steganography

flag = Steganography.decode('encode.jpg')
print flag
encryptCTF{pip_in5t411_5teg4n0graphy}

Stressed out? (Steganography 100)

wavファイルのプロパティを見ると、タイトルは以下のようになっている。

1_4M_Str3ss3d_0ut

wavを対応しているsteghideを試してみる。

$ steghide extract -sf dontstressoutkiddo.wav 
Enter passphrase: ★先ほどのタイトルを指定
wrote extracted data to "flag.jpg".

flag.jpgを取り出せて、そこにフラグが書いてあった。
f:id:satou-y:20190409211547j:plain

encryptCTF{tyl3r_j0s3ph_is_4_g0d}

Hard Looks (Cryptography 75)

"-"を1、"_"を0にして考える。暗号文の長さは8の倍数でないので、先頭に不足分0をつけて、デコードする。

enc = '--_--___--_-_-__--_--__--__-_-__--_--___--__--__--_---__--__-___--_---__---__-__--_---__--______--_---__--_-____--_-____--__--__--_-_-__--_-____--_-____--_--___--_---_--___-___--_-_-__--_---__--__--__--_-____--__--__--_-_-__--_-_-_--__--___--__--__--___-__--__--__--_---__--_-_-_--__--___--_-____---_____--__--__--_-____--_-_-__--__-___--_-____--_-____--_-_-_--__--___--__--__--__--__--_-___--__-_-__--__--__--______--_-_-__--_-_-__--_-____--_---__--_-____---_____--__--_--__--___--__-___--___-__--_---_--__-__'

enc = enc.replace('-', '1')
enc = enc.replace('_', '0')

while True:
    if len(enc) % 8 == 0:
        break
    enc = '0' + enc

msg = ''
for i in range(0, len(enc), 8):
    msg += chr(int(enc[i:i+8], 2))

flag = msg.decode('hex')
print flag
encryptCTF{W45_17_H4RD_3N0UGH?!}

RSA_Baby (Cryptography 100)

qとnが分かっているので、pは算出できる。あとはそのまま復号する。

from Crypto.Util.number import *

e = 65537
q = 9896984395151566492448748862139262345387297785144637332499966426571398040295087125558780121504834847289828037371643927199404615218623314326851473129699891

with open('flag.enc', 'r') as f:
    data = f.read()

c = int(data.split('\n')[1], 16)
n = int(data.split('\n')[2].split(': ')[1])
p = n / q

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)

flag = long_to_bytes(m)
print flag
encryptCTF{74K1NG_B4BY_S73PS}

Julius,Q2Flc2FyCg== (Cryptography 150)

Base64デコードして、シフトしたら復号できた。

enc = 'fYZ7ipGIjFtsXpNLbHdPbXdaam1PS1c5lQ'

while True:
    if len(enc) % 4 == 0:
        break
    enc += '='

dec = enc.decode('base64')

flag = ''
for i in range(len(dec)):
    code = ord(dec[i]) - 24
    flag += chr(code)

print flag
encryptCTF{3T_7U_BRU73?!}

AEeeeeS (Cryptography 200)

AESキーは2進数になっているが、バイナリに変えると16バイトになる。あとはそのままAES復号を行う。

from Crypto.Cipher import AES

with open('AEeeeeS.key', 'r') as f:
    data = f.read().strip()

while True:
    if len(data) % 8 == 0:
        break
    data = '0' + data

key = ''
for i in range(0, len(data), 8):
    key += chr(int(data[i:i+8], 2))

enc = 'c68145ccbc1bd6228da45a574ad9e29a77ca32376bc1f2a1e4cd66c640450d77'
cipher = AES.new(key, AES.MODE_ECB)
flag = cipher.decrypt(enc.decode('hex'))
print flag
encryptCTF{3Y3S_4R3_0N_A3S_3CB!}

(TopNOTCH)SA (Cryptography 300)

nをfactordbで素因数分解する。

57891041571118599917733172578294383243762455810797917992757930072844611988433
= 194038568404418855662295887732506969011 * 298348117320990514224871985940356407403

あとはそのまま復号する。

from Crypto.PublicKey import RSA
from Crypto.Util.number import *

with open('pubkey.pem', 'r') as f:
    pub_data = f.read()

pubkey = RSA.importKey(pub_data)
n = pubkey.n
e = pubkey.e

p = 194038568404418855662295887732506969011
q = 298348117320990514224871985940356407403
assert n == p * q

with open('flag.enc', 'r') as f:
    data = f.read()

c = int(data.split('\n')[1], 16)

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)
flag = long_to_bytes(m)
print flag
encryptCTF{1%_0F_1%}

Sunshine CTF 2019 Writeup

この大会は2019/3/30 22:00(JST)~2019/4/1 10:00(JST)に開催されました。
今回もチームで参戦。結果は1205点で464チーム中33位でした。
自分で解けた問題をWriteupとして書いておきます。

DiscordSlam (Misc 5)

Discordに入り、#lobbyチャネルを見るとフラグが書いてある。

sun{w3lcom3_t0_d1Sc0RdM4n1A!!!}

WelcomeCrypto (Crypto 50)

https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号

ROT47
Org lbh pna'g fbyir guvf punyyratr!

sun{w3lC0m3_T0_da_k4g3!}
sun{w3lC0m3_T0_da_k4g3!}

CB1 (Crypto 50)

NATO phonetic alphabet
https://en.wikipedia.org/wiki/NATO_phonetic_alphabet

上記を参考にしながら音声を聞いてみる。

November
Six
★begin message
Hotel
Kilo
Charlie
Golf
Xray
Kilo
Zulu
November
Kilo
Oscar
Juliett
Kilo
Yankee
Uniform
Lima
Sierra
Golf
Xray
India
November
★repeat
Hotel
Kilo
Charlie
Golf
Xray
Kilo
Zulu
November
Kilo
Oscar
Juliett
Kilo
Yankee
Uniform
Lima
Sierra
Golf
Xray
India
November

文字にすると、こうなる。

HKCGXKZNKOJKYULSGXIN

https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号

Rotation 6:
BEWARETHEIDESOFMARCH
BEWARETHEIDESOFMARCH

VolgaCTF 2019 Qualifier Writeup

この大会は2019/3/30 0:00(JST)~2019/4/1 0:00(JST)に開催されました。
今回もチームで参戦。結果は550点で414チーム中120位でした。
自分で解けた問題をWriteupとして書いておきます。

Shadow Cat (crypto 100)

shadowファイルがあるので、それを解析するところから始める。
パスワードが設定されているところだけ切り出し、passwdファイルも適当に作成する。

$ cat passwd
jr:x:1000:1000:,,,:/home/jr:/bin/bash
z:x:1001:1000:,,,:/home/jr:/bin/bash
a:x:1002:1000:,,,:/home/jr:/bin/bash
x:x:1003:1000:,,,:/home/jr:/bin/bash
q:x:1004:1000:,,,:/home/jr:/bin/bash
l:x:1005:1000:,,,:/home/jr:/bin/bash
v:x:1006:1000:,,,:/home/jr:/bin/bash
e:x:1007:1000:,,,:/home/jr:/bin/bash
f:x:1008:1000:,,,:/home/jr:/bin/bash
b:x:1009:1000:,,,:/home/jr:/bin/bash
r:x:1010:1000:,,,:/home/jr:/bin/bash
g:x:1011:1000:,,,:/home/jr:/bin/bash
n:x:1012:1000:,,,:/home/jr:/bin/bash
o:x:1013:1000:,,,:/home/jr:/bin/bash
p:x:1014:1000:,,,:/home/jr:/bin/bash
s:x:1015:1000:,,,:/home/jr:/bin/bash
c:x:1016:1000:,,,:/home/jr:/bin/bash
w:x:1017:1000:,,,:/home/jr:/bin/bash
d:x:1018:1000:,,,:/home/jr:/bin/bash
t:x:1019:1000:,,,:/home/jr:/bin/bash
h:x:1020:1000:,,,:/home/jr:/bin/bash
m:x:1021:1000:,,,:/home/jr:/bin/bash
k:x:1022:1000:,,,:/home/jr:/bin/bash
i:x:1023:1000:,,,:/home/jr:/bin/bash
y:x:1024:1000:,,,:/home/jr:/bin/bash
j:x:1025:1000:,,,:/home/jr:/bin/bash
u:x:1026:1000:,,,:/home/jr:/bin/bash
$ cat shadow
jr:$6$jKcSVswg$AWA/PLZVcOcQb6404vSsjmyJzgtC97R3iRDXSOBshuYmCcDVDhaIS6C4fVZnOI6EGhaUnrUWK2Y3jYhQ4vcmu0:17792:0:99999:7:::
z:$6$AEqLtEqq$1ojEoCgug5dzqeNfjGNE9p5SZFwIul8uOFp9vMZEz50oiUXOVFW3lw1S0fuvFY5ggi5CfbfoWaMDr2bvtSNRC/:17930:0:99999:7:::
a:$6$9Eg69bYI$q75YWUVWb4MYzkcExXukpt.VJ3fX458iZJm1ygpTLwX.CgroHpmeSG88By.zQmKyOHCvBHoA0Q001aBqbkVpg/:17930:0:99999:7:::
x:$6$5TF0Txe3$APSNzUSjFmMbsmrkCS9qE84qfu4AI2dNEjqm2PRKgSjncBTI4lECXofQ8abdAtYX6tST6FGCgOdvLlDYQTCJx0:17930:0:99999:7:::
q:$6$I3iqZL0m$nxHWvcLz7lg/ZKoKfX9dq5k0uqkOtKgLdyREAQxQkfPkVvbNHPfQaoCFfnXl1BoX1vgOcEDghVPvfRUrs6dGp1:17930:0:99999:7:::
l:$6$n8iJWaW/$M1Od8seiEL6h3L.egHubBYAk.cd8/LUctESIm69/r.gvP0eqabusN5/D1rNu1qDHOkgRpHf8PWGSb8zoxrgrp1:17930:0:99999:7:::
v:$6$GatagTHS$I1UfNfn3NGP5Vre0z7s3DGqFpjN5Pw2XhAHSw6ZSMwaMAsf8IteFedXovNlHLuIXvR9ezeya89XEOq2We7CcW1:17930:0:99999:7:::
e:$6$ZO6YiExi$7DBV0zMIqf8iy.zVTL7gbHetCmJ3LL4ROEYG/UME4Tmym82vZYkFWjiNpCGapvF83QNJKFJOjkhXMgFLfkhza.:17930:0:99999:7:::
f:$6$HXoF2OZ1$onkVfp52IRdd2OipQv3rPPsGr7QradAFTFnmXv5c9xkGy4xcgJFkoaSJzMQCtfWuU2FQ3BN3lyL47SyoIoPmy0:17930:0:99999:7:::
b:$6$I9Uq9PRG$euVEYx5TR2lUFe/k7s0e8us8xgl7j/cbiYRnvba.eFfMSSPsm5I.gcShqOLqAa58m5VISomkPpHlJ1xLgCRxw/:17930:0:99999:7:::
r:$6$J.qms5y0$YOMnlR0V.WQjqAyik3nU7TDdy8hZQZWfhF8CTJHJtd/0mrANrxBvULoXNiJnvX.yn5T3QBFu4wk3xrFye.uus1:17930:0:99999:7:::
g:$6$b4GkIuzQ$j/prK.Jy304eu6W/NG0Yz4mHDOc3BavkYRBomdjVG.fAksRM/xIDRoWcKcJw66YZmVGcV51YkIwVZHVfA//KU.:17930:0:99999:7:::
n:$6$f2QD.cIu$n70jbCSe0QVz7M48SVE2Z..IPDV0QjIfZ8D40oQOO9smt0ZeA2I0sSO927VIr1SJwupjZairVR0T/pKQ0QG3N1:17930:0:99999:7:::
o:$6$qyBGOX79$OAvGVjmH69C.0ZfObcJ0DcKzoSWHhBBt9sPVjbIJtYmT4nV/TU/zCiKgCkSQaQJ.n.vTjAScdh9htzjBzTwOT/:17930:0:99999:7:::
p:$6$EW1gOlyH$omuxKElrI3DeVoxrLet3OW3MfuFPwLwefVxOr62E.wo7szQ6.swTec1edCFiKnPc42XxMRGsrNJn36mkc2dgH/:17930:0:99999:7:::
s:$6$TXxh4vV5$v1vF49YZQnonSZrKwBWNp7rpxIRoQY/ooEODqjsdwwdoY8sso.y/EOsoC3GFlpCLYnEY./n/1BuNID5njg0CV.:17930:0:99999:7:::
c:$6$IYxRoIyR$eEmGTNPNd6HPQibc/UBdQ5zgR/dGQ1dtCuSl0lUmvmbrSKbYEf/SEDlX4fgP.JQXlyFqEgu4NOBiu2eozpAM.0:17930:0:99999:7:::
w:$6$RmCyBroe$EovezmWQJVvQFGd6.ei2.SfzGJG22CsV47tTnyfKx30TbuG1VMgsk0de6NOQ04bKNML9fbuMu0Pw3TMf82zye1:17930:0:99999:7:::
d:$6$..CK41Sn$36X19X0jrviLxVVk.KtR9zbHMML0mg1XzMNQgP7eOpGMF1JYSbZHAyReDhNVkm1WaNn3lfO2CsAww14fZZads1:17930:0:99999:7:::
t:$6$zVFV5HoW$q9WyP6/D0kPL.n7s.FvPcOSRfvcUFQu5QMPps6VbQUD5RMozQsP14GtUiOa/H2V2pU6c6OxcgRqruaLejPaES1:17930:0:99999:7:::
h:$6$S1RnJ8DK$F/FWFf9En/mn6YqZ67/gOnMT0WdSuaEyn2OArTJbG1IGHd0pUs9TiGDE9P.PhRB6XyHUgA5l/LBmBW8PpJg9M.:17930:0:99999:7:::
m:$6$SqkKQRak$nZHDrq3vdnajdLzotrW3J9kYzUvzPaUrs6NZaKkkcVN0KiCfUhJfgM6WJvZjZR7hBGWkfIwhZymko7wtsq49s1:17930:0:99999:7:::
k:$6$ZLCr2itT$tZ0.u7TsXPc3nAntIOepETGkhqfVG1IKaiuW0mAH5QROVuc7fonE43qEhUFT2LHMftYDdTQRAMHpRNMM8Yn1c1:17930:0:99999:7:::
i:$6$ZlWmheB2$DBLJQPLVhhEdA/iATrOYiqFv4i5TcmBUSX6.tZDo63YP4dcdlAuBnFU65xXIRP1tpNsCS7kc6Fu6jPMS2F7aP1:17930:0:99999:7:::
y:$6$rIoO6U2u$c5usMXbFP9S75qmDyBBWz1QZuyGH0MGq3mXN32kYipoL5XCFEHjmTRVcuZkmed5OzAopV0CgyA49QzILz5Rmq/:17930:0:99999:7:::
j:$6$Gpavrajq$me1yxZQ0OiJFedrTmxFsyP5zwOePuFJmgujUWun0h5bCOIVeuJuaIUTDHGCYxkT6mw41BTjlx9c3QvdsG8o0o.:17930:0:99999:7:::
u:$6$0w3EeszD$bUDQorjCKku1sjtCWMQfJ3ZRmsC5N.LN7CQnjvyCbcq5wSD33x2t/TVXA6jnjtajv8nIZc.Aj.oY80lm44Dhy0:17930:0:99999:7:::
underscore:$6$RVUCQJFr$fsKkPUT9Pp5QlsZblSLJ4yKkfBxNMWN0TS.q7ticuEr/HQFdEbyiwK5JmaKKS9UDFzUsY6mhe1knnRbwy7K0s/:17930:0:99999:7:::
underscore:x:1027:1000:,,,:/home/jr:/bin/bash
$ unshadow passwd shadow > passwd_shadow
$ john --single passwd_shadow 
Warning: detected hash type "sha512crypt", but the string is also recognized as "crypt"
Use the "--format=crypt" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 28 password hashes with 28 different salts (sha512crypt, crypt(3) $6$ [SHA512 128/128 AVX 2x])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
n                (n)
a                (a)
d                (s)
g                (d)
t                (u)
r                (y)
6g 0:00:01:28 DONE (2019-03-30 04:27) 0.06815g/s 1102p/s 1104c/s 1104C/s jrl1901..jl1900
Use the "--show" option to display all of the cracked passwords reliably
Session completed

一部しか解析できない。すべて一文字のパスワードとみて、辞書ファイルを作成して試してみる。

$ john --wordlist=words.txt passwd_shadow 
Warning: detected hash type "sha512crypt", but the string is also recognized as "crypt"
Use the "--format=crypt" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 28 password hashes with 28 different salts (sha512crypt, crypt(3) $6$ [SHA512 128/128 AVX 2x])
Remaining 22 password hashes with 22 different salts
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
p                (h)
o                (t)
i                (e)
s                (j)
y                (p)
k                (b)
j                (f)
z                (underscore)
h                (v)
u                (k)
q                (m)
x                (o)
f                (w)
b                (x)
m                (g)
e                (c)
c                (q)
1                (jr)
l                (r)
v                (i)
w                (l)

この結果をもとにユーザ名が暗号文、パスワードが平文として、暗号文を置換してみる。

vaff_iafi_mpamxcns_iafimak_ayrahf_ybpxf_cn_kiq_fiajurf

意味が通らない。zと_以外、平文と暗号文の関係を逆にしてみる。

import string

table = string.maketrans('nasduyhtejpbfzvkmowxgcqJril', 'nadgtrpoisykj_huqxfbmec1lvw')

with open('encrypted.txt', 'r') as f:
    enc = f.read()

flag = enc.translate(table)
print flag
flag = flag.replace('J', 'jr')
print flag
pass_hash_cracking_hashcat_always_lurks_in_the_shadows
VolgaCTF{pass_hash_cracking_hashcat_always_lurks_in_the_shadows}

Blind (crypto 200)

RSAのsigningの問題。実行したいコマンドはcat flag。cat flagの整数値mが素因数分解できたら、分解したもののsignatureからcat flagのsignatureを算出できる。

m = m1 * m2
sig1 = m1**d % n
sig2 = m2**d % n
sig = (m1**d * m2**d) %d = (sig1 * sig2) % d

m = 7161132565001953639
$ python -m primefac 7161132565001953639
7161132565001953639: 103 408479 170205956447

このことからcat flagを実行して、フラグを表示させる。

import socket
import base64
from Crypto.Util.number import *

def recvuntil(s, tail):
    data = ''
    while True:
        if tail in data:
            return data
        data += s.recv(1)

n = 26507591511689883990023896389022361811173033984051016489514421457013639621509962613332324662222154683066173937658495362448733162728817642341239457485221865493926211958117034923747221236176204216845182311004742474549095130306550623190917480615151093941494688906907516349433681015204941620716162038586590895058816430264415335805881575305773073358135217732591500750773744464142282514963376379623449776844046465746330691788777566563856886778143019387464133144867446731438967247646981498812182658347753229511846953659235528803754112114516623201792727787856347729085966824435377279429992530935232902223909659507613583396967

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('blind.q.2019.volgactf.ru', 7070))

sign = '1'
cmd = 'ls'
send_msg = sign + ' ' + cmd
data = recvuntil(s, ':')
print data + send_msg
s.sendall(send_msg + '\n')

cmd = 'cat flag'
cmd_int = int(cmd.encode('hex'), 16)
## factor(cmd_int) = 103 * 408479 * 170205956447
cmd1_int = 408479
cmd2_int = cmd_int / cmd1_int

sign = '1'
cmd = 'sign'
send_msg = sign + ' ' + cmd
data = recvuntil(s, ':')
print data
print send_msg
s.sendall(send_msg + '\n')

cmd1 = base64.b64encode(long_to_bytes(cmd1_int))
data = recvuntil(s, ':')
print data + cmd1
s.sendall(cmd1 + '\n')

sign = '1'
cmd = 'sign'
send_msg = sign + ' ' + cmd
data = recvuntil(s, ':')
print data
print send_msg
s.sendall(send_msg + '\n')

signature1 = int(data.split('\r\n')[1])

cmd2 = base64.b64encode(long_to_bytes(cmd2_int))
data = recvuntil(s, ':')
print data + cmd2
s.sendall(cmd2 + '\n')

data = recvuntil(s, ':')
print data

signature2 = int(data.split('\r\n')[1])

sign = str((signature1 * signature2) % n)
cmd = 'cat flag'
send_msg = sign + ' ' + cmd
print  send_msg
s.sendall(send_msg + '\n')

data = recvuntil(s, '\n')
data += recvuntil(s, '\n').strip()
print data

実行結果は以下の通り。

Enter your command:1 ls

flag
private_key.py
server.py

Enter your command:
1 sign

Enter your command to sign:Bjuf

17638787469642039435318581385618808297268376774234879657983959486588488518602104982122891156593317044292766030845687731228499038739756875964186679785382736177049112662098526726358737509567429436403098863630151350780807034720456854963415607468739968754202506998385349296290129607434002404738918120538229071656978017808466596814950226784314700317088934348540608219987543730472643691009060755872926833135443517648919261924577246564970061451282151317589401229797945517104524760889684000361260339607131828972845536284354091935086720068566032733497050626156914558343862499749490577165803099065730472936347462992778753796941
Enter your command:
1 sign

Enter your command to sign:D/HNwQU5

25546621042567021743921441656485106150773028771975739792395292839524991698929912254223690969899881717956277954181362001941084170643987790900354897720201198641241865168483559029881918212009804506724025449021632061776772429841832820822248791924286727129159693622880243660612170666428534633987262142793095526136680155894125768641992318145581399148611597046840862019696663630366704554135979924498320452192198270792856017753126515861945695789558862132300276176002546820728010847776307161844028813344882966643480384082356366425666269055026587464161965878493074595945942518737289891784899462357520515374555016064334840465912
Enter your command:
24276592954466402792157532919706447334355948690098023035375614012157378412616233865544533025515869836447793226406373271083160180523082800598281266834619631704245143244545577613294590334637358684061108293899492468337030535564036215463887905645938530571058038030943037016298964167966109577883005551522062164917343818964991120441652232394288629520079832539360872798332983684006902802429243645009242747601354050053448137191986860769673762567997572657102990869994555786984110522299362261357652756180804304984027320764350120137457095771345729635881422070403269427999652221843885023772233721400195669139542781850847904777323 cat flag

VolgaCTF{B1ind_y0ur_tru3_int3nti0n5}
VolgaCTF{B1ind_y0ur_tru3_int3nti0n5}

b00t2root '19 Writeup

この大会は2019/3/30 2:30(JST)~2019/3/31 2:30(JST)に開催されました。
今回もチームで参戦。結果は4155点で323チーム中5位でした。
自分で解けた問題をWriteupとして書いておきます。

Welcome (misc)

slackに入ると、#generalチャネルにフラグが書いてあるメッセージがピン止めされている。

b00t2root{w3lc0me_h0pe_y0u_h4v3_fun}

Steve Rogers (linux)

添付のシェルスクリプトでログインする。プロセスを見ると、フラグを引数にして実行されているものがあった。

steve@2ad43eba9d74:~$ ls -la
total 20
drwxr-xr-x 2 steve steve 4096 Mar 29 15:30 .
drwxr-x--x 1 root  root  4096 Mar 29 15:30 ..
-rw-r--r-- 1 steve steve  220 Mar 29 15:30 .bash_logout
-rw-r--r-- 1 steve steve 3771 Mar 29 15:30 .bashrc
-rw-r--r-- 1 steve steve  807 Mar 29 15:30 .profile
steve@e7d4a74360d0:~$$ ps -ef | grep b00t2root | grep -v grep
root         1     0  0 22:03 pts/0    00:00:00 bash /tmp/42.sh b00t2root{Cmd_l1n3_fl4g5_4r3_0bv10u5}
b00t2root{Cmd_l1n3_fl4g5_4r3_0bv10u5}

cuz_rsa_is_lub (crypto)

nをFermat法で素因数分解して、復号する。

from Crypto.Util.number import *

def isqrt(n):
    x = n
    y = (x + n // x) // 2
    while y < x:
        x = y
        y = (x + n // x) // 2
    return x

def fermat(n):
    x = isqrt(n) + 1
    y = isqrt(x * x - n)
    while True:
        w = x * x - n - y * y
        if w == 0:
            break
        elif w > 0:
            y += 1
        else:
            x += 1
    return x - y, x + y

n = 71641831546926719303369645296528546480083425905458247405279061196214424558100678947996271179659761521775290973790597533683668081173314940392098256721488468660504161994357
e = 65537
c = 63127079832500412362950100242549738176318170072331491750802716138621322974529994914407846448954487685068331564008936808539420562251661435790855422130443584773306161128156

p, q = fermat(n)

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)

flag = long_to_bytes(m)
print flag
b00t2root{RSA_c4n_b3_vuln3r4bl3}

Scatter Me (crypto)

x:y:1というデータが;区切りで並んでいる。scatter(散布)というタイトルからも散布図を書けばフラグになりそう。

import matplotlib.pyplot as plt
import numpy as np

with open('scatter.txt', 'r') as f:
    data = f.read()

codes = data.split(';')
x = np.arange(len(codes), dtype = 'float')
y = np.arange(len(codes), dtype = 'float')
for i in range(len(codes)):
    x[i] = float(codes[i].split(':')[0])
    y[i] = float(codes[i].split(':')[1])

plt.scatter(x, y, label="flag")
plt.legend()
plt.show()

f:id:satou-y:20190403212407p:plain

b00t2root{300728}

Genetics (crypto)

https://github.com/JohnHammond/ctf-katanaのDNA Codeの対応表を参考に復号する。

import string

dna_code_tbl = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 .'

enc = 'ACCAGTAAAACGTTGAGACAGTTGAATATCAAACTACACCGAATTCATATGTCACAGCGGCCGACACAGATGATAACA'

enc = enc.replace('A', '00')
enc = enc.replace('C', '01')
enc = enc.replace('G', '10')
enc = enc.replace('T', '11')

dec = ''
for i in range(0, len(enc), 6):
    idx = int(enc[i:i+6], 2)
    dec += dna_code_tbl[idx]

print dec

実行結果は以下の通り。

flag is dnaCrypto1sAwesome
b00t2root{dnaCrypto1sAwesome}

Let's Travel (crypto)

異なる2つの文字列を与えて、同じHash値になればよい。頭に\x00をつけてもHash値は変わらないので、そのような2つを送信する。

import socket

def recvuntil(s, tail):
    data = ''
    while True:
        if tail in data:
            return data
        data += s.recv(1)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('18.188.225.135', 2001))

place1 = 'Angkor'
place2 = '\x00' + place1
data = recvuntil(s, '1: \n').strip()
print data + place1
s.sendall(place1 + '\n')
data = recvuntil(s, '2: \n').strip()
print data + place2
s.sendall(place2 + '\n')
data = recvuntil(s, '\n').strip()
data += recvuntil(s, '\n').strip()
data += recvuntil(s, '\n').strip()
print data
b00t2root{C0ngr4tulati0n5!_y0u_c4n_n0w_go_4nywh3re}

Strange Oracle (crypto)

RSA LSB Oracle Attackで攻めたいが、アクセス回数制限があるため、普通のやり方だと攻撃しきれない。何回か試したところ、最初の方のアクセスでは、lsbは0になることが分かっている。そのため、制限ぎりぎりアクセスできるよう決め打ちで最初はlsbは0として、途中からアクセスして判断する方式にする。

import socket
from fractions import Fraction
from Crypto.Util.number import long_to_bytes

def recvuntil(s, tail):
    data = ''
    while True:
        if tail in data:
            return data
        data += s.recv(1)

def egcd(a, b):
    x,y, u,v = 0,1, 1,0
    while a != 0:
        q, r = b//a, b%a
        m, n = x-u*q, y-v*q
        b,a, x,y, u,v = a,r, u,v, m,n
    gcd = b
    return gcd, x, y

def lsb_oracle(s, enc):
    data = recvuntil(s, ': ')
    print data
    print '2'
    s.sendall('2\n')
    data = recvuntil(s, ': ')
    print data + enc
    s.sendall(enc + '\n')
    data = recvuntil(s, '\n').strip()
    print data.encode('hex')
    print data
    if data == 'I like it!':
        return 0
    else:
        return 1

LIMIT = 450

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('18.188.225.135', 2002))

data = recvuntil(s, ': ')
print data

#### calculate n ####
try_rsa_enc = []
for m in [2, 4, 16]:
    print '1'
    s.sendall('1\n')
    data = recvuntil(s, ': ')
    print data + chr(m)
    s.sendall(chr(m) + '\n')
    data = recvuntil(s, '\n').strip()
    print data
    enc = data[12:]
    try_rsa_enc.append(int(enc, 16))
    data = recvuntil(s, ': ')
    print data

diff1 = (try_rsa_enc[0]) ** 2 - try_rsa_enc[1]
diff2 = (try_rsa_enc[1]) ** 2 - try_rsa_enc[2]

n, _, _ = egcd(diff1, diff2)
e = 65537

for i in range(2, 10):
    if n % i == 0:
        n = n / i
        break

#### get encrypted flag ####
print '3'
s.sendall('3\n')
enc_flag = recvuntil(s, '\n').strip()
print enc_flag

#### get flag ####
c = int(enc_flag, 16)
bounds = [0, Fraction(n)]

i = 0
while True:
    print 'Round %d' % (i+1)
    i += 1

    c2 = (c * pow(2, e, n)) % n
    h2 = '%x' % c2
    if len(h2) % 2 == 1:
        h2 = '0' + h2
    if i > (1024 - LIMIT + 4):
        lsb = lsb_oracle(s, h2)
    else:
        lsb = 0
    if lsb == 1:
        bounds[0] = sum(bounds)/2
    else:
        bounds[1] = sum(bounds)/2
    diff = bounds[1] - bounds[0]
    diff = diff.numerator / diff.denominator

    if diff == 0:
        m = bounds[1].numerator / bounds[1].denominator
        break
    c = c2

flag = long_to_bytes(m)
print flag
b00t2root{dr_s7r4ng3_1s_4_m4rv3l0u5_0r4cl3}