本文记录一下刚刚结束的CTF校赛,感觉本次校赛题目出得实在有点坑,但也算学到了不少东西。

Crypto

easy_ECC

问题描述

已知椭圆曲线加密Ep(a,b)参数为

p = 15424654874903

a = 305423748

b = 315284355172

G(15079176652031,83237376468)

私钥为

k = 655321

求公钥K(x,y)

思路

常规的椭圆曲线加密问题,直接根据加密原理编写解密程序,得到两个结果。根据问题提示,考虑 flag 为两数的异或结果。

解密脚本

def add(A, B):
    if A == (0, 0):
        return B
    if B == (0, 0):
        return A
    x1, y1 = A
    x2, y2 = B
    if A != B:
        λ = (y2 - y1) * pow((x2 - x1), p - 2, p)
    else:
        λ = (x1 * x1 * 3 + a) * pow(2 * y1, p - 2, p)
    x3 = λ * λ - x1 - x2
    y3 = λ * (x1 - x3) - y1
    return (x3 % p, y3 % p)


if __name__ == '__main__':
    p = 15424654874903
    a = 305423748
    b = 315284355172
    G = (15079176652031, 83237376468)
    k = 655321
    C = (0, 0)
    for i in range(k):
        C = add(C, G)
    print(C)
    print(C[0] ^ C[1])

心得

本题没有什么特别之处,就是标准的椭圆曲线加密问题的求解,重点在于理解椭圆曲线加密的原理。

Read_the_Message

问题描述

# python 3.7

import base64
from urllib.parse import quote
def main(key = "init_key", message = "init_message"):
    s_box = init_sbox(key)
    crypt = str(excrypt(message, s_box))
    return  crypt
def init_sbox(key):
    s_box = list(range(256))
    j = 0
    for i in range(256):
        j = (j + s_box[i] + ord(key[i % len(key)])) % 256
        s_box[i], s_box[j] = s_box[j], s_box[i]
    return s_box
def excrypt(plain, box):
    res = []
    i = j = 0
    for s in plain:
        i = (i + 1) % 256
        j = (j + box[i]) % 256
        box[i], box[j] = box[j], box[i]
        t = (box[i] + box[j]) % 256
        k = box[t]
        res.append(chr(ord(s) ^ k))
    cipher = "".join(res)
    print(str(base64.b64encode(cipher.encode('utf-8')), 'utf-8'))
    return (str(base64.b64encode(cipher.encode('utf-8')), 'utf-8'))
# main("jnsec","xxxxxxxxxxxx")

# ciphertext = w7okesOpwr0YYEtYwqYPSQ==
# key = jnsec

# flag = JNCTF{xxxxxxxxxxxx}
# Try to decrypt it !

思路

题目给出了加密程序以及密文,则将加密程序反写得到解密程序,并将密文作为输入,得到 flag 值。

解密脚本

import base64


def init_sbox(key):
    s_box = list(range(256))
    j = 0
    for i in range(256):
        j = (j + s_box[i] + ord(key[i % len(key)])) % 256
        s_box[i], s_box[j] = s_box[j], s_box[i]
    return s_box


def decrypt(cipher, box):
    res = []
    i = j = 0
    cipher = base64.b64decode(cipher).decode()
    for s in cipher:
        i = (i + 1) % 256
        j = (j + box[i]) % 256
        box[i], box[j] = box[j], box[i]
        t = (box[i] + box[j]) % 256
        k = box[t]
        res.append(chr(ord(s) ^ k))
    for r in res:
        print(r, end='')


if __name__ == '__main__':
    ciphertext = "w7okesOpwr0YYEtYwqYPSQ=="
    key = "jnsec"
    box = init_sbox(key)
    decrypt(ciphertext, box)

心得

本题重点在于程序的正确反写,即先 base64 解密,再单独对每个字符进行处理,需要注意的是异或(^)反写仍然是异或。

baby_rsa

问题描述

# baby_rsa
c = 10037709916698055005402119185691833127096330074819596592018139982119855547820
n = 73069886771625642807435783661014062604264768481735145873508846925735521695159
e = 65537

思路

给出了 RSA 加密的c(密文)、n、e,则考虑先将 n 大数分解得到 p 和 q,再根据 RSA 加密的原理求解 d,并根据解密公式求解明文 m,最后将 m 转换成 bytes 格式,得到 flag值。

解密脚本

from Crypto.Util.number import long_to_bytes

n = 73069886771625642807435783661014062604264768481735145873508846925735521695159
p = 189239861511125143212536989589123569301
q = 386123125371923651191219869811293586459
c = 10037709916698055005402119185691833127096330074819596592018139982119855547820
e = 65537
phi_n = (p - 1) * (q - 1)
k = 0
while True:
    if (k * phi_n + 1) % e == 0:
        d = (k * phi_n + 1) // e
        break
    k += 1
print("d值为:", d)
m = pow(c, d, n)
print("m值为:", m)
print(long_to_bytes(m))

心得

本题我最后没有求解成功,按理说就是很常规的 RSA 加密求解,但是本题出的很坑的一点在于题目并没有说明最后需要对明文 m 进行 long_to_bytes() 的处理,这很容易让解题者误以为是明文求解错误。不过另一方面也反映了我对于将整型数据转换成字节数据这一知识点的缺失。

MISC

easy_disk

问题描述

题目给出了一个名为 bomb.img 的磁盘文件,查找 flag 值。

思路

dishgenius 打开虚拟磁盘文件 bomb.img,打开后点击右边窗口的“浏览文 件”,发现三个0字节的空文件,点击软件上面的“恢复文件”,得到一个 ZIP 压缩包和一个 txt 文件。

txt 文件提示为“C4炸弹”,百度可知CS系列游戏中的 C4 炸弹密码为7355608, 解压后得到一个二维码,但二维码三个定位点均被抹去,利用ps等软件恢复二 维码后,扫描二维码得到flag。

心得

本题我倒是没有按照正规解法——分析磁盘文件来解决。在看到磁盘文件时,我第一时间想到使用 foremost 文件将其分离,只得到一个 ZIP 压缩包,随后根据题目描述的 bomb 等信息考虑到暴力破解该压缩包,最终也成功将压缩包解压。但是如果密码相对复杂那么暴力破解可能不会成功,因此了解各种文件类型的分析方法是至关重要的。

big_shark

问题描述

题目给出一个 pcapng 格式的数据包文件,查找 flag 值。

思路

以 wireshark 为例,打开流量包,筛选 HTTP 协议,发现成堆的 sql 注入攻击流量,进一步简单筛选带有关键词的 http 协议流量 http contains “select” 。

按时间顺序记录下攻击语句中的 ascii 值,解码后拼接在一起发现是一串字符且以 = 结尾,判断为 base64 编码,因此再 base64 解码,得到flag。

解密脚本

import base64

dic = [83, 107, 53, 68, 86, 69, 90, 55, 85, 51, 70, 115, 97, 86, 57, 74, 77, 49, 57, 84, 77, 70, 57, 74, 98, 110, 82,
       108, 99, 106, 82, 122, 100, 68, 70, 117, 90, 51, 48, 61]
m = ""
for e in dic:
    m += chr(e)
print(m)
print(base64.b64decode(m).decode())

心得

我虽然对计算机网络的知识几乎几无所知,也不清楚 pcapng 是什么文件类型,但还是在查阅网络资料的情况下,按照网上已有的样例,按部就班的得到了相应的 ascii 码并最终将其解码,可见用好搜索引擎的重要性(doge)。

magic_photo

问题描述

题目给了一张 jpg 格式图片,查找 flag 值。

思路

使用 010Editor 打开图片,发现16进制编码中有Unicode以及坐标。

将 unicode 转换成字符,得到一串 emoji 字符,根据提示进行 emoji-aes 在线解密:emoji-aes.

根据图二坐标绘制点阵图,得到 emoji-aes 解密密钥。

最后进行在线解密,得到 flag 值。

心得

本题我认为出的是非常坑了,我首先用 foremost 将图片分离得到 ZIP 压缩包和一个提示的文本文件,文本提示“你了解这位小姐姐嘛?”,猜测为图片中人物的某些信息。

故使用百度识图得到图片人物为吉克隽逸,最终得到压缩包密码为她的生日 19880513。

解压后得到一个音频文件,为她演唱的一首歌,在歌的末尾含有一串摩尔斯电码,观察该音频文件的波形图并记录下摩尔斯电码并解密,得到**JNCTFDOY0UTH1NKYOUARERI9HT?**,显然,这是一个虚假的 flag。

个人认为,坑不是不能有,但得到这样错误结果的代价也是不小的,且正确解法与错误解法之间完全没有任何联系,希望出题人能平衡好这其中的差别。