2024全国大学生信息安全竞赛创新实践能力赛初赛 CISCN2024 部分题目个人Writeup
以下为我个人在比赛中A出来的题目的题解。
解题过程
Power Trajectory Diagram
使用py中的numpy和pandas库读取npz文件并保存为csv文件,代码如下:
import numpy as np
import pandas as pd
np.set_printoptions(threshold=np.inf)
a1 = np.load('attachment.npz', allow_pickle=True)
print(a1.files)
print('read:', a1)
index = a1['index']
myin = a1['input']
myout = a1['output']
mytra = a1['trace']
# print(mytra.shape)
df = pd.DataFrame(mytra)
df.to_csv('data1.csv', index=False)
df1 = pd.DataFrame({'index':index, 'input': myin})
df1.to_csv('data2.csv', index=False)
得到data1.csv、data2.csv,合并得到data.csv。
打开data.csv,可以看到功耗数据,根据https://zhuanlan.zhihu.com/p/157585244,我认为该题的关键是在于找到与其它字符不同的字符,就是该index的正确密码。
基于此,利用Excel的折线图功能,例如第四个字符,如图所示:
可以看出,有一条绿色线与其它线都不同,为c,所以第四个字符就是c。
依次重复,得到整个密钥:_ciscn_2024_
即flag:flag{_ciscn_2024_}
androidso_re
使用GDA反编译题目所给的apk,可以看到判断flag的函数:
可以看到,程序是对输入的flag进行DES加密后与正确flag的密文进行比对,密文为:JqslHrdvtgJrRs2QAp+FEVdwRPNLswrnykD/sZMivmjGRKUMVIC/rw==
其中获取key和iv函数如下图:
可以看到,这两个函数为native函数,是在secret_entrace之中的,用压缩软件打开apk,可以从lib文件夹里找到该库。
解压下来,用IDA反编译,首先找到生成iv的函数:
根据代码可以判断此为ROT13加密,变换为65-49=16位,原文为 F2IjBOh1mRW=
,加密得到iv:
接下来找到生成key的函数,根据代码可以判断,此为RC4加密,密钥为 YourRC4Key
,原文为 TFSecret_Key
,加密后经历了一次异或,key为 0x038933B8540C206
,加密后,截取其前8位得到key:
最后,使用key和iv对密文进行解密得到flag:
通风机
使用binwalk可以分离出一个zlib文件,使用python解压,代码如下:
import zlib
def decompress_zlib_file(input_filename, output_filename):
with open(input_filename, 'rb') as compressed_file:
compressed_data = compressed_file.read()
decompressed_data = zlib.decompress(compressed_data)
with open(output_filename, 'wb') as output_file:
output_file.write(decompressed_data)
# Example usage
input_file = '35.zlib'
output_file = 'decompressed_data.txt'
decompress_zlib_file(input_file, output_file)
用Winhex 打开 decompressed_data.txt,可以看到经过base64编码后的flag。
解码得到flag:
古典密码
根据题目名称,枚举各种古典密码,最后发现是Atbash密码,base64解密后得到接近flag的一串字符。
随后使用一把梭工具,发现其为栅栏密码并解密,得到flag。
whereThel1b
查看python代码,发现它使用了一个so,将输入的flag进行加密后与encry对比,判断flag。
首先对那个so文件进行逆向,发现根本看不懂。所以使用黑盒测试的方法,分别输入a、b、c、d、e、f、g、h,发现a、b、c,d、e、f,g、h加密后的第一个字符分别相等,第三、四个字符全部相等。同时输入1、2、3->4,4->8。通过这些规律发现该加密与base64有关,于是我输入56/4*3=42个a,观察得出的ret,与base64('a'*42)做对比,如图所示:
import base64
import whereThel1b
def main(flag):
encry = [108, 117, 72, 80, 64, 49, 99, 19, 69, 115, 94, 93, 94, 115, 71, 95, 84, 89, 56, 101, 70, 2, 84, 75, 127, 68, 103, 85, 105, 113, 80, 103, 95, 67, 81, 7, 113, 70, 47, 73, 92, 124, 93, 120, 104, 108, 106, 17, 80, 102, 101, 75, 93, 68, 121, 26]
ret = whereThel1b.trytry(flag)
print(flag.decode(), ret[0], end=' ')
if ret[0] == 108:
print(flag)
main(b'a'*42)
print([i for i in base64.encodebytes(b'a'*42)])
观察发现并无明显规律,尝试是否为异或加密,解密代码如下:
encry = [108, 117, 72, 80, 64, 49, 99, 19, 69, 115, 94, 93, 94, 115, 71, 95, 84, 89, 56, 101, 70, 2, 84, 75, 127, 68, 103, 85, 105, 113, 80, 103, 95, 67, 81, 7, 113, 70, 47, 73, 92, 124, 93, 120, 104, 108, 106, 17, 80, 102, 101, 75, 93, 68, 121, 26]
a = [111, 79, 118, 80, 67, 85, 86, 72, 70, 78, 116, 93, 74, 73, 80, 77, 84, 119, 78, 122, 81, 103, 64, 79, 106, 71, 100, 69, 106, 113, 79, 123, 95, 121, 66, 94, 114, 66, 88, 75, 95, 65, 93, 124, 126, 127, 75, 75, 71, 75, 113, 73, 73, 84, 117, 75]
b = [89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 10]
ret = []
for i in range(0, len(encry)):
ret += [a[i] ^ b[i] ^ encry[i]]
print(ret)
for i in ret:
print(chr(i),end="")a
发现明显的Zmxh,为flag的base64编码,解密得到flag:
gdb_debug
用IDA反编译,定位main函数,可以发现程序逻辑为:
-
输入str,将str每一个字符异或一个随机数,得到str2
-
定义str3,随机交换洗牌,得到一个0-38的随机不重复的序列
-
根据str3,得到str4,使得
str4[i]=str2[str[3]]
-
将str4每一个字符异或一个随机数
-
根据str4,得到s1,使得
s1[i]=str4[i]^byte_5636B30010A0
,其中byte_5636B30010A0固定且已给出 -
将s1与s2作对比,若相同,则输入flag正确,其中s2为"congratulationstoyoucongratulationstoy"
这个程序的特性在于,在置随机数种子时,使用的是当前时间按位与0xF0000000的结果为种子,使得种子在很长一段时间执行时都相同。
基于此,我们可以提前求出要用到的随机数(要在Linux系统上运行,与Win的rand()逻辑不同):
srand(((int)time(0))& 0xF0000000);
char rand1[38];
unsigned int rand2[38],rand3[38];
for(int i=0;i<len;i++)
{
rand1[i] = rand();
// cout << (unsigned int)(rand1[i]&0xff) << " ";
}
for(int i=len-1;i;--i)
{
rand2[i] = rand()%(i+1);
// cout << (unsigned int)(rand2[i]&0xff) << " ";
}
for(int i=0;i<len;i++)
{
rand3[i] = rand();
}
然后根据上面所述步骤,反过来计算一遍,完整代码如下:
# Run in linux
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <vector>
#include <algorithm>
#include <stack>
#include <set>
#include <map>
#include <ctime>
#include <unistd.h>
#include "defs.h"
// #include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double DD;
int main()
{
srand(((int)time(0))& 0xF0000000);
char s2[] = "congratulationstoyoucongratulationstoy";
unsigned char byte_10A0[] = {
0xBF, 0xD7, 0x2E, 0xDA, 0xEE, 0xA8, 0x1A, 0x10, 0x83, 0x73, 0xAC, 0xF1, 0x06, 0xBE, 0xAD, 0x88,
0x04, 0xD7, 0x12, 0xFE, 0xB5, 0xE2, 0x61, 0xB7, 0x3D, 0x07, 0x4A, 0xE8, 0x96, 0xA2, 0x9D, 0x4D,
0xBC, 0x81, 0x8C, 0xE9, 0x88, 0x78, 0x00, 0x00
};
char rand1[38];
unsigned int rand2[38],rand3[38];
int len = strlen(s2);
cout << "len:" << len << endl;
for(int i=0;i<len;i++)
{
rand1[i] = rand();
// cout << (unsigned int)(rand1[i]&0xff) << " ";
}
for(int i=len-1;i;--i)
{
rand2[i] = rand()%(i+1);
// cout << (unsigned int)(rand2[i]&0xff) << " ";
}
for(int i=0;i<len;i++)
{
rand3[i] = rand();
}
for(int i=0;i<len;i++)
{
s2[i] ^= byte_10A0[i];
}
for(int i=0;i<len;i++)
{
s2[i] ^= rand3[i];
}
int str3[39];
for(int i=0;i<len;i++)
{
str3[i] = i;
}
for(int i=len-1;i;--i)
{
int temp = str3[i];
str3[i] = str3[rand2[i]];
str3[rand2[i]] = temp;
}
//s2[i]=str2[str3[i]];
char str2[39] = {0};
for(int i=0;i<len;i++)
{
str2[str3[i]] = s2[i];
}
for(int i=0;i<len;i++)
{
str2[i] ^= rand1[i];
}
cout << str2;
return 0; //-22 61 13 92
}
在Linux上运行,得到flag: