Loading

NSSCTF round#22逆向

NSSCTF round#22逆向

1.wp要及时写不然忘光光 2.赛题分文件夹放

ezcrypt

下载下来是python打包的exe,解包出pyc用pycdc反编译看一下

嗯不认识BEFORE_WITH命令。丢到gpt4o里看看

还蛮准确的,和作者提供的源码一样。不过对填充的处理不对,原程序是填充'\x00'。不过比自己硬看好太多了,pycdas弄出来的少一些提示信息,不太好看。

#generated by gpt4o

from Crypto.Cipher import Blowfish
from Crypto.Random import get_random_bytes
import os

def encrypt_file(input_path, output_path, key):
    # 创建Blowfish加密器,使用CBC模式
    cipher = Blowfish.new(key, Blowfish.MODE_CBC)
    
    # 读取文件内容
    with open(input_path, 'rb') as file:
        plaintext = file.read()
    
    # 计算需要填充的长度,使数据长度是块大小的整数倍
    bs = Blowfish.block_size
    padding_length = bs - len(plaintext) % bs
    padding = bytes([padding_length]) * padding_length  # 使用PKCS5填充  #不正确,实际上是用0填充
    plaintext += padding
    
    # 加密数据
    encrypted_data = cipher.iv + cipher.encrypt(plaintext)
    
    # 将加密后的数据写入输出文件
    with open(output_path, 'wb') as file:
        file.write(encrypted_data)

# 主程序
key = b'crypto.SomeEncode'
input_path = './input'
output_path = './output'

encrypt_file(input_path, output_path, key)

可以看到最后生成的out文件是由iv和加密的text拼接成的。故等会解密的时候先把iv和密文分开再弄弄。翻了翻原exe解包出的文件,还有一个crypto.cpython.pyc文件,反编译一下

这里有一个someencode,encrypt是修改过的tea加密。似乎是出题错了,SomeEncode实际应该由v和k解密所得而非经由encrypt加密。脚本

def encrypt(v, k):
    v0 = v[0]
    v1 = v[1]
    (key0, key1, key2, key3) = (k[0], k[1], k[2], k[3])
    sum = 0
    delta = 0x9E3779B9
    for _ in range(32):
        sum = sum + delta & 0xFFFFFFFF
        v0 = v0 + ((v1 << 3) + key0 ^ v1 + sum ^ (v1 >> 4) + key1 ^ 596) & 0xFFFFFFFF
        v1 = v1 + ((v0 << 3) + key2 ^ v0 + sum ^ (v0 >> 4) + key3 ^ 2310) & 0xFFFFFFFF
    return (v0, v1)


def decrypt(v, k):
    v0 = v[0]
    v1 = v[1]
    (key0, key1, key2, key3) = (k[0], k[1], k[2], k[3])
    delta = 0x9E3779B9
    sum = (delta * 32) & 0xFFFFFFFF
    for _ in range(32):
        v1 = v1 - ((v0 << 3) + key2 ^ v0 + sum ^ (v0 >> 4) + key3 ^ 2310) & 0xFFFFFFFF
        v0 = v0 - ((v1 << 3) + key0 ^ v1 + sum ^ (v1 >> 4) + key1 ^ 596) & 0xFFFFFFFF
        sum = (sum - delta) & 0xFFFFFFFF
    return (v0, v1)

def encrypt_all(v, k):
    encrypted = []
    for i in range(0, len(v), 2):
        encrypted.extend(encrypt(v[i:i + 2], k))
    return encrypted
def decrypt_all(v,k):
    decrypted=[]
    for i in range(0,len(v),2):
        decrypted.extend(decrypt(v[i:i+2],k))
    return decrypted

v = [
    1396533857,
    0xCC8AE275,
    0x89FB8A63,
    940694833]
k = [
    17,
    34,
    51,
    68]


char_output = decrypt_all(v,k)
for j in range(4):
    for i in range(4)[::-1]:
        print(chr(char_output[j] >> 8 * i & 255),end='')
        
#EzNssRevProjects

似乎不仅说明key要解密出来,而且说明主程序里的key要调用这个模块获取而不是字符串"cipher.SomeEncode"。写脚本提取下iv做Blowfish解密,output文件也是解包exe的时候解包出来的。

import os
from Crypto.Cipher import Blowfish

def decrypt_file(file_path, key):
    # Read the file as a byte stream
    with open(file_path, 'rb') as file:
        data = file.read()

    # Create a Blowfish cipher object with the provided key
    iv=data[:Blowfish.block_size]
    encrypted_data=data[Blowfish.block_size:]
    cipher = Blowfish.new(key,Blowfish.MODE_CBC,iv)

    # Decrypt the data
    decrypted_data = cipher.decrypt(encrypted_data)

    # Write the decrypted data back to the file
    with open(file_path, 'wb') as file:
        file.write(decrypted_data)

# Example usage
file_path = r'C:\Users\abc\Desktop\output'
key = b'EzNssRevProjects'

decrypt_file(file_path, key)

打开解密后的文件看一下,是个虚拟机,不过还好不同指令只是对对应数据进行不同的运算

这里还很奇怪,v5==0的时候是a2[4]-a2[1],但是实际上看wp要按照+运算才能解出来,难道是动态的时候还有操作?写脚本解一下

#include<stdio.h>
int main(){
    unsigned char ida_chars[] =
    {
        0x37, 0x8D, 0x0F, 0xAB, 0x2D, 0x98, 0x37, 0xB5,
        0x48, 0xA6, 0x30, 0xDA, 0x0C, 0xED, 0x1B, 0xB8,
        0x00, 0xE9, 0x24, 0x98, 0x17, 0x81, 0xED, 0xB6,
        0x26, 0x8C, 0x21, 0xDE, 0x04, 0xDE, 

    };
    int v4[]={35,18,69,86,103};
    int v5;
    for(int i=29;i>=0;i--){
        v5=i%4;
        if(i%4==3){
            ida_chars[i]-=v4[2]+v4[0];
        }else if(v5<=3){
            if(v5==2){
                ida_chars[i]+=v4[1]^v4[3];
            }else if(v5<=2){
                if(v5){
                    if(v5==1){
                        ida_chars[i]^=v4[0]-v4[2];
                    }
                }else{
                    ida_chars[i]^=v4[4]+v4[1];
                }
            }
        }
    }
    for(int j=0;j<30;j++){
        printf("%c",ida_chars[j]);
    }
    return 0;
}

//NSSCTF{M1xtru3_Py7h0n_1N_Rev}

简简又单单

打开app简单看一眼,要弄出来用户名和密码

username是xtea加密,异或了个918

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

#define DELTA 0x9e3779b9
#define NUM_ROUNDS 32

void decrypt(uint32_t v[2], uint32_t k[4]) {
    uint32_t sum = 0xC6EF3720;
    for (int i = 0; i < NUM_ROUNDS; i++) {
        v[1] -= (((v[0]<<4 ^ v[0]>>5) + v[0])^918) ^ (sum + k[(sum>>11) & 3]);
        sum -= DELTA;
        v[0] -= (((v[1]<<4 ^ v[1]>>5) + v[1])^918) ^( sum + k[sum & 3]);
    }
}

void hex_to_int_array(const char* hex_str, uint32_t* int_array) {
    size_t len = strlen(hex_str);
    for (size_t i = 0; i < len; i += 8) {
        sscanf(hex_str + i, "%8x", &int_array[i / 8]);
    }
}

void int_array_to_string(const uint32_t* int_array, size_t len, char* str) {
    for (size_t i = 0; i < len; i++) {
        str[i * 4] = (char)((int_array[i] >> 24) & 0xFF);
        str[i * 4 + 1] = (char)((int_array[i] >> 16) & 0xFF);
        str[i * 4 + 2] = (char)((int_array[i] >> 8) & 0xFF);
        str[i * 4 + 3] = (char)(int_array[i] & 0xFF);
    }
    str[len * 4] = '\0';
}
//3c36eb49 81acb0c0 fac269ae ca5bf9ec
int main() {
    const char* encrypted_username_hex1 = "3c36eb4981acb0c0";
    uint32_t encrypted_username_int1[4];
    hex_to_int_array(encrypted_username_hex1, encrypted_username_int1);

    uint32_t key[4] = {305419896, 1450748877, -1985229329,  -839970252};
    decrypt(encrypted_username_int1, key);

    char decrypted_username[17];
    int_array_to_string(encrypted_username_int1, 4, decrypted_username);
    for (int i = 0; i < 4; i++) {
        printf("%c", decrypted_username[3-i]);
    }
    for (int i = 0; i < 4; i++) {
        printf("%c", decrypted_username[7-i]);
    }
    // printf("Decrypted Username: %s\n", decrypted_username);
    const char* encrypted_username_hex2 = "fac269aeca5bf9ec";
    uint32_t encrypted_username_int2[4];
    hex_to_int_array(encrypted_username_hex2, encrypted_username_int2);
    decrypt(encrypted_username_int2, key);
    char decrypted_username2[17];
    int_array_to_string(encrypted_username_int2, 4, decrypted_username2);
    for (int i = 0; i < 4; i++) {
        printf("%c", decrypted_username2[3-i]);
    }
    for (int i = 0; i < 4; i++) {
        printf("%c", decrypted_username2[7-i]);
    }
    return 0;
}
//username NS5_R0Un6_z2_apK

password的校验是调用的native方法,用Android killer解包看下so文件,也提示了是rc4

ValidatePassword

或者可以放到模拟器里动调一下,rc4是修改过128轮的,运算完转成hex比较。写脚本试一下

def rc4_decrypt(key, ciphertext):
    # Convert the key from string to a list of integers
    key = [ord(char) for char in key]

    # Convert the hexadecimal ciphertext to a list of integers
    ciphertext = [int(ciphertext[i:i+2], 16) for i in range(0, len(ciphertext), 2)]

    S = list(range(128))
    j = 0
    out = []

    # 初始化S盒
    for i in range(128):
        j = (j + S[i] + key[i % len(key)]) % 128
        S[i], S[j] = S[j], S[i]

    # 生成密钥流并解密密文
    i = j = 0
    for char in ciphertext:
        i = (i + 1) % 128
        j = (j + S[i]) % 128
        S[i], S[j] = S[j], S[i]
        k = S[(S[i] + S[j]) % 128]
        out.append(chr(char ^ k))

    return ''.join(out)

print(rc4_decrypt('NS5_R0Un6_z2_apK', '572e180b1a680b3e5276344b241d5b52525a043173346b1355442028'))
#NSSCTF{V3ry_4z_1ib_W1th_4pk}

ezhook

看了看比较后输出right?wrong?应该主程序就不在main这了。函数列表里转了一下看到
image

像是有东西的样子,查了下系统调用,sub140001030把这个函数作为参数读入

image

动调下可以发现sub140001030用sub140001240的地址覆盖了MessageBoxA调用的地址。值得注意MessageBoxA把buf1作为参数读入。

在sub140001240里调用了sub1400014e0对输入进行处理,看起来是xxtea,delta是0x11451981

image

拿密文和key做下解密,flag直接出来了,其他的异或运算似乎没有影响加解密。

#include<stdio.h>
#define delta 0x11451981
int main(){
    unsigned char ida_chars[] =
    {
    0xE4, 0xE7, 0xFE, 0xE3, 0x17, 0x1C, 0xDE, 0x32, 0xE6, 0xB8, 
    0x68, 0x40, 0x40, 0xD8, 0x72, 0xFA, 0x88, 0x14, 0xE1, 0x85, 
    0xCD, 0x81, 0xAA, 0xDE, 0x1D, 0xE8, 0x92, 0x41, 0xB8, 0x1E, 
    0x5E, 0xCF, 0xCE, 0x49, 0x27, 0x22, 0x39, 0x7D, 0x50, 0xDA
    };
    //
    unsigned int v[10] = {0x0E3FEE7E4, 0x32DE1C17, 0x4068B8E6, 0x0FA72D840, 0x85E11488,0x0DEAA81CD, 0x4192E81D, 0x0CF5E1EB8, 0x222749CE, 0x0DA507D39};
    unsigned int key[4] = {0x6C316548, 0x53734E30, 0x14451121, 0x811919};
    unsigned int sum = 0;
    unsigned int y,z,p,rounds,e;
    int n = 10;
    int i = 0;
    rounds = 6 + 52/n;
    y = v[0];
    sum = rounds*delta;
     do
     {
        e = sum >> 2 & 3;
        for(p=n-1;p>0;p--)
        {
            z = v[p-1];
            v[p] -= ((((z>>5)^(y<<2))+((y>>3)^(z<<4))) ^ ((key[(p&3)^e]^z)+(y ^ sum)));
            y = v[p];
        }
        z = v[n-1];
        v[0] -= (((key[(p^e)&3]^z)+(y ^ sum)) ^ (((y<<2)^(z>>5))+((z<<4)^(y>>3))));
        y = v[0];
        sum = sum-delta;
     }while(--rounds);
 
    for(i=0;i<n;i++)
    {
        printf("%c%c%c%c",*((char*)&v[i]+0),*((char*)&v[i]+1),*((char*)&v[i]+2),*((char*)&v[i]+3));
        //printf("%c%c%c%c",*((char*)&v[i]+3),*((char*)&v[i]+2),*((char*)&v[i]+1),*((char*)&v[i]+0));
    }
    return 0;
}

//NSSCTF{C0ngr@tulat1ons!H0Ok_bY_1t_s3lf!}

Go!Go!Go!

看着main函数似乎是想修改shell64.exe的内容

image

查看一下main函数另一个参数的内容

image

image

以mz开头,似乎是包含了一个程序,用resourcehacker提取下

image

dump下来后用ida看看,是go语言,应该是要分析的部分了

image

进到Function1里看看

image

第一次fprintln应该是打印提示,fscan是读入第一段flag,进入crypto_md5_Sum看看

image

应该是标准算法。回到fun1里,看到md5下面有比较的内容

image

用hashcat破解一下

image

因为之前计算过所以用--show参数可以展示,key1: G0@K3y。下面是另一个fscan,然后调用sha256做摘要之后对比

image

同样用hashcat算一下

hashcat -m 1400 -a 3 c32a69f4609191a2c3e6dbe2effc329bff20617230853462e8745f2c058bec2f ?a?a?a?a?a?a

image

key2: n3SC1f

image

fun1就分析完了,下面看fun2。看起来是rc4,那么直接把密文放进去动调下应该就好

image

动调下可以发现前面的两个key是拼接起来作为rc4的key。替换输入为密文

image

flag: NSSCTF{Y0u_F1nd_1t!K3ep_YoR_fl@g_3afe!g0!GO!Og!}

image

posted @ 2024-05-15 23:14  yuhury  阅读(14)  评论(0编辑  收藏  举报