2023古剑山

misc

数独(gaps拼图)

给了一个image.png和sudoku.png 先把数独解了一下 得到下面的结果

image-20240131180350976

但是不知道有什么用 再看一下image.png 需要重新拼一下图 就用gaps就行了

原图像的大小是630x630 小图片是9x9 因此使用的size参数就是70

gaps --image=image.png --generations=50 --populations=200 --size=70 --save 

image_solution1

这里我当时是以为数独有什么用 把解好的数独都写在原图片之后再拼的 实际上我们拼完之后直接按照从上到下 从左到右的顺序读flag就行

幸运饼干(DPAPI技术 Chrome数据加密)

附件是一个hint.jpg和flag.zip flag.zip里面有个和hint.jpg大小一样的文件 直接明文攻击 先将hint.jpg压缩为hint.zip 在ARCHPR里面进行明文攻击

image-20240131211749661

得到密码

sv@1v3z

解压压缩包 得到一个压缩包 一个admin.txt和Cookie文件 先看一下Cookie文件格式 用file命令 是个sql数据库文件

image-20240131223747841

admin.txt里面是使用mimikatz对计算机进行分析的记录 看到里面有一个NTML密码

image-20240131224435108

解一下 密码是54231

image-20240131231126598

先看一下前置知识

DPAPI,Data Protection Application Programming Interface,是Windows系统的一个数据保护接口,它本质上使用了Windows通过用户自己登录(sids,登录密码等),以及域登录后的一些数据生成的密钥,并且使用内置的算法,对用户指定的数据进行加密。通常Windows上的浏览器历史数据,邮件加密,wifi密码等等都会以这种方式进行数据加密。

Chrome使用DPAPI保存了我们的登录密码和cookie值

用户使用Chrome访问网站进行登录时,可以选择是否保存密码。当选择保存密码时,Chrome先将密码进行加密,再保存在SQLite数据库文件中,数据库文件路径位于:

%LocalAppData%\Google\Chrome\User Data\Default\Login Data

同上,保存Cookie时,数据库文件路径为:

%LocalAppData%\Google\Chrome\User Data\Default\Cookies

存储的cookie值被加密为DPAPI blob来进行保护;我们可以通过使用Mimikatz这个工具来对Chrome中的SQLite数据库进行解析:

mimikatz dpapi::chrome /in:'%LocalAppData%\Google\Chrome\User Data\Default\Cookies'

解密DPAPI blob用到的相关概念:

DPAPI blob:一段密文,可以使用Master Key对其解密

Master Key:64字节,用于解密DPAPI blob;使用用户登录密码、SID和16字节随机数加密后保存在Master Key file中

Master Key file:二进制文件,可以使用用户登录密码对其解密,得到Master Key

对采用DPAPI技术加密的数据进行解密,需要获取当前操作系统登录用户对应的 Master Key,而获取 MasterKey 需要知道用户名、密码以及对应的SID,然后利用这些数据生成一个 blob 加密过程中使用的 MasterKey,从而对目标blob进行解密

大致的过程是

dpapi::masterkey /in:{masterkeyfile} /sid:{sid} /password:{password} /protected

得到masterkey之后 使用masterkey(原key值或者sha1加密值都可以)解密Cookies/logindata

dpapi::chrome /in:{Cookies} /masterkey:{masterkey} /unprotect

这样就可以得到Cookies的加密数据的hex值或者直接得到明文

toto✌🏻的这篇文章写的很全面 狠狠膜拜了

电子取证中Chrome各版本解密Cookies、LoginData账号密码、历史记录

我们用sqlcipher打开Cooike这个sql数据库文件 看到有一个encrypted_value 提示是blob 那就是一个利用DPAPI技术加密的Chrome数据

image-20240201001135197

我们结合前置知识 现在我们有了用户名 密码 在flag.zip解压后的文件里面还有一个压缩包 文件名就是SID 压缩包里面的文件应该就是masterkey文件

S-1-5-21-726299542-2485387390-1117163988-1001

有了这些我们就可以得到masterkey

dpapi::masterkey /in:.\S-1-5-21-726299542-2485387390-1117163988-1001\e5f8e386-7041-4f16-b02d-304c71040126 /sid:S-1-5-21-726299542-2485387390-1117163988-1001 /password:54231 /protected 

这个地方遇到了几个问题 一是Cookies文件解压之后在文件夹里面是看不到的 但是用vscode打开文件夹作为工作区是可以看到的 二是注意一下最好是将文件夹或者Cookies文件直接放在mimikatz程序的文件夹中 放在其他位置有可能不出结果

image-20240201195817441

下面就是用masterkey解密Cookies文件

dpapi::chrome /in:.\Cookies /masterkey:7a4d2ffbb42d0a1ab46f0351260aef16cae699e03e9d6514b3bf10e2977c5d228fda4a48e39b7b8a06a443c39653c2a3c3656596e7edc84e1c9682511c8343ac /unprotect

直接得到flag

image-20240201195956233

另 在学习姿势的时候 还看到了一些将Cookies识别分割为数据库的方法

binwalk Cookies
dd if=Cookies bs=1 skip=0 of=1.db

image-20240201201726904

同类型的题:[红明谷CTF 2022]MissingFile

我们再来看一下这道类似的题 主要是学习一下面对这种题的思路和解题步骤

附件是一个内存镜像文件memory 题目描述为

好像被攻击者入侵了,但是赶到现场的时候,已经只剩下一个空的文件夹了,快照能找到攻击者留下的秘密吗?

首先看一下被入侵的痕迹

.\volatility.exe -f E:\Desktop\比赛\古剑山\misc\[红明谷CTF2022]MissingFile\memory --profile=Win7SP1x86_23418 hashdump

可以看到有一个link3用户 还有一个NewGuest用户 既然是被入侵 那NewGuest应该就是攻击者入侵之后创建的账户

image-20240201203208646

NewGuest用户的密码还是个可破解的哈希值 123456

image-20240201205917974

再看一下攻击用户进行的文件操作 看到使用了mimikatz的记录

.\volatility.exe -f E:\Desktop\比赛\古剑山\misc\[红明谷CTF2022]MissingFile\memory --profile=Win7SP1x86_23418 filescan | findstr "NewGuest"

image-20240201203152737

使用 mftparser 对内存中的 MFT 条目进行分析,并将结果保存至 mftparser.txt

MFT(Master File Table)是NTFS文件系统中的一个重要概念,它是用来存储文件和目录元数据的数据结构。MFT条目(MFT entry)是MFT中的一个记录,每个文件或目录在MFT中都有一个对应的MFT条目。

每个MFT条目包含了文件或目录的元数据信息,如文件名、文件大小、创建时间、修改时间等。MFT条目还包含了指向文件数据的指针,以及其他一些属性信息。

MFT条目的结构可以分为两部分:固定长度部分和可变长度部分。固定长度部分包含了MFT条目的基本信息,如文件类型、标志位等。可变长度部分则包含了文件属性信息,如文件名、时间戳等。

MFT条目在NTFS文件系统中起着非常重要的作用,它记录了文件和目录的元数据信息,使得文件系统能够有效地管理和访问文件。通过MFT条目,文件系统可以快速定位到文件的位置和属性信息,从而实现对文件的读取和操作。
.\volatility.exe -f E:\Desktop\比赛\古剑山\misc\[红明谷CTF2022]MissingFile\memory --profile=Win7SP1x86_23418 mftparser >mftparser.txt

在扫描文件的时候 找到了一个Hacker文件夹 我们在mftparser.txt里面继续找这个文件夹 找到了里面的S3cret文件 将其进行保存

image-20240201203830998

with  open('S3cert.txt','r') as f:
    data = f.readlines()
    for line in data:
        new_line = line[12:60:1]
        print(new_line)

image-20240201205733741

我们使用mimikatz检查一下S3cert文件

privilege::debug 
dpapi::blob /in:./S3cert

image-20240201210221496

找到masterkey的guid 在mftparser.txt里面找一下这个guid

470a5148-d8c9-4453-bf41-f0c09d158bfd

找到了masterkey文件

image-20240201210539783

一样 将这个masterkey文件导出来 命名为470a5148-d8c9-4453-bf41-f0c09d158bfd

下面就和幸运饼干的步骤一样了 先得到masterkey

dpapi::masterkey /in:.\470a5148-d8c9-4453-bf41-f0c09d158bfd /sid:S-1-5-21-206512979-2006505507-2644814589-1001 /password:123456 /protected

image-20240201211142048

092c4220064c30bc7f8b15d2d48957c4926af0632149b9c08cd87f34fc43aa1204d775bdc6ab429a0d4d0826fb80b08250b125d92913e2f7578cf778073bfe38

在解密S3cert文件

dpapi::blob /in:.\S3cert /masterkey:092c4220064c30bc7f8b15d2d48957c4926af0632149b9c08cd87f34fc43aa1204d775bdc6ab429a0d4d0826fb80b08250b125d92913e2f7578cf778073bfe38 /unprotect

得到16进制的明文

image-20240201214002735

解一下

image-20240201214035675

i_have_the_flag

附件是一个js文件还有一个html文件 看一下html文件

image-20240201201933126

随便测试一下 发现输入不同错误内容的时候 回显的内容不一样 也不像base64编码 那就转向分析js文件 看到一段关键代码

function ck(s) {
    try {
        ic
    } catch (e) {
        return;
    }
    var a = [118, 108, 112, 115, 111, 104, 104, 103, 120, 52, 53, 54];
    if (s.length == a.length) {
        for (i = 0; i < s.length; i++) {
            if (a[i] - s.charCodeAt(i) != 3)
                return ic = false;
        }
        return ic = true;
    }
    return ic = false;
}
  • 该函数的目的是检查输入字符串是否满足特定条件,即字符串的每个字符的 ASCII 值与数组中对应位置的数字相差 3。
  • 如果满足条件,函数返回 true,否则返回 false

很简单 我们直接将上面数组a的ascii码全部减三 在解ascii码就可以得到正确的key了

image-20240201235958771

simpleedu123

输入 就可以得到正确的值了 就是flag

image-20240202000046406

jpginside

附件是jpginside.xxx这样一个文件 不知道是什么类型的 用file命令看一下

是一个pyc文件 在线反编译一下

store = [
    141,
    183,
    139,
    129,
    116,
    117,
    .....
    49,
    50]
key = raw_input('Please input the key:')
with open('excellent.jpg', 'wb') as jpg:
    for i in range(len(store)):
        jpg.write(chr(store[i] ^ ord(key[i % len(key)])))

先脚本恢复key 在上面的加密脚本里面用key加密的方式是对key的每一位循环使用 那我们只要取一部分明文和jpg图片格式里面相同的部分就可以恢复密钥 通过观察jpg图片结构 我们可以发现jpg的前13位都是一样的 那我们就选取这部分恢复我们的密钥

image-20240202234745688

encrypted_data = [0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01]

# 已知的加密后的数据
store = [141, 183, 139, 129, 116, 117, 123, 116, 122, 114, 33, 115, 110]

# 恢复密钥的过程
key = ""
for i in range(len(store)):
    original_byte = store[i] ^ encrypted_data[i]
    key += chr(original_byte)

print("Recovered key:", key)

得到结果是

rotate1234!ro

那按照前面说的密钥是按位循环使用 我们就取前11位就是完整密钥

rotate1234!

有了密钥我们直接利用上面的加密脚本 得到exllcent.jpg

store = [
    141,
    183,
    139,
    129,
    116,
    117,
    .....
    49,
    50]
key = rotate1234!
with open('excellent.jpg', 'wb') as jpg:
    for i in range(len(store)):
        jpg.write(chr(store[i] ^ ord(key[i % len(key)])))

注意这个脚本的运行欢迎是python2.7 python3运行可能会报错

得到图片

image-20240203000535700

看文件结构发现尾部有一个压缩包

image-20240203001001490

分离出来发现crc有问题 修改为504B

image-20240203001035922

正常解压 需要密码 猜测是前面的key 得到jpek.txt

jpek{39i0jf49229fie5j33f02403hj953012}

随波逐流梭了

image-20240203001322559

crypto

Vigenere+++

import sys
from secret_file  import *
def _l(idx, s):
    return s[idx:] + s[:idx]
def main(p, k1, k2):
    s = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz_{}"
    t = [[_l((i+j) % len(s), s) for j in range(len(s))] for i in range(len(s))]
    i1 = 0
    i2 = 0
    c = ""
    for a in p:
        c += t[s.find(a)][s.find(k1[i1])][s.find(k2[i2])]
        i1 = (i1 + 1) % len(k1)
        i2 = (i2 + 1) % len(k2)
    return c

flag="flag{************************}"
key="**********"

# * 为马赛克,长度为1。 
# hint:  可以自己尝试下运行加密函数,看看秘钥对加密结果的影响。 
# hint:  首先根据线索求秘钥,秘钥不唯一,找到一个有效的,就能爆破flag了。 
print main(flag, key, key[::-1])

# 程序运行结果(即密文为):
kY0awfsdlY1FFL8C3bi4GSYCF{8W_E

比较无脑 ctfwiki原题

2017 SECCON Vigenere3d

# exp2.py
enc_str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz_{}'
dec_dic = {k:v for v,k in enumerate(enc_str)}
encrypt = 'kY0awfsdlY1FFL8C3bi4GSYCF{8W_E'
flag_bg = 'flag{**************************}'

sim_key = [dec_dic[encrypt[i]]-dec_dic[flag_bg[i]] for i in range(5)] # 破解模拟密钥
sim_key = sim_key + sim_key[::-1]

flag_ed = [dec_dic[v]-sim_key[k%10] for k,v in enumerate(encrypt)] # 模拟密钥解密
flag_ed = ''.join([enc_str[i%len(enc_str)] for i in flag_ed]) # 解码
print(flag_ed)

# flag{kynFTW2PRdH9lCZBf8IKDe6U}

guess_the_key

给了一个main.c 是加密函数的代码 给了msg01是明文 msg01.enc、msg02.enc是密文 先看加密代码

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv) {
    if (argc != 3) {
        printf("USAGE: %s INPUT OUTPUT\n", argv[0]);
        return 0;
    }
    FILE* input  = fopen(argv[1], "rb");
    FILE* output = fopen(argv[2], "wb");
    if (!input || !output) {
        printf("Error\n");
        return 0;
    }
    char key[] = "guessthekey";
    char d, q, t = 0;
    int ijk = 0;
    while ((q = fgetc(input)) != EOF) {
        d = (q + (key[ijk % strlen( key )] ^ t) + ijk*ijk) & 0xff;
        t = q;
        ijk++;
        fputc(d, output);
    }
    return 0;
}

其实关键的一步就是d = (q + (key[ijk % strlen( key )] ^ t) + ijk*ijk) & 0xff;

d是密文[i] q是明文[i] key是密钥[i] t在经历一次循环之后就是明文[i-1] ijk就是一个单独的参数 在每一次循环结束之后都自加1

由此我们利用明文msg.01和msg01.enc来破解出key

在这段加密代码中 key是每一位循环使用的 但对于前几位明文(不超过key的长度)来说 key[ijk % strlen( key ) 这一部分其实就是key[i]

我们可以利用爆破的方法 利用上面这个公式反推出q[i] = d[i] - key[i]^q[i-1] - pow(ijk, 2)

我们使用循环遍历每一位key[i]的值 当出现满足q[i] == text[i] (text是已知的明文)的key[i]时 我们就将这个key输出 下面是脚本

ciphertext = [0x9E,0x97,0x4B,0xD2,0x9A,0x8B,0xAD,0xA1,0x89,0x09,0xDE,0xAD,0x69,0x23,0x4E,0x76,0x70,0xAB,0xE4,0x97,0x44,0x22,0x81,0x8D,0x7F,0x22,0x23,0x70,0x7F,0xB5,0xFF,0x68,0x72,0xC1,0xC2,0x4B]
text = "Hi,there is nothing here,heiheihei."
t = 0
i = 0
for p in ciphertext:
        for key in range(31, 125):  # 这里表示的是key的ascii值
            q = (p - (key ^ t) - i*i) & 0xff
            q = chr(q)  # 将q的ascii值转换为字符
            try:
                if q == text[i]:
                    print(chr(key), end="")  # 将key的ascii值转换为字符输出
                    t = ord(q)               # 将q的ascii值赋予t
                    i += 1
                    break
            except:
                 print('')
                    
# VeryVeryLongKeyYouWillNeverKnowVery

我们可以看到key出现了循环的部分 那么正确的key就是VeryVeryLongKeyYouWillNeverKnow

有了key 我们就可以继续写脚本获得msg02

msg02_enc = [0xA9,0x9F,0x83,0x45,0xEE,0x87,0x9B,0x6E,0x0E,0xC3,0xD4,0xE9,0xD5,0x61,0x36,0x81,0x70,0x96,0xD4,0xD7,0xF9,0xE4,0xC9,0x8C,0xD3,0xEA,0xDE,0xAC,0x7B,0xC5,0xA9,0x84,0x97,0xCB,0xB8,0xA8,0x8A,0x95,0x54,0x6D,0xBA,0xC0,0x7B,0xA0,0x06,0x68,0x9F,0x02,0xA8,0xCD,0x2A,0x52,0x49,0x91,0xE7,0x4A,0x71,0x6B,0xA8,0x1E,0x8E,0xBB,0xDC,0xED,0x7C,0x0B,0x5C,0x04,0x74,0x6B,0xBE,0x1C,0xC1,0x59,0xBC,0xAD,0x12,0xC2,0xFB,0xDA,0xEB,0x26,0xB1,0x61,0xED,0xE0,0x5D,0xF2,0xC8,0xA3,0x27,0xC5,0x96,0x58,0xAD,0xF5,0x8D,0x54,0x05,0xBC,0x47,0xAD,0x0C,0xE9,0xC0,0xAF,0x48,0x02,0x25,0x1E,0xC9,0xAB,0x6F,0x5B,0x37,0x30,0xBD,0x3A,0xC8,0xC7,0xCD,0xA0,0x4F,0xD9,0xBC,0x72,0x7E,0x84,0x53,0xB5,0x87,0x48,0xE5,0x8D,0x92,0xA9,0xC7,0xBC,0xEE,0x13,0x01,0xE7,0x5D,0x73,0x99,0x59,0x29,0xDC,0x1A,0xEF,0xA6,0xBB,0xB6,0xFD,0x12,0x86,0x82,0x7E,0x4C,0x6F,0x84,0xBA,0xF7,0x52,0x80,0x92,0x0D,0xB0,0xD9,0x07,0x40,0xF3,0x17,0x95,0xAF,0xC9,0xBB,0xE8,0xE7,0xF1,0x08,0x75,0xF4,0xF1,0x03,0x1C,0xC3,0x11,0x36,0x49,0xAA,0x04,0x69,0xF7,0xA0,0xC5,0xCD,0x17,0xC6,0x23,0x6B,0xBE,0xE7,0x7B,0xE2,0xE6,0x4B,0xD4,0x5E,0x55,0xC3,0x0C,0x54,0xD3,0x5C,0x05,0x79,0xCE,0x1B,0xD4,0x91,0x50,0xF6,0xB4,0x36,0x41,0x46,0xD5,0x38,0xB1,0x21,0xE0,0xE2,0x38,0xA2,0x65,0xB7,0x16,0x71,0xF7,0x82,0x56,0x4D,0x22,0xE2,0x3B,0xEE,0x89,0x1E,0xA7,0xB3,0x46,0xFA,0x82,0x83,0x3D,0xB1,0x8C,0x85,0x92,0xB7,0x52,0x99,0x13,0xBA,0x72,0x43,0xDB,0x10,0xE8,0xA0,0x5B,0x39,0xDA,0xB3,0xF8,0xF8,0xE3,0xAF,0xA2,0x6A,0x29,0x2F,0x82,0x91,0x6E,0x41,0x58,0x77,0xC8,0xAD,0xA8,0x89,0xCF,0x00,0xB3,0xB6,0x27,0x5F,0xC6,0xD6,0xAF,0xB3,0x1C,0x6B,0xF1,0x25,0xB8,0x20,0xA0,0xD1,0x89,0xBA,0x04,0xF9,0xD5,0x8E,0x0B,0xB0,0x10,0x8B,0x37,0x99,0xBC,0xBA,0x05,0xB3,0x58,0xA3,0x5C,0xF4,0x86,0x43,0xEA,0x08,0x1D,0x79,0xFE,0x1B,0x05]
key = 'VeryVeryLongKeyYouWillNeverKnow'
msg02 = ''
t = 0
for i in range(len(msg02_enc)):
     if i ==0:
        q = chr((msg02_enc[i] - (ord(key[i % len(key)]) ^ 0) - pow(i, 2)) & 0xff)
     else:
        q = chr((msg02_enc[i] - (ord(key[i % len(key)]) ^ ord(t)) - pow(i, 2)) & 0xff)
     t = q
     msg02 += q
print(msg02)

# She had been shopping with her Mom in Wal-Mart. She must have been 6 years old, this beautiful brown haired, freckle-faced image of innocence. It was pouring outside. The kind of rain that gushes over the top of rain gutters, so much in a hurry to hit the Earth, it has no time to flow down the spout.flag{101a6ec9f938885df0a44f20458d2eb4}

babyRSA

p=165183720742741436051373219716388644270093189046466421563632727622389425827620783096218651072108769567350808642169644915755493944233905573858905774991122631609402471527613272585988802294622263573574301013199411535656758222265554222107815469076608655188293263358371274025455477828555535371028164366376886408977
q=120848273460784230746197749214740170558670241437030497317956826606952430354830550737450520592481405802317202852411775956584677841602475259120706429378240071206662182089399302414435162197602907213282222144680788273948123482886712835590321726087823477518087588076504167863011019333002124841000448268076303735731
e=33
c=10407733127291995335613764691145477155502676597183852092212444772475748406250517097288411248334115120781386833588013995106957807313657632637086223225958539244315092039575434338289689184523710991223212333496000621300008178955253701172159259970353872359828291763446333588873982621853358272632447440961028670921631505593309092190417674648927653583956106734654954561031328286272044755552317084498103486458373580383410475085969677647030080606373264155592552338785789990114607084241499363324045488462563945268471178702696791804080490936763759252660049728533344304874474003893472238560682850602644793844258072019357796047919

上面就是我们能拿到的数据

看yolo师傅的博客学到是 有限域内开方 RSA中e和phi不互素问题

from Crypto.Util.number import *
p=165183720742741436051373219716388644270093189046466421563632727622389425827620783096218651072108769567350808642169644915755493944233905573858905774991122631609402471527613272585988802294622263573574301013199411535656758222265554222107815469076608655188293263358371274025455477828555535371028164366376886408977
q=120848273460784230746197749214740170558670241437030497317956826606952430354830550737450520592481405802317202852411775956584677841602475259120706429378240071206662182089399302414435162197602907213282222144680788273948123482886712835590321726087823477518087588076504167863011019333002124841000448268076303735731
e=33
c=10407733127291995335613764691145477155502676597183852092212444772475748406250517097288411248334115120781386833588013995106957807313657632637086223225958539244315092039575434338289689184523710991223212333496000621300008178955253701172159259970353872359828291763446333588873982621853358272632447440961028670921631505593309092190417674648927653583956106734654954561031328286272044755552317084498103486458373580383410475085969677647030080606373264155592552338785789990114607084241499363324045488462563945268471178702696791804080490936763759252660049728533344304874474003893472238560682850602644793844258072019357796047919
n = p*q

P.<a>=PolynomialRing(Zmod(p),implementation='NTL')
f=a^e-c
mps=f.monic().roots()

P.<a>=PolynomialRing(Zmod(q),implementation='NTL')
g=a^e-c
mqs=g.monic().roots()

flag=[]
for mpp in mps:
    x=mpp[0]
    for mqq in mqs:
        y=mqq[0]
        solution = CRT_list([int(x), int(y)], [p, q])
        flag.append(solution)
for i in flag:
    m=long_to_bytes(i)
    if b'flag'in m:
        print(m)

image-20240208224859887