翻之前的记录发现GKCTF没有复现,水一篇
QQQQT
Enigma Virtual Box打包的QT程序,可以不脱壳
base58加密,字符串56fkoP8KhwCf3v7CEz
flag{12t4tww3r5e77}
Crash
观察字符串发现为go语言,7.5可以装一个插件,7.6可以直接反编译出。
在main_main函数中发现主要逻辑
if ( flag[1] == 43 && *(_DWORD *)v0 == 'TCKG' && *(_WORD *)(v0 + 4) == '{F' && *(_BYTE *)(v0 + 42) == '}' )
{
main_check(*flag, 43uLL);
if ( v1 )
{
v6[0] = &qword_523A20;
v6[1] = &off_5724B0; // Congratulation
fmt_Fprintln((__int64)&off_574420, qword_625B28, (__int64)v6, 1LL);
}
在main_check查看
第一次加密
main_encrypto(a1 + 6, 24LL); // 3des
if ( v3 == 44 )
{
runtime_memequal(v2, (__int64)"o/aWPjNNxMPZDnJlNp0zK5+NLPC4Tv6kqdJqjkL0XkA=");
在main_encrypto中
__int64 __usercall crypto_des_NewTripleDESCipher@<rax>(__int64 a1, __int64 a2)
{
__int64 v3; // [rsp+20h] [rbp-10h]
if ( a2 != 24 )
return runtime_convT64(a2);
v3 = runtime_newobject((__int64)&unk_534820);
crypto_des___ptr_desCipher__generateSubkeys(v3, a1, 8LL);
crypto_des___ptr_desCipher__generateSubkeys(v3 + 128, a1 + 8, 8LL);
crypto_des___ptr_desCipher__generateSubkeys(v3 + 256, a1 + 16, 8LL);
return v3;
}
可知为3des加密,并且可以得到key和iv
aKeyWelcometoth db '{',0Dh,0Ah ; DATA XREF: .data:off_61E540↓o
.noptrdata:0000000000608840 db ' "key": "WelcomeToTheGKCTF2021XXX",',0Dh,0Ah
.noptrdata:0000000000608840 db ' "iv": "1Ssecret"',0Dh,0Ah
.noptrdata:0000000000608840 db '}',0
第二次加密
Encrypt_HashHex2(44LL);
if ( v4 == 64 )
{
runtime_memequal(44LL, (__int64)"6e2b55c78937d63490b4b26ab3ac3cb54df4c5ca7d60012c13d2d1234a732b74");
进入加密函数
__int64 __usercall Encrypt_HashHex2@<rax>(__int64 a1, __int64 a2, __int64 a3)
{
__int128 v4; // [rsp+18h] [rbp-50h]
__int64 v5; // [rsp+18h] [rbp-50h]
__int64 v6; // [rsp+20h] [rbp-48h]
__int64 v7; // [rsp+48h] [rbp-20h]
__int64 v8; // [rsp+50h] [rbp-18h]
__int64 v9; // [rsp+58h] [rbp-10h]
v8 = Encrypt_Sha256(a1, a2, a3);
v7 = 2 * v6;
v9 = runtime_makeslice((__int64)&unk_523B60, 2 * v6, 2 * v6);
*((_QWORD *)&v4 + 1) = encoding_hex_Encode(v9, v7, v7, v8);
runtime_slicebytetostring(0LL, v9, v7, v4);
return v5;
}
加密为sha256
第三段加密
Encrypt_HashHex5(v7, v13, v16, v7, v13);
if ( v14 == 128 )
{
runtime_memequal(
v8,
(__int64)"6500fe72abcab63d87f213d2218b0ee086a1828188439ca485a1a40968fd272865d5ca4d5ef5a651270a52ff952d955c9b7"
"57caae1ecce804582ae78f87fa3c9");
加密函数
__int64 __usercall Encrypt_HashHex5@<rax>(__int64 a1, __int64 a2, __int64 a3)
{
__int128 v4; // [rsp+18h] [rbp-50h]
__int64 v5; // [rsp+18h] [rbp-50h]
__int64 v6; // [rsp+20h] [rbp-48h]
__int64 v7; // [rsp+48h] [rbp-20h]
__int64 v8; // [rsp+50h] [rbp-18h]
__int64 v9; // [rsp+58h] [rbp-10h]
v8 = Encrypt_Sha512(a1, a2, a3);
v7 = 2 * v6;
v9 = runtime_makeslice((__int64)&unk_523B60, 2 * v6, 2 * v6);
*((_QWORD *)&v4 + 1) = encoding_hex_Encode(v9, v7, v7, v8);
runtime_slicebytetostring(0LL, v9, v7, v4);
return v5;
}
第四次加密
main_hash(a1 + 38, 4LL, 128LL, v8);
if ( v9 == 32 )
runtime_memequal(v3, (__int64)"ff6e2fd78aca4736037258f0ede4ecf0"
加密函数
__int64 __usercall main_hash@<rax>(__int64 a1, __int64 a2)
{
const char *v2; // rdi
_BYTE v4[24]; // [rsp+10h] [rbp-80h]
__int64 v5; // [rsp+10h] [rbp-80h]
__int64 v6; // [rsp+18h] [rbp-78h]
__int128 v7; // [rsp+18h] [rbp-78h]
__int64 v8; // [rsp+20h] [rbp-70h]
__int64 v9; // [rsp+28h] [rbp-68h]
__int64 v10; // [rsp+28h] [rbp-68h]
__int128 v11; // [rsp+48h] [rbp-48h] BYREF
char v12[32]; // [rsp+58h] [rbp-38h] BYREF
__int128 v13; // [rsp+78h] [rbp-18h]
v9 = runtime_stringtoslicebyte((__int64)v12, a1, a2, v6, v8);
v10 = crypto_md5_Sum(v7, *((__int64 *)&v7 + 1), v9, v7);
v11 = *(_OWORD *)&v4[8];
v13 = 0LL;
runtime_convT2Enoptr((__int64)&unk_524360, (__int64)&v11, v5, *(__int64 *)&v4[8]);
v13 = *(_OWORD *)v4;
fmt_Sprintf(v2);
return v10;
}
md5
脚本
上述四种加密只有3DES为公钥加密可以解密,sha256,512,md5为hash加密需要爆破
itertools库爆破得更快
from Crypto.Cipher import DES3
import base64
import itertools
import string
import hashlib
def des3_cbc_decrypt(secret_key, secret_value, iv):
def unpad(s): return s[0:-ord(s[-1])]
res = DES3.new(secret_key.encode("utf-8"), DES3.MODE_CBC, iv)
base64_decrypted = base64.b64decode(secret_value.encode("utf-8"))
encrypt_text = res.decrypt(base64_decrypted)
result = unpad(encrypt_text.decode())
return result
def sha256crash(sha256enc):
code = ''
strlist = itertools.product(string.ascii_letters + string.digits, repeat=4)
for i in strlist:
code = i[0] + i[1] + i[2] + i[3]
encinfo = hashlib.sha256(code.encode()).hexdigest()
if encinfo == sha256enc:
return code
def sha512crash(sha256enc):
code = ''
strlist = itertools.product(string.ascii_letters + string.digits, repeat=4)
for i in strlist:
code = i[0] + i[1] + i[2] + i[3]
encinfo = hashlib.sha512(code.encode()).hexdigest()
if encinfo == sha256enc:
return code
def md5crash(sha256enc):
code = ''
strlist = itertools.product(string.ascii_letters + string.digits, repeat=4)
for i in strlist:
code = i[0] + i[1] + i[2] + i[3]
encinfo = hashlib.md5(code.encode()).hexdigest()
if encinfo == sha256enc:
return code
if __name__ == '__main__':
key = "WelcomeToTheGKCTF2021XXX"
iv = b"1Ssecret"
cipher = "o/aWPjNNxMPZDnJlNp0zK5+NLPC4Tv6kqdJqjkL0XkA="
part1 = des3_cbc_decrypt(key, cipher, iv)
part2 = sha256crash(
"6e2b55c78937d63490b4b26ab3ac3cb54df4c5ca7d60012c13d2d1234a732b74")
part3 = sha512crash(
"6500fe72abcab63d87f213d2218b0ee086a1828188439ca485a1a40968fd272865d5ca4d5ef5a651270a52ff952d955c9b757caae1ecce804582ae78f87fa3c9")
part4 = md5crash("ff6e2fd78aca4736037258f0ede4ecf0")
flag = "GKCTF{" + part1 + part2 + part3 + part4 + "}"
print(flag)
# GKCTF{87f645e9-b628-412f-9d7a-e402f20af940}
app-debug1
static {
System.loadLibrary("native-lib");
}
public native boolean check(String arg1) {
}
@Override // androidx.appcompat.app.AppCompatActivity
protected void onCreate(Bundle arg3) {
super.onCreate(arg3);
this.setContentView(0x7F09001C); // layout:activity_main
this.mBtnLogin = (Button)this.findViewById(0x7F070023); // id:btn_login
this.mEtflag = (EditText)this.findViewById(0x7F07001E); // id:bEn_edittext
this.mBtnLogin.setOnClickListener(new View.OnClickListener() {
@Override // android.view.View$OnClickListener
public void onClick(View arg4) {
if(MainActivity.this.check(MainActivity.this.mEtflag.getText().toString())) {
Toast.makeText(MainActivity.this, "You Are Right!flag is flag{md5(input)}", 0).show();
return;
}
Toast.makeText(MainActivity.this, "Sorry your flag is wrong!", 0).show();
}
});
}
}
发现check函数,主要验证逻辑在native层
用压缩包打开lib文件夹里的so文件,IDA反编译
__int64 __fastcall Java_com_example_myapplication_MainActivity_check(__int64 a1, __int64 a2, __int64 a3)
{
int i; // [xsp+24h] [xbp-4Ch]
unsigned __int8 v5; // [xsp+4Ch] [xbp-24h]
char v6[24]; // [xsp+50h] [xbp-20h] BYREF
__int64 v7; // [xsp+68h] [xbp-8h]
v7 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
sub_3FE20(a1, a3);
if ( sub_40040(v6) == 7 )
{
for ( i = 0; i <= 6; ++i )
byte_C80E0[i] = *(_BYTE *)sub_40064(v6, i);
v5 = sub_3ED8C(byte_C80E0);
}
else
{
v5 = 0;
}
sub_3FC04(v6);
_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
return v5;
}
加密函数,很明显是tea加密
bool __fastcall sub_3ED8C(unsigned int *a1)
{
unsigned int i; // [xsp+20h] [xbp-20h]
int sum; // [xsp+24h] [xbp-1Ch]
unsigned int v1; // [xsp+28h] [xbp-18h]
unsigned int v0; // [xsp+2Ch] [xbp-14h]
v0 = *a1;
v1 = a1[1];
sum = 0;
for ( i = 0; i < 0x20; ++i )
{
sum += dword_C8010;
v0 += (16 * v1 + dword_C8000) ^ (v1 + sum) ^ ((v1 >> 5) + dword_C8004);
v1 += (16 * v0 + dword_C8008) ^ (v0 + sum) ^ ((v0 >> 5) + dword_C800C);
}
*a1 = v0;
a1[1] = v1;
return *a1 == 0xF5A98FF3 && a1[1] == 0xA21873A3;
}
利用TracerPid的反调试,对付这种反调试是把TracerPid的值改为0就行了,这个反调试实际上就是将check函数里面的key给改了,当发现调试器时,会使用假的key,只要没有检测到调试器才会使用真key
修改key的值的函数
void sub_3EF18()
{
dword_C8004 = 7;
dword_C8008 = 8;
dword_C800C = 6;
}
脚本
就是tea加密,注意key在调用时发生变化
#include <stdio.h>
#include <stdint.h>
//加密函数
void encrypt(uint32_t *v, uint32_t *k)
{
uint32_t v0 = v[0], v1 = v[1], sum = 0, i; /* set up */
uint32_t delta = 0x9e3779b9; /* a key schedule constant */
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; /* cache key */
for (i = 0; i < 32; i++)
{ /* basic cycle start */
sum += delta;
v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
} /* end cycle */
v[0] = v0;
v[1] = v1;
}
//解密函数
void decrypt(uint32_t *v, uint32_t *k)
{
uint32_t v0 = v[0], v1 = v[1], i; /* set up */
uint32_t delta = 0x458BCD42;
uint32_t sum = delta * 32; /* a key schedule constant */
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; /* cache key */
for (i = 0; i < 32; i++)
{ /* basic cycle start */
v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
sum -= delta;
} /* end cycle */
v[0] = v0;
v[1] = v1;
}
int main()
{
uint32_t v[32] = {0xF5A98FF3, 0xA21873A3}, k[4] = {9, 7, 8, 6};
// v为要加密的数据是两个32位无符号整数
// k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
// printf("string: %u %u\n", v[0], v[1]);
// encrypt(v, k);
// printf("encode: %x %x\n", v[0], v[1]);
decrypt(v, k);
printf("decode: %x %x\n", v[0], v[1]);
unsigned long len = 8;
for (int i = 0; i < len; i++)
{
printf("%c", (*((char *)v + i)) & 0xff);
}
return 0;
}
得出GKcTFg0
注意需要md5加密
flag{77bca47fe645ca1bd1ac93733171c9c4}
SoMuchCode
找到scanf
交叉引用找到调用他的函数,sub_140001310(8i64, flag, &dword_14000A6F8);
很明显是xxtea加密
发现就是一个xxtea加密,改了dealt,中间的for循环没了,变成好多步,导致一堆代码。然后后面就需要得到密文了,密文是在后面调试过程中生成的。直接用IDA调,得出数据
脚本
#include <stdio.h>
#include <stdlib.h>
#define DELTA 0x33445566
int main()
{
unsigned int v[8] = {0x993CAB5C, 0x3F40E129, 0x777791DE, 0x737DFEA6, 0x0ECCF59E6, 0x0C9604CE3, 0x9682C0A5, 0x556F2A1E};
unsigned int key[4] = {0x000036B0, 0x00013816, 0x00000010, 0x0001E0F3};
unsigned int sum = 0;
unsigned int y,z,p,rounds,e;
int n = 8;
int i = 0;
rounds = 12;
y = v[0];
sum = (rounds*DELTA)&0xffffffff;
do //0x9E3779B9*(52/35)-0x4AB325AA,测试来要循环7次
{
e = sum >> 2 & 3;
for(p=n-1;p>0;p--) //34次循环
{
z = v[p-1];
v[p] = (v[p] - ((((z>>5)^(y<<2))+((y>>3)^(z<<4))) ^ ((key[(p^e)&3]^z)+(y ^ sum)))) & 0xffffffff;
y = v[p];
}
z = v[n-1];
v[0] = (v[0] - (((key[(p^e)&3]^z)+(y ^ sum)) ^ (((y<<2)^(z>>5))+((z<<4)^(y>>3))))) & 0xffffffff;
y = v[0];
sum = (sum-DELTA)&0xffffffff;
}while(--rounds);
for(i=0;i<8;i++)
{
printf("%c%c%c%c",*((char*)&v[i]+0),*((char*)&v[i]+1),*((char*)&v[i]+2),*((char*)&v[i]+3));
}
return 0;
}
flag{9b34a61df773acf0e4dec25ea5fb0e29}
KillerAid -未解出
看官方WP是AES加密,我死活看不出来,就先这样吧,这破题看一晚上
之后补上
一共两个文件 exe 是用C#开发的WinForm NETFrameWork
使用ILSpy反编译exe文件看到在MainWindows中的逻辑
private void Check_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(ID.Text) || string.IsNullOrEmpty(Code.Text))
{
MessageBox.Show("ID 或 Code不能为空!");
return;
}
byte[] bytes = Encoding.ASCII.GetBytes(ID.Text);
byte[] bytes2 = Encoding.ASCII.GetBytes(Code.Text);
int num = Math.Max(bytes.Length, bytes2.Length);
byte[] array = new byte[9] { 7, 90, 115, 1, 117, 99, 114, 97, 24 };
for (int i = 0; i < num; i++)
{
bytes[i % bytes.Length] ^= bytes2[i % bytes2.Length];
}
for (int j = 0; j < bytes.Length; j++)
{
if (bytes[j] != array[j])
{
MessageBox.Show("请检查ID 或 Code 是否正确");
return;
}
}
if (!CheckCode(Code.Text))
{
MessageBox.Show("请检查ID 或 Code 是否正确");
return;
}
MessageBox.Show("flag{" + ID.Text + "@" + Code.Text + "}");
}
DLL文件
应该不可能爆破吧,看看DLL文件
好难看,为什么有那么多do-while
行移位
v12 = v2[1];
v2[1] = v2[5];
v2[5] = v2[9];
v2[9] = v2[13];
v13 = v2[10];
v2[13] = v12;
v14 = v2[2];
v2[2] = v13;
v15 = v2[14];
v2[10] = v14;
v16 = v2[6];
v2[6] = v15;
v17 = v2[15];
v2[14] = v16;
v18 = v2[3];
v2[3] = v17;
v2[15] = v2[11];
v2[11] = v2[7];
v2[7] = v18;
列位移
do
{
v2_0 = *(v19 - 2);
v2_1 = *(v19 - 1);
v2_3 = v19[1];
v2_2 = *v19;
v19 += 4;
v25 = v2_1 ^ v2_0 ^ v2_2 ^ v2_3;
*(v19 - 6) = v25 ^ v2_0 ^ (2 * (v2_1 ^ v2_0)) ^ (27 * ((unsigned __int8)(v2_1 ^ v2_0) >> 7));
*(v19 - 5) = v25 ^ v2_1 ^ (2 * (v2_2 ^ v2_1)) ^ (27 * ((unsigned __int8)(v2_2 ^ v2_1) >> 7));
*(v19 - 4) = v25 ^ v2_2 ^ (2 * (v2_2 ^ v2_3)) ^ (27 * ((unsigned __int8)(v2_2 ^ v2_3) >> 7));
*(v19 - 3) = v25 ^ v2_3 ^ (2 * (v2_3 ^ v2_0)) ^ (27 * ((unsigned __int8)(v2_3 ^ v2_0) >> 7));
--v20;
}
while ( v20 );
通过上述可得code:Meaningless_!$!%*@^%#%_Code
脚本
from z3 import *
flag2 = bytearray(b'Meaningless_!$!%*@^%#%_Code')
id = [BitVec('a%d' % i, 8) for i in range(9)]
fuckId = [i for i in id]
target =[7, 90, 115, 1, 117, 99, 114, 97, 24]
tt = bytearray(target)
for j in range(len(flag2)):
id[j % len(target)] ^= flag2[j % len(flag2)]
s = Solver()
for i in range(9):
s.add(id[i] == target[i])
s.check()
res = s.model()
id_ = ''
for i in range(9):
id_ += chr(res[fuckId[i]].as_long())
print(id_)
# ginkgo_CX
flagflag{ginkgoCX@Meaningless!$!%*@^%#%_Code}