【Loading 8/9】Crypto_ctfshow_WriteUp | _新手必刷_菜狗杯

1 - 密码签到

题目

密文为: 63746673686f777b77656c636f6d655f325f636169676f755f6375707d
flag格式为 ctfshow{明文}

分析

只有十六进制字符,看着像 HEX 编码,hackbar 解码得到 flag。

Flag

ctfshow{welcome_2_caigou_cup}

2 - Caesar

题目

密文如下: Zhofrph wr FWIvkrz yhjhwdeoh grj fxs!
flag格式为 ctfshow{明文}

分析

如题,这题是凯撒密码,逐偏移量尝试解密,当偏移量为 3 时得到 flag:


也可编码解密,这里使用的是 C:

char s[] = "Zhofrph wr FWIvkrz yhjhwdeoh grj fxs!";
for (int i = 0; i < 26; ++i) {
	for (int j = 0; j < strlen(s); ++j) {
		if (s[j] == ' ' || s[j] == '!') {
			continue;
		}
		++s[j];
		if (s[j] == 'Z' + 1) {
			s[j] = 'A';
		}
		else if (s[j] == 'z' + 1) {
			s[j] = 'a';
		}
	}
	printf("%d %s\n", i + 1, s);
}

Flag

ctfshow{Welcome to CTFshow vegetable dog cup!}

参考

在线凯撒密码加密解密

3 - 0x36d

题目

密文:
😫🙄👰😰👣🙋😱👧👌👷👯👩😴👖👫👚🙃👹👏👏😶👳😫👕🙂🙊👵👶👨👰👮🙉👶👵👸👲👺👮👑😶👴😫🙊👫😴👬👹👤👑😱👗🙃👐😶
提示: 有没有一种可能,标题就是密码?

分析

一开始以为是 Base100,试了一下发现不对。以 Crypto 区出题人的尿性(bushi,多半又是什么新颖的加密方式。搜索相关信息发现一种名为 Txtmoji 的加密方式。


将 emoji 表情放入第一栏,第二栏根据提示应该需要一个十进制数,放入 0x36d 的十进制数,得到 flag:

Flag

ctfshow{emoji_is_funny}

参考

Txtmoji | Encrypt Text to Emojis

4 - 类型-7

题目

密文如下: 094F5A0F0A0D1805103B0B3D143117183B720438350A45550967674D1E064F2969784440455A460F1A1B

分析

观察到密文共 84 个字符,且英文字符范围在 A-F 之间,虽然题目看不懂但是是非常标准的十六进制(鼓掌

然而 base 系列和 hex 编码都不对……


与上一题同理,搜索题目发现是一种名为 cisco type7 的密码,解密得到 flag。

Flag

ctfshow{Wow_u_Kn0w_Ci$c0_Type7_P@ssword!}

参考

cisco type7密码解密、在线解密cisco type7密码、在线cisco type7密码生成、在线生成cisco type7管理员密码--查错网

5 - g4的密码小课堂

题目

一个 py 文件,打开是:

分析

根据内容可以判断为 RSA 算法。需要得出两对 \(p\)\(q\) 后算出 \(\varphi(n)\),结合 \(e\) 得到 \(d\),由 \(c^{d}\equiv m\mod n\) 计算出 \(m\),将 \(m\) 转为 bytes 类型即可得到 flag。

# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2022-11-11 13:46:24
# @Last Modified by:   h1xa
# @Last Modified time: 2022-11-11 13:46:32
# @email: h1xa@ctfer.com
# @link: https://ctfer.com


# D0_U_kn0w_wh4t_1s_double_p_q?
from Crypto.Util.number import *
import gmpy2
from secret import flag

m = bytes_to_long(flag)  # 根据函数名判断是个bytes类型转long的函数
p1 = getPrime(256)  # 获取长度为256bits的素数p1
p2 = gmpy2.next_prime(p1)  # 获取p1后的下一个素数p2
q1 = getPrime(256)  # 获取长度为256bits的素数q1
q2 = gmpy2.next_prime(q1)  # 获取q1后的下一个素数q2
n = p1*p2*q1*q2  # 连乘四个素数得到n
e = 65537
c = pow(m, e, n)  # 计算m的e次方模n得到c
print(f'n = {n}')  # 输出“n=”+n的值
print(f'e = {e}')  # 输出“e=”+e的值
print(f'c = {c}')  # 输出“c=”+c的值

'''
n = 32481415283829255738340971974996440308678927230347135108620374939715138530763511922162670183907243606574444169915409791604348383760619870966025875897723568019791384873824917630615306169399783499416450554084947937964622799112489092007113967359069561646966430880857626323529067736582503070705981530002918845439
e = 65537
c = 13000287388412632836037240605681731720629565122285665653580432791960428695510699983959843546876647788034949392762752577597448919397451077080119543495058705350347758604475392673242110787093172219487592930482799866421316089027633497253411081184454114601840835490688775466505809830410778091437211186254631834255

'''

根据代码可以判断,\(n\) 由两个 \(p\) 及两个 \(q\) 相乘得到,而 \(p2,q2\) 均取 \(p1,q1\) 的后一个素数,因此对 \(n\) 开根号得到数的范围在 \((p1q1,p2q2)\)\((p1q2,p2q1)\) 之间。


平方差遍历法。令 \(p1q1\)\(a1-b1\)\(p2q2\)\(a1+b1\)。则 \(n=p1q1p2q2=(a1-b1)(a1+b1)=a1^{2}-b1^{2}\),可推出 \(b1^{2}=a1^{2}-n\)。对 \(b1^{2}\)\(0\) 开始遍历,即令 \(a1\) 初始值为 \(\sqrt{n}\),遍历过程中 \(a1\) 以步长为 \(1\) 递增,直至 \(a1^{2}-n\) 的结果开方后正好为整数为止,此时得到的整数即为 \(b1\) 的值。


同理,令 \(p1q2\)\(a2-b2\)\(p2q1\)\(a2+b2\),推出 \(b2^{2}=a2^{2}-n\)。对上述方法中的 \(a\) 继续递增,得到开方后的整数即为 \(b2\) 的值。

import gmpy2

pq = []  # 存放分解结果
a = gmpy2.iroot(num, 2)[0]  # n开根号,返回整数部分
while 1:
    B_2 = pow(a, 2) - n  # 根号n整数部分的平方 - n
    if gmpy2.is_square(B_2):  # 判断B_2是否为完全平方数
        b = gmpy2.iroot(B_2, 2)[0]  # B_2开根号,返回整数部分
        pq_1 = a - b
        pq_2 = a + b
        pq.append([pq_1, pq_2])  # 存入p1q1,p2q2和p1q2,p2q1
        if len(pq) == 2:  # 如果存入两组数据
            break  # 退出循环
    a += 1  # 如果B_2不是完全平方数,a加1后重新循环


分解得到的两组结果分别为 \((p1q1,p2q2)\)\((p1q2,p2q1)\)。由于 \(p,q\) 都是素数,因此对两组结果中的四个数两两求公因数即可得到 \(p1,q1,p2,q2\)。其中,在得到一组 \(p,q\) 后可直接将直接分解出的结果对该组数据进行整除得到另一组 \(p,q\),从而提高计算效率。

import gmpy2

pq = factor(n)
p1q1, p2q2 = factor(n)[0]
p1q2, p2q1 = factor(n)[1]
p1 = gmpy2.gcd(p1q1, p1q2)
p2 = gmpy2.gcd(p2q1, p2q2)
q1 = p1q1/p1
q2 = p2q2/p2

最后通过 \(p1,q1,p2,q2\) 计算 \(\varphi(n), d, m\),最终得到 flag。

import gmpy2
from Crypto.Util.number import *

varphi_n = (p1-1)*(q1-1)*(p2-1)*(q2-1)  # 求varphi_n
d = gmpy2.invert(e, varphi_n)  # 求e模varphi_n的逆元d
m = pow(c, d, n)  # 求m
return long_to_bytes(m)


完整代码如下:

import gmpy2
from Crypto.Util.number import *

n = 32481415283829255738340971974996440308678927230347135108620374939715138530763511922162670183907243606574444169915409791604348383760619870966025875897723568019791384873824917630615306169399783499416450554084947937964622799112489092007113967359069561646966430880857626323529067736582503070705981530002918845439
e = 65537
c = 13000287388412632836037240605681731720629565122285665653580432791960428695510699983959843546876647788034949392762752577597448919397451077080119543495058705350347758604475392673242110787093172219487592930482799866421316089027633497253411081184454114601840835490688775466505809830410778091437211186254631834255


def factor(num):
    pq = []  # 存放分解结果
    a = gmpy2.iroot(num, 2)[0]  # n开根号,返回整数部分
    while 1:
        B_2 = pow(a, 2) - n  # 根号n整数部分的平方 - n
        if gmpy2.is_square(B_2):  # 判断B_2是否为完全平方数
            b = gmpy2.iroot(B_2, 2)[0]  # B_2开根号,返回整数部分
            pq_1 = a - b
            pq_2 = a + b
            pq.append([pq_1, pq_2])  # 存入p1q1,p2q2和p1q2,p2q1
            if len(pq) == 2:  # 如果存入两组数据
                break  # 退出循环
        a += 1  # 如果B_2不是完全平方数,a加1后重新循环

    p1q1, p2q2 = pq[0]
    p1q2, p2q1 = pq[1]
    p1 = gmpy2.gcd(p1q1, p1q2)
    p2 = gmpy2.gcd(p2q1, p2q2)
    q1 = p1q1 // p1  # 需要整除,直接除生成的q1为mpfr类型
    q2 = p2q2 // p2

    varphi_n = (p1-1)*(q1-1)*(p2-1)*(q2-1)  # 求varphi_n
    d = gmpy2.invert(e, varphi_n)  # 求e模varphi_n的逆元d
    m = pow(c, d, n)  # 求m
    return long_to_bytes(m)


if __name__ == '__main__':
    print(factor(n))

Flag

ctfshow{you_Know__doub1e_g2_1s_g4_s1m0n}

参考

gmpy2.next_prime-CSDN文库
gmpy2-、moddemod-CSDN
RSA算法原理(一)-阮一峰的网络日志
RSA算法原理(二)-阮一峰的网络日志
print(f‘‘)的用法-Joey9898-CSDN
CTF ——crypto ——RSA原理及各种题型总结-Captain Hammer-CSDN
ctf密码学常用python库-_Mind-博客园
RSA-p和q挨得很近(费马分解)-爱码蔡蔡子-CSDN
请解释上述python代码中gmpy2.iroot(c + i _ n, e)[0]的含义与函数的用法-CSDN文库
Integers—gmpy2 2.2.0a2 documentation
win10下python3 Crypto.Util.number模块下载问题以及附加问题的解决方法-创造之斧-CSDN
python - mpz 变量的算术运算结果是什么?-stack overflow
CTFshow-菜狗杯-Crypto-g4的密码小课堂-ACMer也想玩密码学-@bash-This is Sparta-白泽安全-CSDN

【未完成】6 - ACMer也想要玩密码学

题目

是个 markdown 文档,内容如下:
·························题目由此开始·························
苏卡和米古丽是一对好♂朋♂友,但是因为宝可梦出题组的任务原因,两人不得不分开,分别前往不同的地方执行任务,远隔千里的他们只能通过网络调情,但是两人都不希望有别人窥探他们的隐私,便找到了探姬,想让她开发一款独立在通信频道外的动态令牌。
有了令牌的存在,他们在公共频道中的消息均用密文收发,g4对米古丽惦记已久,但因为令牌的干扰,无法得知他们的聊天内容,无奈的他骇进了探姬的电脑,但令牌已经交付,程序并没有备份,只找到了下面这份研发文档:
2022/11/06 02:02
  由于是通过电磁波无线传输数据,难免会出现一些偏差(比特翻转,即传输的二进制数据中某一位0110),而为了修正这个偏差,我让令牌的动态码在每次传输数据时都会额外传输一些信息用于标记某一行或某一列1的数量为偶数还是奇数,这样就能够判断是否发生错误并进行修正。
设定规则如下:
  假如令牌传输10111110这串二进制数据,则将其划分为一个二维矩阵,其中第一行以及每行的第一位不存储实际数据,而是存储标识位。

  • 第一行第一位为无效位
  • 第一行中(除第一位外)的每一位用于标记当前列(不包括第一行)中1的奇偶数
  • 从第二行开始的每一行的最高位用于标记当前行(不包括第一位)中1的奇偶数

按照这样的规定,当标识位为1时表示该标识对应的行/列上1的数量为偶数,否则为奇数。
  按照上述规则,以1011111000100110110101110111001为例子,可划分为下面这样的二维矩阵:

                 10111110
	         1011111000100110110101110111001
-----------------------------------------------------------------------
	  | -> | 00100000111011001001010001000110
 10111110 | -> | 11011111000100110110101110111001
                  

为了方便起见,我将要传输的数据量设定为31bit的整数倍,并且在发送时就完成数据的二维化,每次发送一个总数,一直到发送EOF终止符,每个令牌组暂时固定为32位。
唔,得益于宝可梦出题组的优秀设备和考虑到两人的位置环境,以及传输环境,我可以保证数据中最多只有一个错误比特,且标识位一定没有发生比特翻转。
不过后面写加密的时候一整串二进制数字好像有点大,求余到依依吧。
虽然只是简单的异或,但拿不到令牌也解不动,就不套什么加密了,直接交付吧嘿嘿嘿。
附,一些实验数据:
Get

1546654568
-70259354
-2000941226
-1489037912
1386538991
1594702065
-75294974
-1434138292
337572188
462963458
-1837589791
-1559749412
186694557
746658962
560535175
-2056966048
1095910008
-1583830502
-1631453206
1808401715
939657852
390762637
-13704376
-1390791142
1680636301
-1326154359
-544089785
-1760715624
-1216236641
-1318240568
-1937065123
-1610385537
1952841665

Fixed

1111011110011111110110101100110
0001000101111000000111101010110
0100111001111110001010110101000
1010010101001001110011111101111
1011111000011010011100011110001
1111011100000110001011100000010
0101010100001001100100101001100
0010100000111101111000101011100
0011011100110000100001100000010
0010010011110001001101011100001
0100011000010000001110011011100
0001011001000001011101110011101
0101100100000010001110010010010
0100001011010010001011010000111
0000101011001010011000001100000
1000001010100100100001001111000
0100001100110001010101000011010
0011110110000011111111111101010
1101011110010100000010100110011
0111000000000100000101001111100
0010111010010101001000010001101
1111111001011101110001101001000
0101101000110100011011000011010
1100100001011000111100110001101
0110000111101000111110110001001
1011111100100011101100101000111
0010111000011011001110010011000
0110111100000011011001110011111
0110001011011010011111011001000
0001100100010101011101101011101
0100000000000110111011101111111
1110100011001011111111111000001

Get

2096378315
-1019477714
2080289518
-1727136162
1502102710
-1832108076
-555146381
1712506508
-27994096
-1299254794
-1869148956
579468139
1591020671
-365684825
-65376630
1884283465
-915867079
455125684
-250554391
-579567935
-936826919
-1151748324
1420102180
-1189273008
-1604344733
1562642112
918473631
1681318183
250232196
541480022
367645158
119210050
747853341

Fixed

1000011001111000000000100101110
1111011111111101011001011101110
0011001000011011111111001011110
1011001100010000100010010110110
0010010110011000011111111010100
1011110111010010010001101110011
1100110000100101100011010001100
1111110010101001101100000010000
0110010100011101111000111110110
0010000100101110000110011100100
0100010100010011111101101101011
1011110110101010000110001111111
1101010001101000001011110100111
1111100000110100110111010001010
1110000010011111110001001001001
1001001011010001111101000111001
0011011001000001010101010110100
1110001000100001101011111101001
1011101011101000111111011000001
1001000001010010010011111011001
0111011010110011011011100011100
1010100101001010000101000100100
0111001000111010010001001010000
0100000010111111010010001100011
1011101001001000000011011000000
0110110101111101100101110011111
1100100001101101110000100100111
0001110111010100011110110000100
0100000010001100101010001010110
0010101111010011101000111100110
0000111000110110000000001000010
0101100100100110101011000011101

g4想知道聊天内容,他已经编写好了解密脚本,而你需要完成解码脚本,两者结合得到他们聊天的内容。
你得到的传输量如下:

1075134823
1301324306
-617273449
-1172376622
1415836736
-295015838
1906939195
1058922943
-1950130772
1087806288
-295089743
-798022633
-600713425
1524733253
205134249
-1918510534
-1745351820
1728344637
1814440199
-698023571
911415104
-606256197
-1954053178
584869358
1471096571
542686205
-1413881575
-50654904
1018161262
-1456425359
-226298669
-970339864
2041042125

在公共频道捕获的密文如下:

tthgYfobWGhnenk;WaxcZe7:b`x9ldgr

g4已经为你准备好了脚本

str = ""

key = []
with open("bitkey.txt") as f:
    for line in f:
        key.append((int(line,2)%11))

for i in range(len(str)):
    print(chr(ord(str[i])^key[i]),end="")

·························题目到此结束·························

分析

7 - Base47

题目

神必字符: E9CVT+HT5#X36RF4@LAU703+F$E-0N\$@68LMXCVDRJJD5@MP#7MUZDTE?WWLG1S#L@+66H@59KTWYK8TW0RV
神必字典:0123456789ABCDEFGHJKLMNPQRSTUVWXYZ?!@#$%^&*-+

分析

base 系列好像没有 base47 这种编码。前段时间刷水友赛,提示 base 的本质是进制,所以猜测这道题也基于进制转换。后来看 WP 证实了自己的猜想。


WP 的解码源码:

`(lambda a, b: __import__('libnum').n2s(sum([b.index(a[i]) * (len(b) ** (len(a) - i - 1)) for i in range(len(a))])))('E9CV^T+HT5#X36RF4@LAU703+F$E-0N$@68LMXCVDRJJD5@MP#7MUZDTE?WWLG1S#L@+^66H@59KTWYK8TW0RV', '0123456789ABCDEFGHJKLMNPQRSTUVWXYZ?!@#$%^&*-+')`

总的来说就是将 45 进制(字典字符数)(我也不知道为什么题目是 Base47)的密文转为 256 进制的 ASCII 码表字符串。


试着根据原理打了一遍代码:

import libnum

ciper = 'E9CV^T+HT5#X36RF4@LAU703+F$E-0N$@68LMXCVDRJJD5@MP#7MUZDTE?WWLG1S#L@+^66H@59KTWYK8TW0RV'
key = '0123456789ABCDEFGHJKLMNPQRSTUVWXYZ?!@#$%^&*-+'

num = 0  # 密文转十进制数

for i in range(len(ciper)):
    p = key.index(ciper[i])  # 找到每个密文字符对应字典的下标
    num += p * (len(key) ** (len(ciper) - i - 1))  # 45进制转十进制后求和

s = libnum.n2s(num)  # 十进制数转字符串 ascii码表 256进制

print(s)

Flag

ctfshow{AbaAba_AbaAbaAba_Ababababa_Never_Gonna_Give_You_Up}

参考

No module named 'libnum'-CSDN文库
libnum库的安装与使用-abtgu-CSDN
Python import() 函数-菜鸟教程
Python 之 lambda 函数完整详解 & 巧妙运用-Nick Peng-CSDN

8 - @bash

题目

密文如下: OLEVNFNFAR

分析

查了亿下,这题考查埃特巴什(Atbash)密码,它的明文对应字母表逆序相同位置的密文,即 A 对应 Z,B 对应 Y,以此类推。

用 C 编写代码对密文进行解密:

char s[] = "OLEVNFNFAR";
for (int i = 0; i < strlen(s); ++i) {
	s[i] = 'Z' - s[i] + 'A';
}
printf("%s", s);

得到 flag 内容。

Flag

ctfshow{LOVEMUMUZI}

参考

古典密码-移位密码|埃特巴什密码Atbash-labster-博客园

9 - This is Sparta

题目

密文如下:WFlniseseds_lh @codycowoot~Bm guf~oev rsTy ec ha!tgufon!oeplwj? t!a{i!Ca gy@Tba oi}

分析

记得打开附件看密文全文!题目里显示的密文缺俩空格!(悲


密文全文共 91 字符,根据题目提示这题考查斯巴达密码,即栅栏密码。
将 91 分解质因数,得到 7 和 13,将密文按 7 个字符一组进行划分,得到 flag 内容:

Flag

ctfshow{yo~~~~~~Tanji_is_@_Boy!!?!@}

posted @ 2023-12-17 22:12  Guanz  阅读(757)  评论(0编辑  收藏  举报