文章目录
- [NSSRound#12 Basic]Bulbasaur
- [NSSRound#12 Basic]Secrets in Shadow
- [NSSRound#12 Basic]奇怪的tex文件
- [NSSRound#12 Basic]坏东西
- [NSSRound#12 Basic]ordinary forensics
- [NSSRound#12 Basic]Noisy Cube
- [NSSRound#12 Basic]ability
- [NSSRound#12 Basic]strange python
[NSSRound#12 Basic]Bulbasaur
附件是一个压缩包和一张PNG图片,压缩包是被加密的,加密的密码肯定在图片中被隐写的,开始以为是lsb隐写,因为在观察通道的时候发现白色通道部分会有黑色横线,但是一直提取不出来。结果是盲水印隐写,通过盲水印提取能够看到密码blind watermark。
解开压缩包密码后发现是阴阳怪气编码,第一次遇到,稍微看了下原理,非常简单,主要就是由就 这 ¿和不 会 吧 ?两个字符组成,前面那个代表0,后面那个代表1,将要编码的数据转成8位的二进制然后用字符代替即可。进行解码即可得到flag。
解码网站:
阴阳怪气编解码
[NSSRound#12 Basic]Secrets in Shadow
ssh连接后发现flag就在根目录,但是只有root权限能够读取,因此要进行提权,搜索文件后发现/etc/shadow能够被修改,可以直接把/etc/shadow的root密码清空掉,通过su命令提升到root用户就无需密码了。
[NSSRound#12 Basic]奇怪的tex文件
打开附件,会发现有一堆的tex文件,一共有44张,开始以为是什么高深的东西,然后就去网上找什么是tex,然后tex怎么编译等等,发现都没法得到想要的东西,后面仔细观察发现每个tex好像藏了一张PNG图片,于是提取出来就看到了flag。
from pwnlib.util.fiddling import unhex
for i in range(44):
f = open(f'A:\下载\奇怪的tex文件\Tex\\{i}.tex', 'rb').read().hex()
file = open(f'A:\下载\奇怪的tex文件\Tex\\result\\{i}.png', 'wb')
file.write(unhex(f[64::]))
即可看到flag
[NSSRound#12 Basic]坏东西
附件是一个加密的压缩包,使用爆破工具爆破发现密码就是文件名,一顿解压发现藏了很多压缩包,使用脚本进行解压,就会得到一个flag.pdf文件。
import zipfile
name = '38940'
while True:
fz = zipfile.ZipFile(name + '.zip', 'r')
fz.extractall(pwd=bytes(name, 'utf-8'))
name = fz.filelist[0].filename[0:9].rstrip('.zip')
fz.close()
pdf文件是损坏的,版本是1.7,然后就被题目给迷惑了,题目叫坏东西,就感觉要把这个pdf文件修好打开,但是在网上找了很久工具什么的都不成功。结果是直接使用记事本打开就能够看到flag的每一个字符隐藏在了/Filter /FlateDecode哪里,终究是没观察文件本身,至于/FlateDecode是PDF文件的某种zlib的解压缩算法。
[NSSRound#12 Basic]ordinary forensics
一道取证题+磁盘加密的形式考的,提示给的也很多,提示了veraCrypt软件,并且给了一个unknown文件,正好与veraCrypt对应,在打的时候,找到了密码,但是一直显示错误,没想到这个密码竟然是提取出来某个压缩包的密码,里面才是磁盘文件😭,一直没注意里面的压缩包文件。
将两个文件都提取出来,会看到提示和加密的压缩包,提示上面写了vera,useful.zip是加密的,里面藏的才是vera要用的密码,在cmdscan中可以看到压缩包的密码,解压出来的就是vera的keyfile。
进行挂载后能得到一个secret文件,用过linux查看可以得到它是一个ext3文件,将它挂载会看到一个flag.zip文件,使用如下命令直接挂载到home目录:
mount -o loop secret ~/
查看压缩包的信息看到密码的格式是username_hostname,继续从镜像文件中找,通过注册表项中的可以看到信息,可以找到密码为Mario_Princess-Peach,解压后即可得到flag
[NSSRound#12 Basic]Noisy Cube
很陌生的题目,看着WP大概了解了一下。
题目给了一个burp.xml和一个py代码文件,burl.xml中是装了流量,在最下方可以通过base64的request和response看到通过ssti即%7B%7Burl_for._globals_._builtins_%5B%27_import_%27%5D(%27os%27).popen(%27type%20matrix333.txt%27).read()%7D%7D读取了matrix和app.py文件,将matrix文件提取出来,有一串一串的数字。
再看附件中的py文件,可以看到matrix333的写入代码,对代码进行一步一步的分析,发现代码首先将flag的值进行了base64编码,然后将编码的结果分成了三部分即三段字符串,后通过三段字符串分别生成3个二维码,通过二维码的像素点数据生成二维码01矩阵,像素点0对应矩阵中的1,然后将三段矩阵数据传入create_cube函数中生成cube后,将cube通过pos_noise加入噪点,然后最终结果通过每一行数据与行号异或转成long数据写入matrix333中,这里得到了matrix333的结果数据,先进行解密。解密后发现都是列表,并且最后一列只有0和1,应该是进行用于进行分割的。将点扩大10倍后绘图就能还原出三个二维码,扫码即可得到flag。
py文件:
import sys
import qrcode
from base64 import b64encode as base64
from loguru import logger
import random
from Crypto.Util.number import *
# from secret import flag
def create_matrix(input_string: str) -> list:
# 创建二维码对象
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=1, # 调整像素大小
border=0, # 调整边框大小
)
# 将文本添加到二维码对象
qr.add_data(input_string)
qr.make(fit=True)
# 获取二维码的图像并加载像素数据
img = qr.make_image(fill_color="black", back_color="white")
pixels = img.load()
# 遍历像素数据并创建矩阵
matrix = []
for y in range(img.size[1]):
row = []
for x in range(img.size[0]):
pixel = pixels[x, y]
if pixel == 0:
row.append(1)
else:
row.append(0)
matrix.append(row)
logger.info(input_string)
logger.info(len(matrix))
logger.info(matrix)
return matrix
def create_cube(string1: list, string2: list, string3: list) -> list:
cube_size = len(string1)
if cube_size == len(string2) and cube_size == len(string3):
matrix = [[[1 for x in range(cube_size)] for y in range(cube_size)] for z in range(cube_size)]
for x in range(cube_size):
for y in range(cube_size):
if string1[x][y] == 0:
for z in range(cube_size):
matrix[z][x][y] = 0
if string2[x][y] == 0:
for z in range(cube_size):
matrix[x][z][y] = 0
if string3[x][y] == 0:
for z in range(cube_size):
matrix[x][y][z] = 0
logger.info(matrix)
return matrix
else:
logger.error('输入的字符串长度不一致')
return [None]
def init(flag: str) -> list[int, list]:
part_size = int(len(flag)/3)
logger.info(f"{part_size=}")
string1 = flag[:part_size] #将base64后的flag分成三部分
string2 = flag[part_size:part_size*2]
string3 = flag[part_size*2:]
logger.info(f"{string1=}, {string2=}, {string3=}")
matrix1 = create_matrix(string1)#创建对应的二维码,生成0和1的矩阵
size = len(matrix1)
matrix2 = create_matrix(string2)
matrix3 = create_matrix(string3)
cube = create_cube(matrix1, matrix2, matrix3)
return [size, cube]
def encrypt(message: str, line: int) -> str:
logger.info(message)
logger.info(bytes_to_long(message.encode()))
logger.info(str(line), str(bytes_to_long(message.encode()) ^ line))
return str(bytes_to_long(message.encode()) ^ line)
def pos_noise(x: int, y: int, z: int) -> list:
for _ in range(noise_cnt):
yield [random.randint(x - noise_size, x + noise_size),
random.randint(y - noise_size, y + noise_size),
random.randint(z - noise_size, z + noise_size)]
logger.remove()
logger.add(sys.stderr, level='SUCCESS') # 只输出警告以上的日志
input_flag = b''
result = init(str(base64(input_flag)))
size = result[0]
counter = 0
box_size = 10
noise_size = (box_size // 2) - 1
noise_cnt = noise_size ** 3 // 8
logger.success(result)
result_list = []
for i in range(size):
for j in range(size):
for k in range(size):
for res in pos_noise((i + 1) * box_size, (j + 1) * box_size, (k + 1) * box_size):
# counter += 1
temp_list = res
temp_list.append(result[1][i][j][k])
result_list.append(temp_list)
# m = encrypt(f'{temp_list[0]} {temp_list[1]} {temp_list[2]} {temp_list[3]}', counter)
# f.write(f'{m}\n')
random.shuffle(result_list)
with open('matrix333.txt', 'w') as f:
for i in result_list:
counter += 1
m = encrypt(f'{i[0]} {i[1]} {i[2]} {i[3]}', counter)
f.write(f'{m}\n')
# for res in pos_noise(i+1, j+1, k+1):
# counter += 1
# res.append(result[1][i][j][k])
# m = encrypt(f'{res[0]} {res[1]} {res[2]} {res[3]}', counter)
# f.write(f'{m}\n')
# counter += 1
# m = encrypt(f'{i + 1} {j + 1} {k + 1}', counter)
# f.write(f'{m}\n')
# f.write(f'{i + 1} {j + 1} {k + 1}\n'
# if result[1][i][j][k] == 1:
# counter += 1
# m = encrypt(f'{i + 1} {j + 1} {k + 1}', counter)
# f.write(f'{m}\n')
# f.write(f'{i + 1} {j + 1} {k + 1}\n')
from Crypto.Util.number import long_to_bytes
import matplotlib.pyplot as plt
f = open('A:\下载\\mara.txt', 'r+', encoding='utf-8')
l = f.readlines()
res = set()
for x in range(len(l)):
result = long_to_bytes(int(l[x].strip()) ^ (x + 1)).decode().split(" ")
print(result[0])
if result[3] == "1":
for i, x in enumerate(result):
result[i] = round(int(x), -1)
res.add(tuple(result[:-1]))
print(res)
x = []
y = []
z = []
for r in res:
x.append(r[0])
y.append(r[1])
z.append(r[2])
ax = plt.subplot(projection="3d")
ax.scatter(x, y, z, c="r")
ax.view_init(elev=90, azim=0)
plt.savefig("./save3.png")
ax.clear()
ax.scatter(y, z, x, c="r")
ax.view_init(elev=90, azim=0)
plt.savefig("./save1.png")
ax.clear()
ax.scatter(z, x, y, c="r")
ax.view_init(elev=90, azim=0)
plt.savefig("./save2.png")
ax.clear()
也就只能复现理解下,依旧不太懂,刚接触Misc不久,可能以后还得慢慢了解。
[NSSRound#12 Basic]ability
与Linux的Capabilities有关,众所周知,linux在权限控制中分为root权限和普通用户权限,其中SUID权限的文件或命令会使普通用户在执行的时候赋予了该文件或者命令所有者的身份去运行,这样子使得权限面太广,容易被利用。因此就诞生了capabilities属性,通过capabilities更加细分权限控制,通过setcap和getcatp可以查看和设置程序文件的capabilities 属性,拥有capabilities的文件可以赋予更精确的特全,隔绝SUID文件的风险性。
如图为ping命令添加capabilities的权限,允许使用原始套接字和允许执行系统管理任务
这题就是与Capabilities有关,dig命令被设置了cap_dac_override=ep即忽略DAC访问权限,可以通过此命令读取flag,dig命令在查询DNS时,会将查询的内容也给显示出来。
[NSSRound#12 Basic]strange python
使用nc连接端口后,是一个python shell,通过dir()查看当前范围的内置变量,发现有flag、random、seed等,发现flag是被随机数打乱的,可以进行还原。
import random
flag=['87', 'cc', 'c2', 'SC', '-4', '5-', 'b1', '4c', '22', 'NS', 'a5', '7b', 'a}', 'TF', '-a', '2e', '7e', '61', '61', '97', '{d', 'e-']
seed=114514
random.seed(seed)
result=[]
for i in range(22):
result.append(i)
random.shuffle(result)
result_flag=[]
for i in range(len(flag)):
print(result.index(i))
result_flag.append(flag[result.index(i)])
print(''.join(result_flag))
根据巨魔师傅的WP,这里还有解法2,通过capablities来读取预加载脚本,因为python启动交互式终端的时候,会读取PYTHONSTARTUP这一环境变量作为shell启动时的预先加载脚本,预加载脚本中有flag。
看其它师傅的解法,因为运行python的环境应该是高权限,所以可以通过python语句修改/etc/passwd,使得ctf用户与root用户在同一组,即可直接读取预加载脚本。