[amateurs CTF 2024] crypto/pilfer-techies

2024-04-13 04:04

本文主要是介绍[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字节,再加上猜的两个自己整理成一段。。。。

这篇关于[amateurs CTF 2024] crypto/pilfer-techies的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/899095

相关文章

2024年流动式起重机司机证模拟考试题库及流动式起重机司机理论考试试题

题库来源:安全生产模拟考试一点通公众号小程序 2024年流动式起重机司机证模拟考试题库及流动式起重机司机理论考试试题是由安全生产模拟考试一点通提供,流动式起重机司机证模拟考试题库是根据流动式起重机司机最新版教材,流动式起重机司机大纲整理而成(含2024年流动式起重机司机证模拟考试题库及流动式起重机司机理论考试试题参考答案和部分工种参考解析),掌握本资料和学校方法,考试容易。流动式起重机司机考试技

【专题】2024飞行汽车技术全景报告合集PDF分享(附原数据表)

原文链接: https://tecdat.cn/?p=37628 6月16日,小鹏汇天旅航者X2在北京大兴国际机场临空经济区完成首飞,这也是小鹏汇天的产品在京津冀地区进行的首次飞行。小鹏汇天方面还表示,公司准备量产,并计划今年四季度开启预售小鹏汇天分体式飞行汽车,探索分体式飞行汽车城际通勤。阅读原文,获取专题报告合集全文,解锁文末271份飞行汽车相关行业研究报告。 据悉,业内人士对飞行汽车行业

高效录音转文字:2024年四大工具精选!

在快节奏的工作生活中,能够快速将录音转换成文字是一项非常实用的能力。特别是在需要记录会议纪要、讲座内容或者是采访素材的时候,一款优秀的在线录音转文字工具能派上大用场。以下推荐几个好用的录音转文字工具! 365在线转文字 直达链接:https://www.pdf365.cn/ 365在线转文字是一款提供在线录音转文字服务的工具,它以其高效、便捷的特点受到用户的青睐。用户无需下载安装任何软件,只

2024网安周今日开幕,亚信安全亮相30城

2024年国家网络安全宣传周今天在广州拉开帷幕。今年网安周继续以“网络安全为人民,网络安全靠人民”为主题。2024年国家网络安全宣传周涵盖了1场开幕式、1场高峰论坛、5个重要活动、15场分论坛/座谈会/闭门会、6个主题日活动和网络安全“六进”活动。亚信安全出席2024年国家网络安全宣传周开幕式和主论坛,并将通过线下宣讲、创意科普、成果展示等多种形式,让广大民众看得懂、记得住安全知识,同时还

2024/9/8 c++ smart

1.通过自己编写的class来实现unique_ptr指针的功能 #include <iostream> using namespace std; template<class T> class unique_ptr { public:         //无参构造函数         unique_ptr();         //有参构造函数         unique_ptr(

论文翻译:arxiv-2024 Benchmark Data Contamination of Large Language Models: A Survey

Benchmark Data Contamination of Large Language Models: A Survey https://arxiv.org/abs/2406.04244 大规模语言模型的基准数据污染:一项综述 文章目录 大规模语言模型的基准数据污染:一项综述摘要1 引言 摘要 大规模语言模型(LLMs),如GPT-4、Claude-3和Gemini的快

免费也能高质量!2024年免费录屏软件深度对比评测

我公司因为客户覆盖面广的原因经常会开远程会议,有时候说的内容比较广需要引用多份的数据,我记录起来有一定难度,所以一般都用录屏工具来记录会议内容。这次我们来一起探索有什么免费录屏工具可以提高我们的工作效率吧。 1.福晰录屏大师 链接直达:https://www.foxitsoftware.cn/REC/  录屏软件录屏功能就是本职,这款录屏工具在录屏模式上提供了多种选项,可以选择屏幕录制、窗口

论文翻译:ICLR-2024 PROVING TEST SET CONTAMINATION IN BLACK BOX LANGUAGE MODELS

PROVING TEST SET CONTAMINATION IN BLACK BOX LANGUAGE MODELS https://openreview.net/forum?id=KS8mIvetg2 验证测试集污染在黑盒语言模型中 文章目录 验证测试集污染在黑盒语言模型中摘要1 引言 摘要 大型语言模型是在大量互联网数据上训练的,这引发了人们的担忧和猜测,即它们可能已

轻松录制每一刻:探索2024年免费高清录屏应用

你不会还在用一些社交工具来录屏吧?现在的市面上有不少免费录屏的软件了。别看如软件是免费的,它的功能比起社交工具的录屏功能来说全面的多。这次我就分享几款我用过的录屏工具。 1.福晰录屏大师 链接直达:https://www.foxitsoftware.cn/REC/  这个软件的操作方式非常简单,打开软件之后从界面设计就能看出来这个软件操作的便捷性。界面的设计简单明了基本一打眼你就会轻松驾驭啦

梳理2024年,螺丝钉们爱用的3款剪辑软件

这年头,视频到处都是,就跟天上的星星一样数不清。不管你是公司里的新面孔,还是职场上的老狐狸,学会怎么剪视频,就好比找到了赢的秘诀。不管是给上司汇报工作,展示你的产品,还是自己搞点小视频记录生活,只要是剪辑得漂亮,肯定能一下子吸引大家的目光,让人记得你。咱们今天就来侃侃现在超火的三款视频剪辑工具,尤其是PR剪辑,你肯定听说过,这货在剪辑界可是大名鼎鼎,用它剪视频,既专业又麻利。 NO1. 福昕轻松