picoCTF,Cryptography,40/50
题目站点链接 https://play.picoctf.org/
最初将所有题解放在一个帖子里,帖子太长了不便于阅读,
为了方便后期编辑和阅读。2023年02月10日,将帖子拆分,按照题目类型分为六类:
picoCTF-General Skills,基本技能类
picoCTF-Cryptography,密码类
picoCTF-Forensics,取证类
picoCTF-Web Exploitation,网页开发类
picoCTF-Reverse Engineering,逆向类
picoCTF-Binary Exploitation,二进制类
下面,给出题解,逐步完善中……
注意:很多题目flag是变化的,每一个账号解题得到的flag不一样,所以,下面帖子里的flag仅供参考,但解题思路方法是一样的。
2019 picoCTF
01、The Numbers,50分
下载得到一个图片,是一个密码表
16 = p
9 = i
3 = c
15 = o
3 = c
20 = t
6 = f
正好是字母顺序
所以,flag翻译
20 | 8 | 5 | 14 | 21 | 13 | 2 | 5 | 18 | 19 | 13 | 1 | 19 | 15 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
t | h | e | n | u | m | b | e | r | s | m | a | s | o | n |
picoCTF{thenumbersmason}
02、13,100分
直接rot13就行了
picoCTF{not_too_bad_of_a_problem}
03、caesar,100分
下载得到一个flag,用凯撒移位就行了picoCTF{ynkooejcpdanqxeykjrbdofgkq},位移量是4
picoCTF{crossingtherubiconvfhsjkou}
04、Easy1,100分
下载txt,打开后是一张密码表,找他们的交叉点。
a = "UFJKXQZQUNB"
b = "SOLVECRYPTO"
for i in range(len(a)):
print(chr((ord(a[i]) - ord(b[i])) % 26 + ord('A')), end="")
用Python代码,就可以了。
picoCTF{CRYPTOISFUN}
05、Flags,200分
旗语,看不懂,网上有旗语例子,注意提示大写字母PICOCTF{},有两个字对不到。
类似这样的。有的不一样,大家多找找。
PICOCTF{F1AG5AND5TUFF}
06、la cifra de,200分
有一些专门解密书本密码的网站,比如:https://www.guballa.de,或者quipqiup
picoCTF{b311a50_0r_v1gn3r3_c1ph3ra966878a}
07、Mr-Worldwide,200分
这个有点问题,虽然一看就是坐标,查到了国家和城市,但是翻译英语,一些英文的城市名,国家名不了解,一开始用国家名第一个字,后来找城市名,后来查网站……
(35.028309, 135.753082) = Kyoto, Japan
(46.469391, 30.740883) = Odesa, Ukraine
(39.758949, -84.191605) = Dayton, United States
(41.015137, 28.979530) = İstanbul, Turkey
(24.466667, 54.366669) = Abu Dhabi, United Arab Emirates
(3.140853, 101.693207) = Kuala Lumpur, Malaysia
_
(9.005401, 38.763611) = Addis Ababa, Ethiopia
(-3.989038, -79.203560) = Loja, Ecuador
(52.377956, 4.897070) = Amsterdam, Netherlands
(41.085651, -73.858467) = Sleepy Hollow, United States
(57.790001, -152.407227) = Kodiak, United States
(31.205753, 29.924526) = Alexandria Governorate, Egypt
picoCTF{KODIAK_ALASKA}
08、rsa-pop-quiz,200分
快速的解码
# pip3, pwn
from tokenize import String
from pwn import *
import gmpy2
import rsa
from ecdsa import numbertheory
from Crypto.Util.number import *
r = remote('jupiter.challenges.picoctf.org', 18821)
global p, q, n, e, d, plaintext, ciphertext
global send_text
def get_re_txt(r_text):
global p, q, n, e, d, plaintext, ciphertext
p = 0
q = 0
n = 0
e = 0
d = 0
plaintext = ''
ciphertext = ''
r_text_arr = r_text.split('\n')
for r_text_len in r_text_arr:
r_text_len = r_text_len.strip()
print(r_text_len)
if r_text_len.find('p :') > -1:
n_start = r_text_len.find(':')
t_txt = r_text_len[n_start + 1:]
p = int(t_txt.strip())
elif r_text_len.find('q :') > -1:
n_start = r_text_len.find(':')
t_txt = r_text_len[n_start + 1:]
q = int(t_txt.strip())
elif r_text_len.find('n :') > -1:
n_start = r_text_len.find(':')
t_txt = r_text_len[n_start + 1:]
n = int(t_txt.strip())
elif r_text_len.find('e :') > -1:
n_start = r_text_len.find(':')
t_txt = r_text_len[n_start + 1:]
e = int(t_txt.strip())
elif r_text_len.find('d :') > -1:
n_start = r_text_len.find(':')
t_txt = r_text_len[n_start + 1:]
d = int(t_txt.strip())
elif r_text_len.find('plaintext :') > -1:
n_start = r_text_len.find(':')
t_txt = r_text_len[n_start + 1:]
plaintext = int(t_txt.strip())
elif r_text_len.find('ciphertext :') > -1:
n_start = r_text_len.find(':')
t_txt = r_text_len[n_start + 1:]
ciphertext = int(t_txt.strip())
def send_answer(i):
global p, q, n, e, d, plaintext, ciphertext
global send_text
print('第', i, '题')
send_text = ''
if i == 1:
n = p * q
send_text = str(n)
u_text = r.recvuntil(b'n:').decode()
print(u_text + send_text)
r.sendline(send_text.encode())
elif i == 2:
q = n / p
send_text = str(int(q))
u_text = r.recvuntil(b'q:').decode()
print(u_text + send_text)
r.sendline(send_text.encode())
# 第三题无解
elif i == 4:
n = (p - 1) * (q - 1)
send_text = str(n)
u_text = r.recvuntil(b'(n):').decode()
print(u_text + send_text)
r.sendline(send_text.encode())
elif i == 5:
c = pow(plaintext, e, n)
send_text = str(c)
u_text = r.recvuntil(b'ciphertext:').decode()
print(u_text + send_text)
r.sendline(send_text.encode())
# 第六题无解
elif i == 7:
d = numbertheory.inverse_mod(e, (p - 1) * (q - 1))
send_text = str(d)
u_text = r.recvuntil(b'd:').decode()
print(u_text + send_text)
r.sendline(send_text.encode())
elif i == 8:
q = n // p
d = numbertheory.inverse_mod(e, (p - 1) * (q - 1))
plaintext = pow(ciphertext, d, n)
send_text = str(plaintext)
u_text = r.recvuntil(b'plaintext:').decode()
print(u_text + send_text)
r.sendline(send_text.encode())
print(long_to_bytes(plaintext).decode())
else:
print(r.recv().decode())
i = 1
while i < 9:
re_text = r.recvuntil(b'(Y/N):').decode()
if re_text.find('pico') > -1:
print(re_text)
break
get_re_txt(re_text)
if i == 3 or i == 6:
r.sendline(b'N')
print('N')
else:
r.sendline(b'Y')
print('Y')
send_answer(i)
i = i + 1
r.interactive()
r.close()
这段代码,我写了两天。
picoCTF{wA8_th4t$_ill3aGal…oa2d2239b}
09、Tapping,200分
打开网站是一段莫斯密码,找个网站翻一下就行了,CyberChef解码
.--. .. -.-. --- -.-. - ..-. { -- ----- .-. ... ...-- -.-. ----- -.. ...-- .---- ... ..-. ..- -. ..--- -.... ---.. ...-- ---.. ..--- ....- -.... .---- ----- }
PICOCTF{M0RS3C0D31SFUN2683824610}
注意全是大写字母
10、miniRSA,300分
python代码:
# pip3
import gmpy2
from Crypto.Util.number import *
N = ***
e = 3
ciphertext = ***
print('N:', len(str(N)), N)
print('e:', len(str(e)), e)
print('ciphertext:', len(str(ciphertext)), ciphertext)
while True:
flag_n, result = gmpy2.iroot(ciphertext, e)
if result:
break
else:
ciphertext += n
flag = long_to_bytes(flag_n).decode()
print('flag_n:', len(str(flag_n)), flag_n)
print('flag:', len(str(flag)), flag)
picoCTF{n33d_a_lArg3r_e_606ce004}
11、waves over lambda,300分
一打开一个字符加密的文章,用https://www.quipqiup.com/,解码就行了,第一行是flag,
congrats here is your flag - frequency_is_c_over_lambda_dnvtfrtayu
注意不要引导符包围,就是下面这样的:
frequency_is_c_over_lambda_dnvtfrtayu
2021 picoCTF
01、Mod 26,10分
这是一个MOD加密,用 CyberChef 解密就可以了。
CyberChef 官方网站 https://cyberchef.org/ ,连网运行,也可以下载到本地运行。
picoCTF{next_time_I’ll_try_2_rounds_of_rot13_ZNMldSDw}
02、Mind your Ps and Qs,20分
首先要安装pycrypto包,有的系统有crypto包,没有py两个字不行。安装pycrypto包有点麻烦,因为这个包更新来不及,python更新太快了。要改大写字母什么的,大家多试一试应该可以。
一开始写了一个代码破解,根本不行,数字太大了,破解不了,运行了十几个小时,没结果。
看网上资料,有一个网站,https://www.alpertron.com.ar/ECM.HTM,专门分解质数。
分解得到一下结果:
Sum of divisors(除数和): 1280 678415 822214 057864 524798 453297 819182 586878 551472 605962 163661 472646 940143 071288 (82 digits)
Euler’s totient(欧拉数) : 1280 678415 822214 057864 524798 453297 819181 234364 596418 349127 352680 639289 550089 776560 (82 digits)
Euler’s totient = (p-1)*(q-1)
分解1=1899 107986 527483 535344 517113 948531 328331 (40 digits)
分解2=674357 869540 600933 870145 899564 746495 319033 (42 digits)
n = a² + b² + c² + d²
a = 26481 383158 789014 264976 449287 798616 448871 (41 digits)
b = 19821 680606 975075 440953 654068 924348 464143 (41 digits)
c = 11967 881538 052191 814617 816407 333314 436687 (41 digits)
d = 6579 175573 416272 287400 380364 608330 046808 (40 digits)
用了 7 分钟多时间
这样就有了 c n e p q
写一段Python代码:
# 这是CTF试验 RAS
from Crypto.Util.number import long_to_bytes, inverse, bytes_to_long
c = 62324783949134119159408816513334912534343517300880137691662780895409992760262021
n = 1280678415822214057864524798453297819181910621573945477544758171055968245116423923
e = 65537
p = 1899107986527483535344517113948531328331
q = 674357869540600933870145899564746495319033
# 计算p-1与q-1的最小公倍数
phi = (p - 1) * (q - 1)
# inverse 逆运算
d = inverse(e, phi)
# 计算 c^d%n
m = pow(c, d, n)
# long_to_bytes 正整数转化为byte类型字符串
fstr = long_to_bytes(m)
# bytes_to_long 测试一下转换
rm_num = bytes_to_long(fstr)
print('phi: ', phi, '(', len(str(phi)), '位)')
print('d_num: ', d, '(', len(str(d)), '位)')
print('m_num: ', m, '(', len(str(m)), '位)')
print('fstr: ', fstr)
print('rm_num: ', rm_num)
这样就可以了
picoCTF{sma11_N_n0_g0od_05012767}
03、Easy Peasy,40分
研究了很久,用nc mercury.picoctf.net 64260
连接服务器。
一步一步打开服务器,获得一个flag加密串:551257106e1a52095f654f510a6b4954026c1e0304394100043a1c5654505b6b
分析源程序,发现一个关键点,50000个字节,还有一段
kf = open(KEY_FILE, "rb").read()
if stop >= KEY_LEN:
stop = stop % KEY_LEN
key = kf[start:] + kf[:stop]
else:
key = kf[start:stop]
key_location = stop
分析一下,取key,是和要加密的字串长度一致,一截一截的取,当取到50000个字节时,从头再取。估计key文件50000个字节。
result = list(map(lambda p, k: "{:02x}".format(ord(p) ^ k), flag, key))
print("This is the encrypted flag!\n{}\n".format("".join(result)))
这段的意思是flag和key做了 ^ 操作,也就是“异或”操作。
异或操作的特点是:
A^B=C,可以推导出 C^B=A
所以,
flag_original ^ key = flag_encrypt
flag_encrypt ^ key = flag_original
在连接服务器的时候,第一个返回就是flag_encrypt(加密flag),所以,
只要获得key就行了。
上面分析,key取了50000个就从头取。
我们的思路就是,先送50000个没用的字节,然后再送flag_encrypt,这时候,系统就会从key文件的开头取key,取的长度和flag_original(原始flag)一样长,做异或操作,就得到flag_original。
于是在同事的帮助下,一起写了一个pwn程序。
from pwn import *
r = remote("mercury.picoctf.net", 64260)
r.recvline() # 舍去一行
r.recvline() # 再舍去一行
# 获取加密后的flag
flag_encrypt = bytes.fromhex(r.recvline().decode())
# 取得flag长度
flag_len = len(flag_encrypt)
def encode_txt(m: str):
"""
加密函数
:param m:发送要加密字串
:return: 返回加密后的字串
"""
r.sendlineafter(b"What data would you like to encrypt? ", m.encode())
r.recvline()
r_txt = bytes.fromhex(r.recvline().decode())
return r_txt
# 发送50000个字节,注意减去了flag长度,连接系统的时候已经操作一次了,加密了flag。
# 返回值不需要无意义,舍去。
encode_txt("a" * (50000 - flag_len))
# 再把系统一开始获得的flag_encrypt,也就是加密后的flag,送给系统,就能返回原始的flag
flag_original = encode_txt(flag_encrypt.decode())
print("picoCTF{" + flag_original.decode() + "}")
picoCTF{3a16944dad432717ccc3945d3d96421a}
04、New Caesar,60分
下载得到一个py文件,是加密程序,有两个加密阶段,先做一个b16转换,然后做shift转换,shift转换用字符和key进行了运算。其中这两句
assert all([k in ALPHABET for k in key])
assert len(key) == 1
确立了key必须是ALPHABET 中的字母,并且必须是1位。
从这两句
LOWERCASE_OFFSET = ord("a")
ALPHABET = string.ascii_lowercase[:16]
可以看出,ALPHABET 只取了前16个小写字母
LOWERCASE_OFFSET是位移,取了a的ascii值97。
然后,我们逆向写一个解密程序,key依次取小写字母前16个。
import string
LOWERCASE_OFFSET = ord("a")
ALPHABET = string.ascii_lowercase[:16]
cipher_text = "mlnklfnknljflfmhjimkmhjhmljhjomhmmjkjpmmjmjkjpjojgjmjpjojojnjojmmkmlmijimhjmmj"
def b16_decode(solve):
dec_str = ""
for idx in range(0, len(solve), 2):
c1 = solve[idx]
c2 = solve[idx + 1]
c1 = ALPHABET.index(c1)
c2 = ALPHABET.index(c2)
binary1 = "{0:04b}".format(c1)
binary2 = "{0:04b}".format(c2)
binary = int(binary1 + binary2, 2)
dec_str += chr(binary)
return dec_str
def unshift(c, k):
t1 = ord(c) + LOWERCASE_OFFSET
t2 = ord(k) + LOWERCASE_OFFSET
return ALPHABET[(t1 - t2) % len(ALPHABET)]
def is_ascii(s):
for ch in s:
if ord(ch) > 126 or ord(ch) < 32:
return False
return True
for letter in ALPHABET:
dec = ""
for i, c in enumerate(cipher_text):
dec += unshift(c, letter)
dec = b16_decode(dec)
if is_ascii(dec):
print("key:", letter, "解码,Flag: picoCTF{" + dec + "}")
用 is_ascii(s)函数判断解密字串是否是可见字符。
如果不判断可见字符,得到结果如下:
key: a 解码,Flag: picoCTF{ËÚµÚÛ•µÇ˜ÊÇ—Ë—žÇÌšŸÌœšŸž–œŸžžžœÊËȘǜÉ}
key: b 解码,Flag: picoCTF{ºÉ¤ÉÊ„¤¶‡¹¶†º†¶»‰Ž»‹‰Ž
‹ŽŒ‹¹º·‡¶‹¸}
key: c 解码,Flag: picoCTF{©¸“¸¹s“¥v¨¥u©u|¥ªx}ªzx}|tz}||{|z¨©¦v¥z§}
key: d 解码,Flag: picoCTF{˜§‚§¨b‚”e—”d˜dk”™gl™iglkcilkkjki—˜•e”i–}
key: e 解码,Flag: picoCTF{‡–q–—QqƒT†ƒS‡SZƒˆV[ˆXV[ZRX[ZZYZX†‡„TƒX
}
key: f 解码,Flag: picoCTF{v
`
†@`rCurBvBIrwEJwGEJIAGJIIHIGuvsCrGt}
key: g 解码,Flag: picoCTF{et_tu?_a2da1e18af49f649806988786deb2a6c}
key: h 解码,Flag: picoCTF{TcNcd.NP!SP T 'PU#(U%#('/%(''&'%STQ!P%R}
key: i 解码,Flag: picoCTF{CR=RS=OBOCODDBC@OA}
12?>0}
key: k 解码,Flag: picoCTF{!001û-þ -ý!ýô-"ðõ"òðõôüòõôôóôò !.þ-ò/}
key: l 解码,Flag: picoCTF{/
/ ê
íììãïäáïäãëáäããâãáíá}
}
ÈèúËýúÊþÊÁúÿÍÂÿÏÍÂÁÉÏÂÁÁÀÁÏýþûËúÏü}
key: o 解码,Flag: picoCTF{íü×üý·×éºìé¹í¹°é±°¸¾±°°¿°¾ìíêºé¾ë}
key: p 解码,Flag: picoCTF{ÜëÆëì¦ÆØ©ÛØ¨Ü¨¯ØÝ« Ý« ¯§ ¯¯®¯ÛÜÙ©ØÚ}
其中有大量的不可见字符,且有控制符破坏排版各式。
判断可见字符后,仅有两条
key: g 解码,Flag: picoCTF{et_tu?_a2da1e18af49f649806988786deb2a6c}
key: h 解码,Flag: picoCTF{TcNcd.NP!SP T 'PU#(U%#('/%(''&'%STQ!P%R}
提交测试,获得最终的flag
picoCTF{et_tu?_a2da1e18af49f649806988786deb2a6c}
05、Mini RSA,70分
from decimal import *
from tqdm import tqdm # 进度条
N = Decimal(***省略)
e = Decimal(3)
c = Decimal(**省略)
def int_to_ascii(number):
m_hex = hex(int(number))[2:-1] # 数字 转 16进制
m_asc = "".join(
chr(int(m_hex[i: i + 2], 16)) for i in range(0, len(m_hex), 2)
) # 16进制 转 字符
# print(m_asc)
return m_asc
getcontext().prec = 280 # 高精度
padding = 0
# 下面 K 值从 0 到 5000
for k in tqdm(range(3000, 5000)):
m = pow(k * N + c, 1 / e)
m_ascii = int_to_ascii(m)
if "pico" in m_ascii:
padding = k
print( m_ascii.strip())
break
print("Padding:", padding)
getcontext().prec = 600
m = pow(padding * N + c, 1 / e)
m_ascii = int_to_ascii(m)
print("Flag:", m_ascii.strip())
picoCTF{e_sh0u1d_b3_lArg3r_6e2e6bda}
06、Dachshund Attacks,80分
真不知道怎么做,参考了网上资料,找到了一个包owiener
,这个包安装有点麻烦。
owiener包至2023年1月,是1.08版本,支持Python 3.5 - 3.11
我的Python是3.9,但就是报错[未解析的引用]
,不知道怎么情况。
查了包目录,有owiener-1.0.8.dist-info
文件夹,没有owiener
文件夹,全盘搜索也找不到owiener文件或文件夹
然后从github上下载了owiener.py
拷贝到项目的venv\Lib\site-packages
目录下,就可以了。
owiener项目:https://github.com/orisano/owiener
from pwn import *
import owiener
host = 'mercury.picoctf.net'
port = 36463
io = connect(host, port)
io.recvline()
e = int(str(io.recvline(), "ascii").split(": ")[1].strip())
n = int(str(io.recvline(), "ascii").split(": ")[1].strip())
c = int(str(io.recvline(), "ascii").split(": ")[1].strip())
print("e =", e)
print("n =", n)
print("c =", c)
d = owiener.attack(e, n)
decrypted_text = pow(c, d, n)
flag = binascii.unhexlify(hex(decrypted_text)[2:]).decode()
print("Flag:", '\033[1;31m' + flag + '\033[0m')
注意:'\033[1;31m' + flag + '\033[0m')
是打印的时候文字是红色。效果如下:
picoCTF{proving_wiener_2635457}
07、No Padding, No Problem,90分
写了一个代码
from pwn import *
# context.log_level = "debug"
URL, PORT = 'mercury.picoctf.net', 42248
sel = 0
r = remote(URL, PORT)
print(r.recvline())
print(r.recvline())
print(r.recvline())
print(r.recvline())
n_s = r.recvline()
n_s = n_s[2:].strip()
n = int(n_s.decode())
e_s = r.recvline()
e_s = e_s[2:].strip()
e = int(e_s.decode())
ciphertext_s = r.recvline()
ciphertext_s = ciphertext_s[11:].strip()
ciphertext = int(ciphertext_s.decode())
print('n:', n)
print('e:', e)
print('ciphertext:', ciphertext)
c = n + ciphertext
print('c:', c)
r.recvuntil(b'Give me ciphertext to decrypt:')
r.sendline(str(c).encode())
r_s = r.recvline()
r_s = r_s[13:].strip()
renumber = int(r_s.decode())
print('renumber', renumber)
def int_to_ascii(m):
m_hex = hex(int(m))[2:-1] # Number to hex
m_ascii = "".join(
chr(int(m_hex[i: i + 2], 16)) for i in range(0, len(m_hex), 2)
) # Hex to Ascii
return m_ascii
res = int_to_ascii(renumber)
print(res)
picoCTF{m4yb3_Th0se_m3s54g3s_4r3_difurrent_7416022}
08、Pixelated,100分
picoCTF{1b867c3e}
09、Play Nice,110分
PIayfair加密,普莱费尔密码(英文:Playfair cipher 或 Playfair square)是一种使用一个关键词方格来加密字符对的加密法,
1854年由一位名叫查尔斯·惠斯通(Charles Wheatstone)的英国人发明。
Here is the alphabet: 0fkdwu6rp8zvsnlj3iytxmeh72ca9bg5o41q
Here is the encrypted message: herfayo7oqxrz7jwxx15ie20p40u1i
#!/usr/bin/python3 -u
SQUARE_SIZE = 6
def generate_square(alphabet):
global row
assert len(alphabet) == pow(SQUARE_SIZE, 2)
matrix = []
for i, letter in enumerate(alphabet):
if i % SQUARE_SIZE == 0:
row = []
row.append(letter)
if i % SQUARE_SIZE == (SQUARE_SIZE - 1):
matrix.append(row)
return matrix
def get_index(letter, matrix):
for row in range(SQUARE_SIZE):
for col in range(SQUARE_SIZE):
if matrix[row][col] == letter:
return row, col
print("letter not found in matrix.")
exit()
alphabet = "0fkdwu6rp8zvsnlj3iytxmeh72ca9bg5o41q"
matrix = generate_square(alphabet)
msg = ""
enc_msg = "herfayo7oqxrz7jwxx15ie20p40u1i"
for i in range(0, len(enc_msg), 2):
a = get_index(enc_msg[i], matrix)
b = get_index(enc_msg[i + 1], matrix)
if a[0] == b[0]:
msg += matrix[a[0]][(a[1] - 1) % SQUARE_SIZE] + matrix[b[0]][(b[1] - 1) % SQUARE_SIZE]
elif a[1] == b[1]:
msg += matrix[(a[0] - 1) % SQUARE_SIZE][a[1]] + matrix[(b[0] - 1) % SQUARE_SIZE][b[1]]
else:
msg += matrix[a[0]][b[1]] + matrix[b[0]][a[1]]
print(msg)
用这个程序解码得到下面的信息
What is the plaintext message? emf57mgc51tp693dtt4g3h7f8ouwq3
Congratulations! Here's the flag: 007d0a696aaad7fb5ec21c7698e4f754
试了好几次,才发现,这个flag,不要picoCTF{},直接写内容
这个flag,不要picoCTF{},直接写007d0a696aaad7fb5ec21c7698e4f754。巨坑。
007d0a696aaad7fb5ec21c7698e4f754
10、Double DES,120分
研究了很久,连接网站,获得一个flag加密串
再给一个8位数字串,又给一个加密串,看程序是两个6位密码加密。
写程序跑了一下,一天了才跑了1%多,根本测不出来。
后来查了资料,问了专业的老师。从加密开头端和结果端,两头开始测,然后用查字典的方法,intersection
,查交叉点,获取到中间值,解出两个key。
交叉点如果不止一个,就有更多的解。
叫什么 碰撞攻击(解密)
方法。
修改了程序
from Crypto.Cipher import DES
from pwn import *
from tqdm import tqdm # 进度条
r = remote("mercury.picoctf.net", 33425)
t = r.recvline().decode()
print('t:', t)
encrypted_flag_txt = r.recvline().decode()
encrypted_flag_txt = encrypted_flag_txt.strip()
r.recvuntil(b"encrypt? ")
known_text = '20050819'
r.sendline(known_text.encode())
ciphertext = r.recvline().decode()
ciphertext = ciphertext.strip()
print('encrypted_flag_txt:', encrypted_flag_txt)
print('known_text:', known_text)
print('ciphertext:', ciphertext)
r.close()
def pad(msg):
block_len = 8
over = len(msg) % block_len
pad_n = block_len - over
return (msg + " " * pad_n).encode()
encrypted_flag = binascii.unhexlify(encrypted_flag_txt)
custom_known_text = pad(binascii.unhexlify(known_text).decode())
custom_ciphertext = binascii.unhexlify(ciphertext)
encrypt_table = {}
for key in tqdm(range(999999), desc="key1"):
key = (f"{key:06}" + ' ').encode()
cipher = DES.new(key, DES.MODE_ECB)
encrypted_custom = cipher.encrypt(custom_known_text)
encrypt_table[encrypted_custom] = key
decrypt_table = {}
for key in tqdm(range(999999), desc="key2"):
key = (f"{key:06}" + ' ').encode()
cipher = DES.new(key, DES.MODE_ECB)
decrypted_custom = cipher.decrypt(custom_ciphertext)
decrypt_table[decrypted_custom] = key
print("查字典比较...")
encrypt_table_set = set(encrypt_table.keys())
decrypt_table_set = set(decrypt_table.keys())
for encrypt_decrypt_value in encrypt_table_set.intersection(decrypt_table_set):
encrypt_key = encrypt_table[encrypt_decrypt_value]
decrypt_key = decrypt_table[encrypt_decrypt_value]
print("KEY1:" + encrypt_key.decode())
print("KEY2:" + decrypt_key.decode())
cipher1 = DES.new(encrypt_key, DES.MODE_ECB)
cipher2 = DES.new(decrypt_key, DES.MODE_ECB)
flag_intermediate = cipher2.decrypt(encrypted_flag)
print(flag_intermediate)
flag = cipher1.decrypt(flag_intermediate).decode()
print("Flag: picoCTF{" + flag.strip() + '}')
运行结果:
11、Compress and Attack,130分
一开始写解密函数,突然发现key是32位,发现不太可能,后来改用pwn测试密码,提示说flag是 lowercase letters, underscores
分析程序,是将flag加上用户输入字符串,做压缩,然后做Salsa20加密,还随机生成一个32位的加密key,所以想用逆函数解密几乎不可能。
于是,就用字符串,正向测试,测到一个特征……
寻找这个特征,系统每次加密都给出加密后的字串长度,经过分析,字符串重复,长度就是48,字符串不重复就是49或者更多,这个与加密方式和压缩方式有关。
from pwn import *
import string
r = remote("mercury.picoctf.net", 29675)
flag_char = '_}' + string.ascii_letters
flag = "picoCTF{"
num_test = 0
while True:
# 定义一个字典payload_dic
payload = ''
for char in flag_char:
try:
r.recvuntil(b'encrypted: ')
payload = flag + char
num_test = num_test + 1
r.sendline(payload.encode())
r.recvline()
r.recvline()
r_txt = r.recvline().decode()
val = int(r_txt[:-1])
print('正在测试:', num_test, payload, val)
if val == 48:
break
except:
r = remote("mercury.picoctf.net", 29675)
flag = payload
if flag[-1] == '}':
break
r.close()
print("找到了:", flag)
特别加了测试次数记录,运行结果如下
正在测试: 341 picoCTF{sheriff_you_solved_the_crimd 49
正在测试: 342 picoCTF{sheriff_you_solved_the_crime 48
正在测试: 343 picoCTF{sheriff_you_solved_the_crime_ 49
正在测试: 344 picoCTF{sheriff_you_solved_the_crime} 48
[*] Closed connection to mercury.picoctf.net port 29675
找到了: picoCTF{sheriff_you_solved_the_crime}
picoCTF{sheriff_you_solved_the_crime}
12、Scrambled: RSA,140分
找了一个程序,怎么都不行,解密到10来个字母就出错。后来想了一个办法,把已经解出来的字母填到程序里,大大减少了循环的次数
from pwn import *
import string
chars = string.digits + "_-{}" + string.ascii_letters
decrypted_flag = ''
# 这个是一次一次增加的
know_flag = 'picoCTF{bad_1d3a5_7571572}'
known = []
r = remote("mercury.picoctf.net", 47987)
flag = r.recvline(keepends=False).decode()[6:]
flag = flag.strip()
oldLen = len(flag)
r.recvline()
r.recvline()
l_index = 0
while '}' not in decrypted_flag:
for i in chars:
if l_index < len(know_flag):
i = know_flag[l_index]
payload = decrypted_flag + i
r.recvuntil(b'give me: ')
r.sendline(payload.encode())
sleep(1)
enc = r.recvline(keepends=False).decode()
enc = enc.replace('Here you go:', '').strip()
midp = int(len(enc) / 2)
print(payload, '->', len(enc), enc[:10], enc[midp:midp + 10], enc[-10:])
for chunks in known:
enc = enc.replace(chunks, '')
if enc in flag:
flag = flag.replace(enc, '')
known.append(enc)
decrypted_flag += i
print(decrypted_flag, '|flag长度:' + str(len(flag)) + '/' + str(oldLen))
l_index = l_index + 1
break
enc = ''
picoCTF{bad_1d3a5_7571572}
13、It’s Not My Fault 1,300分
题目给了加密源文件,读取了flag.txt,
使用了MD5加密,RSA和CRT
get_flag 函数,要求15分钟内得到p和q的值,传入p+q
需要有一个HASH数,前5位字母,后五位是16进制
网上找到暴力破解程序
注意,这是一个多线程程序,在kali(Linux)系统下运行正常,在windows系统中运行出错。
import time
import gmpy2
from gmpy2 import mpz
from functools import partial
from multiprocessing import Pool
from pwn import *
host = "mercury.picoctf.net"
port = 27379
def solve_md5(string_start, hash_end):
idx = 0
while True:
test_string = string_start + str(idx)
test_string_hash = str(hashlib.md5(test_string.encode("utf-8")).hexdigest())
if test_string_hash[-len(hash_end):] == hash_end:
return test_string
idx += 1
io = connect(host, port)
md5_pow_info = io.recvline().decode().strip()
string_start = re.findall('"(.*?)"', md5_pow_info)[0]
hash_end = md5_pow_info[-6:]
log.info("MD5 PoW string must start with %s and hash must end with %s"
% (string_start, hash_end))
progress = log.progress("Bruteforcing MD5 PoW")
md5_solution = solve_md5(string_start, hash_end)
progress.success("MD5 PoW String Found: %s" % md5_solution)
io.sendline(md5_solution.encode())
progress = log.progress("Getting public modulus and clue")
n = int(io.recvline().decode().strip().replace("Public Modulus : ", ""))
e = int(io.recvline().decode().strip().replace("Clue : ", ""))
progress.success("Success")
BITS = 20
MAX_RANGE = 1 << BITS
def bruteforce_test(d_p, e, n, m):
# Apply the algorithm from https://bitsdeep.com/posts/attacking-rsa-for-fun-and-ctf-points-part-4/
# to check if a value for `d_p` is correct
p = gmpy2.gcd(m - pow(m, e * d_p, n), n)
if p > 1:
# If the value of `d_p` is correct, then find `q` from the
# bruteforced value of `p`.
n = mpz(n)
q = n // p
return True, (mpz(p), mpz(q))
return False, d_p
def bruteforce_solve(e, n, m, progress, num_process = 6):
_bruteforce_test = partial(bruteforce_test, e=e, n=n, m=m)
pool = Pool(num_process)
# Loop through all possible values for `d_p` in chunks of 1000 using a pool
# of worker processes.
for return_values in pool.imap(_bruteforce_test, range(MAX_RANGE), chunksize=1000):
# If one of the values returns `True` then return the `(p, q)` tuple.
if return_values[0]:
return return_values[1]
# If the solution was not found, then log the progress
elif return_values[1] % 1000 == 0:
percent_complete = (return_values[1] / MAX_RANGE) * 100
progress.status(str(round(percent_complete, 2)) + "%")
def second_to_text(ts: int) -> str:
"""
时间的秒数转换成时分秒
:param ts: 秒数
:return: 固定格式的 xxx小时xx分钟xx秒
"""
m, s = divmod(ts, 60)
h, m = divmod(m, 60)
r_txt = str(s) + '秒'
if m > 0:
r_txt = str(m) + '分钟' + r_txt
if h > 0:
r_txt = str(h) + '小时' + r_txt
# print(r_txt)
return r_txt
progress = log.progress("Bruteforcing RSA-CRT d_p")
m = random.randint(1000, 100_000) # Random number as long as `m < n`
bruteforce_start = time.time()
p, q = bruteforce_solve(e, n, m, progress)
bruteforce_time = int(time.time()-bruteforce_start)
print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), second_to_text(bruteforce_time))
progress.success("p=%i, q=%i" % (p, q))
# Add then convert the `mpz` values to string so they can be sent properly
solution = str(p + q)
io.sendline(solution.encode())
io.interactive()
计算时间的部分是后来加的,用来计算时候超过了15分钟。
每次运行得到的p、q不一样,时间也不一样,
命令方式运行,界面更加美观一些。
picoCTF{1_c4n’7_b3l13v3_17’5_n07_f4ul7_4774ck!!!}
14、New Vignere,300分
维吉尼亚密码
源程序
LOWERCASE_OFFSET = ord("a")
ALPHABET = string.ascii_lowercase[:16]
大大减少了字母的范围
flag = "redacted"
assert all([c in "abcdef0123456789" for c in flag])
key = "redacted"
assert all([k in ALPHABET for k in key]) and len(key) < 15
给出了KEY。
题目密文是:
bkglibgkhghkijphhhejggikgjkbhefgpienefjdioghhchffhmmhhbjgclpjfkp
enumerate函数是罗列序号和数值。shift是序号和数值移位。反正要研究一下。
最后给出代码:
import string
enc = "bkglibgkhghkijphhhejggikgjkbhefgpienefjdioghhchffhmmhhbjgclpjfkp"
ALPHABET = string.ascii_lowercase[:16]
key = "oedcfjdbe"
b16 = ""
loop = 0
for i in enc:
k = ALPHABET.index(key[loop % len(key)])
loop = loop + 1
index = ALPHABET.index(i)
if k <= index:
b16 += chr(index - k + 97)
elif k <= index + 16:
b16 += chr(index + 16 - k + 97)
flag = ""
print(b16)
for i in range(0, len(b16), 2):
if b16[i + 1] in ALPHABET and b16[i] in ALPHABET:
index1 = ALPHABET.index(b16[i])
index2 = ALPHABET.index(b16[i + 1])
flag += chr((index1 << 4) + index2)
print(flag)
picoCTF{698987ddce418c11e9aa564229c50fda}
2021 redpwn
01、spelling-quiz,100分
有一个乱序加密的解码网站
https://www.quipqiup.com/
把brcfxba_vfr_mid_hosbrm_iprc_exa_hoav_vwcrm
输入,按照字典解码,然后,看上去是一句话的就是了……
picoCTF{perhaps_the_dog_jumped_over_was_just_tired}
02、triple-secure,150分
从加密程序这一段
p = getPrime(1024)
q = getPrime(1024)
r = getPrime(1024)
n1 = p * q
n2 = p * r
n3 = q * r
可以看出
q、p、r为1024位质数,
p为n1、n2的公约数
q为n1、n3的公约数
r为n2、n3的公约数
加密程序,引入flag、e和n1、n2 、n3 分别迭代做一次pow计算得到c
写逆函数解得flag
# 读出文件里的数据
import math
import params
from gmpy2 import gcd, invert
with open('public-key.txt', 'r') as f:
n1 = int((f.readline()[3:]).strip())
n2 = int((f.readline()[3:]).strip())
n3 = int((f.readline()[3:]).strip())
e = int((f.readline()[3:]).strip())
c = int((f.readline()[3:]).strip())
def get_number_len(number: int):
t_n = str(number)
r_t = '[' + str(len(t_n)) + ']'
return r_t
print('n1:', get_number_len(n1), n1)
print('n2:', get_number_len(n2), n2)
print('n3:', get_number_len(n3), n3)
print('e:', get_number_len(e), e)
print('c:', get_number_len(c), c)
p = gcd(n1, n2)
q = gcd(n1, n3)
r = gcd(n2, n3)
print('p:', get_number_len(p), p)
print('q:', get_number_len(q), q)
print('r:', get_number_len(r), r)
def decrypt(c, p, q, e):
ph = (p - 1) * (q - 1)
d = invert(e, ph)
return pow(c, d, p * q)
a1 = decrypt(c, q, r, e)
a2 = decrypt(a1, p, r, e)
a3 = decrypt(a2, p, q, e)
print('a1:', get_number_len(a1), a1)
print('a2:', get_number_len(a2), a2)
print('a3:', get_number_len(a3), a3)
flag = bytes.fromhex(format(a3, 'x')).decode("ascii")
print(flag)
注意,写了一个get_number_len
函数打印出数字位数,有些过程数据可以不打印。
picoCTF{1_gu3ss_tr1pl3_rs4_1snt_tr1pl3_s3cur3!!!}
03、XtraORdinary,150分
加密程序有一个random_strs
数组,encrypt
函数是把传入值与key做异或运算。
代码
for random_str in random_strs:
for i in range(randint(0, pow(2, 8))):
for j in range(randint(0, pow(2, 6))):
for k in range(randint(0, pow(2, 4))):
for l in range(randint(0, pow(2, 2))):
for m in range(randint(0, pow(2, 0))):
ctxt = encrypt(ctxt, random_str)
其中 循环仅做加密次数,计数变量i、j、k、l、m
未参与运算。5次循环后。
ctxt = encrypt(ctxt, random_str)
语句,被运行了很多次。
编写逆函数
from pwn import *
with open('output.txt', 'r') as f:
encode_text = bytes.fromhex(f.read())
random_strs = [b'my encryption method', b'is absolutely impenetrable', b'and you will never', b'ever', b'break it']
def check_invisible(t_str: str) -> bool:
"""
检查不可见字符
"""
if t_str is None:
return False
t_str = t_str.strip()
if t_str == '':
return False
for t in t_str:
if 32 >= ord(t) or ord(t) >= 127:
return False
return True
for i in range(32):
tmp = encode_text
perm = "{0:05b}".format(i)
for j in range(5):
if perm[j] == "1":
tmp = xor(tmp, random_strs[j])
flag_prefix = b"picoCTF"
key = xor(tmp[:7], flag_prefix)
try:
if check_invisible(key.decode()):
flag = xor(tmp, key).decode()
if check_invisible(flag):
print(perm)
print(flag)
except:
pass
这个代码有个投机取巧的地方,嫁定了flag前几个字是"picoCTF",然后异或得到key,来测试。如果flag的前几个字不是预测的字母,就测不出来了。
picoCTF{w41t_s0_1_d1dnt_1nv3nt_x0r???}
04、college-rowing-team,250分
import binascii
def nth_root(x, n):
mid=0
# Start with some reasonable bounds around the nth root.
upper_bound = 1
while upper_bound ** n <= x:
upper_bound *= 2
lower_bound = upper_bound // 2
# Keep searching for a better result as long as the bounds make sense.
while lower_bound < upper_bound:
mid = (lower_bound + upper_bound) // 2
mid_nth = mid ** n
if lower_bound < mid and mid_nth < x:
lower_bound = mid
elif upper_bound > mid and mid_nth > x:
upper_bound = mid
else:
# Found perfect nth root.
return mid
return mid + 1
c = [这里写数组,是密码文件里的C值]
m = []
mstr = []
for i in range(len(c)):
msg = nth_root(c[i], 3)
m.append(msg)
mstr.append(binascii.unhexlify(hex(msg)[2:]))
print(mstr)
2022 picoCTF
01、basic-mod1,100分
把flag的数字mod 37就可以了,然后转换
import string
numberArr = [202, 137, 390, 235, 114, 369, 198, 110, 350, 396, 390, 383, 225, 258, 38, 291, 75, 324, 401, 142, 288, 397]
f = ''
chars = string.ascii_uppercase + string.digits + '_'
for i in numberArr:
i = i % 37
f = f + chars[i]
print('picoCTF{' + f + '}')
picoCTF{R0UND_N_R0UND_B6B25531}
02、basic-mod2,100分
把flag的数字mod 41,然后转换
import string
numberArr = [268, 413, 110, 190, 426, 419, 108, 229, 310, 379, 323, 373, 385, 236, 92, 96, 169, 321, 284, 185, 154, 137,
186]
chars = "0" + string.ascii_uppercase + "0123456789_"
f = ''
for i in numberArr:
f = f + chars[pow(i, -1, 41)]
print('picoCTF{' + f + '}')
picoCTF{1NV3R53LY_H4RD_8A05D939}
03、credstuff,100分
题目说了找用户cultiris
,在usernames.txt
中找,搜索一下,在第378
行。
在password.txt
中,找378行,找到密码,
cvpbPGS{P7e1S_54I35_71Z3}
ROT13移位得到
picoCTF{C7r1F_54V35_71M3}
04、morse-code,100分
有一个网站专门破解音频莫斯密码
https://morsecode.world/international/decoder/audio-decoder-adaptive.html
这个网站听的不太准,要多听几次。
EMH47 H47H 90D W2 0U9H7
WH47_H47H_90D_W20U9H7
picoCTF{WH47_H47H_90D_W20U9H7}
05、rail-fence,100分
查了一下,Rail fence cipher密码,栅栏密码,4位
import numpy as np
import string
alphabet = '._ :' + string.ascii_lowercase + string.ascii_uppercase + string.digits
def zig(flag):
coords = []
x, y = -1, -1
isRising = True
for x in range(len(flag)):
if isRising and y < 3 or y == 0:
isRising = True # case for y==0 condition
y += 1
else:
isRising = False
y -= 1
coord = (x, y)
coords.append(coord)
return coords
def decrypt(flag, key):
counter = 0
matrix = np.full(shape=(key, len(flag)), fill_value=0) # Creating matrix, in this case 4x56
coords = zig(flag)
for coord in coords:
x, y = coord[0], coord[1]
matrix[y, x] = 99 # Filling with 99 to mark indexes
for row in matrix:
array = np.where(row == 99)
for element in array:
for a in element:
row[a] = alphabet.index(flag[counter])
counter += 1
# Priting flag
for coord in coords:
x, y = coord[0], coord[1]
print(alphabet[matrix[y, x]], end="")
flag = 'Ta _7N6DDDhlg:W3D_H3C31N__0D3ef sHR053F38N43D0F i33___NA'
key = 4
decrypt(flag, key)
picoCTF{WH3R3_D035_7H3_F3NC3_8361N_4ND_3ND_D00AFDD3}
06、substitution0,100分
还是这个乱序加密的解码网站
https://www.quipqiup.com/
把文章标题放进去,VOUHMJLTESZCDKWIXNQYFAPGBR
,正好得到一个排序整齐的字典,再猜几个字母,然后,就是了……
picoCTF{5UB5717U710N_3V0LU710N_357BF9FF}
07、substitution1,100分
picoCTF{FR3QU3NCY_4774CK5_4R3_C001_6E0659FB}
08、substitution2,100分
还是https://www.quipqiup.com/,估计是picoCTF的合作广告商~~
但是文章没有分单词,把文章删掉一些,把后面的文字 分开,分开到 qrizsevo tzv xprh so gsikITX{E6W4Q_4E41J515_15_73Y10N5_42VR1770} ,然后就行了……
picoCTF{N6R4M_4N41Y515_15_73D10U5_42EA1770}
09、transposition-trial,100分
观察了一下,每3位变换一次,最后一个调到第一个,自己还原容易出错,写了一段代码
s = 'heTfl g as iicpCTo{7F4NRP051N5_16_35P3X51N3_V9AAB1F8}7'
flag = ''
while len(s) >= 3:
ls = s[0:3]
ts = ls[2] + ls[0:2]
flag = flag + ts
s = s[3:]
print(flag)
picoCTF{7R4N5P051N6_15_3XP3N51V3_A9AFB178}
10、Vigenere,100分
维吉尼亚密码(VigenèreCipher)加密,题目给了key是CYLAB
,用CyberChef
解码
还有,很多CTF竞赛都有类似题目,用key解VigenèreCipher。
picoCTF{D0NT_US3_V1G3N3R3_C1PH3R_2951a89h}
11、Very Smooth,300分
网上找的程序,e为什么等于0x10001?没搞明白!
n = ***
c = ***
e = 0x10001
def pollard(n):
a = 2
b = 2
while True:
a = pow(a, b, n)
d = gcd(a - 1, n)
if 1 < d < n:
return d
b += 1
p = pollard(n)
q = n // p
phi = 1
for i in [p, q]:
phi *= (i - 1)
d = inverse(e, phi)
m = pow(c, d, n)
flag = long_to_bytes(m).decode('UTF-8')
print(flag)
picoCTF{p0ll4rd_f4ct0r1z4at10n_FTW_376ebfe7}