SECCON-CTF-2014-Decrypt-It-easy

article/2025/9/21 5:01:22

题目

给了三个文件ecrypt1.bin,readme.txt,rnd

其中readme.txt:

$ ./rnd crypt1.png ecrypt1.bin

Solve

看到前面有一个$,猜测可能和linux系统有关,这一条信息有点像使用./目录下的rnd对crypt1.png进行某种操作,输出为ecrypt1.bin.

file一下

file /home/mangofeng/桌面/rnd
/home/mangofeng/桌面/rnd: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=6a1443272dd530117d3c63884e195120a845c499, stripped

是一个32位ELF文件,通过32ida反编译找主函数可以得到伪代码:

int __cdecl main(int a1, char **a2)
{unsigned int v3; // eaxFILE *v4; // [esp+10h] [ebp-10h]FILE *v5; // [esp+14h] [ebp-Ch]char ptr; // [esp+1Fh] [ebp-1h] BYREFif ( a1 <= 2 )return 1;v3 = time(0);srand(v3);v4 = fopen(a2[1], "rb");v5 = fopen(a2[2], "wb");while ( fread(&ptr, 1u, 1u, v4) == 1 ){ptr ^= rand() % 256;fwrite(&ptr, 1u, 1u, v5);}fclose(v4);fclose(v5);return 0;
}
  unsigned int v3; // eaxFILE *v4; // [esp+10h] [ebp-10h]FILE *v5; // [esp+14h] [ebp-Ch]char ptr; // [esp+1Fh] [ebp-1h] BYREF/*上面这些都是定义了各种变量*/
  if ( a1 <= 2 )return 1;//判断了一个a1是否小于等于2,若是的话,则返回1,下面语句都不会执行,则我们的a1应大于2
v3 = time(0);
//time()参数一般为0或者Null相当于 取系统时间():
#include <iostream>
#include <ctime>
using namespace std;int main()
{int a;a=time(0);//time(0)返回的是系统的时间(从1970.1.1午夜算起),单位:秒cout<<a<<endl;
}
//1648089941
即时间戳:
时间戳是指格林威治时间19700101000000(北京时间19700101080000)起至现在的总秒数。
在CPP中,时间戳默认就是10位,其精度是秒
  srand(v3);

先提一提rand()

rand()函数常用来生成伪随机数
rand()函数是使用线性同余法做的,它并不是真的随机数,因为其周期特别长,所以在一定范围内可以看成随机的。
rand()函数不需要参数,它将会返回0到RAND_MAX之间的任意的整数。

如:

#include<time.h>
#include<stdlib.h>
#include<iostream>
using namespace std;int main()
{for(int i = 0; i < 10; i++)
{cout << rand()<<endl;
}
}/*
41
18467
6334
26500
19169
15724
11478
29358
26962
24464*/

srand()

srand()为初始化随机数发生器,用于设置rand()产生随机数时的种子。传入的参数seed为unsigned int类型,通常我们会使用time(0)或time(NULL)的返回值作为seed。
srand(time(0));
for(int i = 0; i < 10; i++)
{cout << rand() << endl;
}
/*22954
16198
7083
19454
25323
32035
19825
3031
24100
11374*/

而且:

此方法默认参数为srand(1),当种子确定以后,输出也是确定的.例:

srand(1);
for(int i = 0; i < 10; i++)
{cout << rand() << endl;
}
cout<<time(0);
/*
41
18467
6334
26500
19169
15724
11478
29358
26962
24464
1648090722
*/
/*
41
18467
6334
26500
19169
15724
11478
29358
26962
24464
1648090746
*/

我运行了两次上述代码,可通过时间戳看出,发现rand()结果是一样的.


让我们再回到反编译的代码:

  v3 = time(0);srand(v3);

通过上述分析我们得知,此处设置了一个种子,种子为运行时的时间戳.

  v4 = fopen(a2[1], "rb");v5 = fopen(a2[2], "wb");
//这句话就有点像python里面的,v4就是我要读取数据的文件,打开方式为read binary;v5就是我要写入数据的文件,打开方式为write binary;
  while ( fread(&ptr, 1u, 1u, v4) == 1 ){ptr ^= rand() % 256;fwrite(&ptr, 1u, 1u, v5);}
fread函数用于从文件流中读取数据,其函数原型为:
size_t fread(void* buffer, size_t size, size_t count, FILE*stream);【参数设置】
1) 
buffer为接收数据的地址,对于fread来书是要读出数据的地址,即数据保存的地址
2)  size是要读出内容的单字节数。
3)  count是要进行读出size字节的数据项的个数。
4)  stream为目标文件指针
fwrite()
size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);-- buffer:指向数据块的指针-- size:每个数据的大小,单位为Byte(例如:sizeof(int)就是4)-- count:数据个数-- stream:文件指针

然后大概就是原来文件的数据与生成的rand()异或得到新的文件数据.

然后我们之前分析了srand()函数,只要其传入参数一致,生成的rand()即一致

然后我们去分析一下生成文件ecrypt1bin的相关更改时间

可以通过stat命令查看文件的状态

stat /home/mangofeng/桌面/ecrypt1.bin文件:/home/mangofeng/桌面/ecrypt1.bin大小:45989           块:96         IO 块:4096   普通文件
设备:801h/2049d        Inode:21607       硬链接:1
权限:(0766/-rwxrw-rw-)  Uid:( 1000/mangofeng)   Gid:( 1000/mangofeng)
最近访问:2022-03-24 11:12:41.937672000 +0800
最近更改:2014-11-22 22:46:30.000000000 +0800    #this
最近改动:2022-03-24 11:12:42.026137518 +0800
创建时间:2022-03-24 11:12:41.938137515 +0800

将该更改时间转换为时间戳

a = "2014-11-22 22:46:30"
#将其转换为时间数组
import time
timeArray = time.strptime(a, "%Y-%m-%d %H:%M:%S" )
#转换为时间戳:
timeStamp = int (time.mktime(timeArray))
print(timeStamp)
#1416667590

或者

stat --printf=%Y /home/mangofeng/桌面/ecrypt1.bin                                                                                                       
1416667590

然后用一下解密脚本:

#include <stdio.h>
#include <stdlib.h>int main(int argc, char *argv[]) {FILE *cipher = fopen(argv[1], "rb");FILE *plain = fopen(argv[2], "wb");unsigned int seed = atoi(argv[3]);int c;srand(seed);c = (fgetc(cipher) & 0xff) ^ (rand() & 0xff);while (!feof(cipher)) {fputc(c, plain);c = (fgetc(cipher) & 0xff) ^ (rand() & 0xff);}fclose(plain);fclose(cipher);
}

然后用一下gcc编译

gcc /home/mangofeng/桌面/decode.c -o /home/mangofeng/decode┌──(mangofeng㉿kali)-[~]
└─$ gcc /home/mangofeng/桌面/decode.c -o /home/mangofeng/桌面/decode                                                                                               ┌──(mangofeng㉿kali)-[~]
└─$ /home/mangofeng/桌面/decode /home/mangofeng/桌面/ecrypt1.bin /home/mangofeng/桌面/output.png 1416667590

1

其中N都不是太大的值,可以分解成pq

先一步一步干吧,用yafu分解一下

N1=0xB8AE199365
print(int(N1))
p1 = 913799
q1 = 868019
N2=0xB86E78C811
print(int(N2))
p2 = 904727
q2 = 875543
N3=0x7BD4071E55
print(int(N3))
p3 = 890459
q3 = 597263
assert (q1*p1==N1)
assert (q2*p2==N2)
assert (q3*p3==N3)

因为N都不是太大,就会导致加密的m不能太大,再看有三块,推测flag应该是三块短的字符串拼凑在一起

解法一:

已知flag格式为:SECCON{}

则试一试一块flag大概有多长

N, B, FLAG = 0xB8AE199365, 0xFFEEE, b'SECCON{'
for i in range(1, len(FLAG)+1):M = bytes_to_long(FLAG[0:i])print(FLAG[0:i],'\t',str(hex(M * (M + B) % N)),end="\n")
b'S'     0x52fc213
b'SE'    0x54f0cb0bf
b'SEC'   0x8c0ad9b877
b'SECC'          0x704d68c1fb
b'SECCO'         0x8d5051562b
b'SECCON'        0x2339eed575
b'SECCON{'       0x1bce931b16

发现到五位长度以后,明文就经过了取模操作,即我们猜测flag每一块被分成不超过五位的明文块

验证一下

B=[0xFFEEE,0xFFFEE,0xFEFEF]
C=[0x8D5051562B,0x5FFA0AC1A2,0x6008DDF867]
p=[913799,904727,890459]
q=[868019,875543,597263]
N=[p1*q1,p2*q2,p3*q3]
test=b'SECCO'
M=bytes_to_long(test)
print(bool(hex(M*(M+B[0])%N[0])=='0x8d5051562b'))
#True

则我们就可以去用ascii爆破剩下的两个明文块,每个长度最多为5;

N, B =N[1],B[1]
for i in range(32, 127):for j in range(32, 127):for k in range(32, 127):M = ('N{' + chr(i) + chr(j) +chr(k))M =bytes_to_long(bytes(M, encoding='UTF-8'))if ((M * (M + B) % N) == int(0x5FFA0AC1A2)):print('N{' + chr(i) + chr(j) + chr(k))
#N{Ra_
for i in tqdm.tqdm(range(32, 127)):for j in range(32, 127):for k in range(32, 127):for l in range(32,127):MM = chr(i) + chr(j) +chr(k)+chr(l)+'}'MM =bytes_to_long(bytes(MM, encoding='UTF-8'))if (((MM * (MM + B[2]))% N[2]) == int(0x6008DDF867)):print(chr(i) + chr(j) + chr(k)+chr(l)+'}')break
#b1_N}

SECCON{Ra_b1_N}

解法二:

N大概在2^40左右,要想从modN下找到一个值满足条件是不现实的

但我们已经把N分解成了p,q,根据中国剩余定理可以把条件拆成两个
{ C p ≡ m p ⋅ ( m p + B ) ( m o d p ) C q ≡ m q ⋅ ( m q + B ) ( m o d q ) \begin{cases}C_p\equiv m_p\cdot(m_p+B)\pmod{p}\\C_q\equiv m_q\cdot(m_q+B)\pmod{q}\end{cases} {Cpmp(mp+B)(modp)Cqmq(mq+B)(modq)
p,q都比较小,可以爆破,寻找到满足上述两式的 m p 和 m q m_p和m_q mpmq
{ m ≡ m p ( m o d p ) m ≡ m q ( m o d q ) \begin{cases}m\equiv m_p\pmod{p}\\m\equiv m_q\pmod{q}\end{cases} {mmp(modp)mmq(modq)
然后就可以根据CRT求解出m啦。

def CRT(moudle,a):M = reduce((lambda x,y : x * y),moudle)result = 0for mi in moudle:Mi = M // miinv_Mi = gmpy2.invert(Mi,mi)result = (result + a[moudle.index(mi)] * Mi * inv_Mi) % Mreturn (result % M)
for i in range(0,3):for a1 in [m_p for m_p in range(p[i]) if (C[i] % p[i]) == (m_p * (m_p + B[i])) % p[i]]:# 遍历所有小于p[i]的值for a2 in [m_q for m_q in range(q[i]) if (C[i] % q[i]) == (m_q * (m_q + B[i])) % q[i]]:# 遍历所有小于q[i]的值x = CRT([p[i],q[i]],[a1,a2])print(long_to_bytes(x))
b'eh\xc6Q('
b'W_V"\xa7'
b'aN\xb3q\xd0'
b'SECCO'
b'\x86V\t\xc5\xee'
b'i\xf3\x16f\xc4'
b'N{Ra_'
b'2\x18_\x025'
b'\x19\xa2\x97\xdf\xe9'
b'%\xf5"\x992'
b'U\xde\xd4\x954'
b'b1_N}'
SECCON{Ra_b1_N}

总结

解出png的过程还挺有意思的,就是后面这个加密方式有点头疼,爆破就完了。

参考

M3ng@L哥哥的链接

https://blog.csdn.net/u013745804/article/details/82379266

https://blog.csdn.net/stf1065716904/article/details/73656036

https://blog.csdn.net/yang2011079080010/article/details/52528261

https://blog.csdn.net/km_moon/article/details/84737151

https://blog.csdn.net/weixin_44604541/article/details/113613885


http://chatgpt.dhexx.cn/article/35srFoKC.shtml

相关文章

JAVA使用mysql加密解密函数(AES_ENCRYPT/AES_DECRYPT)

前言 最近大学做JavaWeb课设时发现&#xff0c;很多同学的有关于账号密码都是明文储存在mysql数据库中&#xff0c;其实这样做在实际应用中是非常不安全的&#xff0c;其实mysql最近本身就提供了非常好的两个函数来供我们去加密解密我们要存入的数据&#xff0c;下面我们就来介…

Encrypt 和 Decrypt 加密--解密

[例句]In order to encrypt and decrypt, you need an encryption algorithm and a key. 为了进行加密和解密&#xff0c;您需要加密算法和密钥。 数据加密 数据加密利用密码技术对信息进行加密&#xff0c;实现信息的隐蔽&#xff0c;从而起到保护信息安全的作用。它通过加密…

CSS选择器分类大全

选择器(选择符)就是根据不同需求把不同的标签选出来这就是选择器的作用。 简单来说&#xff0c;就是选择标签用的。 分类&#xff1a;基础选择器、复合选择器 目录 一、基础选择器 二、复合选择器 三、CSS3选择器补充&#xff1a; &#xff08;一&#xff09;层级选择器&am…

CSS选择器常见用法总结

文章目录 一、选择器的基本概念二、基础选择器1.标签选择器2. 类选择器3. id选择器4. 通配符选择器基础选择器总结 三、复合选择器1. 后代选择器2. 子代选择器3. 并集选择器4. 伪类选择器4.1 链接伪类选择器4.2 force 伪类选择器 复合选择器总结 一、选择器的基本概念 功能&am…

css深度选择器deep

1.为什么要有deep 1.当我们给组件设置scoped的时候&#xff0c;此时我们组件的css样式只会对自己的内容生效&#xff0c;不会对子组件里面的内容生效。 <style lang"scss" scoped> .login-page {min-height: 100vh;background: url(/assets/login-bg.svg) no-r…

CSS学习笔记之选择器(一)

目录 CSS选择器 标签选择器 id选择器 class选择器 后代选择器 群组选择器 * CSS 规则由两个主要的部分构成&#xff1a;选择器&#xff0c;以及一条或多条声明: 选择器通常是您需要改变样式的 HTML 元素。 每条声明由一个属性和一个值组成。 属性&#xff08;property&…

属性选择器

1、属性选择器的写法&#xff1a; 标签名[属性] { 声明即样式 } 运行结果&#xff1a; 如上图&#xff0c;只要有 type 属性的 input 标签样式都会发生变化 当然&#xff0c;也可以给属性加上属性值来限制条件 运行结果&#xff1a; input[type"text"] ---- 只有 t…

选择器类型

2.1 id选择器 用法 : 给html标签添加id属性和属性值,然后用css选取,并进行一系列操作 example: html:<div id"我是id选择器"></div> css选取: 用 #id里面的内容,此时是 #我是id选择器 进行操作: #我是id选择器{ width:100px; height:100px; } id…

vue时间选择器

1、年-月-日 时:分 效果展示&#xff1a; 打开选择器&#xff1a; <div class"label">记录日期:</div> <nut-cell :showIcon"true" :isLink"true" click.native"switchPicker"><span slot"title"&…

CSS —— 选择器(带你深入了解如何使用选择器)

一、基础选择器 基础选择器&#xff1a;标签&#xff08;元素&#xff09;选择器&#xff0c;类选择器&#xff0c;id 选择器&#xff0c;通配符选择器。 标签选择器&#xff1a;html 代码中的标签&#xff0c;如 <html>、<body>、<h1>、<p>、<im…

选 择 器

目录 1、三种基本选择器&#xff08;重要&#xff09; &#xff08;1&#xff09;基本选择器 &#xff08;2&#xff09;类选择器 class &#xff08;3&#xff09;id选择器 2、层次选择器 &#xff08;1&#xff09;后代选择器 &#xff08;2&#xff09;子选择器 &am…

AD域基础

文章目录 AD域基础1.什么是AD域2.AD域和工作组的区别2.1 工作组特点及优缺点2.2 AD域特点及优缺点 3.为什么要做AD域管理4.AD域可以做什么 AD域基础 1.什么是AD域 active directory 活动目录&#xff0c;指一组服务器和工作站的集合&#xff0c;域中的目录是始终呈激活可用&a…

创建AD域

创建AD域&#xff08;skills.cn&#xff09;&#xff1a; &#xff08;1&#xff09;安装首台域控制器 &#xff08;2&#xff09;安装第二台域控制器 &#xff08;3&#xff09;将member server加入到skills.cn域 &#xff08;4&#xff09;将client加入到skills.cn域 建域的必…

AD域详细介绍和部署

文章目录 网络基础域一、概述二、部署域模式1. 部署活动目录&#xff08;AD&#xff09;2. 客户机加入域3. 组织单位&#xff08;OU&#xff09;4. 组策略4.1 概述4.2 创建GPO4.3 组策略应用顺序4.4 编辑测试组策略4.5 阻止继承4.6 强制组策略4.7 计算机&用户脚本4.8 计算机…

AD域允许普通用户打开需要管理员权限的软件

问题场景描述 公司的所有电脑都加了域&#xff0c;防止有些不安分的员工擅自下载及安装一些其他软件&#xff0c;导致电脑变卡或者中毒等&#xff0c;所以加了域后&#xff0c;安装任何软件都由IT来安装&#xff0c;但是这里涉及到一个问题&#xff0c;安装的时候需要管理员密…

AD域组策略管理

一、Active Directory组策略管理 ADManager Plus是一个基于web的Active Directory管理和报表工具&#xff0c;只需点击几下即可帮助为多个域管理组策略对象(GPO)。它还提供预制的组策略(GPO)报表&#xff0c;可快速提取GPO相关信息。 使用ADManager Plus轻松管理Windows Acti…

AD域:添加辅域控制器(辅域服务器)

前言&#xff1a; 如下我创建了一个AD域&#xff0c;现在只有一个主域控制器&#xff0c;也就是创建AD域的第一台服务器默认为主域控制器。为防止主域服务器出现故障影响整个域的运作&#xff0c;故现在要为该域添加一个辅域服务器&#xff0c;用来做备用域服务器。 域&#…

什么是AD域?域能给公司带来什么好处?哪款AD域管理工具比较好?

经营公司中很重要的一点就是对公司的管理&#xff0c;所以基本上所有中型公司为了更方便地管理公司电脑&#xff0c;打印机和用户都会请IT咨询公司来做AD域的设计。当然很多小型公司也有这样的设计以方便未来的成长。AD域会给公司带来有很多好处&#xff0c;并且给公司成长留足…

AD域文件权限管理

活动目录文件权限管理 在网络上共享资源时&#xff0c;首先关心的是谁可以访问这些资源以及级别。在 Active Directory &#xff08;AD&#xff09; 环境中管理文件服务器可能很乏味&#xff0c;而且必须一次一个用户完成&#xff0c;这一事实使其成为系统管理员最耗时的活动之…

AD域实现统一用户管理

AD域服务 什么是目录&#xff08;directory&#xff09;呢&#xff1f; 日常生活中使用的电话薄内记录着亲朋好友的姓名、电话与地址等数据&#xff0c;它就是 telephone directory&#xff08;电话目录&#xff09;&#xff1b;计算机中的文件系统&#xff08;file system&am…