前言
今年的DDCTF
玩的非常充实,7天的时间里基本每天都在学习新东西,整个Writeup
会按题目类型进行分类。
(╯°□°)╯︵ ┻━┻
签到题就直接pass了,从第二道开始。题目就给出了一串16进制
的字符:1
d4e8e1f4a0f7e1f3a0e6e1f3f4a1a0d4e8e5a0e6ece1e7a0e9f3baa0c4c4c3d4c6fbb9b2b2e1e2b9b9b7b4e1b4b7e3e4b3b2b2e3e6b4b3e2b5b0b6b1b0e6e1e5e1b5fd
一开始是想看看是不是什么文件的,但没有发现。
而且观察发现按两位数转10进制的话全都超出了可见字符的
ASCII
码表,当然解题时也考虑过对每两个16进制进行^
异或,但都不对。然后尝试对每两个字符转出来的10进制
进行减128
,因为这样每个值得范围就能落在ASCII
码表。1 | import re |
第四扩展FS
附件下下来发现很大,用winhex
查看了一下,发现后面全是填充的,然后还有个压缩包,我们把它提取出来,但需要解压密码,然后解压密码可以在原图片的备注
信息里找到。
接着我们就能拿到一堆乱码的字符。
因为题目描述中说到
频次
很重要,所以尝试使用在线的词频密码分析网站,但没有发现,所以考虑另一种可能,就是不用还原成句子,只需要里面的每个词的频次。然后写个脚本跑一下:1 | # -*- coding:utf-8 -*- |
流量分析
下载后使用wireshark
进行分析,流量分析题如果不掌握点小技巧,估计眼睛会看瞎。
打开后直接拉到灰色
报文块,如下图,然后一把TCP追踪流
过去。
然后逐个查看流,然后你会发现有两个压缩包,但后面发现那就是坑来的,反正我没用上。。。。在查看他们的邮件往来记录的时候有一张图片引起了我的注意:
恢复过来后看起来比较奇怪,,因为以前也没搞过密码,所以一开始还真没注意到它的作用。
接着再往下走,有一个地方引起了我的注意。
TLSv1.2
是https
上使用的,后面的传输数据都会被服务器的私钥进行加密,然后这时想起上面得到的那张图片,就联系起来了。百度了一下在wireshark
里还原https
数据的方法:传送门。按照这里的方法进行恢复,然后就能拿到flag了。
PS一下:这里对我提取出来的文件进行
md5
,但都没有得到题目中给的那个值。。。
安全通信
题目描述:1
请通过nc 116.85.48.103 5002答题,mission key是f49348cf84d390da52498077ae7137d5,agent id随意填就可以
1 | #!/usr/bin/env python |
这道题考的是aes ecb
的攻击方法,经过一通google
后找到了一个比较有参考意义的writeup,传送门。
需要了解的是ECB
是分组
加密的(一组16个字符),也就是一块一块的,这点是我们后续攻击的重要前提。
然后我们看一下他这个脚本,经过分析后我们可以发现加密的消息的
长度
我们是可以控制的。
而且对于一次
连接
,在同一密钥的情况下,我们可以随意加密任何数据任何次。
所以攻击手段就很清晰了。
1 | 明文分组(16个字符) 对于的密文(对得到的一长串密文进行32位切割) |
如上所示,我们可以控制name
的长度来使(在这种情况下)第4块
的最后一个字符是flag
中我们要求的第一个字符,而它明文的前15个字符我们是知道的,密文我们也知道,所以我们可以使用这样来暴力猜测出最后一位
到底是什么。1
2
3
4
5
6
7# 伪代码
raw = 'ion's flag is: '
for ch in range(33,128):
tmp = raw + chr(ch)
my_miwen = encrypt(tmp)
if my_miwen == true_miwen:
print('要求的是:',tmp)
所以只要我们设计好填充的长度
就能将flag的所有字符都爆破出来。填充的时候有一个需要注意的地方就是块的推进
(进位),因为你不能无限制退后,所以当我们用完填充块的大小后要更新需要爆破的块。代码如下: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
44
45
46
47
48
49
50
51
52# coding:utf-8
import socket # 导入 socket 模块
import re
sc = None
c = '-'
def sendAgent(agent):
global sc
sc = socket.socket() # 创建 socket 对象
host = "116.85.48.103" # 获取本地主机名
port = 5002 # 设置端口
addr = (host, port)
sc.connect(addr) # 绑定端口号
sc.recv(1024) # 打印接收的数据
sc.send('f49348cf84d390da52498077ae7137d5\n')
sc.recv(1024) # 打印接收的数据
sc.send(agent+'\n')
return sc.recv(1024)
def tryChar():
raw = '0000000000000000'
allstr = "Connection for mission: {}, your mission's flag is: "
while True:
# 除去 {},
req = 15 - (len(allstr)-2) % 16 # 求出要填充的长度
raw_t = raw[:req]
raw_t_concat = allstr.format(raw_t).replace('-','{',-1).replace('=','}',-1)
print(raw_t_concat)
rex = re.compile('.{1,16}') # 对明文进行分组
tmp = re.findall(rex, raw_t_concat)
print(tmp)
rs_len = len(tmp) # 分组长度
rs = tmp[-1] # 要爆破的块(最后一块)
print('正在爆破:%s' % rs)
# 重新连接 发送Agent = raw_t
firstRs = sendAgent(raw_t)
block = re.findall('.{32}',firstRs)[rs_len-1] # 拿到密文对应需要爆破的明文分组的一块
# 爆破
for cs in range(33,128):
sc.recv(1024)
sc.send(rs+ chr(cs)+'\n')
rss = sc.recv(1024).split('\n') [0]
if rss==block:
c = chr(cs)
allstr+=c.replace('{','-').replace('}', '=')
break
if c=='}':
print('over!')
return
tryChar()
最后得到flag:DDCTF{87fa2cd38a4259c29ab1af39995be81a}
。