这次比赛是跟我们a1natas打的,最后也是取得了16名的好成绩,也是第一次在xctf这种大型比赛中拿到一血🩸🥰,也多亏了队友们,成功ak了misc。
但还是不得不说,这个misc的**Why not read it out?**真恶心吧
量子双生影 还是蛮简单的,考了一个ntfs,image combiner,二维码。
先用7z打开rar,会发现另一张图片,解压一下,得到总共两张图片
两张图片stegsolve,image combiner一下,得到这个
用支付宝扫一下就出了(扫不出的话调整下视角)
PaperBack 网上搜索一下就可以发现是利用程序将电脑内容打印到纸上的,我们现在有了这个纸上的内容,需要返回到电脑的内容
在github里搜关键词paperbake可以搜到https://github.com/timwaters/paperback,然后找到官网https://www.ollydbg.de/Paperbak/
下载后将图片拖进去就可以得到一个flag.ws
打开发现是空格tab隐写
手敲一下忽略掉最前面的00000得到
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 01100 0110011 1001000 1000011 1010100 1000110 1111011 1110111 1100101 1101100 1100011 1101111 1101101 1100101 1011111 1110100 1101111 1011111 1101100 0110011 1101000 1100011 1110100 1100110 0110010 0110000 0110010 0110101 1111101 00
转换ascii得到flag:L3HCTF{welcome_to_l3hctf2025}
LearnRag RAG泄露,附件给了嵌入向量
原本想相似性查询去爆的,但是都是些无意义的字符
转变思路为向量逆向,用工具vec2text
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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 import pickleimport torchimport numpy as npdef solve_with_vec2text (): """使用vec2text库逆向RAG embedding""" print ("🚀 使用vec2text库解决RAG embedding逆向问题" ) print ("=" *60 ) print ("📦 步骤1: 确保vec2text库已安装" ) try : import vec2text print ("✅ vec2text库已安装" ) except ImportError: print ("❌ vec2text库未安装,请运行: pip install vec2text" ) print ("🔧 安装命令:" ) print (" pip install vec2text" ) return print ("\n📂 步骤2: 加载RAG数据" ) class RagData : def __init__ (self ): pass import __main__ __main__.RagData = RagData globals ()['RagData' ] = RagData try : with open ('rag_data.pkl' , 'rb' ) as f: rag_data = pickle.load(f) print ("✅ RAG数据加载成功" ) if hasattr (rag_data, 'embedding_model' ): print (f" 模型: {rag_data.embedding_model} " ) if hasattr (rag_data, 'embeddings' ): embeddings = np.array(rag_data.embeddings) print (f" 向量数量: {len (embeddings)} " ) print ( f" 向量维度: {embeddings.shape[1 ] if len (embeddings.shape) > 1 else '?' } " ) else : print ("❌ 未找到embeddings数据" ) return except Exception as e: print (f"❌ 加载RAG数据失败: {e} " ) return print ("\n🤖 步骤3: 加载vec2text的GTR corrector模型" ) try : corrector = vec2text.load_pretrained_corrector("gtr-base" ) print ("✅ GTR corrector模型加载成功" ) except Exception as e: print (f"❌ 加载corrector失败: {e} " ) print ("🔧 尝试其他加载方式..." ) try : inversion_model = vec2text.models.InversionModel.from_pretrained( "jxm/gtr__nq__32" ) corrector_model = vec2text.models.CorrectorEncoderModel.from_pretrained( "jxm/gtr__nq__32__correct" ) corrector = vec2text.load_corrector( inversion_model, corrector_model) print ("✅ 手动加载corrector成功" ) except Exception as e2: print (f"❌ 手动加载也失败: {e2} " ) print ("📋 可能需要先下载模型或使用其他方法" ) return print ("\n🔄 步骤4: 转换embedding格式" ) embeddings_tensor = torch.tensor(embeddings, dtype=torch.float32) if torch.cuda.is_available(): embeddings_tensor = embeddings_tensor.cuda() print ("✅ 使用GPU进行逆向" ) else : print ("⚠️ 使用CPU进行逆向(可能较慢)" ) print (f" Tensor形状: {embeddings_tensor.shape} " ) print ("\n🎯 步骤5: 执行embedding逆向重建" ) reconstructed_texts = [] print ("🔍 开始逐个重建文本..." ) for i, embedding in enumerate (embeddings_tensor): try : print (f" 处理向量 #{i+1 } /{len (embeddings_tensor)} " ) single_embedding = embedding.unsqueeze(0 ) reconstructed = vec2text.invert_embeddings( embeddings=single_embedding, corrector=corrector, num_steps=20 , sequence_beam_width=4 ) reconstructed_text = reconstructed[0 ] if reconstructed else "" reconstructed_texts.append(reconstructed_text) print (f" 重建文本: '{reconstructed_text} '" ) if "L3HCTF" in reconstructed_text or "flag" in reconstructed_text.lower(): print (f" 🎉 发现疑似flag: {reconstructed_text} " ) except Exception as e: print (f" ❌ 向量#{i+1 } 逆向失败: {e} " ) reconstructed_texts.append("" ) print (f"\n📊 步骤6: 分析重建结果" ) print ("🔍 所有重建的文本:" ) for i, text in enumerate (reconstructed_texts): if text.strip(): print (f" 向量#{i+1 } : {text} " ) text_lower = text.lower() if any (keyword in text_lower for keyword in ['l3hctf' , 'flag' , 'ctf' ]): print (f" 🎯 可能的flag内容!" ) potential_flags = [] for text in reconstructed_texts: if text and ("L3HCTF{" in text or "flag{" in text or "ctf{" in text): potential_flags.append(text) if potential_flags: print (f"\n🏆 发现潜在flag:" ) for flag in potential_flags: print (f" 🎉 {flag} " ) else : print (f"\n🤔 未发现明显的flag格式,但重建的文本可能包含线索" ) print (f"📋 重建文本汇总:" ) for i, text in enumerate (reconstructed_texts): if text.strip(): print (f" {i+1 } . {text} " ) return reconstructed_texts def alternative_vec2text_approach (): """备用的vec2text方法""" print ("\n🔄 备用方法: 简化的vec2text逆向" ) print ("=" *50 ) try : import vec2text print ("🔧 尝试简化的逆向方法..." ) class RagData : pass import __main__ __main__.RagData = RagData with open ('rag_data.pkl' , 'rb' ) as f: rag_data = pickle.load(f) embeddings = torch.tensor(rag_data.embeddings, dtype=torch.float32) try : corrector = vec2text.load_pretrained_corrector( "text-embedding-ada-002" ) print ("✅ 使用OpenAI embedding corrector" ) except : print ("❌ 无法加载预训练模型" ) return for i, emb in enumerate (embeddings): try : result = vec2text.invert_embeddings( embeddings=emb.unsqueeze(0 ), corrector=corrector, num_steps=5 ) print (f"向量#{i+1 } : {result[0 ] if result else 'N/A' } " ) except Exception as e: print (f"向量#{i+1 } 失败: {e} " ) except Exception as e: print (f"备用方法也失败: {e} " ) if __name__ == "__main__" : texts = solve_with_vec2text() if not texts or not any (texts): alternative_vec2text_approach()
Please Sign In 通过代码可知系统使用ShuffleNet V2模型提取人脸特征向量,通过比较用户上传图像与预存特征向量的差异(MSE < 5e-6)进行身份验证。附件中已有特征向量文件embedding.json,而模型特征向量可被逆向工程生成匹配图像。有两个方向,一个是用特征反演生成图像;一个是上传特征向量注入的假图等伪造手法。
但是我目前的伪造手法都无法通过系统的要求,可能有大佬可以。以下是我的特征反演代码:
(精简版)注意最后要使用png格式保存,因为jpg是有损压缩。
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 53 54 import torchimport torch.optim as optimfrom torchvision import transformsfrom torchvision.models import shufflenet_v2_x1_0from PIL import Imageimport jsonmodel = shufflenet_v2_x1_0(pretrained=True ) model.fc = torch.nn.Identity() model.eval () with open ("embedding.json" , "r" ) as f: target_embedding = torch.tensor(json.load(f)).unsqueeze(0 ) input_img = torch.nn.Parameter( torch.rand(1 , 3 , 224 , 224 ) * 0.5 + 0.25 , requires_grad=True ) optimizer = optim.Adam([input_img], lr=0.05 ) for i in range (1500 ): optimizer.zero_grad() generated_embedding = model(input_img) loss = torch.mean((target_embedding - generated_embedding) ** 2 ) loss.backward() optimizer.step() input_img.data.clamp_(0 , 1 ) if i % 100 == 0 : print (f"Iteration {i} , Loss: {loss.item():.8 f} " ) if loss.item() < 1e-6 : print (f"Success at iteration {i} !" ) break output_img = transforms.ToPILImage()(input_img.squeeze(0 ).detach()) output_img.save("solution.png" , format ="PNG" ) print ("Successfully generated solution.png" )
最后cmd上传图像:(curl -X POST http://1.95.34.119:50001/signin/ -F “file=@solution.png”)
Why not read it out? 史题当然是要压轴出场了。出的很好下次别出了
010打开文件发现是jpg,末尾有一串倒转过来的base64,改后缀得到一张谜语文字。
看到
猜测是游戏里的文字,根据题目里给出的“小狐狸”进行搜索,找到游戏TUNIC
然后根据一堆解说发现,这纸上面的文字不太一样,猜测是魔改过的。在IGN review里搜索tunic查看评论,发现最开始两段的内容和纸上前两段对应,于是开始逐个翻译。
第一条是flag内容,后面4条是规则
最后翻译如下:
1.the content of flag is come on little brave fox
2.o→0,L→1
3.A→@
4.e大写
5.下划线连接每个词
还有
最后得到flag:L3HCTF{c0mE_0n_1itt1E_br@vE_f0x}
这真恶心吧,,这个文字是音标+重叠+游戏魔改,得自己建立符号对应的表,还得音标转字母。。
彩蛋(其实是乱七八糟的笔记..):
还有
额为了这道题甚至动上了笔。。。