内容简介:今年的签到题就直接pass了,从第二道开始。题目就给出了一串一开始是想看看是不是什么文件的,但没有发现。
前言
今年的 DDCTF 玩的非常充实,7天的时间里基本每天都在学习新东西,整个 Writeup 会按题目类型进行分类。
(╯°□°)╯︵ ┻━┻
签到题就直接pass了,从第二道开始。题目就给出了一串 16进制 的字符:
d4e8e1f4a0f7e1f3a0e6e1f3f4a1a0d4e8e5a0e6ece1e7a0e9f3baa0c4c4c3d4c6fbb9b2b2e1e2b9b9b7b4e1b4b7e3e4b3b2b2e3e6b4b3e2b5b0b6b1b0e6e1e5e1b5fd
一开始是想看看是不是什么文件的,但没有发现。
而且观察发现按两位数转10进制的话全都超出了可见字符的 ASCII 码表,当然解题时也考虑过对每两个16进制进行 ^ 异或,但都不对。然后尝试对每两个字符转出来的 10进制 进行减 128 ,因为这样每个值得范围就能落在 ASCII 码表。
import re
sss = 'd4e8e1f4a0f7e1f3a0e6e1f3f4a1a0d4e8e5a0e6ece1e7a0e9f3baa0c4c4c3d4c6fbb9b2b2e1e2b9b9b7b4e1b4b7e3e4b3b2b2e3e6b4b3e2b5b0b6b1b0e6e1e5e1b5fd'
nums = re.findall('\w{2}',sss)
flag = ''
for one in nums:
ch = chr(int(one,16) - 128)
flag += ch
print(flag)
# That was fast! The flag is: DDCTF{922ab9974a47cd322cf43b50610faea5}
第四扩展FS
附件下下来发现很大,用 winhex 查看了一下,发现后面全是填充的,然后还有个压缩包,我们把它提取出来,但需要解压密码,然后解压密码可以在原图片的 备注 信息里找到。
接着我们就能拿到一堆乱码的字符。
因为题目描述中说到 频次 很重要,所以尝试使用在线的词频密码分析网站,但没有发现,所以考虑另一种可能,就是不用还原成句子,只需要里面的每个词的频次。然后写个脚本跑一下:
# -*- coding:utf-8 -*-
with open ('file.txt','r') as f:
dd3 = f.read()
a = {}
for i in dd3:
if i not in a:
a[i]=1
else:
a[i] +=1
b = sorted(a.items(), lambda x, y: cmp(x[1], y[1]), reverse=True)
flag = ''
for i in b:
flag += i[0]
print(flag)
# DCTF{x1n9shaNgbIci}
流量分析
下载后使用 wireshark 进行分析,流量分析题如果不掌握点小技巧,估计眼睛会看瞎。
打开后直接拉到 灰色 报文块,如下图,然后一把 TCP追踪流 过去。
然后逐个查看流,然后你会发现有两个压缩包,但后面发现那就是坑来的,反正我没用上。。。。在查看他们的邮件往来记录的时候有一张图片引起了我的注意:
恢复过来后看起来比较奇怪,,因为以前也没搞过密码,所以一开始还真没注意到它的作用。
接着再往下走,有一个地方引起了我的注意。
TLSv1.2 是 https 上使用的,后面的传输数据都会被服务器的私钥进行加密,然后这时想起上面得到的那张图片,就联系起来了。百度了一下在 wireshark 里还原 https 数据的方法: 传送门 。按照这里的方法进行恢复,然后就能拿到flag了。
PS一下:这里对我提取出来的文件进行 md5 ,但都没有得到题目中给的那个值。。。
安全通信
题目描述:
请通过nc 116.85.48.103 5002答题,mission key是f49348cf84d390da52498077ae7137d5,agent id随意填就可以
#!/usr/bin/env python
import sys
import json
from Crypto.Cipher import AES
from Crypto import Random
def get_padding(rawstr):
remainder = len(rawstr) % 16
if remainder != 0:
return '\x00' * (16 - remainder)
return ''
def aes_encrypt(key, plaintext):
plaintext += get_padding(plaintext)
aes = AES.new(key, AES.MODE_ECB)
cipher_text = aes.encrypt(plaintext).encode('hex')
return cipher_text
def generate_hello(key, name, flag):
message = "Connection for mission: {}, your mission's flag is: {}".format(name, flag)
return aes_encrypt(key, message)
def get_input():
return raw_input()
def print_output(message):
print(message)
sys.stdout.flush()
def handle():
print_output("Please enter mission key:")
mission_key = get_input().rstrip()
print_output("Please enter your Agent ID to secure communications:")
agentid = get_input().rstrip()
rnd = Random.new()
session_key = rnd.read(16)
flag = '<secret>'
print_output(generate_hello(session_key, agentid, flag))
while True:
print_output("Please send some messages to be encrypted, 'quit' to exit:")
msg = get_input().rstrip()
if msg == 'quit':
print_output("Bye!")
break
enc = aes_encrypt(session_key, msg)
print_output(enc)
if __name__ == "__main__":
handle()
这道题考的是 aes ecb 的攻击方法,经过一通 google 后找到了一个比较有参考意义的writeup,传送门。
需要了解的是 ECB 是 分组 加密的(一组16个字符),也就是一块一块的,这点是我们后续攻击的重要前提。
然后我们看一下他这个脚本,经过分析后我们可以发现加密的消息的 长度 我们是可以控制的。
而且对于一次 连接 ,在同一密钥的情况下,我们可以随意加密任何数据任何次。
所以攻击手段就很清晰了。
明文分组(16个字符) 对于的密文(对得到的一长串密文进行32位切割)
Connection for m ----> eae138090c7a60d97a6c54ce15fe7888 # 第1块
ission: 12345678 ----> aaaab01d2ba84861447153e790047db4 # 第2块
90123, your miss ----> d9d2a15efa5c6f75097d89f1e5cc629c # 第3块
ion's flag is: D ----> 8e16cf020a37e52a28fdee32d469c2b4 # 第4块
DCTF{afafjafj101 ----> 5ca9b0e48d1dec7bb06a73dd163380a0 # 第5块
.....
如上所示,我们可以控制 name 的长度来使(在这种情况下) 第4块 的最后一个字符是 flag 中我们要求的第一个字符,而它明文的前15个字符我们是知道的,密文我们也知道,所以我们可以使用这样来暴力猜测出 最后一位 到底是什么。
# 伪代码
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的所有字符都爆破出来。填充的时候有一个需要注意的地方就是块的 推进 (进位),因为你不能无限制退后,所以当我们用完填充块的大小后要更新需要爆破的块。代码如下:
# 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} 。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。