中国石油第三届网络安全攻防大赛|Spiral Writeup
次阅读
5 min read
偶然间看到群友赛后发的题目,觉得有一些题目比较新颖,所以正好做一下玩玩🤪
Spiral
打开附件看到有flag.7z和spiral.png两个文件,猜测是需要从spiral.png图片中提取key,然后再去解密flag.7z压缩包。
首先,打开spiral.png看看:
分析后发现这是一张尺寸为1024*1024的正方形图片,根据题目名称和图片内容,它含有非常明显的螺旋特征。再查看图片的hex数据:
发现图片中存在提示:
ZmxhZ3tmYWtlX2ZsYWd9IEJ1dCBSR0JB6Imy5b2p56m66Ze057y65LiA5LiN5Y+v5ZOmfiDmiJHmlZnkvaDov5jljp86YUhSMGNITTZMeTlpYkc5bkxtTnpaRzR1Ym1WMEwwZFhYM2RuTDJGeWRHbGpiR1V2WkdWMFlXbHNjeTh4TWpBME1EWXhPVEk9C
base64解密后可以看到提示:
flag{fake_flag} But RGBA色彩空间缺一不可哦~ 我教你还原:aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dXX3dnL2FydGljbGUvZGV0YWlscy8xMjA0MDYxOTI=
再次base64解密可以看到螺旋矩阵的算法实现https://blog.csdn.net/GW_wg/article/details/120406192
def function(n):
matrix = [[0] * n for _ in range(n)]
number = 1
left, right, up, down = 0, n - 1, 0, n - 1
while left < right and up < down:
# 从左到右
for i in range(left, right):
matrix[up][i] = number
number += 1
# 从上到下
for i in range(up, down):
matrix[i][right] = number
number += 1
# 从右向左
for i in range(right, left, -1):
matrix[down][i] = number
number += 1
for i in range(down, up, -1):
matrix[i][left] = number
number += 1
left += 1
right -= 1
up += 1
down -= 1
# n 为奇数的时候,正方形中间会有个单独的空格需要单独填充
if n % 2 != 0:
matrix[n // 2][n // 2] = number
return matrix
根据以上信息,大胆猜测spiral.png是按照从左到右、从上到下的顺序读取一张正常的图片像素之后,再按照螺旋的方法写入新的矩阵里面所生成的。
写入螺旋矩阵顺序:
- 填充上行从左到右
- 填充右列从上到下
- 填充下行从右到左
- 填充左列从下到上
所以我们可以根据这个猜想先按照螺旋的方法读取spiral.png所有像素,然后再按行还原这个图片。
from PIL import Image
import numpy as np
def reverse_spiral(image_path):
image = Image.open(image_path)
pixels = np.array(image)
n = image.width
result = np.zeros_like(pixels)
matrix = [[0] * n for _ in range(n)]
number = 1
left, right, up, down = 0, n - 1, 0, n - 1
while left < right and up < down:
# 从左到右
for i in range(left, right):
matrix[up][i] = number
number += 1
# 从上到下
for i in range(up, down):
matrix[i][right] = number
number += 1
# 从右向左
for i in range(right, left, -1):
matrix[down][i] = number
number += 1
# 从下到上
for i in range(down, up, -1):
matrix[i][left] = number
number += 1
left += 1
right -= 1
up += 1
down -= 1
# n 为奇数的时候,正方形中间会有个单独的空格需要单独填充
if n % 2 != 0:
matrix[n // 2][n // 2] = number
'''
获取当前位置在螺旋遍历中对应的索引index
(0,0)->1
(0,1)->2
(0,2)->3
(0,3)->4
(0,4)->5
(0,5)->6
(0,6)->7
(0,7)->8
(0,8)->9
(0,9)->10
'''
for i in range(n):
for j in range(n):
# print('(' + str(i) + ',' + str(j) + ')', end = '->')
print(matrix[i][j])
index = matrix[i][j] - 1
x = index // n
y = index % n
result[x][y] = pixels[i][j]
# 图片二值化
result[result != 255] = 0
result_image = Image.fromarray(result)
return result_image
image_path = "./spiral.png"
restored_image = reverse_spiral(image_path)
restored_image.save("flag.png")
restored_image.show()
跑完脚本之后成功还原这张图片,但是发现只有PASSWORD字样,解密flag.7z无果。
观察这张图片下方发现有大量留白,使用stegSolve分析可以看到flag.7z的压缩包密码:
但是这样不是很优雅,根据题目提示RGBA色彩空间缺一不可
,读取所有像素后发现有类似于254的灰度像素值,因此大胆猜测还有一些灰度像素点肉眼无法分辨,可以暴力一些直接把图片二值化,像素不为255的全部变成0,也就是黑色。
# 图片二值化
result[result != 255] = 0
成功拿到flag.7z压缩包密码mUv8vvGRMNK5mgbxPNsH
打开压缩包后是一个flag字符画,调整窗口大小即可拿到flag: