2021 BugkuCTF “长城杯“ ——“你这flag保熟吗“ 详解

废话:第一次参加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看到一个大佬的题解:

2021“长城杯”_MISC_“你这flag保熟吗”_复现

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}


参考文章:2021“长城杯”_MISC_“你这flag保熟吗”_复现_你这flag保熟吗 ctf-CSDN博客

  • 24
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值