SECCON-CTF-2014-Decrypt-It-easy

SECCON-CTF-2014-Decrypt-It-easy

k e y w o r d s : keywords: keywords: c语言rand()CRT

D e s c r i p t i o n Description Description

$ ./rnd crypt1.png ecrypt1.bin

附带给出rndecrypt1.bin

A n a l y s i s Analysis Analysis

由于在题目写到的类似命令的格式中,rnd似乎是执行文件,linux命令file查看文件类型

$ file rnd
/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

看到是elf文件,实际上就是linux系统版本的exe可执行文件(windows可执行文件是exe

反编译rnd文件,得到主函数

int __cdecl main(int a1, char **a2)
{
  unsigned int v3; // eax
  FILE *v4; // [esp+10h] [ebp-10h]
  FILE *v5; // [esp+14h] [ebp-Ch]
  char ptr; // [esp+1Fh] [ebp-1h] BYREF

  if ( 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;
}

发现原来的文件数据与rand()生成的随机数异或,并且使用了srand()设置种子

并且种子seed = time(0),相当于当时的时间(是格林尼治时间1970年1月1日00:00:00到当前时刻的时长,时长单位是秒)

由于rand()函数的性质,只要seed一致,rand()生成的随机数就是一样的,也就意味着我们可以借此恢复原文件数据(只要随机数与此时已知的ecrypt1.bin文件数据相异或即可)

那么首先要找到当时的时间,而实际上ecrypt1.bin就是通过这样的加密过程生成的,那么文件的生成时间一定是当时作为seed的值,可以通过linux命令stat;得到

最近访问:2022-03-23 20:17:39.817896184 +0800
最近更改:2014-11-22 22:46:30.000000000 +0800
最近改动:2022-03-23 20:17:34.565896062 +0800

“最近更改”时间,需要将此时间转换为时间戳的形式

import time
t = "2014-11-22 22:46:30"
#转换成时间数组
timeArray = time.strptime(t, "%Y-%m-%d %H:%M:%S")
#转换成时间戳
timestamp = time.mktime(timeArray)
#1416667590.0

最后代入解密脚本即可(由于使用的是c语言的rand()函数,如果是使用其他语言的随机数生成函数,可能是使用的不同的生成随机数算法,导致生成的随机数不同)

#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);
}

由于原来的加密程序是elf,也即是linux系统下执行的加密过程;所以我们需要在linux编译这个解密脚本(windows下编译此脚本生成的exe无法使用)

gcc decode.c -o decode

进行解密

decode ecrypt1.bin output.png 1416667590.0

得到png图片

请添加图片描述

发现这里的 N N N相对很小,可以直接找在线网站进行分解,也就已知三对p,q;而 N N N这么小,可以猜测所加密使用的 m m m也很小,那么就意味着flag被拆开成了三部分的较小的 m m m

由于加密过程完全没有按套路出牌(可能有相关论文介绍这种加密的解密方式,但是没有找到),既然 N N N这么小,并且 p , q p,q p,q也已知,可以尝试爆破

直接在模 N N N的情况下爆破是困难的,因为 N N N已经达到了 2 38 2^{38} 238左右,是不可能进行爆破的(也就是遍历在该模数情况下的所有值)

那么可以减小模数,将 N N N换做 p , q p,q 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)
其中 C p C_p Cp代表在模 p p p情况下的 C C C,同理 m p m_p mp

当然这里的所有 B B B都是小于 p , q p,q p,q的,所以不用在原来的基础上模 p , q p,q p,q

那么由于 p , q p,q p,q是较小的,已经小于 2 20 2^{20} 220了,完全可以进行爆破(遍历该模数下所有的值,找出满足以上同余式的值)

找到之后就可以构造出
{ 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)求解同余式方程组的根

由于可能有多个值满足条件,最后的结果转换为字节类型,观察是否是flag的格式即可判断筛选

S o l v i n g   c o d e Solving~code Solving code

from functools import reduce
import gmpy2
from Crypto.Util.number import *

p = [868019, 875543, 597263]
q = [913799, 904727, 890459]
C = [0x8D5051562B, 0x5FFA0AC1A2, 0x6008DDF867]
B = [0xFFEEE,0xFFFEE,0xFEFEF]
def CRT(moudle,a):
    M = reduce((lambda x,y : x * y),moudle)
    result = 0
    for mi in moudle:
        Mi = M // mi
        inv_Mi = gmpy2.invert(Mi,mi)
        result = (result + a[moudle.index(mi)] * Mi * inv_Mi) % M
    return result % M

for i in range(0,len(p)):
    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])
            try:
                if "2" not in bytes.decode(long_to_bytes(x)): # 简单过滤掉了一个不是flag的又是可见字符的字符串
                    print(bytes.decode(long_to_bytes(x)),end="")
            except:
                continue
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
CTFd是一个用于举办和参加CTF(Capture The Flag)安全竞赛的平台。根据引用\[1\]和引用\[2\]的内容,你可以按照以下步骤在Ubuntu上搭建CTFd平台: 1. 首先,确保你已经安装了虚拟机并配置好了Ubuntu系统。具体的安装和配置步骤可以参考相关的教程。 2. 配置阿里云镜像下载源文件。这可以加快软件包的下载速度。你可以按照引用\[1\]中的指导进行配置。 3. 进入CTFd目录。在终端中使用cd命令进入CTFd的目录。 4. 使用gunicorn工具配置CTFd。根据引用\[2\]和引用\[3\]的内容,你可以使用以下命令配置gunicorn工具: ``` gunicorn --bind 0.0.0.0:8000 -w 5 "CTFd:create_app()" ``` 5. 如果你希望在重启电脑后再次运行CTFd平台,确保以root权限运行。在Ubuntu终端中使用sudo命令运行上述命令。 这样,你就可以在Ubuntu上成功搭建CTFd平台了。请注意,这只是一个简单的搭建过程,具体的配置和使用方法可能会有所不同,你可以参考相关的文档和教程进行更详细的了解和操作。 #### 引用[.reference_title] - *1* *2* *3* [基于Ubuntu搭建CTFd平台(全网最全)](https://blog.csdn.net/qq_25953411/article/details/127489944)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

M3ng@L

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值