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这了。函数列表里转了一下看到
像是有东西的样子,查了下系统调用,sub140001030把这个函数作为参数读入
动调下可以发现sub140001030用sub140001240的地址覆盖了MessageBoxA调用的地址。值得注意MessageBoxA把buf1作为参数读入。
在sub140001240里调用了sub1400014e0对输入进行处理,看起来是xxtea,delta是0x11451981
拿密文和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的内容
查看一下main函数另一个参数的内容
以mz开头,似乎是包含了一个程序,用resourcehacker提取下
dump下来后用ida看看,是go语言,应该是要分析的部分了
进到Function1里看看
第一次fprintln应该是打印提示,fscan是读入第一段flag,进入crypto_md5_Sum看看
应该是标准算法。回到fun1里,看到md5下面有比较的内容
用hashcat破解一下
因为之前计算过所以用--show参数可以展示,key1: G0@K3y。下面是另一个fscan,然后调用sha256做摘要之后对比
同样用hashcat算一下
hashcat -m 1400 -a 3 c32a69f4609191a2c3e6dbe2effc329bff20617230853462e8745f2c058bec2f ?a?a?a?a?a?a
key2: n3SC1f
fun1就分析完了,下面看fun2。看起来是rc4,那么直接把密文放进去动调下应该就好
动调下可以发现前面的两个key是拼接起来作为rc4的key。替换输入为密文
flag: NSSCTF{Y0u_F1nd_1t!K3ep_YoR_fl@g_3afe!g0!GO!Og!}
标签:username,file,22,int,encrypted,char,key,round,NSSCTF From: https://www.cnblogs.com/y0hv2y/p/18195034