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}

0CTF/TCTF 2019 Quals Writeup

この大会は2019/3/23 15:00(JST)~2019/3/25 15:00(JST)に開催されました。
今回もチームで参戦。結果は10点で915チーム中227位でした。
あまり時間が空いていなかったとはいえ、
中核の問題が1問も解けなかったのは残念です。
自分で解けた問題をWriteupとして書いておきます。

Welcome (Misc)

freenodeで#0ctf2019チャネルに入る。

15:01 *topic : Hello, everyone! Hope you enjoy our game on https://ctf.0ops.sjtu.cn/. And here is your cute flag: flag{Welcome_t0_0ctf_2019!!!}
flag{Welcome_t0_0ctf_2019!!!}

Questionnaire (Misc)

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

Hope you enjoy 0ctf this year! Here is you flag: flag{Th4nk_Y0u_fOr_Play1ng_0ctf2019}
flag{Th4nk_Y0u_fOr_Play1ng_0ctf2019}

Securinets CTF Quals 2019 Writeup

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

HIDDEN (Misc)

証明書が有効になっていないので、証明書を見てみる。
f:id:satou-y:20190327194939p:plain
発行者にフラグが入っていた。

Securinets{HiDDeN_D@tA_In_S3lF_S3iGnEd_CeRtifICates}

MAZE (Crypto)

101個の公開鍵と1個の暗号ファイルがある。この公開鍵の1つで暗号化されたと思われる。この101個の中の2個のnで共通の素数を持っていないかを探し、復号してみる。

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

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 is_printable(s):
    for c in s:
        if ord(c) < 32 or ord(c) > 126:
            return False
    return True

n_list = []
e_list = []

for i in range(101):
    file = 'chall/pub%d.pem' % i
    with open(file, 'r') as f:
        pub_data = f.read()

    pubkey = RSA.importKey(pub_data)
    n_list.append(pubkey.n)
    e_list.append(pubkey.e)

e = e_list[0]

for n_pair in list(itertools.combinations(n_list, 2)):
    p = egcd(n_pair[0], n_pair[1])[0]
    if p > 1:
        ps = [p]
        qs = [n_pair[0]/p, n_pair[1]/p]
        ns = [n_pair[0], n_pair[1]]

with open('chall/cipher.txt', 'r') as f:
    c = int(f.read())

for i in range(len(ns)):
    phi = (ps[0] - 1) * (qs[i] - 1)
    d = inverse(e, phi)
    m = pow(c, d, ns[i])

    flag = long_to_bytes(m)
    if is_printable(flag):
        print flag

結果、2つの公開鍵で共通する素数があったので、両方で復号してみると、片方がフラグになった。

securinets{rs4_1s_g00d_s0m3t1m3s!}

Useless Admin (Crypto)

Many Time Pad Attackのコード
https://github.com/Jwomers/many-time-pad-attack/blob/master/attack.py
を流用して、解読する。

#!/usr/bin/env python 
import string
import collections
import sets

# XORs two string
def strxor(a, b):     # xor two strings (trims the longer input)
    return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b)])

# 12 unknown ciphertexts (in hex format), all encrpyted with the same key
c1 = "1b0605000e14000d1b524802190b410700170e10054c11480807001806004e4f1f4f01480d411400531158141e1c100016535a480c000c031a000a160d421e004113010f13451e0c0100100a020a1a4e165f500d0c1e041a090b001d0515521c0a0410000a4f4b4d1d1c184d071600071c0a521d1706540940"
c2 = "1e10524e001f11481c010010070b13024f0704590903094d0c000e4f0711000615001911454217161a1a45040149000a5218404f1e0012060b1b590a1048171741140c01174c0d49174f0c8d4fc7520211531b0b0c1e4f"
c3 = "1d0c04451352001a000154431b014109450a0a0b000045490403520a1d16490008535848085942071c0d0c57101c0045111c40430c4e111c0b1b1c451d4f071712010508475518061d00060a1b0a1a4c165d"
c4 = "160d074300061d071b524e06190b134e450a0b0a4d4c12411d004f014045491b4649074804001100011d4504520612451e165d53064e164e1d060d0d44541a0041031b0b06540d1a070004001d4b074800531c04101d4f"
c5 = "1a1d524912521548120045021b4e1506490a0859150345531d12521b4e094909030003011148420453074d161e05540b071e4c451b000a084a1d1c04084c0b45060b060a4742070618534218070210484512020043100e191e5956111a1c001c1f0b5c"
c6 = "1a1d5248000154041a1c47430d0b04000005015900140c4f04534f094e08490103000000045442111b11001b1b1d000917535a48004e021d4a0e0b0044491c03080a001a024c11490748074f02040054451a1d150c1b150d020d0e"
c7 = "1a1d5249125215481613500a1b0f0d4e4d0d1c0d000700001d1c001b06004f1d0f5a11480745040a011100181c0c540d13000e44085404404a061716014e010c0308104e084e0d4911450506011853540a5304120a1a154c0a1843001b45541c481607051b431f480d001e0400000c531d01011d00124441010200190d0800000000000e54060001100a1b4d0b040d105347"
c8 = "0a0607000913020d551300041d0f0f0a0003061f154c034f1b53530602004e0c030c541f0454110a1d5a001e0649190419165d00104f104e1b1a101101001b0b1705051b0642040c5341114f0e4b104f0803110b0a060f42"
c9 = "160d074300061d071b524e06190b134e450a0b0a4d4c12411d004f014045491b4649074804001100011d4504520612451e165d53064e16424a1810110c00060d04440e1c02411c0c00544209001953540d165009021a1542"
c10 = "1e10524e001f11481c010010070b13024f0704590903094d0c000e4f0711000615001911454217161a1a45040149000a5218404f1e0012060b1b590a1048171741140c01174c0d49174f4201001f534b0b1c074b"
c11 = "1a49134d4113540a0713490d434e160f541700174f4c11480c53520a1d1100000000190d4549114512544d12000c540402034b4e0d491d40"
c12 = "1a4905410f06110c55064f430a00054e540c0a591603174c0d5f000d1b110006414c1848164516111f1100111d1b54001c17474e0e001c011f1d0a4b"
ciphers = [c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12]
# The target ciphertext we want to crack
target_cipher = "1a1d5249125215481613500a1b0f0d4e4d0d1c0d000700001d1c001b06004f1d0f5a11480745040a011100181c0c540d13000e44085404404a061716014e010c0308104e084e0d4911450506011853540a5304120a1a154c0a1843001b45541c481607051b431f480d001e0400000c531d01011d00124441010200190d0800000000000e54060001100a1b4d0b040d105347"

# To store the final key
final_key = [None]*150
# To store the positions we know are broken
known_key_positions = set()

# For each ciphertext
for current_index, ciphertext in enumerate(ciphers):

        counter = collections.Counter()
        # for each other ciphertext
        for index, ciphertext2 in enumerate(ciphers):
                if current_index != index: # don't xor a ciphertext with itself
                        for indexOfChar, char in enumerate(strxor(ciphertext.decode('hex'), ciphertext2.decode('hex'))): # Xor the two ciphertexts
                                # If a character in the xored result is a alphanumeric character, it means there was probably a space character in one of the plaintexts (we don't know which one)
                                if char in string.printable and char.isalpha(): counter[indexOfChar] += 1 # Increment the counter at this index
        knownSpaceIndexes = []

        # Loop through all positions where a space character was possible in the current_index cipher
        for ind, val in counter.items():
                # If a space was found at least 7 times at this index out of the 9 possible XORS, then the space character was likely from the current_index cipher!
                if val >= 7: knownSpaceIndexes.append(ind)
        #print knownSpaceIndexes # Shows all the positions where we now know the key!

        # Now Xor the current_index with spaces, and at the knownSpaceIndexes positions we get the key back!
        xor_with_spaces = strxor(ciphertext.decode('hex'),' '*150)
        for index in knownSpaceIndexes:
                # Store the key's value at the correct position
                final_key[index] = xor_with_spaces[index].encode('hex')
                # Record that we known the key at this position
                known_key_positions.add(index)

# Construct a hex key from the currently known key, adding in '00' hex chars where we do not know (to make a complete hex string)
final_key_hex = ''.join([val if val is not None else '00' for val in final_key])
# Xor the currently known key with the target cipher
output = strxor(target_cipher.decode('hex'),final_key_hex.decode('hex'))
# Print the output, printing a * if that character is not known yet
print ''.join([char if index in known_key_positions else '*' for index, char in enumerate(output)])

'''
Manual step
'''
# From the output this prints, we can manually complete the target plaintext from:
# The secuet-mes*age*is: Wh** usi|g **str*am cipher, nev***use th* k*y *ore than onc*
# to:
# The secret message is: When using a stream cipher, never use the key more than once

# We then confirm this is correct by producing the key from this, and decrpyting all the other messages to ensure they make grammatical sense
target_plaintext = "it is a capital mistake to theorize before one has data. insensibly one begins to twist facts to suit theories, instead of theories to suit facts."
print target_plaintext
key = strxor(target_cipher.decode('hex'),target_plaintext)
for cipher in ciphers:
        print strxor(cipher.decode('hex'),key)

最終的な結果は上記のコードで、結果は以下の通り。

*t is a capixal m**toke *o theorn*e*before one has d*t*. *n}en*i*l* o***begins*vo*t*i*************************************************************
it is a capital mistake to theorize before one has data. insensibly one begins to twist facts to suit theories, instead of theories to suit facts.
how often have i said that when you have excluded the impossible whatever remains, however improbable, must be the truth.
my name is sherlock holmes. it is my business to know what other people don・ャ!"t know.
never trust to general impressions, my boy, but concentrate yourself upon details.
education never ends, watson. it is a series of lessons with the greatest for the last.
it is a great thing to start life with a small number of really good books which are your very own.
it has long been an axiom of mine that the little things are infinitely the most important.
it is a capital mistake to theorize before one has data. insensibly one begins to twist facts to suit theories, instead of theories to suit facts.
you have a grand gift for silence, watson. it makes you quite invaluable as a companion.
education never ends, watson. it is a series of lessons, with the greatest for the last.
my name is sherlock holmes. it is my business to know what other people do not know.
i am a brain, watson. the rest of me is a mere appendix.
i wanted to end the world, but i'll settle for ending yours.
Securinets{i wanted to end the world, but i'll settle for ending yours.}