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翻译

2085142113251819131191514
thenumbersmason

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分

还是https://www.quipqiup.com/

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}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值