本文主要是介绍NSSCTF Round#22 Reverse个人专项赛 WP,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
1. ezcrypt(史)
pyinstxtractor.py解包exe,然后pycdc反编译NSSCTF.pyc
得到的源码并不完整,但是重要的部分已经有了,就是一个blowfish加密
但是密钥是crypto.SomeEncode
,这并不是字面意义的字符串,而是引用的其他文件
NSSCTF.exe_extracted
可以找到可疑文件crypto.cpython.pyc
是一个魔改的TEA(这里出题逻辑有问题,按给的代码逻辑SomeEncode是v加密得到的,但实际上却要我们解密得到)
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{unsigned int enc[5] ={1396533857,0xCC8AE275,0x89FB8A63,940694833};unsigned int key[4] ={17,34,51,68};int i, j;for (i = 0; i < 4; i += 2){unsigned int v0 = enc[i], v1 = enc[i + 1], sum = 0xC6EF3720;unsigned int delta = 0x9e3779b9;unsigned int k0 = key[0], k1 = key[1], k2 = key[2], k3 = key[3];for (j = 0; j < 32; j++){v1 -= ((v0 << 3) + k2 ^ v0 + sum ^ (v0 >> 4) + k3 ^ 2310) & 0xFFFFFFFF;v0 -= ((v1 << 3) + k0 ^ v1 + sum ^ (v1 >> 4) + k1 ^ 596) & 0xFFFFFFFF;sum -= delta & 0xFFFFFFFF;}enc[i] = v0; enc[i + 1] = v1;}for (i = 0; i < 4; i++){printf("%u ", enc[i]);}printf("%s", enc);//sNzEveRsjorPstcereturn 0;
}
这里有个每四位一个逆序(第三行就是ASCII转字符的意思),解出来手动改一下吧,就是 EzNssRevProjects
#ezcrypt wp
from Crypto.Cipher import Blowfish
from Crypto.Util.Padding import unpad
from Crypto.Random import get_random_bytes def decrypt_file(input_path, output_path, key): with open(input_path, 'rb') as f: # 读取整个文件内容,包括IV和密文 data = f.read() # 分割IV和密文 iv = data[:Blowfish.block_size] print(iv)ciphertext = data[Blowfish.block_size:] print(ciphertext) # 验证密钥长度 #if len(key) != Blowfish.key_size: # raise ValueError("Invalid key size for Blowfish. It must be exactly 8 bytes long.") # 创建一个新的Blowfish cipher对象,并设置密钥和IV cipher = Blowfish.new(key, Blowfish.MODE_CBC, iv=iv) # 解密数据 plaintext = (cipher.decrypt(ciphertext), Blowfish.block_size) print(plaintext[0])# 将解密后的数据写入输出文件 with open(output_path, 'wb') as f: f.write(plaintext[0]) key = b'EzNssRevProjects'
input_path = 'D:\\下载\\CTF附件\\NSS22re\\output'
output_path = 'D:\\下载\\CTF附件\\NSS22re\\flag'decrypt_file(input_path, output_path, key)
密钥解码得到一个二进制文件,IDA打开是个虚拟机(笑)
动调不出来(这里逻辑也莫名其妙,明明这个代码就应该能跑出flag,但实际上是要你把代码反过来写)
硬做,动调取出opcode,key
VM函数抄下来符号改一下,有个地方给的代码还不对,看汇编也是sub
这里应该是加号
#include<stdio.h>
__int64 __fastcall vm(unsigned char* a1, int* a2, int a3)
{__int64 result; // raxint i; // [rsp+1Ch] [rbp-8h]int v5; // [rsp+20h] [rbp-4h]for (i = 0; ; ++i){result = i;if (i >= a3)break;v5 = i % 4;if (i % 4 == 3){a1[i] -= a2[8] + a2[0];}else if (v5 <= 3){if (v5 == 2){a1[i] += a2[4] ^ a2[12];}else if (v5 <= 2){if (v5){if (v5 == 1){a1[i] ^= a2[0] - a2[8];}}else{a1[i] ^= a2[16] + a2[4];}}}}return result;
}
int main()
{unsigned char opcode[] ={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 key[] ={0x23, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x45, 0x00,0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x67};vm(opcode, key, 30);printf("%s", opcode);//NSSCTF{M1xtru3_Py7h0n_1N_Rev}return 0;
}
2. EzHook
看起来只是异或,但是最后的字符有?很奇怪
联系题目实际上使用了Windows IAT Hook技术
动调试试,断在密文处
随你怎么输都是走左边
F8到MessageBoxA这里F7进去
进到另一个函数里,应该是hook注入的函数
这里是真正的提示分支,qword_7FF63596A090应当是真实的MessageBox
我们注意所有输入在78行xxtea加密之后,经过密文比较。又在83和88行解密
只要在解密之前把输入a2改成密文Str2即可
a2应该存在rcx寄存器里
每16个地址Change byte一次,把对应的密文填进去
然后跑到这里,a1点进去
3. 简简又单单
jadx打开
md还是个魔改XTEA
#include<stdio.h>
void xtea_decipher(unsigned int num_rounds, unsigned int v[2], unsigned int const key[4]) {unsigned int i;unsigned int v0 = v[0], v1 = v[1], delta = 0x9E3779B9, sum = delta * num_rounds;for (i = 0; i < num_rounds; i++) {v1 -= ((((v0 << 4) ^ (v0 >> 5)) + v0)^918) ^ (sum + key[(sum >> 11) & 3]);sum -= delta;v0 -= ((((v1 << 4) ^ (v1 >> 5)) + v1)^918) ^ (sum + key[sum & 3]);}v[0] = v0; v[1] = v1;
}
int main()
{unsigned int enusr[] = { 0x3c36eb49,0x81acb0c0,0xfac269ae,0xca5bf9ec }; // 密文unsigned int key[4] = { 0x12345678, 0x5678ABCD, 0x89ABCDEF, 0xCDEF1234 }; // 密钥for (int i = 0; i < 4; i += 2) {unsigned int tmp[2] = { 0 };tmp[0] = enusr[i];tmp[1] = enusr[i + 1];xtea_decipher(32, tmp, key);enusr[i] = tmp[0];enusr[i + 1] = tmp[1];}printf("%s", enusr);//NS5_R0Un6_z2_apKreturn 0;
}
密码看不到,因为在native层中
解压apk文件,找到so文件
可以搜到相应的函数
Java_com_example_nss_MainActivity_validatePassword
内可以找到密文
应该是RC4加密过,但是脚本跑不出,又魔改了
Java_com_example_nss_MainActivity_encryptWithRC4
看看
改成了128轮加密
加密密钥是用户名
#ez_APK wp
def rc4(data, key):S = list(range(128))j = 0out = []for i in range(128):j = (j + S[i] + key[i % len(key)]) % 128S[i], S[j] = S[j], S[i]i = j = 0for char in data:i = (i + 1) % 128j = (j + S[i]) % 128S[i], S[j] = S[j], S[i]out.append(char ^ S[(S[i] + S[j]) % 128])return bytes(out)data = bytes.fromhex("572e180b1a680b3e5276344b241d5b52525a043173346b1355442028")
key = b'NS5_R0Un6_z2_apK'
decrypted = rc4(data, key)
print(decrypted)
#NSSCTF{V3ry_4z_1ib_W1th_4pk}
4. GO!GO!GO!
shell看不出东西,分析主文件
好像是调用了shell
大量的WinAPI调用,有个函数进去看看
似乎是要卸载表面上的内存映像,注入傀儡进程
查询qword_140005080的交叉引用定位到
动调发现qword_140005080内资源开头为MZ,是PE文件
ResourceHacker正好可以提取
非常恶心go语言
这是输入的key1,可以用hashcat爆破
hashcat -m 0 -a 3 b098cacb2d43b882ef9a83168d13c3a7 ?a?a?a?a?a?a
稍等一段时间,烧一会GPU就出来了 G0@K3y
key2是一个道理
hashcat -m 1400 -a 3 c32a69f4609191a2c3e6dbe2effc329bff20617230853462e8745f2c058bec2f ?a?a?a?a?a?a
得到n3SC1f
又输入一段东西进了main_Function2
第三次输入的就是flag
发现RC4特征
有魔改,但是无所谓直接动调
先找一下RC4的密钥,应该和之前的输入有关
看到密钥被拼接了
由于RC4是对称加密,所以可以考虑把密文提出来再写入用原程序解密
先动调取出密文
然后重新动调,找到存储flag的内存位置
patch成密文
动调到加密之后
这篇关于NSSCTF Round#22 Reverse个人专项赛 WP的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!