[amateurs CTF 2024] crypto/pilfer-techies

这题费了几天,昨天写到11点半才基本完成程序,需要交互2000多,远程太慢了交互两次就断掉了,反正本地能成,程序逻辑上正确了。小鸡块也写了WP了等写完马上去看小鸡块神的思路。也许有的问题很大呢?

先简单看下题:

#!/usr/local/bin/python3import hmac
from os import urandomdef strxor(a: bytes, b: bytes):return bytes([x ^ y for x, y in zip(a, b)])class Cipher:def __init__(self, key: bytes):self.key = keyself.block_size = 16self.rounds = 256self.debug = Falsedef F(self, x: bytes):return hmac.new(self.key, x, 'md5').digest()[:15]def encrypt(self, plaintext: bytes):plaintext = plaintext.ljust(((len(plaintext)-1)//self.block_size)*16+16, b'\x00')ciphertext = b''for i in range(0, len(plaintext), self.block_size):block = plaintext[i:i+self.block_size]idx = 0for _ in range(self.rounds):L, R = block[:idx]+block[idx+1:], block[idx:idx+1]L, R = strxor(L, self.F(R)), Rblock = L + Ridx = R[0] % self.block_sizeif self.debug:print(block.hex())ciphertext += blockreturn ciphertext.hex()key = urandom(16)
cipher = Cipher(key)
flag = open('flag.txt', 'rb').read().strip()print("pilfer techies")
while True:choice = input("1. Encrypt a message\n2. Get encrypted flag\n3. Exit\n> ").strip()if choice == '1':pt = input("Enter your message in hex: ").strip()pt = bytes.fromhex(pt)print(cipher.encrypt(pt))elif choice == '2':print(cipher.encrypt(flag))else:breakprint("Goodbye!")

先取idx为0,从明文取第idx位(这个字节%10作为下下轮的idx),用hmac加密生成一个15字节的密钥(F函数)与明文其它部分异或,后边加上idx位的明文。

前边有一题是只有1轮,那个直接可以恢复。这个有256轮比较麻烦。

获取密钥R:

题目是通过R来加密,只需要获取所有的F(R)就可以解密了(256组),第1步是恢复R[0]

先构造一个明文看它的加密过程:

01...EF
构造的时文0x...
第1轮idx=0x^F(0)[0]0
第2轮idx=0xf0^F(x^F(0)[0])[15]x^F(0)[0]

当某个idx位置的字符%10==0xf时,后边的idx变为0xf则一直用这个值对前边加密一直到256次结束,由于加密直接是异或,所以后边的每两个会相互抵消。

第1步 取得R[0]的第1位

当构造一个串,第1位为0,第2位为x,如果x^F(0)[0]%0x10=0xf时,只需要两轮便会结束(后边互相抵消)。这个可以通过爆破获取所有值,但需要判断哪一个是正确的,因为所有密文值的尾字节都是F结束。

这样我来构造两个明文:

pt1 = bytes([0,i]+[0]*14)
pt2 = bytes([0,i^0x70]+[0xff]*14)

如果1的i^F(0)[0]%0x10==0xf只有两轮加密,则2也成立。则1与2的尾(i^F(0))[-1]^(i^0x80^F[0])[-1]==0x70 ,反之如果1不成立虽然最后也会是0xf但1和2加密次数不一定相同则尾号极大概率不同。

这时候通过i和尾号可以得到F(0)的第1字节: i^F(0)[0]=c[-1] => F(0)[0] = c[-1]^i

第2步 取得全部的R[0]

这是个来构造第2个明文,让流程idx变成0->0->f->f,由于已知F(0)的第1字节,所以设定让第2字节加密后为0,然后爆破第3字节让它再次加密后为尾号f

构造明文0f00xy
第1轮f00^f00=0x^f(0)[1]y^f(0)[-1]0
第2轮x^f(0)[1]^f(0)[0]=xfy^f(0)[-1]^f(0)[-2]0^F(0)[-1]0
第3轮0^F(0)[-1]^F(xf)[-2]0^F(xf)[-1]xf
第4轮y^f(0)[-1]^f(0)[-2]0^F(0)[-1]0xf

当有两轮f的情况,最后两轮的f(xf)会互相抵消,第4轮与第2轮比只是向前错了一位。c[13]=F(0)[-1]而当设置x,...y都为0时c[12]=f(0)[-1]^f(0)[-2]这样一直向前可以得到R[0]剩余的1-14位。

第3步 取得R的第F列

回到第1步取得的数据,第1步当设置流程为0->F时只有两轮加密F0和Fxf

现在已经知道R[0]那么就可以推出R[xf],直接设置第1个为0第2个为R[0][0]^0xXF取得的密文与R[0]异或即可

第4步 取得全部的R

也是第1步的两轮流程,设置第1字节为s,第s%0x10+1字节为i爆破,由于只有两轮加密,而第2轮分部的尾号f的R已经在上一步全都得到,可以异或出其它值。但同第1步一样需要在尾字节符合的情况下作个反转其它字符来验证。

由于一共256-1-16组,每组爆破16种情况加几次验证直到通过,总次平均在8*239次以上。所以爆破量还是满大的。

这步其实并不需要解出全部的R,只需要在解密时需要哪一个再去爆破,每个平均9次,可以减少爆破量

解密:

获得全部的密钥后,解密就是从后向前查表。

第用最后一字节A对应的密钥对密文解密,通过解密后的尾字节B判断A插回到哪个位置。

同样有个问题,尾号为F时,由于加密是偶数次,可能出现尾号两轮都是F的情况,需要分别处理两次。

这个流程因为轮数未知无法判断结束,需要通过flag明文的特征(可见字符和pad \0)来判断。

解题代码:

包装的公共函数

from pwn import *cnt = 0
def get_v(pt):global cntcnt+=1p.sendlineafter(b'> ', b'1')p.sendlineafter(b"Enter your message in hex: ", pt.hex().encode())msg = p.recvline().strip().decode()return bytes.fromhex(msg)def get_tv(r0,r1=0,r2=0):return get_v(bytes([r0,r1,r2]+[0]*13))def get_flag():p.sendlineafter(b'> ', b'2')msg = p.recvline().strip().decode()return bytes.fromhex(msg)

总流程

def step1():global Rfor i in range(0x10):v1 = get_v(bytes([0,i]+[0]*14))v2 = get_v(bytes([0,i^0x70]+[0xff]*14))if v1[-1]^v2[-1]==0x70:print(i, v1.hex())R0 = get_r00(i^v1[-1])   #1,取得R0R[0] = R0[:-1]print('R[0]=', bytes(R[0]).hex())print(f"{cnt =}")get_rf()                 #2,取得第F列  16#get_rx()                 #3,取得第0列 +15#print(R)print(f"{cnt =}")#解密for i in range(0, len(enc_flag),16):decrypt(enc_flag[i:i+16])print(f"{cnt =}")break

取得R[0][0]


#取得F(b'\x00')
#L^F(0)^F(0)^F(f)
def get_r00(f_0_0):print(f_0_0)for i in range(0x10):v = get_tv(0,f_0_0,i)if v[-2] != 0 : continuev2 = get_tv(0,f_0_0,i^0x70)if v2[-2] != 0: continueprint('Found:',i,v.hex())#f_0_1 = i^f_0_0R0 = [0]for j in range(13,-1,-1):R0.append(v[j]^R0[-1])R0.append(f_0_0)R0 = R0[::-1]print('R[0]',bytes(R0).hex())  #6bb5b072e39d1faf5dcad9f197837abreakreturn R0

取得尾号f列的R

'''
>>> get_v(0,0x6b^0xf)
0fb5b072e39d1faf5dcad9f197837a00
688f9f6f38387a4f4c9dffe49977ad0f
>>> get_v(0,0x6b,0x6b^0xb5^0xf)
0064b072e39d1faf5dcad9f197837a00
0f05c2917e82b0f29713286614f97a00
d8fd7cf227972785956c6867e377ad0f
05c2917e82b0f29713286614f97a000f #偶数次R(0)
688f9f6f38387a4f4c9dffe49977ad0f F0[100]
6bb5b072e39d1faf5dcad9f197837a   R0
dd3fed8ca527d51286440e731a0dad   Rf
'''
#0xf 0x1f 0x2f ... f列
#取得第F列def get_rf():global Rfor i in range(0xf,0x100,0x10):v = get_tv(0, R[0][0]^i)R[i] = [i for i in xor(v[:-1], bytes(R[0]+[0])[1:])]print(f'R_{i:x}', bytes(R[i]).hex())

取得剩余的全部R

#补全其它
'''
01 A B C ...     01000800000000000000000000000000
0f A C ...  01     dd7775c35d921c46f404c7682c5d437fdd75c35d921c46f404c7682c5d4301
A  C ... 01 0f     8009b22d640ecb6f2e636ba734310e5d7c7170f6128d9b2aa4038b69720f7f
'''
def get_rx(s):global Rs1 = s%0x10for i in range(0x100):pt = [0]*16pt[0],pt[s1 + 1]=s,i v = get_v(bytes(pt))if v[-2] != s^R[v[-1]][-1]: continuept = [0xff]*16            #第2次取数,两次比较,减少碰撞取错pt[0],pt[s1 + 1]=s,i^0x80 v1 = get_v(bytes(pt))if v[-1]^v1[-1] != 0x80: continuet1 = [_ for _ in xor(v[:-2],bytes(R[v[-1]][:-1]))]R[s] = t1[:s1]+[i^v[-1]]+t1[s1:]#print(f">>> {s:x} {i:x} {bytes(R[s]).hex()}={cipher.F(bytes([s])).hex()} ")break

解密

'''
bf1a15ab9aead5616aa5190645377261
f254c239818ee6efbf22fd26a6b3711a
e972490815d417dbdace83652e4b4dfd
bdb42fad8f66745e0268bf39f2800e4b
590abff1ca6e45ca77266575e46ce839
3bf4dc2aedd787ea8bd281b165f4b026
d59eb0020556f207401d454d9863b187
f122b2591ace01be2ee552c890e3e607
dfed5b62b533dc7f25d06e48df632abe
e676085c75fd3053209d69c345a3342a
1800c0c8428c3e8e5bbc1bac23838b69
8d70d881e274ac1db5be359be0d40abc
cfc99964d2f8b21fa6a6a61a04868be0
f83546173cf934c079a84e273e3d3bcf
'''
def decrypt(enc):senc = encfor _ in range(2):enc = sencprint('way:',_)if _ == 1:  #两个xf结尾if R[enc[-1]] == '':get_rx(enc[-1])enc = xor(enc[:-1],bytes(R[enc[-1]]))+enc[-1:]#print(enc.hex())m = 30while m:if R[enc[-1]] == '':get_rx(enc[-1])idx = enc[-1]enc1 = xor(enc[:-1],bytes(R[idx]))pos = enc1[-1]%0x10m -=1if all([1 if 0x20<=i<0x7f or i==0 else 0 for i in enc1.rstrip(b'\x00')]):enc = bytes([idx])+ enc1print(enc)breakelse:enc = enc1[:pos] + bytes([idx]) + enc1[pos:] #print(enc.hex())return enc 

开始

#------ready-----------------------
R = ['']*0x100#context.log_level = 'debug'
#p = remote('chal.amt.rs', 1415)
p = process(['py', './pilfer-techies.py'])enc_flag = get_flag()
print(enc_flag)
step1()

其它

看了小鸡块的WP Crypto趣题-Oracle | 糖醋小鸡块的blog

大神的思路完全不一样,但是显然更简单一点。

由于给定的原因第1个字节v0已知(后边块虽然未知但也可以猜,猜不了的就爆破) 那么通过v0确定下一字符的位置,然后猜下一字符v1。然后反复连接远端,由于密钥是动态的有1/16的概率会使第2个字节v1^F(v0)后的值=xF ,当密文(每次连接重取)与爆破的密文后两字节相同时说明字符正确并且都是走过0->v0->v1->xf->xf 的加密过程,给出的flag是经过这个流程加密猜的也是相同,则两个异或就能得到明文的14字节,再加上猜的两个自己整理成一段。。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/810887.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

二分查找基本模版

二分&#xff1a;通过不断取中点&#xff0c;重复将一个区域一份为二&#xff0c;使其不断缩小范围直至找到答案 本质&#xff1a;性质/边界&#xff0c;而不是单调性 时间复杂度&#xff1a;O(logN) 代码模版&#xff1a; //找到的是最左侧的数值点 while(l < r){ //尽…

[数据结构]——二叉树——堆的实现

1. 堆的概念及结构 如果有一个关键码的集合K { &#xff0c; &#xff0c; &#xff0c;…&#xff0c; }&#xff0c;把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中&#xff0c;并满足&#xff1a; < 且 < ( > 且 > ) i 0&#xff0c;1&…

前端面试题(1)

1&#xff0c;CSS盒子模型 CSS的盒模型有两种&#xff1a;标准盒模型&#xff0c;IE盒模型。IE盒模型&#xff0c;其中content包含了内边距padding和边框border。盒子的实际宽度 contentmargin。标准盒模型&#xff0c;盒子的实际宽度 content(内容)padding(内边距)border(边…

rocky9 yum 安装与配置MySQL8

1.前置条件&#xff1a; 把yum包更新到最新 [rootlocalhost ~]# yum update 查看系统中是否已安装 MySQL 服务 rpm -qa|grep mysql 如果有安装mysql,则需要先卸载之前安装的mysql&#xff1a;yum -y remove mysql 然后再查看mysql是否都卸载完成,如果还有没卸载完成的&am…

小程序开发SSL证书下载和安装

在开发小程序时&#xff0c;确保数据的安全传输至关重要&#xff0c;而实现这一目标的关键在于正确获取与安装SSL证书。以下详细介绍了从获取到安装SSL证书的完整流程&#xff0c;以助您为小程序构建可靠的加密通信环境。 一、小程序SSL证书类型选择&#xff1a; 域名验证型D…

创新指南|全球需求低迷中国企业出海趋势洞察和创新机会

对于企业出海来说&#xff0c;第一步判断趋势非常重要&#xff0c;这甚至事关企业生死。比如十年前的2014年&#xff0c;中国最大的两家电商平台阿里和京东成功IPO&#xff08;上市&#xff09;后&#xff0c;认为接下来最大的机会在于中国市场的消费升级。与阿里、京东不同&am…

TypeScript基础语法

这里写自定义目录标题 变量条件控制循环函数类和接口模块开发 变量 TypeScript在JavaScript的基础上加入了静态类型检查功能&#xff0c;因此每一个变量都有固定的数据类型。 let msg: string hello worldlet 声明变量的关键字&#xff0c; const 则代表常量 msg 变量名称 &…

Linux:软件包管理器 - yum

Linux&#xff1a;软件包管理器 - yum Linux的软件安装方式源代码安装rpm包安装yum安装 yum三板斧yum listyum installyum remove yum生态yum源 Linux的软件安装方式 源代码安装 在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序 源代码安…

git知识

如何将develop分支合并到master分支 #简单版 git checkout master git pull origin master git merge origin/develop # 解决可能的冲突并提交 git push origin master#复杂版 git checkout master # 拉取远程 master 分支的最新代码并合并到本地 git pull origin master # 拉…

新时代·高质量·硬道理丨开放的大门越开越大、开放的水平越来越高

新时代下&#xff0c;中国坚定不移地实施扩大高水平对外开放战略&#xff0c;致力于构建更高层次、更宽领域的开放型经济体系。以下是对新时代高质量硬道理这一主题下&#xff0c;中国开放大门越开越大、开放水平越来越高的几个关键点分析&#xff1a; 全方位开放格局 政府工…

YOLOv8打印模型结构配置信息并查看网络模型详细参数:参数量、计算量(GFLOPS)

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

计算机基础知识-第7章-程序的本质(2)——算法与数据结构概论

一、算法数据结构程序 提出这一公式并以此作为其一本专著的书名的瑞士计算机科学家尼克劳斯沃思&#xff08;Niklaus Wirth&#xff09;由于发明了多种影响深远的程序设计语言&#xff0c;并提出结构化程序设计这一革命性概念而获得了1984年的图灵奖。他是至今惟一获此殊荣的瑞…

Java——测试相关

1、测试的常用方法 单元测试&#xff1a;针对软件中最小可测试单元进行的测试&#xff0c;如类、方法等。功能测试&#xff1a;检查软件的各项功能是否按照需求规格书执行&#xff0c;通常包括正常功能、边界情况和异常情况的测试。性能测试&#xff1a;检查软件的速度、响应时…

【Linux杂货铺】文件系统

目录 &#x1f308;前言&#x1f308; &#x1f4c1; 硬盘 &#x1f4c2; 物理结构 &#x1f4c2; 存储结构 &#x1f4c2; CHS定址法 &#x1f4c2; 操作系统对硬盘的管理和抽象 &#x1f4c1; 文件系统 &#x1f4c2; 分区 &#x1f4c2; 分组 &#x1f4c2; inode号 分配…

1113. 红与黑--Flood Fill 算法

目录 1113. 红与黑--Flood Fill 算法---宽搜&#xff08;BFS&#xff09; 输入格式 输出格式 数据范围 输入样例&#xff1a; 输出样例&#xff1a; 思路&#xff1a; 代码&#xff1a; 运行结果&#xff1a; 1113. 红与黑--Flood Fill 算法---宽搜&#xff08;BFS&am…

无人机技术在光伏电站勘探中的重要应用

随着科技的不断进步和创新&#xff0c;无人机技术在各个领域中都发挥着越来越重要的作用。其中&#xff0c;光伏电站对于无人机的应用也成为了行业内的高效运维方式之一&#xff0c;凭借无人机卓越的性能和可靠性&#xff0c;有效的减少了人力运维的危险性和延迟性&#xff0c;…

Java研学-RBAC权限控制(三)

四 部门管理 1 数据库表 CREATE TABLE department (id bigint(20) NOT NULL AUTO_INCREMENT,name varchar(255) DEFAULT NULL,sn varchar(255) DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT9 DEFAULT CHARSETutf8;2 实体类 Data public class Department {…

股票价格预测 | Python股票价格数据导入和处理

文章目录 文章概述代码设计导入处理文章概述 股票价格预测 | Python股票价格数据导入和处理 代码设计 导入 import os import numpy as np import csv import pandas as pd import matplotlib.pyplot

【NC16596】计算系数

题目 计算系数 组合数&#xff0c;快速幂 思路 这是一道数学题&#xff0c;由之前的数学知识可以知道&#xff0c;题目要我们算一个数&#xff1a; ( C k m a n b m ) m o d 10007 (C_k^ma^nb^m)\mod\ 10007 (Ckm​anbm)mod 10007 题意很明显&#xff0c;没有弯弯绕&#xff…

python入门(一)配置环境和选择IDE

Python&#xff0c;作为一种简洁易懂的编程语言&#xff0c;近年来在全球范围内受到了广泛的关注和追捧。它不仅语法简单明了&#xff0c;易于上手&#xff0c;而且拥有强大的第三方库和广泛的应用领域。从数据分析、机器学习到Web开发&#xff0c;Python都能发挥出色的性能&am…