废话:第一次参加CTF比赛,啥也不会,去刷刷真题吧,刷到21年“长城杯”Misc题--“你这flag保熟吗”。下载解压,两张图片一个压缩包,解压要密码,像这种有压缩包要密码,还有图片或者其他文件的,直接不要去想暴力破解了。将图片分离得到一个passwor.xls和hint.txt。两个都打开了,看不出来啥,直接网上搜WP。好!搜来的题解看了,更加懵了,什么希尔伯特曲线,什么brainfuck(BF)密码,什么BF执行的栈,什么atbash密码。学了好久,记录一下。
正文
1、下载附件分析
- 先看压缩包,要密码
- 再看图片
- 看图片属性(无果)、图片是否隐写(无果)、用winhex打开图片(搜pass、password、key、flag等关键字,无果)、查看是否有文件合并(有果)
2、图片文件分离
2.1用binwalk查看,发现两张图片里面都有东西
binwalk 1.png
binwalk 2.png
2.2 图片分离
binwalk -e 1.png
binwalk -e 2.png
得出
直接看password.xls和hint.txt
3、分析解密
打开password.xls
看不出来什么,打开hint.txt
把下面那串代码Vm0wd2QyUXIVWGxW拿去base64解密
有奇奇怪怪的字符,肯定不是密码
难道是将xls里面的字符拼起来base64解密
但是后面这么多=号,应该不是
后面没思路,去搜WP看到一个大佬的题解:
4、希尔伯特曲线?
看到出来像希尔伯特曲线,接下来怎么画呢?去搜了一下,看了一篇文章:希尔伯特曲线
发现几点:
- 2n*2n的方格,查看password.xls的是256*256的方格
- 根据上图的方法构造,最后的图一定是对称的
- 图中起点是左下那个格子开始,结束是右下的格子。xls目前画出的图是倒置的,所以起点是左上,结束应该是右上
- 最后画出的图像,跟着曲线将字符连接起来的字符串应该以“Vm0wd2QyUXlVWGxW”开始,以“=”号结束
分析到这里,知道该怎么画后面的线了,难道用手去画剩下的?不!python里HilbertCurve函数就可以画了。关于这个函数的用法,我没看太懂。希尔伯特曲线 ·PyPI
直接用大佬的代码
import xlrd
from hilbertcurve.hilbertcurve import HilbertCurve
data = xlrd.open_workbook('./password.xls')
table = data.sheets()[0]
rows = table.nrows # 获取表格的行数 -- 256
cols = table.ncols # 获取表格的列数 -- 256
hilbert_curve = HilbertCurve(17, 2)
str1 = ''
# 遍历曲线上每个点的字符,拼接到str1中
for k in range(rows * cols):
i, j = hilbert_curve.point_from_distance(k)
str1 += table.cell_value(i, j)
print(str1)
我一直纠结,HilbertCurve(17, 2)这里为啥是17和2
后面知道这个函数第二个参数n表示维度。这里表格是二维的,所以为2
那p=17怎么来的呢,直接试了一下其他数字,发现当p=9时,沿着曲线连接的字符串也以“Vm0wd2QyUXlVWGxW”开始,“=”结束。
根据以“Vm0wd2QyUXlVWGxW”开始,以“=”结束,写了下面的代码:
import xlrd
from hilbertcurve.hilbertcurve import HilbertCurve
data = xlrd.open_workbook('./password.xls')
table = data.sheets()[0]
rows = table.nrows # 获取表格的行数 -- 256
cols = table.ncols # 获取表格的列数 -- 256
p = 1
while True:
hilbert_curve = HilbertCurve(p, 2)
str1 = ''
# 遍历曲线上每个点的字符,拼接到str1中
for k in range(rows * cols):
i, j = hilbert_curve.point_from_distance(k)
str1 += table.cell_value(i, j)
# 如果字符串以'Vm0wd2QyUXlVWGxW'开始以等"="号,则跳出循环
if str1.startswith("Vm0wd2QyUXlVWGxW") and str1.endswith(table.cell_value(0, cols - 1)):
print(str1)
break
p += 1
输出结果有大串的base64代码,所有的“=”也都在结尾,满足上面自己发现的规律,把等号删除掉只剩下两个或者一个。
str2 = str1.split("=")[0] + "=="
拿去解码,发现还是一大串,大佬说要n次解码,直接上代码
# n次base64解码
import base64
while True:
str2 = base64.b64decode(str2)
print(str2)
结果:
1f_y0u_h4ve_7he_fllllllag,_I_muSt_vvant_1t! 应该就是解压密码
5、brainfuck(BF)密码?
解压打开后是
直接用网上找到BF解释器C语言代码运行(也可以用在线BF运行):
errer难道错误了?看大佬的,BF执行的栈?
BF执行的栈:简单来说就是运行过程中变量的值
我怎么看?慢慢去分析?去搜一下brainfuck转C语言,没搜到我想要的,自己写一个吧
了解了一下brainfuck
就这几个字符,if...else的事
brainfuck转为C语言脚本:
def writeC(text, output_file):
with open(output_file, 'a+') as f:
f.write(text + "\n")
def parse1(bp_file_path, output_file):
ptr = 0
with open(bp_file_path, 'r') as f:
bp = f.read()
for i in list(bp):
if i == '+':
writeC("var[" + str(ptr) + "]++;", output_file)
elif i == '-':
writeC("var[" + str(ptr) + "]--;", output_file)
elif i == '>':
ptr += 1
elif i == '<':
ptr -= 1
elif i == '.':
writeC("putchar(var[" + str(ptr) + "]);", output_file)
elif i == ',':
writeC("scanf(\"&var[" + str(ptr) + "]\");", output_file)
elif i == '[':
writeC("while(var[" + str(ptr) + "]){", output_file)
elif i == ']':
writeC("}", output_file)
def parse2(parse1_file):
with open(parse1_file, 'r') as f:
code = f.read()
print("#include<stdio.h>\n#include<stdlib.h>\nint main(){")
varSum = 0
for i in range(1024):
if "var[" + str(i) + "]" not in code:
varSum = i
break
print("int var[" + str(varSum) + "] = {0};")
preVar = ""
num = 0
for i in code.split("\n"):
if preVar == i:
num += 1
elif preVar == "" and ("++" in i or "--" in i):
preVar = i
num = 1
elif num != 0:
if "+" in preVar:
print(preVar.split("+")[0] + "+=" + str(num) + ";")
else:
print(preVar.split("-")[0] + "-=" + str(num) + ";")
print(i)
num = 0
preVar = ""
else:
print(i)
print("return 0;\n}")
if __name__ == '__main__':
bpfile = './flag.txt' # brainfuck代码存放文件
output1_path = './flag.tmp' # 临时存放文件
parse1(bpfile, output1_path)
parse2(output1_path)
转后的C语言代码
#include<stdio.h>
#include<stdlib.h>
int main(){
int var[47] = {0};
var[1]+=9;
while(var[1]){
var[0]+=13;
var[1]--;
}
var[2]+=3;
while(var[2]){
var[1]+=37;
var[2]--;
}
var[3]+=2;
while(var[3]){
var[2]+=61;
var[3]--;
}
var[4]+=4;
while(var[4]){
var[3]+=29;
var[4]--;
}
var[5]+=3;
while(var[5]){
var[4]+=41;
var[5]--;
}
var[5]+=83;
var[7]++;
var[7]+=5;
while(var[7]){
var[6]+=19;
var[7]--;
}
var[8]+=2;
while(var[8]){
var[7]+=41;
var[8]--;
}
var[9]+=11;
while(var[9]){
var[8]+=11;
var[9]--;
}
var[10]+=2;
while(var[10]){
var[9]+=59;
var[10]--;
}
var[11]+=7;
while(var[11]){
var[10]+=15;
var[11]--;
}
var[11]+=103;
var[13]++;
var[13]+=4;
while(var[13]){
var[12]+=19;
var[13]--;
}
var[14]+=8;
while(var[14]){
var[13]+=11;
var[14]--;
}
var[15]+=6;
while(var[15]){
var[14]+=17;
var[15]--;
}
var[16]+=7;
while(var[16]){
var[15]+=15;
var[16]--;
}
var[16]+=101;
var[18]++;
var[18]+=1;
while(var[18]){
var[17]+=59;
var[18]--;
}
var[19]+=5;
while(var[19]){
var[18]+=19;
var[19]--;
}
var[20]+=7;
while(var[20]){
var[19]+=7;
var[20]--;
}
var[21]+=8;
while(var[21]){
var[20]+=9;
var[21]--;
}
var[22]+=5;
while(var[22]){
var[21]+=19;
var[22]--;
}
var[23]+=4;
while(var[23]){
var[22]+=13;
var[23]--;
}
var[24]+=5;
while(var[24]){
var[23]+=19;
var[24]--;
}
var[24]+=101;
var[25]++;
var[25]+=100;
var[27]++;
var[27]+=5;
while(var[27]){
var[26]+=8;
var[27]--;
}
var[27]+=109;
var[29]++;
var[29]+=6;
while(var[29]){
var[28]+=17;
var[29]--;
}
var[30]+=2;
while(var[30]){
var[29]+=59;
var[30]--;
}
var[31]+=7;
while(var[31]){
var[30]+=15;
var[31]--;
}
var[32]+=9;
while(var[32]){
var[31]+=13;
var[32]--;
}
var[33]+=6;
while(var[33]){
var[32]+=17;
var[33]--;
}
var[34]+=3;
while(var[34]){
var[33]+=11;
var[34]--;
}
var[35]+=5;
while(var[35]){
var[34]+=19;
var[35]--;
}
var[36]+=10;
while(var[36]){
var[35]+=12;
var[36]--;
}
var[37]+=6;
while(var[37]){
var[36]+=17;
var[37]--;
}
var[38]+=7;
while(var[38]){
var[37]+=15;
var[38]--;
}
var[38]+=101;
var[40]++;
var[40]+=1;
while(var[40]){
var[39]+=59;
var[40]--;
}
var[41]+=5;
while(var[41]){
var[40]+=25;
var[41]--;
}
var[41]+=101;
putchar(var[41]);
var[43]+=6;
while(var[43]){
var[42]+=19;
var[43]--;
}
putchar(var[42]);
var[44]+=6;
while(var[44]){
var[43]+=19;
var[44]--;
}
putchar(var[43]);
var[45]+=3;
while(var[45]){
var[44]+=37;
var[45]--;
}
putchar(var[44]);
var[46]+=6;
while(var[46]){
var[45]+=19;
var[46]--;
}
putchar(var[45]);
return 0;
}
发现总有47个变量(我这里是数组形式,开辟了47的空间,下标为0-46) 最后只输出了41、42、43、44、45。那前面0-40呢?写个循环输出一下,放进去执行
int i;
printf("\n");
for(i = 0;i <= 40;i++){
putchar(var[i]);
}
看最后一行输出:uozt{SrRyvig_Xfiev_1H_4_ee0mwviuf!_xfiev}
这还不是flag!
6、阿特巴希密码?
直接用大佬的代码(或埃特巴什密码转换器_Atbash Cipher-ME2在线工具 (metools.info))
list2 = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z']
list3 = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
'W', 'X', 'Y', 'Z']
tmp = 0
string = input("输入:")
output = ''
for i in string:
if i in list2:
tmp = list2.index(i) # 获取索引
output += list2[len(list2) - tmp - 1]
elif i in list3:
tmp = list3.index(i) # 获取索引
output += list3[len(list3) - tmp - 1]
else:
output += i
print(output)
获得flag:flag{hiibert_curve_1s_4_vv0nderfu!_curve}