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!}