Misc
d3readfile
读/var/cache/locate/locatedb得到全文件地址,在里面搜索flag再读即可
d3gif
先分帧
from PIL import Image
import os
import random
class GIFTest:
def __init__(self, file_name):
self.file_name = file_name # 传入的文件名
self.dir_name = self.file_name[:-4] # 根据文件名创建存放分帧图片的文件夹
self.gif_path = os.path.join(os.path.dirname(
__file__), file_name) # 拼接图片文件的完整路径(仅限同一文件夹内)
self.make_dir()
def make_dir(self):
"""用于创建存放分帧图片的文件夹"""
try:
os.mkdir(self.dir_name)
except FileExistsError:
print('<%s>文件夹已存在' % self.dir_name)
self.dir_name += str(random.randint(0, 10))
os.mkdir(self.dir_name)
def framing_test(self):
"""GIF图片分帧"""
img = Image.open(self.gif_path)
try:
while True:
curr = img.tell()
name = os.path.join(self.dir_name, '%s.png' % str(curr + 1))
img.save(name)
img.seek(curr+1)
except Exception as e:
pass
if __name__ == '__main__':
GIFTest('./files/(x,y,bin).gif').framing_test()
然后转下rgb(cv2里是bgr)
import cv2
from PIL import Image
import matplotlib.pyplot as plt
import os
import numpy as np
# 批量转灰度图并保存
# crack文件夹下批量处理将灰度图转成RGB并保存
def convert2gray(filename): # 定义灰度图转RGB图的函数
img = cv2.imread(file_path+'/'+filename, 1) # 1是以BGR图方式去读
RGB_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
cv2.imwrite(out_path + '/RGB' + filename, RGB_img) # 保存在新文件夹下,且图名中加RGB
file_path = "./photo/" # 输入文件夹 # 建立新的目录
out_path = "./new/" # 设置为新目录为输出文件夹
for i in range(1089):
convert2gray(str(i+1)+'.png') # 批量转换
然后猜测背景的颜色是和文件名一样的,直接画33x33的二维码即可
from PIL import Image
img = Image.new('RGB', (33, 33))
for i in range(1089):
imgg = Image.open('./new/RGB'+str(i+1)+'.png')
pi = imgg.getpixel((0, 0))
x = pi[2]
y = pi[1]
col = pi[0]
img.putpixel((x, y), (col*255, col*255, col*255))
img.save('flag.png')
d3image
先看版本
Linux version 4.15.0-142-generic (buildd@lgw01-amd64-039) (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.12)) #146~16.04.1-Ubuntu SMP Tue Apr 13 09:27:15 UTC 2021 (Ubuntu 4.15.0-142.146~16.04.1-generic 4.15.18)
一眼丁真,直接起个ubuntu16来做vol2用的profile
做好了先看眼命令行
特别干净但还是可以看出来挂了个代理去用火狐,于是猜测给的容器是代理
这时候就可以直接恢复下文件系统
python2 vol.py -f ../mem --profile=Linuxubuntu16142x64 linux_recover_filesystem --dump-dir=../file/
然后找到里面的proxychains的配置文件就能看到最后一行的代理了,直接照抄并更改地址
socks5 192.168.31.136 51234 Gigantic_Splight Tearalaments_Kitkalos
然后找了很久的火狐相关记录没有啥结果,于是winhex打开内存嗯看,发现了两个可疑的地址
127.0.0.1:2333
127.0.0.1:2333/magic.7z
一眼是要挂代理访问的,于是试了一下果然有东西,于是得到下一步的压缩包和一个需要token的网站
压缩包很大,但是经过hint以及脑洞以及多次尝试,最终找到了隐写的方法,就是ip为10.0.0.0~10.76.223.231的范围,一共有5038056种,并且其中的每一种都有相应的请求或响应记录,再加上5038056是8的倍数以及hint中的是否可达,很容易联想到二进制,于是写脚本手搓pcap来判断每一个ip是否可达,要分好几种情况,在这不多说了(由于不大会用pyshark就手搓了,还好这里的流量都是一样长)
from tqdm import trange
f = open('data', 'rb')
num = [0]*5038056
for i in trange(9131515):
dataa = f.read(44)
dataaa = dataa[33:36]
dataaaa = int.from_bytes(dataaa, byteorder='big')
if (dataa[37:38] == b'\x03' and dataa[36:37] == b'\x00'):
num[dataaaa] = 1
if (dataa[36:37] == b'\x08'):
if (num[dataaaa] == 1):
num[dataaaa] = 1
continue
if (num[dataaaa] == 0):
num[dataaaa] = 2
continue
if (num[dataaaa] == 3):
num[dataaaa] = 1
continue
if (dataa[37:38] == b'\x00' and dataa[36:37] == b'\x00'):
if (num[dataaaa] == 1):
num[dataaaa] = 1
continue
if (num[dataaaa] == 0):
num[dataaaa] = 3
continue
if (num[dataaaa] == 2):
num[dataaaa] = 0
continue
if (num[dataaaa] == 3):
num[dataaaa] = 1
continue
for i in range(len(num)):
if (num[i] == 0):
print(1, end='')
if (num[i] == 1):
print(0, end='')
if (num[i] == 2):
print(0, end='')
if (num[i] == 3):
print(0, end='')
里面的data文件是pcap包去掉了文件头
得到的结果解二进制刚好是一个7z压缩包,里面是一个3d模型文件
下了个国产的叫追光几何来打开,经过摸索发现模型里面藏了个二维码
扫码得到
3;A6eI`(J{z29|Gz":Dqt;~h*Bvc$7}c"dw'uBJth$Jg(+4+8x9eG7`>83$q5hF%I*)yrcb3+7$*~Dr"G|:K~C{_"Jv5=B9t9|>bwugCE~d&3fd{H;@hD?(DDz~$h#I%I`IB8zKyfHby3x'yfc56fH35|E8$+KGE@(u`7
然后解密
得到token
4765cdef0101
然后再次挂代理访问页面,可是一直是connecting转圈
查看js后了解是需要一个手柄连接才可以连的上,由于五一回家了手柄在学校,就采取手动发包的形式来做
要发的包的形式长这样
await fetch("http://127.0.0.1:2333/api/check", {
"credentials": "include",
"headers": {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0",
"Accept": "application/json, text/plain, */*",
"Accept-Language": "en-US,en;q=0.5",
"Content-Type": "application/json",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin"
},
"referrer": "http://127.0.0.1:2333/",
"body": "{\"text\":\"\"}",
"method": "POST",
"mode": "cors"
});
token的值就在body里面,并且阅读js可以知道token是以emoji的形式发出的,只需要找到对应关系并填在body里发包就可以看到返回的数据了
await fetch("http://127.0.0.1:2333/api/check", {
"credentials": "include",
"headers": {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0",
"Accept": "application/json, text/plain, */*",
"Accept-Language": "en-US,en;q=0.5",
"Content-Type": "application/json",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin"
},
"referrer": "http://127.0.0.1:2333/",
"body": "{\"text\":\"🐻🥔🍆🍉⬆️⬇️⬅️➡️🅰️🅱️🅰️🅱️\"}",
"method": "POST",
"mode": "cors"
});
发过去就看到flag了
最后再解个base32就好了
d3craft
❤来自114
给node-minecraft-protocol打补丁支持1.19.4
npm install mineflayer
https://github.com/PrismarineJS/minecraft-data/pull/703
用这个pr的repo的全部内容 替换node_modules\minecraft-data\minecraft-data 然后
修改node_modules\minecraft-data\data.js 添加
'1.19.4': {
get attributes () { return require("./minecraft-data/data/pc/1.17/attributes.json") },
get blocks () { return require("./minecraft-data/data/pc/1.19.4/blocks.json") },
get blockCollisionShapes () { return require("./minecraft-data/data/pc/1.19.4/blockCollisionShapes.json") },
get biomes () { return require("./minecraft-data/data/pc/1.19.4/biomes.json") },
get effects () { return require("./minecraft-data/data/pc/1.19.4/effects.json") },
get items () { return require("./minecraft-data/data/pc/1.19.4/items.json") },
get enchantments () { return require("./minecraft-data/data/pc/1.19.4/enchantments.json") },
get recipes () { return require("./minecraft-data/data/pc/1.19.4/recipes.json") },
get instruments () { return require("./minecraft-data/data/pc/1.19.4/instruments.json") },
get materials () { return require("./minecraft-data/data/pc/1.19.4/materials.json") },
get language () { return require("./minecraft-data/data/pc/1.19.4/language.json") },
get entities () { return require("./minecraft-data/data/pc/1.19.4/entities.json") },
get protocol () { return require("./minecraft-data/data/pc/1.19.4/protocol.json") },
get windows () { return require("./minecraft-data/data/pc/1.16.1/windows.json") },
get version () { return require("./minecraft-data/data/pc/1.19.4/version.json") },
get foods () { return require("./minecraft-data/data/pc/1.19.4/foods.json") },
get particles () { return require("./minecraft-data/data/pc/1.19.4/particles.json") },
get blockLoot () { return require("./minecraft-data/data/pc/1.19/blockLoot.json") },
get entityLoot () { return require("./minecraft-data/data/pc/1.19/entityLoot.json") },
get loginPacket () { return require("./minecraft-data/data/pc/1.19.2/loginPacket.json") },
get tints () { return require("./minecraft-data/data/pc/1.19.4/tints.json") },
get mapIcons () { return require("./minecraft-data/data/pc/1.16/mapIcons.json") }
}
新建bot的地方加 version: '1.19.4'
const bot = mineflayer.createBot({
...,
version: '1.19.4',
})
下载mc源码
https://github.com/PaperMC/Paper 跟着readme.md配置就行,但是真的慢。。。
漏洞分析
internalTeleport中可以设置玩家(对应连接)的lastPos X Y Z为被传送的位置。
每一次玩家上传坐标,如果玩家上传的坐标在墙里(卡碰撞箱),那么会调用internalTeleport将玩家传送回到玩家上传前的坐标。
如果玩家的lastPos X Y Z 与玩家上传的坐标相差小于等于(1/256)则不会触发插件的PlayerMoveEvent事件。
所以,
①在xz轴移动(1/256)-0.000001,玩家的坐标被设置,因为移动距离小于(1/256),所以不会触发插件事件
②然后在y轴移动-0.000001,玩家的坐标不会被设置,会导致调用internalTeleport将玩家从方块内拉回,internalTeleport将玩家的lastPos X Y Z设置成①中玩家的坐标
③等待服务器向客户端发送拉回的坐标,并且teleport_confirm(因为不teleport_confirm不能进行移动,参见源码,略)
①②③反复执行约5000次,即可移动到(0.5,-60,0.5)的位置,并且不会触发插件的PlayerMoveEvent事件
exp
mc握手比较烦人,采用了mineflayer实现握手,并且禁用了它的物理引擎插件。
const fs = require('fs')
const mineflayer = require('mineflayer')
const bot = mineflayer.createBot({
host:"靶机",
port:114514,
username: new Date().getTime().toString(),
version: '1.19.4',
loadInternalPlugins: false,
plugins: { 'health': true, 'entities': true, 'physics': false, 'chat': true },
physicsEnabled: false
})
bot._client.on("end", function (reason) {
console.log("end", reason)
console.log("player position:", player_position)
console.log("player initial:", initial_player_position)
// exit
process.exit(0)
})
const delta = (1 / 256) - 0.00001
let state = "waiting_for_sync"
let player_position = {
x: -1,
y: -1,
z: -1,
yaw: -1,
pitch: -1,
flags: -1,
}
let initial_player_position = undefined;
let move_count = 0;
bot._client.on("systemChat", function (packet) {
console.log("systemChat", packet)
})
bot._client.on("position", function (packet) {
if (!packet) return;
if (packet.teleportId == 1) return;
console.log("teleport_confirm", packet)
bot._client.write('teleport_confirm', { teleportId: packet.teleportId })
{
player_position.x = packet.x;
player_position.y = packet.y;
player_position.z = packet.z;
if (player_position.yaw !== undefined) {
player_position.yaw = packet.yaw;
player_position.pitch = packet.pitch;
}
player_position.flags = packet.flags;
if (initial_player_position == undefined)
initial_player_position = Object.assign({}, player_position)
}
state = "moving"
console.log("sync_result", player_position)
})
function apply_move(){
if(player_position.z > 0.6){
player_position.z = player_position.z - delta;
}else if (player_position.z < 0.5){
player_position.z = player_position.z + delta;
}else if (player_position.x > 0.6){
player_position.x = player_position.x - delta;
}else if (player_position.x < 0.5){
player_position.x = player_position.x + delta;
}else{
// wave right hand
bot._client.write("arm_animation", {
hand: 0
})
//interact with air
bot._client.write("use_entity", {
target: 0,
type: 2,
mouse: 2,
hand:0,
x:player_position.x,
y:player_position.y,
z:player_position.z,
sneaking: false
})
}
}
function tick() {
console.log("tick", state, move_count, player_position)
if (state == "waiting_for_sync") {
return;
}
else if (state == "moving") {
apply_move();
move_count++;
bot._client.write("position", {
...player_position,
onGround: false
})
state = "falling";
}
else if (state == "falling"){
player_position.y = player_position.y - 0.000001;
bot._client.write("position", {
...player_position,
onGround: false
})
state = "waiting_for_sync";
}
}
setInterval(tick, 5)
bot.on("spawn", function () {
console.log("spawned");
})
Re
d3recover
用bindiff两个对着看,然后找到检查flag的逻辑在2.0的check里面,于是去读对应函数里的逻辑再写个解密即可
import base64
flag = base64.b64decode('08fOyj+E27O2uYDq0M1y/Ngwldvi2JIIwcbF9AfsAl4=')
flag = list(flag)
for i in range(30):
flag[29 - i] = ((flag[29 - i] ^ 0x54) - flag[31 - i]) % 0xff
for i in range(32):
print(chr(flag[i] ^ 0x23), end="")
#flag{y0U_RE_Ma5t3r_0f_R3vocery!}