Automne's Shadow.

TG:HACK CTF 2019 Solved WriteUp

2019/04/21 Share

Crypto

利用下班时间打了下这个比赛,挑了部分解出的有趣题目记录下来供参考。

Echo Chamber

pwntools的简单使用,将获取到的数据重复一遍发送过去

脚本如下:

1
2
3
4
5
6
7
8
9
10
11
from pwn import *

c = remote('echo.tghack.no',5555)
i = 0
while i <= 50:
s = c.recvline()
print s
c.sendline(s.strip())
i += 1
print i
print c.recvline()

得到flag

TG19{behold_the_echo_chamber_of_secrets}

Exclusive Magic Club

automne

上图的二进制代码摘录如下:

00111001 00101000 01000101 01010001 00011110 00010000 00110000 00011100
00110001 00001011 00011000 00000100 00110001 00111101 00010001 00011100
00101011 00011001 00000111 00010001 00110111 00100100 00111011 00000000
00000100 00011000 00001010 00000101 00011111 00110000 00010000 00000001
00000000 00001001

直接Binary转Ascii是乱码,查看本题的分值和相关信息,才发现是XOR运算,也就是和mother_knows_best做异或运算,在此之前,做了诸如对二进制代码按位取反等操作,在下面的脚本也有体现。

注意需要将mother_knows_best转换为二进制,因为原文是34个二进制,所以mother_knows_best,也就是xorkey需要补全。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#encoding=utf-8

secret = "00111001 00101000 01000101 01010001 00011110 00010000 00110000 00011100 00110001 00001011 00011000 00000100 00110001 00111101 00010001 00011100 00101011 00011001 00000111 00010001 00110111 00100100 00111011 00000000 00000100 00011000 00001010 00000101 00011111 00110000 00010000 00000001 00000000 00001001"
step1 = secret.split(" ")
print step1

#the following is wrong try..

step2 = []
for i in step1:
print i
for j in i:
#print j
step2.append(str(int(j)^1))

print step2
step3 = "".join(step2)
print step3
step4 = ""
for l in range(len(step3)//8):
print int(step3[l*8:l*8+8],2)
step4 += step3[l*8:l*8+8] + " "
print step4
#print step4[::-1]

step5 = step4.strip().split(" ")

print step5

#now the xor crack begins..

xorkey = "01101101 01101111 01110100 01101000 01100101 01110010 01011111 01101011 01101110 01101111 01110111 01110011 01011111 01100010 01100101 01110011 01110100 01101101 01101111 01110100 01101000 01100101 01110010 01011111 01101011 01101110 01101111 01110111 01110011 01011111 01100010 01100101 01110011 01110100"
xorkey2 = xorkey.split(" ")
print len(step1)
print len(xorkey2)
flag = ""
for x in range(34):
#print step1[x]
#print int(step1[x],2)
#print int(xorkey2[x],2)
flag += chr(int(step1[x],2)^int(xorkey2[x],2))

print flag

最终得到flag

automne

Josefssons Final Exam

automne

有了上题的经验,第一部分明显是使用good_luck作为xorkey,与二进制异或。

使用类似的脚本处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#encoding=utf-8

secret = "00100011 00100010 00000100 00000010 00001000 00101010 00010111 00001011 00000001 01010101 01010110 00001100 00100010 01101100 00011000 00010001 00001111 00101111 01011110 00011111 00000100 00010001 00011011 00000010 00011001 00101100 00011011 00010011 00101001 01011101 00110010 00111000 00000001 00110011 00101001 01011011 00001101 01011110 01010110 00000110 00011000 00101010 00100011 01010011 00100011 00100001 00111101 01010110"

step1 = secret.split(" ")
print step1

#now the crack begins.
xorkey = "01100111 01101111 01101111 01100100 01011111 01101100 01110101 01100011 01101011 01100111 01101111 01101111 01100100 01011111 01101100 01110101 01100011 01101011 01100111 01101111 01101111 01100100 01011111 01101100 01110101 01100011 01101011 01100111 01101111 01101111 01100100 01011111 01101100 01110101 01100011 01101011 01100111 01101111 01101111 01100100 01011111 01101100 01110101 01100011 01101011 01100111 01101111 01101111"
xorkey2 = xorkey.split(" ")
print len(step1)
print len(xorkey2)
flag = ""
for x in range(48):
#print step1[x]
#print int(step1[x],2)
#print int(xorkey2[x],2)
flag += chr(int(step1[x],2)^int(xorkey2[x],2))

print flag

运行后的输出效果如下:

automne

输出了下面的内容:

DMkfWFbhj29cF3tdlD9pkuDnlOptF2VgmFJ0j19bGFV0HFR9

到这里思路断了,因为使用base64去解密是乱码。
后来考虑是不是使用了移位密码把一段base64字符串做了处理,于是验证了一下

>>> import base64
>>> base64.b64encode("TG19{blablabla}")
'VEcxOXtibGFibGFibGF9'

再看看移位处理后的字符串,前面的字符果然是相同的。

automne

最后就是按照输出的内容修改字符大小写

VEcxOXtzb29uX3lvdV9hcmVfdGhlX2NyeXB0b19tYXN0ZXJ9

得到flag

>>> base64.b64decode("VEcxOXtzb29uX3lvdV9hcmVfdGhlX2NyeXB0b19tYXN0ZXJ9")
'TG19{soon_you_are_the_crypto_master}'

Passing Notes

题目首先给了一段AES的密文

vyLlwWSY1PCK5ELNTPUVdpl8z0rIXiB2+Ybcu/BeXidR3MEiym852HCkS6wHVCr+CdpP6Moe9VQUeFcyq3vZDpVK/orl+8vREYMRrnQR9O4=

然后给出了一段hash

bbb2c5e63d2ef893106fdd0d797aa97a

这段hash可以经过md5解密,解密后的内容为supersecretpassword

最后给出了一段加密代码,告诉我们使用的是AES加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import base64
from Crypto.Cipher import AES
from Crypto import Random
from Crypto.Hash import SHA256
from struct import pack

BS = 16
secret = b'not_the_secret'
key = SHA256.new()
key.update(secret)

def encrypt(key, message):
iv = Random.new().read(BS)
pad_len = BS - divmod(len(message), BS)[1]
padding = [pad_len]*pad_len
padding = pack('b'*pad_len, *padding)
cipher = AES.new(key, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(message + padding))

这里有个坑,就是上面的代码里key.update(secret)得到的不是可用的key

修改代码,再加载正确的密码就可以解出原文。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import base64
from Crypto.Cipher import AES
from Crypto import Random
from struct import pack
import hashlib

BS = 16
secret = b'supersecretpassword'
key = hashlib.sha256(secret.encode('utf-8')).digest()

unpad = lambda s: s[:-ord(s[len(s) - 1:])]

def encrypt(key, message):
iv = Random.new().read(BS)
pad_len = BS - divmod(len(message), BS)[1]
padding = [pad_len]*pad_len
padding = pack('b'*pad_len, *padding)
cipher = AES.new(key, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(message + padding))

def decrypt(key, cipher):
enc = base64.b64decode(cipher)
iv = enc[:16]
cipher = AES.new(key, AES.MODE_CBC, iv)
return unpad(cipher.decrypt(enc[16:])).decode('utf8')

if __name__ == '__main__':
cipher = 'vyLlwWSY1PCK5ELNTPUVdpl8z0rIXiB2+Ybcu/BeXidR3MEiym852HCkS6wHVCr+CdpP6Moe9VQUeFcyq3vZDpVK/orl+8vREYMRrnQR9O4='
print decrypt(key,cipher)

运行

automne

Superb Owlput

superb-owlput.pcap

提供了一个流量包文件供我们分析,使用wireshark分析,发现主要是DNS流量

automne

从上图就可以看到Info信息里的域名长串有点东西
转一下就发现是JPEG的文件头,那么这题的思路就很清楚了,把所有的域名字串拼起来得到一张图片。

automne

这里使用tshark和awk脚本可以很方便的处理,注意-Y是做过滤的

tshark -r superb-owlput.pcap -Y "ip.src == 172.19.1.50 && dns" -T fields -e dns.qry.name | awk '{if ($1~/.dw.tghekk.local/) print $1}' | awk -F '.' '{print $1}' | tr -d '\n' > dns

得到dns后,再使用xxd转换为图片文件,命令如下:

xxd -p -r dns flagdns

然而打开图片后并没有flag,使用exiftool看一下文件头

automne

明显是一段base64字符串
加入=之后就可以解密了。

>>> base64.b64decode("VEcxOXtUSEVfV0FMTFNfQVJFX0NPVkVSRURfV0lUSF9NQUdJQ0FMX1NBTFR9Cg==")
'TG19{THE_WALLS_ARE_COVERED_WITH_MAGICAL_SALT}\n'
CATALOG
  1. 1. Echo Chamber
  2. 2. Exclusive Magic Club
  3. 3. Josefssons Final Exam
  4. 4. Passing Notes
  5. 5. Superb Owlput