CTF-Misc Guide

Contents

This is a simple Guide for CTF in Misc Area.

Misc Guide

最开始接触CTF时,学的最多的就是Misc,各种编码各种加密还有各种软件的使用…

但无奈MIsc涉及的范围实在太广了,于是就萌生了一边学习一边记录的想法,甚至还想为此写一本指南。

一些奇奇怪怪的经历:

1、一段字符串,用base64异或脚本跑,找正常的字符串

2、rockstar 编程语言,在github上面可以找到,然后在本地用pip安装库,把rock文件转换为py文件,运行即可得到flag

3、给你一个.exe安装包文件,flag藏在安装之前的一大串协议中

4、实在做不出来的时候,可以把flag的格式转其他的编码和题目中的信息比对找规律

5、给你一个gpx文件,在线网站https://www.gpsvisualizer.com/map_input解密,然后地名的首字母连起来就是flag

CTF中的常用关键词

1
2
3
4
5
6
# 要搜索的字符列表
search_terms = [
    "key", "password", "dasctf", "k3y", "p@ssword", "passw0rd",
    "p@ssw0rd", "secret", "s3cret", "s3cr3t", "s3cre4","F14ggg"
    # 遇到⼀个加⼀个,CTFer的好习惯
]
1
2
3
4
5
6
7
8
9
# 各种常用关键字的bash64编码
flag                          Zmxh
F14g                          RjE0
DASCTF                        REFTQ1RGe
s3cr3t                        czNjcjN0
secret                        c2VjcmV0
password                      cGFzc3dvc
PNG文件头                      iVBORw0KGgo
ZIP文件头                      UEsDBA

各种加密/编码:

base家族

详细请看:https://www.cnblogs.com/0yst3r-2046/p/11962942.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
1、base16                       flag         666C6167
2、base32[A-Z2-7]               flag         MZWGCZY=
3、base36                       flag         727432
4、base58                       flag         3cr9Ae
5、base64                       flag         Zmxh
6、base85                       flag         Ao(mg
7、base91                       flag         @iH<Z
8、base92                       flag         F#S<I
9、base100                      flag         👝👣👘👞
10、base1024                    flag
11、base2048                    flag         ڥڊװ
12、base65535                   flag         ꍦ鱡

base64还可以换表(表中的字符要求不重复)编码,例如

1
2
3
4
sQ+3ja02RchXLUFmNSZoYPlr8e/HVqxwfWtd7pnTADK15Evi9kGOMgbuIzyB64CJ
SjaoNgS0xgagUTpwe3QwHn4MrbkD/OUwqOQG/bpveg6Mqa4WH0k46
第一行是表,第二行是编码后的密文
cyberchef解密即可得到flag

Tips:base64可以与其他文件格式互相转换(比如图片[会有很多行的base64]),使用在线网站或者随波逐流转换即可 如果出现了很多层乱七八糟的base编码,连CyberChef都识别不出来的话,可以试试用BaseCrack这个开源工具 输入 python basecrack.py -m 运行即可

basecrack

base64隐写:

可以使用以下脚本解密

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# base64隐写解密脚本1
import base64
b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
with open('stego.txt', 'rb') as f:
    bin_str = ''
    for line in f.readlines():
        stegb64 = str(line, "utf-8").strip("\n")
        rowb64 = str(base64.b64encode(base64.b64decode(stegb64)), "utf-8").strip("\n")
        offset = abs(b64chars.index(stegb64.replace('=', '')[-1]) - b64chars.index(rowb64.replace('=', '')[-1]))
        equalnum = stegb64.count('=')  # no equalnum no offset
        if equalnum:
            bin_str += bin(offset)[2:].zfill(equalnum * 2)
        print(''.join([chr(int(bin_str[i:i + 8], 2)) for i in range(0, len(bin_str), 8)]))  # 8 位一组
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# base64隐写解密脚本2
file = open('./base64.txt','r')
a = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
aaa = ''
while True:
    text = file.readline()  # 只读取一行内容
    # 判断是否读取到内容
    text = text.replace("\n", "")
    if not text:
        break
    if text.count('=') == 1:
        aaa = aaa + \
            str('{:02b}'.format((a.find(text[len(text)-2])) % 4))
    if text.count('=') == 2:
        aaa = aaa + \
            str('{:04b}'.format((a.find(text[len(text)-3])) % 16))
file.close()
t = ""
ttt = len(aaa)
ttt = ttt//8*8
for i in range(0,ttt,8):
    t = t + chr(int( aaa[i:i+8],2))
print(t)

或者直接使用PuzzleSolver解密

imgs/image-20241120164908626.png

这里要注意多行base64编码可能会出现需要我们自己补全=的情况(例题-攻防世界 MISC - tunnel)

可以使用下面的脚本补全,也可以直接用上面那个工具补全

1
2
3
4
5
6
7
8
import re
with open('./result.txt','r') as f:
    content = f.readlines()
    for i in content:
        result = re.findall('(.*?).evil.im',i)
        result = result[0] + (4 - len(result[0])%4) * '='
        with open('./base64.txt','a+') as f1:
            f1.write(result+'\n')

MD5加密

1
2
3
4
5
6
明文:admin
32位小写21232f297a57a5a743894a0e4a801fc3 
32位大写21232F297A57A5A743894A0E4A801FC3 
16位小写7a57a5a743894a0e 
16位大写7A57A5A743894A0E 
Tips:十六位其实就是取32位的8-24位

MD5 加密后的密文应该是 纯数字+纯字符

有些 MD5 的 HASH 值可以直接在 somd5 或者 cmd5 上查

python中str类型和byte类型:

1
2
3
4
5
6
7
8
\>>> a = '寒鸦小站'
\>>> type(a)
<class 'str'>
\>>> b = a.encode()
\>>> b
b'\xe5\xaf\x92\xe9\xb8\xa6\xe5\xb0\x8f\xe7\xab\x99'
\>>> type(b)
<class 'bytes'>

emoji-aes编码:

密文由一大串emoji表情组成,解密需要密钥,例如

已知key:th1sisKey,直接使用在线网站解密即可,在线网站源码也可以下载到本地

1
🙃💵🌿🎤🚪🌏🐎🥋🚫😆😍🔬👣🖐🌏😇🥋😇😊🍎🏹👌🌊☃🦓🌏🐅🥋🚨📮🐍🎈📮📂✅🐍⏩⌨🎈😍🌊😇🐍☺💧🥋🍌🎤🍍😇👁🦓😇🍍📮📂🎅😡🍵✖✉🏹⌨🍵🎤😆🍵🚹🏹🍎🚨ℹ☃👑🎤🚪💵😎😀😎🔬💵🦓🏹👉🦓✖😀🐘🔪⌨🎈🥋👌🍌🚹😂✉🍎🍌🏎👌🏹💵👌👁🎃🗒

https://aghorler.github.io/emoji-aes/

词频分析:

一堆文字,看着什么编码都不像的,可能是词频分析,用在线网站跑https://quipqiup.com/

字频分析:

用随波逐流直接分析

摩斯电码:

1
#第一种情况,只有.-或者只有01
1
2
#第二种情况,加入/或者空格来替换换行符
.--/./.-../-.-./---/--/./-/---/-./-.-/-.-./-/..-./--..--/-/...././.--./.-/.../.../.--/---/.-./-../../.../.----/-..../-.../-.--/-/./.../.-./.-/-./-../---/--/.-../-.--/--././-././.-./.-/-/./-../--..-

vigenere(维吉尼亚)密码:

1.给了密文和Key

直接拉到cyberchef中解密即可

2.给了密文,没给密钥,但是知道目标明文的格式

先用B神的脚本爆破出Key,然后再把这个Key放到cyberchef中解密

3.根据对照表,手搓密钥的前几位

vigenere 4.Python代码解密/爆破维吉尼亚加密

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from pycipher import Vigenere


cipher = "rla xymijgpf ppsoto wq u nncwel ff tfqlgnxwzz sgnlwduzmy vcyg ib bhfbe u tnaxua ff satzmpibf vszqen eyvlatq cnzhk dk hfy mnciuzj ou s yygusfp bl dq e okcvpa hmsz vi wdimyfqqjqubzc hmpmbgxifbgi qs lciyaktb jf clntkspy drywuz wucfm"

with open("keys.txt","r") as f:
    lines = f.readlines()

for line in lines:
    key = line.strip()
    res = Vigenere(key).decipher(cipher)
    if "PASSWORD" in res:
        print(f"[+] key: {key}")
        print(f"[+] res: {res.lower()}")

希尔密码:

解密网站:http://www.metools.info/code/hillcipher243.html

已知密文和密钥,并且密钥(key)是一个网址,如http://www.verymuch.net

已知密文和密钥,并且密钥是四个数字

1
2
密文:ymyvzjtxswwktetpyvpfmvcdgywktetpyvpfuedfnzdjsiujvpwktetpyvnzdjpfkjssvacdgywktetpyvnzdjqtincduedfpfkjssne
密钥:3 4 19 11

Rabbi密码:

已知密文和密钥,密文有点像base64编码的(可能有+号)

云隐密码:

特征是:密文只由01248组成

用随波逐流或者CTFD中的脚本直接跑

曼彻斯特与差分曼彻斯特编码:

imgs/image-20240529203318823.png

  1. 曼彻斯特码:从高到低表示 1,从低到高表示 0
  2. 差分曼彻斯特码:在每个时钟周期的起始处(虚线处)有跳变表示 0;无跳变则表示1。

可以直接使用 曼彻斯特编码 转换工具转换

imgs/image-20240529203746999.png

例题1 2016CISCN-传感器1

5555555595555A65556AA696AA6666666955

这是某压力传感器无线数据包解调后但未解码的报文(hex)

已知其ID为0xFED31F,请继续将报文完整解码,提交hex。

提示1:曼联

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
enc = "5555555595555A65556AA696AA6666666955"
res = ''
flag = ''
flag_final = ''
for item in enc:
    # tmp = bin(int(item, 16))[2:].rjust(4, '0')
    # print(tmp, end=' ')
    res += str(bin(int(item, 16))[2:].rjust(4, '0'))
# print(res)
for i in range(0, len(res), 2):
    if res[i:i+2] == '01':
        flag += '1'
    elif res[i:i+2] == '10':
        flag += '0'
# print(flag)
# 这里需要每8位进行一次反转,要不然无法得到校验ID:0xFED31F
for i in range(0, len(flag), 8):
    flag_final += hex(int(flag[i:i+8][::-1], 2))[2:]

print(flag_final.upper())
# FFFFFED31F645055F9

例题2 2016CISCN-传感器2

现有某ID为0xFED31F的压力传感器,已知测得
压力为45psi时的未解码报文为:5555555595555A65556A5A96AA666666A955
压力为30psi时的未解码报文为:5555555595555A65556A9AA6AA6666665665
请给出ID为0xFEB757的传感器在压力为25psi时的解码后报文

和上面那题的思路一样,就是最后多了一步压力位算法和校验位算法猜测

压力位算法:压力每增加5psi压力值增加11

校验位算法:校验值为从ID开始每字节相加的和模256的十六进制值即为校验值

例题3 2017CISCN-传感器1

已知ID为0x8893CA58的温度传感器的未解码报文为:3EAAAAA56A69AA55A95995A569AA95565556
此时有另一个相同型号的传感器,其未解码报文为:3EAAAAA56A69AA556A965A5999596AA95656
请解出其ID,提交flag{不含0x的hex值}

开头的3E提示了差分曼彻斯特编码,就是根据上图中的跳变位置解码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# enc = "3EAAAAA56A69AA55A95995A569AA95565556"
enc = "3EAAAAA56A69AA556A965A5999596AA95656"
res = ''
flag = ''
flag_final = ''
for item in enc:
    # tmp = bin(int(item, 16))[2:].rjust(4, '0')
    # print(tmp, end=' ')
    res += str(bin(int(item, 16))[2:].rjust(4, '0'))
print(res)
for i in range(8, len(res), 2):
    if res[i:i+2][0] != res[i-1]:
        flag += '0'
    else:
        flag += '1'
print(hex(int(flag, 2))[2:].upper())
# 24D8845ABF34119
# 8845ABF3

例题4 2017CISCN-传感器2

已知ID为0x8893CA58的温度传感器未解码报文为:3EAAAAA56A69AA55A95995A569AA95565556
为伪造该类型传感器的报文ID(其他报文内容不变),请给出ID为0xDEADBEEF的传感器1的报文校验位(解码后hex)

以及ID为0xBAADA555的传感器2的报文校验位(解码后hex),并组合作为flag提交。
例如,若传感器1的校验位为0x123456,传感器2的校验位为0xABCDEF,则flag为flag{123456ABCDEF}。

解码步骤和上题一样,就是多考察了一个校验位算法(CRC8)

在最后的结果前面补一个0,然后再计算 CRC8 即可

社会主义核心价值观密码:

解密网址:http://www.hiencode.com/cvencode.html

公正民主公正文明公正和谐:abc

outguess解密图片:

在kali中下载outguess:outguess -k ‘abc’ -r mmm.jpg -t flag.txt

outguess -k ‘key’ -r 加密后的图片.jpg -t 明文.txt

盲文:

使用https://www.qqxiuzi.cn/bianma/wenbenjiami.php?s=mangwen在线翻译

文本加密为音乐符号:

Tips:这里要注意,加密的密文一定是以=结尾的,有时候需要自己把=加上

eg:♭♯♪‖¶♬♭♭♪♭‖‖♭♭♬‖♫♪‖♩♬‖♬♬♭♭♫‖♩♫‖♬♪♭♭♭‖¶∮‖‖‖‖♩♬‖♬♪‖♩♫♭♭♭♭♭§‖♩♩♭♭♫♭♭♭‖♬♭‖¶§♭♭♯‖♫∮‖♬¶‖¶∮‖♬♫‖♫♬‖♫♫§=

直接用在线网站解密:https://www.qqxiuzi.cn/bianma/wenbenjiami.php?s=yinyue

敲击码:

敲击码

….. ../… ./… ./… ../ 5,2 3,1 3,1 3,2 W L L M

Polybius密码(详见CTFwiki)

类似于11,22,11,24这样的

去逗号改成空格,拉入随波逐流直接解密

DES加密算法

例子:

1
2
密文:AK5O3BaZi+p1ci0JxythDZWToTXkFj4dexQ3cOAmYfUwtUVyJahFOcNroC8nAsHyCnmiuOOpJYyOWBV5npW3pg==
密钥:hristina

imgs/image-20241105212634286.png

AES加密算法

在线网站解密:

  1. https://tool.lmeee.com/jiami/aes
  2. https://www.sojson.com/encrypt_aes.html

AES-ECB(不需要IV)

CyberChef解密AES-ECB时需要将IV设置为\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00

如果 key 不足16字节可以尝试在后面补0

AES-CBC(需要填写IV)

Tips: CBC模式下key的长度必须是16bytes的整数倍,但是IV不一定

imgs/image-20241116212331838.png

密钥不足16字节时需要padding补齐16字节

可以使用能自动补齐的在线网站解密 https://www.sojson.com/encrypt_aes.html

imgs/aes1.png

可以将密文和key拉入CaptfEncoder-win-x64-1.3.0解密

imgs/aes2.png

使用openssl进行加解密

1
2
3
4
# 加密
tar -czvf - flag | openssl des3 -salt -k th1sisKey -out ./flag.tar.gz
# 解密
openssl des3 -d -salt -k th1sisKey -in ./flag.tar.gz -out decrypted_file

埃特巴什码(Atbash)

类似于:(+w)v&LdG_FhgKhdFfhgahJfKcgcKdc_eeIJ_gFN

拉入厨子直接解密

1
flag{ ==> Atbash加密 ==> UOZT{

DNA编码

1
AATTCAACAACATGCTGC

1、使用CTFD中的DNAcode脚本解密

https://github.com/omemishra/DNA-Genetic-Python-Scripts-CTF

2、网上找的脚本(红明谷杯2023——hacker)

1
2
3
4
5
6
7
8
table = 'ACGT'
dic = {'AAA': 'a', 'AAC': 'b', 'AAG': 'c',
       'AAT': 'd', 'ACA': 'e', 'ACC': 'f', 'ACG': 'g', 'ACT': 'h', 'AGA': 'i', 'AGC': 'j', 'AGG': 'k', 'AGT': 'l', 'ATA': 'm', 'ATC': 'n', 'ATG': 'o', 'ATT': 'p', 'CAA': 'q', 'CAC': 'r', 'CAG': 's', 'CAT': 't', 'CCA': 'u', 'CCC': 'v', 'CCG': 'w', 'CCT': 'x', 'CGA': 'y', 'CGC': 'z', 'CGG': 'A', 'CGT': 'B', 'CTA': 'C', 'CTC': 'D', 'CTG': 'E', 'CTT': 'F', 'GAA': 'G', 'GAC': 'H', 'GAG': 'I', 'GAT': 'J', 'GCA': 'K', 'GCC': 'L', 'GCG': 'M', 'GCT': 'N', 'GGA': 'O', 'GGC': 'P', 'GGG': 'Q', 'GGT': 'R', 'GTA': 'S', 'GTC': 'T', 'GTG': 'U', 'GTT': 'V', 'TAA': 'W', 'TAC': 'X', 'TAG': 'Y', 'TAT': 'Z', 'TCA': '1', 'TCC': '2', 'TCG': '3', 'TCT': '4', 'TGA': '5', 'TGC': '6', 'TGG': '7', 'TGT': '8', 'TTA': '9', 'TTC': '0', 'TTG': ' '}
cipher = 'TCATCAACAAAT'
plain = ''
for i in range(0, len(cipher), 3):
    plain += dic[cipher[i:i+3]]
print(plain)

Text Encoding Brute Force

如果赛博厨子转完两次Hex后依然是乱码,可以用Text Encoding Brute Force爆破试试看

例子:红明谷杯2023——阿尼亚

Decabit编码

正常的 Decabit编码 是十个字符一组的,如果不是十个一组,就很可能不是 Decabit编码

+-+-++–+- ++—+-++- -+–++-++- +–++-++– –+++++— ++-++—+- +++-+-+— +-+-+—++ —+++-++- -+–++-++- -+–+++-+- -+–++-++- -+–++-++- ++-+-+-+– -+–+++-+- ++-++—+- -++++—+- -+–++-++- ++-+-+-+– +-+++—+- +++-++—- —+++-++- +-+-+—++ ++-+-+-+– +-+-+–++- ++–+–++- -++++—+- +—+++-+- ++-+-+-+– -++++—+- -+–+++-+- +–+-+-++- +++-+-+— +-+++—+- -+–+-+++- -+–++-++- —+++-++- ++++—-+- -++++—+- -+–+++-+- -+–++-++- —-+++++-

直接使用 在线网站 解密即可

如果不是Decabit编码,可以试试看把+-分别用01替换 [2023 楚慧杯-Easy_zip]

仿射密码

有两个key,key-a为必须是(1,3,5,7,9,11,15,17,19,21,23,25)中的一个,key-b是0~25的数字

可以使用在线网站CTF在线工具-在线仿射密码加密|在线仿射密码解密|仿射密码算法|Affine Cipher (hiencode.com)或者随波逐流解密

1
2
3
gezx{j13p5oznp_1t_z_900y_k3z771h_k001}
key-a=17	key-b=77
flag{w13e5hake_1s_a_900d_t3a771c_t001}

BrainFuck编码

可以直接使用在线网站解码,但是flag可能会藏在内存中然后被删去导致无法输出flag,因此可以用下面这个代码输出之前放在内存中的flag

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
char s[30000]={0};
char code[2000];
int len = 0;
int stack[10000];
int stack_len=0;
int main()
{
    char c;
    int i=0,j,k,x=0;
    FILE* f;
    char* p=s+10000;
    f=fopen("./bf.txt","r");
    while(fread(&code[len],1,1,f)==1)
	{
        len++;
    }
    setbuf(stdout,NULL);
    while(i<len) {
        switch(code[i]) {
            case '+':
                (*p)++;
                break;
            case '-':
                (*p)--;
                break;
            case '>':
                p++;
                break;
            case '<':
                p--;
                break;
            case '.':
                putchar((int)(*p));
                break;
            case ',':
                *p=getchar();
                break;
            case '[':
                if(*p) {
                    stack[stack_len++]=i;
                } else {
                    for(k=i,j=0;k<len;k++) {
                        code[k]=='['&&j++;
                        code[k]==']'&&j--;
                        if(j==0)break;
                    }
                    if(j==0)
                        i=k;
                    else {
                        fprintf(stderr,"%s:%dn",__FILE__,__LINE__);
                        return 3;
                    }
                }
                break;
            case ']':
                i=stack[stack_len-- - 1]-1;
                break;
            default:
                break;
        }
        i++;
        x++;
    }
    for(int i = 0; i < stack_len; i++) {
		printf("%c", stack[i]);
	}
    printf("\n");
    for(int i = 0; i < 30000; i++) {
		printf("%c", s[i]);
	}
    return 0;
}

Gronsfeld密码

可以直接使用这个在线网站解密&爆破:https://www.boxentriq.com/code-breaking/gronsfeld-cipher

1
2
3
4
5
6
7
8
# 解密脚本
from pycipher import Gronsfeld

cipher = 'TGLBOMSJNSRAJAZDEZXGHSJNZWHG'
key = [1,50,61,8,9,20,63,41]
secret = Gronsfeld(key).decipher(cipher)

print(secret)

UUencode编码

看起来有点像base85,直接使用在线网站解密即可

1
2
=8S4U,3DR8SDY,C`S-F5F-C(S,S<R-C`Q9F8S87T`
# c55192c992036ef623372601ff3a}

AAencode编码

XXencode编码

随波逐流直接解密即可 [2023 浙江省赛决赛]

无字天书(whitespace)或者snow隐写

一个文件打开都是空白字符

可以使用在线网站解密:https://vii5ard.github.io/whitespace/ 复制进去直接run即可

snow隐写,到snowdos32工具目录下运行 SNOW.EXE -C -p password flag.txt 命令即可

中文电报(中文电码)

类似于下面这种四位数一组的编码,直接在线网站解码即可

5337 5337 2448 2448 0001 2448 0001 2161 1721 1869 6671 0008 3296 4430 0001 3945 0260 3945 1869 4574 5337 0344 2448 0037 5337 5337 0260 0668 5337 6671 0008 3296 1869 6671 0008 3296 1869 2161 1721

Quote-Printable编码

类似于下面这样的编码,直接使用 在线网站 解密即可

flag{ichunqiu_=E6=8A=80=E6=9C=AF=E6=9C=89=E6=B8=A9=E5=BA=A6}

flag{ichunqiu_技术有温度}

Unicode编码

这个编码有很多种格式,比如+U、\u、\x、&#啥的

imgs/image-20241101155218913.png

可以使用这个在线网站解码:https://r12a.github.io/app-conversion/

中文ascii码

1
27880 30693 25915 21892 38450 23454 39564 23460 21457 36865 112 108 98 99 116 102 33719 21462 21069 27573 102 108 97 103 20851 27880 79 110 101 45 70 111 120 23433 20840 22242 38431 22238 22797 112 108 98 99 116 102 33719 21462 21518 27573 102 108 97 103

加上&#和分号,直接 CyberChef 或者 在线网站 解密即可

1
&#27880;&#30693;&#25915;&#21892;&#38450;&#23454;&#39564;&#23460;&#21457;&#36865;&#112;&#108;&#98;&#99;&#116;&#102;&#33719;&#21462;&#21069;&#27573;&#102;&#108;&#97;&#103;&#20851;&#27880;&#79;&#110;&#101;&#45;&#70;&#111;&#120;&#23433;&#20840;&#22242;&#38431;&#22238;&#22797;&#112;&#108;&#98;&#99;&#116;&#102;&#33719;&#21462;&#21518;&#27573;&#102;&#108;&#97;&#103;

培根密码

由 a、b 或者 A、B 或者 0、1 组成的密文,密文中只有两种字符,可以直接使用 随波逐流 解密

Tips:CyberChef 的培根密码解密可能会有点问题,这里建议用随波逐流解密

锟斤拷

这个东西的成因是 Unicode 的替换字符(Replacement Character,�)于 UTF-8 编码下的结果 EF BF BD 重复,在 GBK 编码中被解释为汉字 “锟斤拷”(EF BF BD EF BF BD)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import os

a = input('请选择你的功能(1、加密 2、解密):')
if a == "1":
    s = input('请输入你要加密的话:')
    utf = s.encode('utf')
    gbk = s.encode('utf').decode('gbk', errors='ignore')
    if len(s)%2 == 1:
        gbk = gbk + "�"
    print(gbk)
    os.system("pause")
if a == "2":
    s = input('请输入你要解密的话:')
    gbk = s.encode('gbk')
    utf = s.encode('gbk').decode('utf-8', errors='ignore')
    print(utf)
    os.system("pause")

键盘坐标密码

1
2
3
4
  1 2 3 4 5 6 7 8 9 0
1 Q W E R T Y U I O P
2 A S D F G H J K L
3 Z X C V B N M

例题-i春秋-misc3

1
2
flag{11 21 31 18 27 33 34}
flag{QAZIJCV}

手机九宫格键盘密码

参考链接:https://blog.csdn.net/qq_55011640/article/details/123626280

下面举个栗子就理解了: 82  73  42  31  22  31  33  41  32 U   R   H   D  B   D   F   G   E

不同键盘布局的编码

Qwerty

imgs/image-20241113151943395.png

Qwertz

imgs/image-20241113152029297.png

Azerty

imgs/image-20241113152112365.png

Dvorak(德沃夏克键盘)

imgs/image-20241113151817733.png

imgs/image-20241113151927957.png

Colemak

imgs/image-20241113151902536.png

例题-2023台州市赛初赛 Black Mamba

棋盘密码((ADFGVX,ADFGX,Polybius)

imgs/image-20241018145022295.png

直接使用CaptfEncoder或者随波逐流等工具输入密文和密钥解密即可 imgs/image-20241018145101804.png

ADFGVX密码 默认棋盘:ph0qg64mea1yl2nofdxkr3cvs5zw7bj9uti8 默认密钥:german ADFGX密码 默认棋盘:phqgmeaynofdxkrcvszwbutil 默认密钥:german 波利比奥斯方阵密码 密钥:随机 默认密文字符:ABCDE

福尔摩斯密码

1
·-· ·-· ·-· ·-· ·-· ·-· ·

直接网上查找福尔摩斯密码对照表即可 flag{RRRRRRE}

利用编程代码画图

  1. LOGO编程语言【例题-[RCTF2019]draw 】 在线编译器:https://www.calormen.com/jslogo/
  2. CFRS编程语言【例题-2024宁波市赛初赛 Misc2】 在线画图网站:https://susam.net/cfrs.html

各种文件头/尾:

这里要注意,出题人可能会把文件头的小写字母偷偷改成大写,例如:Rar -> RAR

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
zip 文件头50 4B 03 04 14 00 08 00
rar 文件头52 61 72 21 (Rar!)               文件尾C4 3D 7B 00 40 07 00
7z  文件头37 7A BC AF 27 1C
png 文件头89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52   文件尾49 45 4E 44 AE 42 60 82
jpg 文件头FF D8 FF E0 00 10 4A 46 49 46 00 01
gif 文件头47 49 46 38 39 61GIF89A 47 49 46 38 37 61GIF87A    文件尾00 3B
wav 文件头57415645
gz 文件头1F 8B 08 00
pyc的文件头03 F3 0D 0A
psd的文件头38 42 50 53
TIFF (tif)文件头49492A00
Windows [Bitmap](https://so.csdn.net/so/search?q=Bitmap&spm=1001.2101.3001.7020) (bmp)文件头424D
CAD (dwg)文件头41433130
Adobe Photoshop (psd)文件头38425053
Rich Text Format (rtf)文件头7B5C727466
XML (xml)文件头3C3F786D6C
HTML (html)文件头68746D6C3E
Email [thorough only] (eml)文件头44656C69766572792D646174653A
Outlook Express (dbx)文件头CFAD12FEC5FD746F
Outlook (pst)文件头2142444E
MS Word/Excel (xls.or.doc)文件头D0CF11E0
MS Access (mdb)文件头5374616E64617264204A
WordPerfect (wpd)文件头FF575043
Postscript (eps.or.ps)文件头252150532D41646F6265
Adobe Acrobat (pdf)文件头255044462D312E
Quicken (qdf)文件头AC9EBD8F
Windows Password (pwl)文件头E3828596
AVI (avi)文件头41564920
Real Audio (ram)文件头2E7261FD
Real Media (rm)文件头2E524D46
MPEG (mpg)文件头000001BA
MPEG (mpg)文件头000001B3
Quicktime (mov)文件头6D6F6F76
Windows Media (asf)文件头3026B2758E66CF11
MIDI (mid)文件头4D546864
M4a文件头00000018667479704D3441

Misc——流量分析

详见作者博客中的 Network Traffic Analysis 这篇文章

MIsc——图片题思路:

Tips:

1.各种隐写可以先拉入一键梭哈网站解析一下:https://aperisolve.fr/

2.各种乱七八糟的隐写可以先看看这个UP主的视频:https://space.bilibili.com/39665558

通用思路

1、查看图片属性的详细信息(可能关键信息就在里面)

2、拉入010,查看文件头尾,可能会有不同类型文件文件头混用

3、foremost 或者 binwalk

如果 foremost 没有提取出东西,可以用 binwalk 试一下,可能 binwalk可以提取出东西

例题 - i春秋 CTF Misc class10

4、盲水印隐写(可能是一张图片或者两张图片)

一张图片的情况

可以使用 隐形水印工具V1.2 或者 WaterMark 来提取水印

imgs/bw1.png

两张图片的情况

1
2
3
先把要处理的图片拉入BlindWaterMark-master文件夹,然后使用如下命令
py bwmforpy3.py decode day1.png day2.png flag.png --oldseed
Tips:这里还会出现FFT(傅里叶盲水印):直接运行CTFD中的FFT.py

5、图片的分离和拼接

(1)可以用kali的convert分离和montage拼接命令

1
2
3
4
5
6
分解GIF的命令:convert glance.gif flag.png
水平镜像翻转图片:convert -flop reverse.jpg reversed.jpg
垂直镜像翻转图片:convert -flip reverse.jpg reversed.jpg
合成图片的命令:montage flag*.png -tile x1 -geometry +0+0 flag.png
-tile是拼接时每行和每列的图片数,这里用x1,就是只一行
-geometry是首选每个图和边框尺寸,我们边框为0,图照原始尺寸即可

(2)使用在线网站分解:https://tu.sioe.cn/gj/fenjie/

(3)用py脚本跑

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import os
from PIL import Image
im = Image.new('RGB', (2*201, 600))  # new(mode,size) size is long and width
PATH = 'E:/ctf/glance.gif'
FILE_NAME = [i for i in os.listdir(PATH)]
width = 0
for i in FILE_NAME:
    im.paste(Image.open(PATH+i), (width, 0, width+2, 600))  # box is 左,上,右,下
    width += 2
im.show()

6、像素点合成

注:Linux wc命令用于计算字数。

-l或–lines 显示行数。

-w或–words 只显示字数。

-c或–bytes或–chars 只显示Bytes数。

可以改个标题后用在线网站将txt转换为ppm文件

7、Image conbiner(两张图片)

两张图片可能有部分残缺(可以互补)

给了两张图片时,用Stegsolve.jar,打开其中一张,

然后再Analyze-Image conbiner打开另一张图片

还有可能是给了两张二维码,需要两个二维码每个像素亦或,直接用CTFD中的像素亦或脚本即可

8、OurSecret隐写

拉入OurSecret,输入密码解密,得到隐藏文件

9、拼图题

碎图片合成一张图片

1
2
3
#在Windows中使用imagemagick处理
magick.exe montage *.png -tile 18x10 -geometry 125x125+0+0 flag.jpg
magick montage *.png -tile 40x22 -geometry +0+0 flag-0.png
1
2
3
4
5
6
7
#在kali中处理
拉入kali里处理,如果是碎的图片,
先使用 montage *.PNG -tile 12x12 -geometry +0+0 out.png合成一张图片
*.png表示匹配所有图片
-tile表示图片的张数
-geometry +0+0表示每张图片的间距为0
合成后要先查看图片的宽高(宽高要相等,不相等要用PS调整)

然后把上面合成好的图片使用 Puzzle-Merak 工具进行智能拼图

imgs/puzzles1.png

imgs/puzzles2.png

这里只需要输入 generation、population、size 并用分号分开即可开始自动拼图

也可以使用gaps智能拼图(在kali和wsl里使用都可以)

1
2
3
4
5
6
7
8
gaps --image=out.png --generation=30 --population=144 --size=30 --save 

--image 指向拼图的路径
--size 拼图块的像素尺寸
--generations 遗传算法的代的数量
--population 个体数量
--verbose 每一代训练结束后展示最佳结果
--save 将拼图还原为图像
1
2
3
4
5
6
gaps --image=flag.jpg --generations=50 --population=180 --size=125 --verbose

-generations 你要迭代多少次
-population 你有多少个小拼图
--size 每张小图,也就是拼图小块的大小
--verbose 实时显示

10、提取图中等距像素点/近邻法缩放图片

参考链接:

https://www.bilibili.com/video/BV1Lf4y1r7dZ/?spm_id_from=333.999.0.0

https://github.com/Byxs20

例题-2024浙江省赛决赛-天命人

拿下面这张图片举个栗子

imgs/image-20241120171656554.png

方法一:直接在PS中将宽高都缩小为原来的十分之一,并选择邻近硬边缘即可直接得到隐藏的图片

imgs/image-20241120171835713.png

方法二:在windows的终端中运行CTFD中的Get_Pixels.py(注意所有路径中都不要出现中文)

1
2
3
4
py main.py -f arcaea.png -p 0x0+3828x2148 -n 12x12
py main.py -f 要解密的图片 -p 第一个像素点的XY坐标+最后一个像素点的XY坐标 -n 两个等距像素点的XY距离的差值
如果是等距离提取整张图片中所有像素点,要注意右下角那个点的位置XY都要减去一倍的距离
Tips:在PS中按F8就可以看到每个像素点的具体坐标了

一样可以得到隐藏的图片

imgs/image-20241120172043964.png

11、pixeljihad(有密码)

直接使用在线网站解密即可:PixelJihad (sekao.net)

12、隐写文本可能藏在原图片和隐写文件的中间

直接在010中搜索IEND,然后查看后面有没有额外内容即可

13、silenteye隐写

特征:放大图像后会有行列不对齐的小灰块

直接用 silenteye 打开输入密钥decode即可,默认密钥是 silenteye

14、图片报错改宽高后图片无变化,可以再 foremost 一下

15、DeEgger Embedder隐写

可以直接使用 DeEgger Embedder 工具 extract files

16、flag可能藏在 exif 中

直接在 WSL 中输入以下命令查看即可,如果偷懒也可以直接使用 破空 flag 查找工具 进行查找

1
exiftool 3.jpg

17、给了两张图片,flag藏在每行不同像素的个数中

例题1-2023羊城杯初赛-两支老虎

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from PIL import Image, ImageChops

img1 = Image.open("1.png")
width1,heigth1 = img1.size # 1134,720
img2 = Image.open("2.png") 
width2,heigth2 = img2.size # 1144,720
img2 = img2.crop((0,0,1134,720))
width2,heigth2 = img2.size
# img2.save("3.png")

diff_dit = {}
# 返回差异图像,表示 img1 和 img2 之间的像素差异。
diff = ImageChops.difference(img1,img2)
width3,heigth3 = diff.size
for x in range(width3):
    for y in range(heigth3):
        pixel3 = str(diff.getpixel((x,y)))
        # 统计一下差异像素
        if pixel3 not in diff_dit: 
            diff_dit[pixel3] = 0
        else:
            diff_dit[pixel3] += 1
print(diff_dit) 
# {'(0, 0, 0)': 813891, '(1, 1, 1)': 2533, '(1, 1, 0)': 53}

for y in range(heigth1):
    cnt = 0
    for x in range(width1):
        pixel1 = img1.getpixel((x,y))
        pixel2 = img2.getpixel((x,y))
        if pixel1 != pixel2:
            cnt += 1
    if cnt != 0:
        print(chr(cnt),end='')
# DASCTF{tWo_t1gers_rUn_f@st}

18、两张图片,用StegSolve中的Image Conbiner合成为一张bmp

imgs/image-20241106154249826.png

合成一张bmp后,再使用zsteg扫描

imgs/image-20241106154358962.png

19、图片多个通道存在LSB隐写,StegSolve中把背景颜色相同的勾选上

20、把小说藏进图片

参考链接:https://www.bilibili.com/video/BV1Ai4y1V7rg/?spm_id_from=333.999.0.0&vd_source=31399c09aa0c93655468bde7b13fcc03

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 脚本一
from PIL import Image

img = Image.open("1.bmp")
width,height = img.size # 1326 1326

res = ""
for y in range(height):
    for x in range(width):
        r,g,b = img.getpixel((x,y))
        data = (r << 8) + b
        res += chr(data)
    
with open("decode.txt","w") as f:
    f.write(res)
1
2
3
4
5
# 脚本二
from PIL import Image
from numpy import array
res = bytes(array(Image.open('1.bmp'))[:, :, ::2].flatten()).rstrip(b'\0').decode('utf-16-be')
print(res)

21、Arnold猫脸变换

参考链接:https://1cepeak.cn/post/arnold/

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import matplotlib.pyplot as plt
import cv2
import numpy as np
from PIL import Image

img = cv2.imread('flag.png')

def arnold_encode(image, shuffle_times, a, b):
    """ Arnold shuffle for rgb image
    Args:
        image: input original rgb image
        shuffle_times: how many times to shuffle
    Returns:
        Arnold encode image
    """
    # 1:创建新图像
    arnold_image = np.zeros(shape=image.shape)
    
    # 2:计算N
    h, w = image.shape[0], image.shape[1]
    N = h   # 或N=w
    
    # 3:遍历像素坐标变换
    for time in range(shuffle_times):
        for ori_x in range(h):
            for ori_y in range(w):
                # 按照公式坐标变换
                new_x = (1*ori_x + b*ori_y)% N
                new_y = (a*ori_x + (a*b+1)*ori_y) % N

                # 像素赋值
                # print(image[ori_x, ori_y, :])
                # print(arnold_image[new_x, new_y, :])
                arnold_image[new_x, new_y, :] = image[ori_x, ori_y, :]
        
        # 更新坐标
        image = np.copy(arnold_image)

    cv2.imwrite('flag_arnold_encode.png', arnold_image, [int(cv2.IMWRITE_PNG_COMPRESSION), 0])
    return arnold_image

def arnold_decode(image, shuffle_times, a, b):
    """ decode for rgb image that encoded by Arnold
    Args:
        image: rgb image encoded by Arnold
        shuffle_times: how many times to shuffle
    Returns:
        decode image
    """
    # 1:创建新图像
    decode_image = np.zeros(shape=image.shape)
    # 2:计算N
    h, w = image.shape[0], image.shape[1]
    N = h  # 或N=w

    # 3:遍历像素坐标变换
    for time in range(shuffle_times):
        for ori_x in range(h):
            for ori_y in range(w):
                # 按照公式坐标变换
                new_x = ((a * b + 1) * ori_x + (-b) * ori_y) % N
                new_y = ((-a) * ori_x + ori_y) % N
                decode_image[new_x, new_y, :] = image[ori_x, ori_y, :]

    cv2.imwrite('flag.png', decode_image, [int(cv2.IMWRITE_PNG_COMPRESSION), 0])
    return decode_image

# arnold_encode(img, 1, 2, 3)
arnold_decode(img, 1, 29294, 7302244)

PNG思路

1、CRC错误(不能乱改),改宽高,17~20是宽,21~24是高(可用Pictools脚本快速爆破)

2、LSB(最低有效位)隐写:

没有密钥的情况

1
2
3
4
# 用zsteg快速查看
zsteg -a (文件名)  #查看各个通道的lsb
-b的位数是从1开始的 zsteg zlib.bmp -b 1 -o xy -v
提取文件并导出 zsteg -e b1,r,lsb,xy 3.png > 123.jpg

信息藏在图片中有时候会看不出来,所以还是要用stegsolve.jar过一遍

有密钥的情况(cloacked-pixel)

lsb隐写的可能是加密后的数据,i春秋最喜欢的cloacked-pixel

拉到kali/WSL里用cloacked-pixel命令解密出数据

1
python2 cloacked-pixel-master/lsb.py extract 0.png out.data f78dcd383f1b574b

0.png是隐写后的图片;out.data是隐写内容保存的位置;f78dcd383f1b574b是密钥

3、LSB低位隐写

用CTFD中的脚本跑出隐藏的图片

4、IDAT块隐写

(1) 解压zlib获得原始数据

然后用010提取数据扔进zlib脚本解压获得原始数据

将异常的IDAT数据块斩头去尾之后使用脚本解压,在python2代码如下:

1
2
3
4
5
6
import zlib
import binascii
IDAT = "789C5D91011280400802BF04FFFF5C75294B5537738A21A27D1E49CFD17DB3937A92E7E603880A6D485100901FB0410153350DE83112EA2D51C54CE2E585B15A2FC78E8872F51C6FC1881882F93D372DEF78E665B0C36C529622A0A45588138833A170A2071DDCD18219DB8C0D465D8B6989719645ED9C11C36AE3ABDAEFCFC0ACF023E77C17C7897667".decode('hex')
result = binascii.hexlify(zlib.decompress(IDAT))
print (result.decode('hex'))
print (len(result.decode('hex')))

(2) 加上文件头爆破宽高得到新的图片

一般出问题的 IDAT Chunk 大小都是比正常的小的,很可能在图片末尾

如果不确定是哪一个有问题,可以尝试都提取出来,一个一个分析

可以使用 tweakpng 辅助分析,但是一般用 010 的模板提取分析就够了

我们可用 WSL 中的 pngcheck -v 0.png 检查 IDAT

如下图,最后一个和倒数第二个IDAT明显有问题,因此可以对这两部分进行尝试

imgs/image-20240724171411362.png

借助 010 的模板功能把IDAT块提取出来,加上文件头尾并爆破CRC即可得到另一张图片

imgs/image-20240724171723828.png

Tips:这里有时候也可以不用补文件尾

imgs/image-20240724171731445.png

把文件头尾补完整后直接CRC爆破一下即可

例题1-2023安洵杯-dacongのsecret

例题2-DASCTF2024 暑期挑战赛-png_master

5、png数据末尾藏zip

补上压缩包的文件头,然后提取出来,解压(可用stegpy得到解压密码)。

或者直接foremost提取

6、apngdis_gui

一张png图片还可能是apng,直接用apngdis_gui跑一下,可以分出两张相似的png

7、CVE-2023-28303 截图工具漏洞

一张图片如果有两个IEND块:AE 42 60 82

就很有可能考察的是这个漏洞

可以使用Github上大佬写好的工具一把梭,前提是需要知道原图的分辨率

8、stegpy隐写

stegpy 开源地址 下载好后直接用WSL输入以下命令并输入密码解密即可

也可以直接用 pip 安装: pip3 install stegpy

1
stegpy 1.png -p

JPG思路

1、可以试试用stegdectet看看是什么加密:

.\stegdetect.exe -t jopi -s 10.0 .\0.jpg

stegdectet

出现三颗星不一定就代表一定是这种加密方式

2、JPHS隐写

有可能会有密码

导出步骤 Select File –> seek –> demo.txt –> Save the file

3、steghide隐写

1
2
#如果密码已经知道了
steghide extract -sf filename -p passwd

在WSL或者kali里用Stegseek跑(字典在wordlist里)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#如果密码未知
可以用下面这个脚本爆破
#bruteStegHide.sh
#!/bin/bash

for line in `cat $2`;do
    steghide extract -sf $1 -p $line > /dev/null 2>&1
    if [[ $? -eq 0 ]];then
        echo 'password is: '$line
        exit
    fi
done
1
2
#或者在WSL或者kali里用Stegseek跑(字典在wordlist里)
stegseek filename rockyou.txt

4、outguess隐写

1
2
3
outguess -k "abc" -r mmm.jpg flag.txt
#-k 后面跟的是解密的密钥
#flag.txt是解密后数据保存的位置

5、F5-steganography-master

把要解密的图片拉到F5文件夹中

1
2
3
4
5
#有密码的情况
java Extract beautiful.jpg -p passwd
#无密码的情况
java Extract beautiful.jpg
#解密出来的数据会放到F5文件夹下的output.txt中

6、JPG宽高隐写

010打开JPG图片,找到 struct SOF 块数据,手动调整宽高即可

imgs/image-20240911103611924.png

BMP思路

1、bmp宽高爆破:

删除文件头,并保存为文件名.data,然后用GIMP打开修改宽高(这个比较方便)

或者直接用bmp爆破脚本跑 python script.py -f filename.bmp

1
#用这个脚本要注意对图片一个个使用
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import os
import time
import math
import argparse


parser = argparse.ArgumentParser()
parser.add_argument("-f", type=str, default=None, required=True,
                    help="输入同级目录下图片的名称")
args = parser.parse_args()

SAVE_DIR = os.getcwd()


def save_img(data, width=None, height=None, sqrt_num=None):
    with open(os.path.join(SAVE_DIR, "fix_width.bmp"), "wb") as f:
        f.write(data[:0x12] + width.to_bytes(4,
                byteorder="little", signed=False) + data[0x16:])

    with open(os.path.join(SAVE_DIR, "fix_height.bmp"), "wb") as f:
        f.write(data[:0x16] + height.to_bytes(4,
                byteorder="little", signed=False) + data[0x1a:])

    with open(os.path.join(SAVE_DIR, "fix_sqrt.bmp"), "wb") as f:
        f.write(data[:0x12] + sqrt_num.to_bytes(4,
                byteorder="little", signed=False) * 2 + data[0x1a:])


def get_pixels_size(data):
    bfSize = int.from_bytes(data[0x2:0x2+4], byteorder="little", signed=False)
    bfOffBits = int.from_bytes(
        data[0xa:0xa+4], byteorder="little", signed=False)
    biBitCount = int.from_bytes(
        data[0x1c:0x1c+2], byteorder="little", signed=False)
    channel = biBitCount // 8
    # 由于宽高都会被修改,所以我计算出来的Padding_size也不是正确的,没有意义
    # padding_size = (4 - col * channel % 4) * row if col * channel % 4 != 0 else 0
    # pixels_size = (bfSize - bfOffBits - padding_size) // channel
    return (bfSize - bfOffBits) // channel


if __name__ == '__main__':
    file_path = os.path.abspath(args.f)
    if os.path.splitext(args.f)[-1] != ".bmp":
        print("您的文件后缀名不为BMP!")
        time.sleep(1)
        exit(-1)

    with open(file_path, "rb") as f:
        data = f.read()
    col = abs(int.from_bytes(data[0x12:0x12+4],
              byteorder="little", signed=True))
    row = abs(int.from_bytes(data[0x16:0x16+4],
              byteorder="little", signed=True))
    pixels_size = get_pixels_size(data)

    width, height = pixels_size // row, pixels_size // col
    sqrt_num = int(math.sqrt((pixels_size)))
    save_img(data, width=width, height=height, sqrt_num=sqrt_num)

    print("温馨提示:由于填充字节的问题,所以可能会偏差几个像素!")
    print(f"1.修复宽度: {width}")
    print(f"2.修复高度: {height}")
    print(f"3.修复宽度高度为: {sqrt_num}")
    time.sleep(1)

2、wbStego4open隐写

用wbStego4open直接decode

3、silenteye隐写

直接拉入 silenteye 解密即可

GIF思路

1、分帧提取GIF(在线网站或者工具)

使用ffmpeg提取(如果帧间隔不同,提取出来会有问题)

1
2
# 在Windows或者WSL中执行以下命令进行分离
ffmpeg -i filename.gif frame%04d.png

使用PuzzleSolver提取,是按照帧来进行提取

imgs/image-20241111093328214.png

2、帧间隔隐写

直接使用PuzzleSolver一把梭了

例题1-2024羊城杯初赛-checkin

例题2-2024浙江省赛决赛-非黑即白

imgs/image-20241111093420194.png

或者使用以下命令提取帧间隔

1
identify -format "%s %T \n" 100.gif  	#格式:帧序号 间隔

Webp思路

webp文件用电脑自带的图片看可能会有点问题,建议用浏览器打开这种文件

webp可能是动图,可以用下面这个脚本分离webp中的每帧图片

1
2
3
4
5
6
7
from PIL import Image

img = Image.open('killer.webp')
n_frame = img.n_frames
for i in range(n_frame):
    img.seek(i)
    img.save(f'img/{i}.png')

RAW、ARW文件思路

1、RAW的LSB隐写

ARW文件是 Sony 相机的原始数据格式

可以使用 rawpy 模块读取图片的像素数据,查看是否存在LSB隐写【例:2024 L3HCTF RAWatermark】

示例脚本如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import rawpy
import numpy as np
import libnum

with rawpy.imread('image.ARW') as raw:
    # 从 raw 对象中获取可见的 Bayer 格式图像数据
    bayer_visible = raw.raw_image_visible
    # print(bayer_visible)
    # 用 bitwise_and() 函数将 bayer_visible 中的每个像素值与 1 进行按位与操作,以提取每个像素的最低有效位(LSB)
    lsb_array = np.bitwise_and(bayer_visible, 1)
    # print(lsb_array)
    # 使用 NumPy 数组的 flatten() 方法将 lsb_array 数组展平成一维数组
    lsb_array_flat = lsb_array.flatten()
    # print(lsb_array_flat)
    hidden_message = ''.join(map(str, lsb_array_flat))
    # 将隐写的数据转为十六进制,便于查看文件头
    hex_data = hex(int(hidden_message, 2))
    # print(hex_data[:10]) # 0x504b0304
    # 将二进制数据转换为byte类型数据
    data = libnum.b2s(hidden_message)

with open('flag.zip', 'wb') as f:
    f.write(data)

2、直接改后缀为.data,然后拖入Gimp即可

二维码思路

1、bmp转二维码

2、16进制转pyc

3、字符串制作二维码

1
2
3
直接右键使用B神的脚本制作二维码,制作前注意要把字符串的长度手动修正为平方数
1.0 1制作二维码
2.00 11制作二维码

4、四个TTL值转换一个字节的二进制数

5、Aztec code、DataMatrix、GridMatrix、汉信码、PDF417code等

我们平常见的最多的二维码就是QRcode,但是实际上还有很多不同类型的二维码,这里就简单举几个例子:

imgs/azteccode.gif

imgs/DataMatrix.png

imgs/GridMatrix.png

imgs/汉信码.png

imgs/PDF417code.png

这里要注意的是,出题人可能会把图片反相导致无法直接扫描,因此我们可以先将图片拉入 PS 先进行反相处理

QRcode 二维码的一些考点

详见我博客里的这篇文章

Misc——PDF题思路:

1、直接binwalk或者foremost解出隐藏文件

2、可能是wbStego4open隐写,用wbStego4open直接decode

3、PDF中可能携带了什么文件,可以在Firefox或者别的PDF软件中打开并提取

4、PDF中可能有透明的文字,直接全选复制然后粘贴到记事本中查看即可

5、DeEgger Embedder隐写

可以直接使用 DeEgger Embedder 工具 extract files

Misc——MS-Office题思路

Excel文件:.xls .xlsx

1、拉入010或者记事本,查找flag 2、取消隐藏先前隐藏的行和列 3、条件格式里设置突出显示某些单元格(黑白后可能会有图案) 4、要先将数据按照行列排序后再进行处理

Word文件:.doc .docx

1、直接foremost出隐藏文件

2、与宏有关系的各种攻击与隐写

分析word中的宏需要用到这样一个工具:oletools

这个工具直接在pip中安装即可使用: pip3 install oletools

doc格式可以不需要文档密码直接提取其中的vba宏代码

安装好oletools后直接运行以下代码提取即可,可能加密文档的加密算法就在期中

1
olevba .\attachment.doc > test.txt

3、利用行距来隐写(例:ISCC2023-汤姆历险记)

word中可能有一段是1倍行距,可能又有一段是1.5倍行距,需要根据不同行距敲出摩斯电码(单倍转为.多倍转为-空行转为空格或者分隔符)

Misc——txt题思路:

1、 有可能是ntfs,直接用NtfsStreamsEditor2扫描所在文件夹,然后导出可疑文件【如果是压缩包,一定要用winrar解压】

2、可能是wbStego4open隐写,用wbStego4open直接decode(可能有密钥)

3、如果是那种文件夹套文件夹的题目,可以直接把路径粘贴到everything中,让everything一把梭

4、无字天书(whitespace)&snow隐写

一个文件打开都是空白字符 可以使用在线网站解密:https://vii5ard.github.io/whitespace/ 复制进去直接run即可 snow隐写,到snowdos32工具目录下运行 SNOW.EXE -C -p password flag.txt 命令即可

5、垃圾邮件隐写(spammimic)

例题1-2024强网拟态初赛-PvZ

直接使用以下在线网站解密即可:

https://www.spammimic.com/

Misc——html题思路:

1、可能是wbStego4open隐写,用wbStego4open直接decode

Misc——压缩包思路:

Tips:压缩包的密码可以是中英文字符和符号

​没有思路时可以直接纯数字/字母暴力爆破一下

zip文件结构

三部分:压缩文件源数据区 + 压缩源文件目录区 + 压缩源文件目录结束标志

文件源数据区

HEX 数据 描述 010Editor 模板数据
50 4B 03 04 zip 文件头标记,看文本的话就是 PK 开头 char frSignature[4]
0A 00 解压文件所需 pkware 版本 ushort frVersion
00 00 全局方式位标记(有无加密),头文件标记后 2bytes ushort frFlags
00 00 压缩方式 enum COMPTYPE frCompression
E8 A6 最后修改文件时间 DOSTIME frFileTime
32 53 最后修改文件日期 DOSDATE frFileDate
0C 7E 7F D8 CRC-32 校验 uint frCrc

文件目录区

HEX 数据 描述 010Editor 模板数据
50 4B 01 02 目录中文件文件头标记 char deSignature[4]
3F 00 压缩使用的 pkware 版本 ushort deVersionMadeBy
0A 00 解压文件所需 pkware 版本 ushort deVersionToExtract
00 00 全局方式位标记(有无加密),目录文件标记后 4bytes ushort frFlags
00 00 压缩方式 enum COMPTYPE frCompression
E8 A6 最后修改文件时间 DOSTIME frFileTime
32 53 最后修改文件日期 DOSDATE frFileDate
0C 7E 7F D8 CRC-32 校验 uint frCrc

文件目录结束

50 4B 05 06 目录结束标记 char elSignature[4]
00 00 当前磁盘编号 ushort elDiskNumber
00 00 目录区开始磁盘编号 ushort elStartDiskNumber

常见报错及对应解决方法(借助010的模板功能)

  1. 该文件已损坏-源数据区和目录区的文件名长度被修改了

imgs/image-20240724172656435.png

  1. CRC校验错误-源数据区或目录区的压缩方法被修改了

imgs/image-20240724172708418.png

rar文件结构

HEX 数据 描述 010Editor 模板数据
52 61 72 21 1A 07 00 rar 文件头标记,文本为 Rar!

Main block

HEX 数据 描述 010Editor 模板数据
33 92 B5 E5 全部块的 CRC32 值 uint32 HEAD_CRC
0A 块大小 struct uleb128 HeadSize
01 块类型 struct uleb128 HeadType
05 阻止标志 struct uleb128 HeadFlag

File Header

HEX 数据 描述 010Editor 模板数据
43 06 35 17 单独块的 CRC32 值 uint32 HEAD_CRC
55 块大小 struct uleb128 HeadSize
02 块类型 struct uleb128 HeadType
03 阻止标志 struct uleb128 HeadFlag

Terminator

HEX 数据 描述 010Editor 模板数据
1D 77 56 51 固定的 CRC32 值 uint32 HEAD_CRC
03 块大小 struct uleb128 HeadSize
05 块类型 struct uleb128 HeadType
04 00 阻止标志 struct uleb128 HeadFlag

1、压缩包伪加密

zip文件:

可以直接用ZipCenOp.jar修复:

java -jar ZipCenOp.jar r screct.zip

WinRAR打开、010改标志位、binwalk直接分离

如果压缩文件已损坏,则尝试用winrar打开,工具-修复压缩包

压缩源文件数据区:7-8位表示有无加密

压缩源文件目录区:9-10位表示是否是伪加密

一般这俩地方都是09 00的,大概率就是伪加密了(直接把第二个PK后的09改了就行)

rar文件:

第24个字节尾数为4表示加密,0表示无加密,将尾数改为0即可破解伪加密

2、CRC爆破

适用于压缩包中文件比较小,比如只有几字节的时候

可以使用CTFD中的脚本爆破一下,注意有的脚本只能爆破zip压缩包

如果需要根据CRC值爆破明文的话可以参考这个项目:https://github.com/theonlypwner/crc32

1
python3 crc32.py reverse 0x7c2df918

例题1-BugKu MISC 就五层你能解开吗?

参考文章:https://blog.csdn.net/mochu7777777/article/details/110206427

3、明文攻击

已知所有的明文或三段密钥

使用Advanced Archive Password Recovery破解

有和压缩包中的一样(CRC值一样)的文件时,压缩然后用AAPR进行明文攻击,这个攻击的过程可能需要几分钟

有了完整的三段密钥也可以使用这个工具破解密码

使用pkcrack破解

1
2
#将pkcrack作为系统命令使用
cp pkcrack /usr/sbin/pkcrack
1
pkcrack -c "README.txt" -p README.txt -C flag.zip -P README.zip
1
2
3
4
-C:要破解的目标文件(含路径)
-c:破解文件中的明文文件的名字(其路径不包括系统路径,从zip文件一层开始)
-P:压缩后的明文文件
-p:压缩的明文文件中明文文件的名字(也就是readme.txt在readme.zip中的位置)

已知部分明文

利用bkcrack进行攻击

参考资料

1
2
https://www.freebuf.com/articles/network/255145.html
https://byxs20.github.io/posts/30731.html#%E6%80%BB%E7%BB%93

该利用方法的具体要求如下:

1
2
3
4
至少已知明文的12个字节及偏移,其中至少8字节需要连续。
明文对应的文件加密方式为ZipCrypto Store
Tips:进行明文攻击前要判断制作压缩包的压缩工具,然后对已知明文使用特定工具进行压缩,再进行明文攻击
例子:bkcrack -C \$R9EG7XR.zip -c flag.txt -k 958597ea b9f7740b 622aed5e -d flag.txt

如何判断压缩工具(参考自B神的博客)

压缩攻击 VersionMadeBy(压缩所用版本)
Bandzip 7.06 20
Windows自带 20
WinRAR 4.20 31
WinRAR 5.70 31
7-Zip 63

bkcrack常用参数

1
2
3
4
5
6
7
8
9
-c 要解密的文件
-P 已知明文所在的压缩包
-p 已知的明文部分
-x 压缩包内目标文件的偏移地址  部分已知明文值
-C 加密压缩包
-o offset  -p参数指定的明文在压缩包内目标文件的偏移量
-k 后面加破解出的三段密钥
-d 后面加解密后数据的保存位置
-U 修改压缩包密码并导出	bkcrack -C flag.zip -c hint.jpg -k afb9fee3 f8795353 f6de1d4e -U out.zip 114514

例题:

1
2
3
4
#Tips:
xxd // xxd 命令用于用二进制或十六进制显示文件的内容
-r // 把xxd的十六进制输出内容转换回原文件的二进制内容
-ps // 以 postscript的连续十六进制转储输出,这也叫做纯十六进制转储
1)简单的加密文本压缩包破解
1
flag{16e371fa-0555-47fc-b343-74f6754f6c01}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#攻击步骤如下:
#准备已知明文
echo -n "lag{16e3" > plain1.txt   #连续的8明文
echo -n "74f6" | xxd             #额外明文的十六进制格式,37346636
#攻击,-o是偏移量
bkcrack -C flag_360.zip -c flag.txt -p plain1.txt -o 1 -x 29 37346636
#由于时间较长,为防止终端终端导致破解中断,可以加点小技巧
bkcrack -C flag_360.zip -c flag.txt -p plain1.txt -o 1 -x 29 37346636 > 1.log& 
#后台运行,结果存入1.log
#加上time参数方便计算爆破时间
time bkcrack -C flag_360.zip -c flag.txt -p plain1.txt -o 1 -x 29 37346636 > 1.log&
#查看爆破进度
tail -f 1.log
#使用该秘钥进行解密:
bkcrack -C flag_360.zip -c flag.txt  -k b21e5df4 ab9a9430 8c336475 -d flag.txt
1
2
#-p 指定的明文不需要转换,-x 指定的明文需要转成十六进制
#提到的偏移都是指 “已知明文在加密前文件中的偏移”。
2)利用PNG图片文件头破解
1
2
3
4
5
6
#准备已知明文
echo 89504E470D0A1A0A0000000D49484452 | xxd -r -ps > png_header
#攻击
time bkcrack -C png4.zip -c 2.png -p png_header -o 0 >1.log&
tail -f 1.log
time bkcrack -C png4.zip -c flag.txt -k e0be8d5d 70bb3140 7e983fff -d flag.txt
3)利用压缩包格式破解
1
2
3
4
5
将一个名为flag.txt的文件打包成ZIP压缩包后,发现文件名称会出现在压缩包文件头中,且偏移固定为30。且默认情况下,flag.zip也会作为该压缩包的名称。
已知的明文片段有:
“flag.txt”  8个字节,偏移30
ZIP本身文件头:50 4B 03 04 ,4字节
满足12字节的要求
1
2
3
4
5
6
7
8
9
echo -n "flag.txt" > plain1.txt #-n参数避免换行,不然文件中会出现换行符,导致攻击失效
time bkcrack -C test5.zip -c flag.zip -p plain1.txt -o 30  -x 0 504B0304 >1.log&
tail -f 1.log
bkcrack -C test5.zip -c flag.zip -k b21e5df4 ab9a9430 8c336475  -d flag.zip
#但若想解密2.png,由于是ZipCrypto deflate加密的
#使用deflate算法压缩的文件,解码出来的是Deflate的数据流
#所以解密后需要bkcrack/tool内的inflate.py脚本再次处理
bkcrack -C test5.zip -c 2.png -k b21e5df4 ab9a9430 8c336475  -d 2.png
python3 inflate.py < 2.png > 2_out.png

Tips:如果这里用"XXXXX.txt"作为plaint1.txt无法破解出密钥,可以试试直接去掉后缀再作为plaint1.txt

例如:NKCTF2023——五年Misc,三年模拟

1
2
3
#echo -n "handsome.txt" > plain1.txt 破解失败
echo -n "handsome" > plain1.txt
time bkcrack -C test5.zip -c handsome.zip -p plain1.txt -o 30  -x 0 504B0304 >1.log&
4)EXE文件格式破解
1
2
EXE文件默认加密情况下,不太会以store方式被加密,但它文件格式中的的明文及其明显,长度足够。如果加密ZIP压缩包出现以store算法存储的EXE格式文件,很容易进行破解。
大部分exe中都有这相同一段,且偏移固定为64:

img

1
2
3
4
echo -n "0E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000" | xxd -r -ps > mingwen
time bkcrack -C nc64.zip -c nc64.exe -p mingwen -o64  >1.log&
tail -f 1.log
bkcrack -C nc64.zip -c nc64.exe -k b21e5df4 ab9a9430 8c336475  -d nc64.exe
5)流量包pcapng格式解密
1
2
3
echo -n "00004D3C2B1A01000000FFFFFFFFFFFFFFFF" | xxd -r -ps > pcap_plain1
time bkcrack -C 3.zip -c capture.pcapng -p pcap_plain1 -o 6
bkcrack -C 3.zip -c capture.pcapng  -k e33a580c  c0c96a81 1246d892  -d out.pcapng
6)网站相关文件破解
1
2
3
robots.txt的文件开头内容通常是User-agent: * 
html文件开头通常是 <!DOCTYPE html>
xml文件开头通常是<?xml version="1.0" encoding="UTF-8"?>
1
2
3
echo -n '<?xml version="1.0" encoding="UTF-8"?>' > xml_plain
time bkcrack -C xml.zip -c 123/web.xml -p xml_plain -o 0  //注意相对路径
bkcrack -C xml.zip -c 123/web.xml  -k e0be8d5d 70bb3140 7e983fff  -d web.xml
7)SVG文件格式破解
1
2
3
4
5
6
7
8
9
#SVG是一种基于XML的图像文件格式
echo -n '<?xml version="1.0" ' > plain.txt
bkcrack -C secrets.zip -c spiral.svg -p plain.txt -o 0
#解密 Store算法  直接解密即可
bkcrack -C secrets.zip -c spiral.svg -k c4038591 d5ff449d d3b0c696 -d spiral_deciphered.svg
#解密 deflate算法
bkcrack -C secrets.zip -c advice.jpg -k c4038591 d5ff449d d3b0c696 -d out.jpg
#该文件使用了deflate算法压缩的,解码出来的是Deflate的数据流,因此须将其解压缩。
python3 inflate.py < out.jpge > flag.jpg
8)VMDK文件格式破解
1
2
3
echo -n "4B444D560100000003000000" | xxd -r -ps > plain2
time bkcrack -C Easy_VMDK.zip -c flag.vmdk -p plain2 -o 0
time bkcrack -C Easy_VMDK.zip -c flag.vmdk -k xxx xxx xxx -d flag.vmdk

有时候直接给你部分明文的情况(2023 DASCTFxCBCTF)

直接在bkcrack中使用以下命令即可,key是题目给的压缩包中被压缩文件的部分明文

1
bkcrack -C purezip.zip -c 'secret key.zip' -p key

直接给了加密压缩包中部分文件的情况

例题1 - 2023 古剑山-幸运饼干

  • 可以先把该文件用压缩软件压缩成一个压缩包,然后用 Advanced Archive Password Recovery 明文攻击试试看

  • 用压缩软件把该文件压缩成一个压缩包,然后使用 bkcrack 进行明文攻击

    为什么需要压缩成压缩包呢?因为如果不带上压缩包进行明文攻击的话会报下面这个错误

    1
    2
    3
    
    $ bkcrack -C flag.zip -c 'hint.jpg' -p hint.jpg
    bkcrack 1.5.0 - 2023-03-08
    Data error: ciphertext is smaller than plaintext.

    用 -P 参数带上压缩包后即可正确解密出密钥

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    $ bkcrack -C flag.zip -c hint.jpg -p hint.jpg -P hint.zip
    bkcrack 1.5.0 - 2023-03-08
    [14:37:27] Z reduction using 25761 bytes of known plaintext
    100.0 % (25761 / 25761)
    [14:37:29] Attack on 289 Z values at index 21821
    Keys: afb9fee3 f8795353 f6de1d4e
    100.0 % (289 / 289)
    [14:37:29] Keys
    afb9fee3 f8795353 f6de1d4e

    因此这种情况一定要记得将已有的文件用适当的压缩方法压缩成压缩包,然后用-P参数带上这个压缩包

例题1 - 2023 铁三决赛-baby_jpg

我们先从部分伪加密的压缩包中分离出了 serect.pdf,然后从PDF中 foremost 出了加密压缩包中的 sha512.txt

将 sha512.txt 压缩成 sha512.zip,然后使用下面的命令进行明文攻击即可:

其中 -C 后是要破解的压缩包,-c 后是压缩包中我们要破解的文件,-P 后是我们压缩好的压缩包,-p 后是我们已得的文件

1
2
3
4
5
6
7
8
9
$ bkcrack -C 00000218.zip -c 'sha512.txt' -P sha512.zip -p sha512.txt
bkcrack 1.5.0 - 2023-03-08
[16:14:25] Z reduction using 78 bytes of known plaintext
100.0 % (78 / 78)
[16:14:25] Attack on 104916 Z values at index 6
Keys: ed3fb6a9 1c4a7211 c07461ed
59.9 % (62867 / 104916)
[16:14:52] Keys
ed3fb6a9 1c4a7211 c07461ed

破解出密钥后,用 -U 参数修改压缩包密码并导出

1
2
3
4
5
$ bkcrack -C 00000218.zip -k ed3fb6a9 1c4a7211 c07461ed -U out.zip 111
bkcrack 1.5.0 - 2023-03-08
[16:15:44] Writing unlocked archive out.zip with password "111"
100.0 % (3 / 3)
Wrote unlocked archive.

在比赛中的使用记录

2022 西湖论剑zipeasy

1
bkcrack -C zipeasy.zip -c dasflow.zip -x 30 646173666c6f772e706361706e67 -x 0 504B0304 > 1.log &

2023 DASCTFxCBCTF

利用bkcrack反向爆破密钥

1
2
3
4
5
6
7
bkcrack -k e48d3828 5b7223cc 71851fb0 -r 3 \?b
#bkcrack 1.5.0 - 2023-03-08
#[17:47:50] Recovering password
#length 0-6...
#[17:47:50] Password
#as bytes: 8b e7 dc
#as text: ���

然后如果要对得到的密钥进行MD5加密,可以使用CyberChef(From Hex + MD5)

imgs/MD5.png

Tips:题目做不出来可以尝试多换几个压缩软件:Bandzip、Winrar、7zip、360压缩、2345压缩等

4、暴力破解(爆破时注意限制长度)

可以使用 Advanced Archive Password Recovery 进行爆破

(1) 如果知道部分的密码,可以使用掩码攻击,例如:????LiHua

(2) 没啥思路的时候可以直接用纯数字密码爆破看看,也可以用字典爆破

(3) 如果爆破的速度很慢,可以用 Passware Kit Forensic 2021 v1 (64-bit) 来爆破(也可以自定义字典)

5、连环套压缩包

可以用fcrackzip进行爆破或者使用CTFD中的脚本爆破

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import zipfile
import re
file_name = 'pic/' + 'f932f55b83fa493ab024390071020088.zip'
while True:
  try:
     zf = zipfile.ZipFile(file_name)
     re_result = re.search('[0-9]*', zf.namelist()[0])
     passwd = re_result.group()
     zf.extractall(path='pic/', pwd=re_result.group().encode('ascii'))
     file_name = 'pic/' + zf.namelist()[0]
  except:
     print("get the result")

6、未知后缀的压缩包

可以多用几个压缩软件试试,比如Winrar 7z

7、分卷压缩包合并

1
copy /B topic.zip.001 + topic.zip.002+topic.zip.003+topic.zip.004+topic.zip.005+topic.zip.006 topic.zip

8、压缩包炸弹

很小的压缩文件,解压出来会占据巨大的空间,甚至撑爆磁盘

处理方法:010中直接编辑压缩包文件,看看是否藏有另一个压缩包

9、根据010中的模板修改了某些参数

有些题目可能会修改源数据中压缩包文件中被压缩文件的文件名的长度

源数据中被压缩文件名字的长度对不上也会导致解压后文件无法打开

所以…010的模板功能真的非常非常的好用!

imgs/010.png

10、压缩包密码是不可见字符

字节数很短的情况

直接写个Python脚本爆破即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import zipfile
import libnum

def solve():
    # 在ASCII编码中,一个字符占用8位(1字节)
    for i in range(256):
        for j in range(256):
            fz = zipfile.ZipFile('secret key.zip', 'r')
            password = libnum.n2s(i) + libnum.n2s(j)
            print(f"[+]正在尝试密码{password}")
            try:
                fz.extractall(pwd=password)
                fz.close()
                return password
            except:
                fz.close()
                continue
    return None

if __name__ == "__main__":
    password = solve()
    if password:
        print(f"[+]压缩包解压成功,密码是{password}")
    else:
        print(f"[+]在该范围内找不到压缩包密码,压缩包解压失败")

字节数较长的情况

需要先把密码base64编码一下,然后再base64解码成byte类型作为密码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import base64
import pyzipper

target_zip = '1.zip'
outfile = './solved'

pwd = base64.b64decode(b'aEXigItjVOKAjEbigI8=')
# b'hE\xe2\x80\x8bcT\xe2\x80\x8cF\xe2\x80\x8f'
with pyzipper.AESZipFile(target_zip, 'r') as f:
    f.pwd = pwd
    f.extractall(outfile)

Misc——视频题思路:

1、可能有音频隐写,用mkvtool分离出音频,再拉入Au看频谱图

2、可能是视频中的每一帧图片都有LSB隐写(2023 WMCTF)

3、循环读取视频每一帧图像中指定列的指定像素(2023 极客大挑战)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import cv2
from PIL import Image

# 创建一个视频读取对象,读取名为'kira.mp4'的视频文件。
video = cv2.VideoCapture('kira.mp4')  # type: ignore

# # 设置要提取的帧数,如现在指定的是第100帧
# video.set(cv2.CAP_PROP_POS_FRAMES, 100)
# # 读取视频的指定帧
# ret, frame = video.read()
# # 保存提取的帧为图像文件
# cv2.imwrite('1.png', frame)
# # 释放视频对象
# video.release()

# 定义视频的尺寸为1920x1080
video_size = [1920, 1080]
# 设置起始像素为5
start_pixel = 5
# 设置每个像素块的大小为10
size = 10
# 创建一个新的图像对象,大小为视频尺寸除以像素块大小,即原视频的帧的抽样结果
out = Image.new('RGB', (video_size[0] // size, video_size[1]//size))
# 初始化帧率计数为0
fps_count = 0
# 循环读取每一帧图像中指定列的指定像素
while True:
    print(f"[+] 当前正在读取视频的第{fps_count}帧")
    # 从视频文件中读取一帧,success为是否成功读取帧的结果,frame为读取的帧
    success, frame = video.read()
    # 如果读取失败,跳出循环
    if not success:
        print(f"[X] 视频的第{fps_count}帧读取失败")
        break
    # 对每一行像素进行遍历,从视频的高度减去起始像素并除以像素块大小,得到需要遍历的行数
    for y in range((video_size[1]-start_pixel)//size):
        try:
            # 从当前行中获取一个像素,使用getpixel方法获取指定坐标处的像素,并将其转换为PIL图像格式
            pixel = Image.fromarray(frame).getpixel(
                (start_pixel+fps_count*size, start_pixel+y*size))
            # 将获取的像素值设置为抽样图像的对应像素位置的值
            out.putpixel((fps_count, y), pixel)
        except:
            pass
    # 帧率计数加1,准备下一帧的处理
    fps_count += 1

# 将抽样图像保存为'out.png'文件
out.save('out.png')
out.show()

4、DeEgger Embedder隐写

可以直接使用 DeEgger Embedder 工具 extract files

例题-攻防世界 PyHaHa

Misc——音频题思路:

1、波形图分析:摩斯电码

2、频谱图分析(有时要调高最高频率):

3、LSB(最低有效位隐写):用silenteye解密

4、SSTV慢扫描电视:

SSTV识别可以直接用这个项目里的脚本:https://github.com/colaclanth/sstv

1
2
3
4
5
# 安装步骤:
sudo python3 setup.py install 
# 注意python版本如果低于python3.9,需要将setup.py中的numpy那一行注释
# 使用命令如下,图片会自动命名为result.png
sstv -d flag.wav

imgs/image-20241108232143418.png

Windows中使用RX-SSTV

使用前还要安装虚拟声卡 Virtual Audio Cable

1
2
3
4
5
#使用步骤:
1.点击Setup-Sound Control and Devices将默认输入设备和输出设备都设置为虚拟声卡line1
2.用VLC播放音频(最好不要用Au播放)
3.如果扫描出来的图片有错位,可以点击slant手动修改
4.退出RX-SSTV前要注意把默认的输入/输出设备改回原来的参数

拉入kali用qsstv(有时候要用到反向和反相)

5、电话音分析

用在线网站:http://www.dialabc.com/sound/detect/

或者在dtmf2num.exe里使用dtmf2num -o C:\Desktop\1.wav命令

6、WAV[RIFF]的隐写(有 deepsound 和 silenteye 或者其他):

先用deepsound 试一下,如果需要密码说明就是 deepsound 隐写

如果是deepsound隐写,就先用脚本获取wav文件的哈希值(注释里有使用方法),

然后拉入kali用john爆破hash(如果编码有误,可以先用notepad另存为一下)

执行:john 1.txt

7、wav可能是业余无线电文件:

先用sox把wav转为raw:

sox -t wav latlong.wav -esigned-integer -b16 -r 22050 -t raw latlong.raw

再用multimon-ng分析:

multimon-ng -t raw -a AFSK1200 latlong.raw

8、steghide

1
2
#如果密码已经知道了
steghide extract -sf filename -p passwd

在WSL或者kali里用Stegseek跑(字典在wordlist里)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#如果密码未知
可以用下面这个脚本爆破
#bruteStegHide.sh
#!/bin/bash

for line in `cat $2`;do
    steghide extract -sf $1 -p $line > /dev/null 2>&1
    if [[ $? -eq 0 ]];then
        echo 'password is: '$line
        exit
    fi
done
1
2
#或者在WSL或者kali里用Stegseek跑(字典在wordlist里)
stegseek filename rockyou.txt

9、MP3音频隐写

MP3stego

使用前需要先把要处理的文件放到MP3stego目录下

1
2
3
4
5
6
7
8
#Encode
encode -E data.txt -P pass sound.wav sound.mp3    
data.txt里面放要隐写的txt信息 pass是解密时需要的密码
#Decode
decode -X -P pass sound.mp3   
-X 是提取出隐写的文件
pass是解密时需要的密码 
sound.mp3是待处理的MP3文件

10、WAV还可能是OpenPuff隐写(有密码)

直接用OpenPuff.exe解密即可

11、提取并分析左右声道的差值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 导入模块wavfile
import scipy.io.wavfile as wavfile
# 读取音频文件的采样率和数据
sample_rate, data = wavfile.read("1.wav")
# print(sample_rate, data)
# 创建两个列表来存储左右两声道的数据
left = []
right = []

for item in data:
    # print(item)
    # 第一列的数据是左声道,第二列是右声道
    left.append(item[0])
    right.append(item[1])

diff = [str(left-right) for left, right in zip(left, right)]
res = ''
for item in diff:
    if item == '2':
        res += '1'
    elif item == '1':
        res += '0'
    else:
        continue
with open('res.txt', 'w') as f:
    f.write(res)

12、使用脚本提取数据进行分析

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# 2023 DASCTFxCBCTF
import numpy as np
import wave
import scipy.fftpack as fftpack

SAMPLE_RATE = 44100 # 表示采样率,即每秒钟有多少采样点
SAMPLE_TIME = 0.1 # 表示一个样本的时间,即0.1秒
SAMPLE_NUM = int(SAMPLE_RATE * SAMPLE_TIME) # 计算在SAMPLE_TIME时间内的采样点数
LIST = [800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700]   


def get_len():
    with wave.open('1.wav','rb') as f:
        # 使用numpy从音频文件中读取所有的帧并将其转换为int16数据类型的数组
        wav_data = np.frombuffer(f.readframes(-1),dtype=np.int16)
        N = len(wav_data)
        print(N)
    #这实际上计算了wav文件的总时长(以0.1秒为单位)
    a = (N/(44100*0.1)) / 189
    print(a)

# 傅立叶变换函数。给定时域数据,该函数返回其频域形式的前半部分
def fft(data):
    N = len(data)                                   #获取数据长度
    fft_data = fftpack.fft(data)                    #得到频域信号                      
    abs_fft = np.abs(fft_data)                      #计算幅值    
    abs_fft = abs_fft/(N/2)                             
    half_fft = abs_fft[range(N//2)]                 #取频域信号的前半部分

    return half_fft

# 此函数旨在解码100ms的音频数据。它首先对音频数据进行FFT变换,然后检查LIST中的每个频率,以确定哪些频率具有明显的活动(幅值大于0.8)  
def dec_100ms(wave_data_100_ms):
    fft_ret = fft(wave_data_100_ms)
    for index, freq in enumerate(LIST):
        if np.max(fft_ret[int(freq*SAMPLE_TIME) - 2 : int(freq*SAMPLE_TIME) + 2]) > 0.8:
            print(freq, 'Hz有值',end=" ")
            return index

# 解码整个音频文件中的句子。它首先确定音频中有多少个100ms的段,然后每次解码两个段来生成一个两位数的索引,该索引用于查找与之对应的字符
def dec_sentence(wav_data):
    _100ms_count = len(wav_data) // SAMPLE_NUM    
    # print(_100ms_count) 
    print('待解码音频包含', _100ms_count // 2, '个字')    
    ret = ''
    for i in range(0, _100ms_count, 2):              
        index = 0
        for k in range(2):
            index = index*10 + dec_100ms(wav_data[i*SAMPLE_NUM + k*SAMPLE_NUM : i*SAMPLE_NUM + (k+1)*SAMPLE_NUM])
        print('序号:', index)
        ret += string[index]
    return ret

if __name__ == '__main__':
    # get_len()
    # 题目给了一个字符串序列,所以就是从音频中提取出index,然后根据index找到对应的字符
    string ="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_}{-?!"
    with wave.open('1.wav', 'rb') as f:          #读取为数组
        wav_data = np.frombuffer(f.readframes(-1), dtype=np.int16)
    print(dec_sentence(wav_data))
    # DASCTF{Wh1stling_t0_Convey_informat1on!!!}

13、stegpy隐写

stegpy 开源地址 下载好后直接用WSL输入以下命令并输入密码解密即可

也可以直接用 pip 安装: pip3 install stegpy

1
stegpy 1.wav -p

14、DeEgger Embedder隐写

可以直接使用 DeEgger Embedder 工具 extract files

15、Silenteye隐写

音频文件也可能是 silenteye 隐写,可以拿默认密码 silenteye 解密试试看

Misc——取证题思路:

详解请查看我的另一篇 博客Misc——取证类题目详解

Git文件泄露:

1、利用命令git stash show 显示做了哪些改动

2、利用命令git stash apply导出改动之前的文件

OSINT

1.用yandex识图

Others:

字节序

字节的排列方式有两个通用规则:

1
2
大端序(Big-Endian)将数据的低位字节存放在内存的高位地址,高位字节存放在低位地址。这种排列方式与数据用字节表示时的书写顺序一致,符合人类的阅读习惯。
小端序(Little-Endian),将一个多位数的低位放在较小的地址处,高位放在较大的地址处,则称小端序。小端序与人类的阅读习惯相反,但更符合计算机读取内存的方式,因为CPU读取内存中的数据时,是从低地址向高地址方向进行读取的。

例子:

1
2
3
4
5
6
整型数值168496141 需要4个字节
对应的16进制表示是0X0A0B0C0D
大端序:
0x0A 0x0B 0x0C 0x0D
小端序:
0x0D 0x0C 0xB 0xA

为何要有字节序

1
因为计算机电路先处理低位字节,效率比较高,因为计算都是从低位开始的。所以,计算机的内部处理都是小端字节序。在计算机内部,小端序被广泛应用于现代 CPU 内部存储数据;而在其他场景,比如网络传输和文件存储则使用大端序。

使用Python中的struct模块来处理大小端序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import struct

def display_binary(data):
    #将字节数据转化为十六进制表示形式
    # return ' '.join(['{:02x}'.format(byte) for byte in data])
    return ' '.join([f"{byte:02x}" for byte in data])

# 定义要打包的数据
int_data = 10240099
float_data = 123.456

# 使用默认端序(小端序)打包
packed_int_little = struct.pack('I', int_data)
packed_float_little = struct.pack('f', float_data)

# 使用大端序打包
packed_int_big = struct.pack('>I', int_data)
packed_float_big = struct.pack('>f', float_data)

# 打印打包的结果,display_binary()是以十六进制的形式显示
print("Packed data (Little Endian):")
print(packed_int_little)
print("Int:", display_binary(packed_int_little))
print(packed_float_little)
print("Float:", display_binary(packed_float_little))

print("\nPacked data (Big Endian):")
print(packed_int_big)
print("Int:", display_binary(packed_int_big))
print(packed_float_big)
print("Float:", display_binary(packed_float_big))

# 解包数据,由于返回的是一个元组,所以需要[0]
unpacked_int_little = struct.unpack('I', packed_int_little)[0]
unpacked_float_little = struct.unpack('f', packed_float_little)[0]

unpacked_int_big = struct.unpack('>I', packed_int_big)[0]
unpacked_float_big = struct.unpack('>f', packed_float_big)[0]

# 打印解包的结果
print("\nUnpacked data (Little Endian):")
print("Int:", unpacked_int_little)
print("Float:", unpacked_float_little)

print("\nUnpacked data (Big Endian):")
print("Int:", unpacked_int_big)
print("Float:", unpacked_float_big)

# 验证打包和解包是否保持数据的完整性(float类型的数据先打包再解包后可能会有误差)
assert int_data == unpacked_int_little
# assert float_data == unpacked_float_little

assert int_data == unpacked_int_big
# assert float_data == unpacked_float_big

print("\nData integrity maintained!")

十六进制数据大小端序转换

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
hex_data = """0x00006c66 0x00006761 0x0000617b 0x00006168 0x00005f21 0x00006f79 0x00005f75 0x00006f66 0x00006e75 0x00005f64 0x00007469 0x00007d21 0x00000000 """

def swap_endianness(hex_string):
    hex_bytes = bytes.fromhex(hex_string[2:])
    # 直接使用 bytes 类型的数据翻转即可
    swapped_bytes = hex_bytes[::-1]
    swapped_hex = swapped_bytes.hex()
    swapped_hex = '0x' + swapped_hex
    return swapped_hex


def solved():
    flag = ""
    # hex_data = input("请输入待转换的数据\n")
    hex_list = hex_data.split()
    for hex_num in hex_list:
        swapped_hex = swap_endianness(hex_num)
        print(swapped_hex)
        flag += bytes.fromhex(swapped_hex[2:]).decode()
    print(flag)


if __name__ == "__main__":
    solved()

Linux tar命令

打包压缩

1
2
3
4
5
6
7
8
9
# 打包单独的文件
tar -cvf target.tar filename.txt
# 打包整个目录
tar -cvf target.tar directory
# -c 表示创建新的tar包
# -v 表示显示详细信息
# -f 表示指定目标文件名
# 如果是.tar.gz,就用下面这个命令
tar -czvf out.tar.gz ./*

解压提取

1
2
3
4
5
6
#把压缩包中的所有文件解压到当前目录
tar -xvf target.tar
#把压缩包解压到指定目录
tar -xvf target.tar -C path
# 如果是.tar.gz,就用下面这个命令
tar -xzvf file.tar.gz

pyc隐写

使用开源工具:https://github.com/AngelKitty/stegosaurus

对隐写的内容进行提取即可

imgs/image-20241113181609346.png

0%