Luoingly's Space

阿里云 AliyunCTF 2024 Misc「字」个人题解

March 25, 2024 · Legacy Blog

注意到题给文本存在 Unicode「康熙部首」区的字符,进一步分析后假设该隐写与此相关。那么提出设想:在 Unicode 中,康熙部首涉及的文字既在 4E00~9FFF(中日韩统一表意文字)具有码位,又在 2F00~2FDF(康熙部首)存在码位,那么对于未隐写的文字,可以将其中涉及康熙部首的一部分文字从 4E00~9FFF 替换到 2F00~2FDF 的对应字符,在替换与否中做到数据的隐写。所以写出代码:

# U+2F00 ~ U+2FDF 的康熙部首
kangxi = \
    "⼀⼁⼂⼃⼄⼅⼆⼇⼈⼉⼊⼋⼌⼍⼎⼏" \
    "⼐⼑⼒⼓⼔⼕⼖⼗⼘⼙⼚⼛⼜⼝⼞⼟" \
    "⼠⼡⼢⼣⼤⼥⼦⼧⼨⼩⼪⼫⼬⼭⼮⼯" \
    "⼰⼱⼲⼳⼴⼵⼶⼷⼸⼹⼺⼻⼼⼽⼾⼿" \
    "⽀⽁⽂⽃⽄⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏" \
    "⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟" \
    "⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯" \
    "⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿" \
    "⾀⾁⾂⾃⾄⾅⾆⾇⾈⾉⾊⾋⾌⾍⾎⾏" \
    "⾐⾑⾒⾓⾔⾕⾖⾗⾘⾙⾚⾛⾜⾝⾞⾟" \
    "⾠⾡⾢⾣⾤⾥⾦⾧⾨⾩⾪⾫⾬⾭⾮⾯" \
    "⾰⾱⾲⾳⾴⾵⾶⾷⾸⾹⾺⾻⾼⾽⾾⾿" \
    "⿀⿁⿂⿃⿄⿅⿆⿇⿈⿉⿊⿋⿌⿍⿎⿏" \
    "⿐⿑⿒⿓⿔⿕"

# U+4E00 ~ U+9FFF 的康熙部首
normal = \
    "一丨丶丿乙亅二亠人儿入八冂冖冫几" \
    "凵刀力勹匕匚匸十卜卩厂厶又口囗土" \
    "士夂夊夕大女子宀寸小尢尸屮山巛工" \
    "己巾干幺广廴廾弋弓彐彡彳心戈戶手" \
    "支攴文斗斤方无日曰月木欠止歹殳毋" \
    "比毛氏气水火爪父爻爿片牙牛犬玄玉" \
    "瓜瓦甘生用田疋疒癶白皮皿目矛矢石" \
    "示禸禾穴立竹米糸缶网羊羽老而耒耳" \
    "聿肉臣自至臼舌舛舟艮色艸虍虫血行" \
    "衣襾見角言谷豆豕豸貝赤走足身車辛" \
    "辰辵邑酉釆里金長門阜隶隹雨靑非面" \
    "革韋韭音頁風飛食首香馬骨高髟鬥鬯" \
    "鬲鬼魚鳥鹵鹿麥麻黃黍黑黹黽鼎鼓鼠" \
    "鼻齊齒龍龜龠"

source = open('汉字.纯文本', 'rt', encoding='utf-8').read()

cnt = 0
for char in source:
    if char in kangxi:
        # print("["+char+"]", end='')
        print(1, end='')
        cnt += 1
    elif char in normal:
        # print("{"+char+"}", end='')
        print(0, end='')
        cnt += 1
print()
print(cnt)

# 00100000000010010000010011110110100010011000101101000001010011010011000101000101110001101111010011001101100010000100000000100000101101011110010000110110010010110101111001000011011001010111101000100011011000111001011101000101010111010001010101110100010101010110100100011001101100100011011000100001000000001000111111100000000000000000
# 332

可以得到,原文中共有 332 个涉及康熙部首的字符,这与后来 Hint 中提到的「信息容量 332bits」吻合。先假设进行了字符替换为 1 以及未替换为 0,注意到结尾有连续的 7 个 1 以及连续至结尾的 0,认为其是结束标志。结合 Hint 得知下一步仍与汉字有关,分析后猜测该二进制串每 7bit 为一组,两组表示一个汉字,那么该文本共隐藏了 22 个汉字。

对于汉字的编码,在考虑并排除了中文电码后仍然从 GBK 和 Unicode 方向进行分析。考虑到最终 Flag 与汉字有关,而「从二进制推算出汉字,又尝试从汉字解码出 ASCII 的 Flag」并没有实际意义,猜测汉字内容即表达了 Flag 的内容。进行了各种猜测、尝试后发现,隐写内容前三字符的低 5 位和「阿里云」GBK 编码的低 5 位相同:

10110000 101[00010] <-> 0010000 00[00010]
11000000 111[01111] <-> 0100000 10[01111]
11010100 110[00110] <-> 0110100 01[00110]

所以认为隐写内容是经过额外处理的 GBK 编码的汉字,在进行一番尝试后找到了正确的解码方法:

source = "00100000000010010000010011110110100010011000101101000001010011010011000101000101110001101111010011001101100010000100000000100000101101011110010000110110010010110101111001000011011001010111101000100011011000111001011101000101010111010001010101110100010101010110100100011001101100100011011000100001000000001000111111100000000000000000"

result = []
for i in range(0, len(source), 7):
    byte = int(source[i:i+7], 2)
    if byte == 0b1111111:
        break
    result.append(byte + 0b10100000)
print(bytes(result).decode("gbk"))

# 阿里云夺旗赛左花括二六二六下划五五五四右花括

Tags: #CTF #Writeup #Misc #AliyunCTF

This article is authored by luoingly and licensed under CC BY-NC 4.0

Permalink: https://luoy.ing/posts/aliyunctf-2024-writeup/