运用 Semmle QL 举行破绽搜刮 Part 2 | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

运用 Semmle QL 举行破绽搜刮 Part 2

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

申博网络安全巴士站

申博-网络安全巴士站是一个专注于网络安全、系统安全、互联网安全、信息安全,全新视界的互联网安全新媒体。

————————————-

本系列的第一局部引见了Semmle QLMicrosoft平安相应中间(MSRC)怎样运用它来观察要挟破绽。本文议论了怎样运用此东西举行细节演示,个中包罗怎样运用其实例,而且演示中还包罗Azure固件组件的平安考核模块。

这是Azure效劳举行平安检察防备的一局部,我们从渗入的界限局部来斟酌,并站在进击者的角度举行进击假象。以后我们将在后端效劳中举行情况操纵。

运用 Semmle QL 举行破绽搜刮 Part 2

此次检察的目的之一是基于Linux的嵌入式装备,它与效劳后端和治理后端衔接并在二者之间通报操纵数据。 该装备的重要进击是面向两个接口运用治理协定。

以后我们对其硬件举行手动检察,以后发明其治理协定是基于音讯的,其存在凌驾四百种分歧的音讯范例,每种范例具有其本身的处置惩罚功用。然则手动审计函数很轻易发生毛病,因而运用Semmle扩大我们的代码检察能下降我们的审计难度。我们运用本文中议论的静态剖析手艺统共找到了33个易受进击的音讯处置惩罚函数。

进击界说

我们的第一步是编写一些QL来模仿进击者的数据。 治理协定在要求 相应的基础上事情,个中每一个音讯要求的范例都用种别和敕令来举行编号标识,并在源代码中运用构造数组界说,比方:

MessageCategoryTable g_MessageCategoryTable[] =
{
    { CMD_CATEGORY_BASE,  g_CommandHandlers_Base },
    { CMD_CATEGORY_APP0,  g_CommandHandlers_App0 },
    …
    { NULL,               NULL                   }
};
CommandHandlerTable g_CommandHandlers_Base [] =
{
    { CMD_GET_COMPONENT_VER,  sizeof(ComponentVerReq),  GetComponentVer,  … },
    { CMD_GET_GLOBAL_CONFIG,  -1,                       GetGlobalConfig,  … },    
    …
    { NULL,                   NULL,                     NULL,             … }
};

在上面的示例中,种别范例为CMD_CATEGORY_BASE且敕令范例为CMD_GET_COMPONENT_VER的音讯将路由到GetComponentVer函数。 敕令处置惩罚顺序表还具有有关要求音讯的预期巨细信息,该信息在挪用处置惩罚函数之前将获得考证。

我们运用以下QL界说了音讯处置惩罚顺序表:

class CommandHandlerTable extends Variable { 
  CommandHandlerTable() { 
    exists(Variable v | v.hasName("g_MessageCategoryTable")
      and this.getAnAccess() = v.getInitializer().getExpr().getAChild().getChild(1)
    ) 
  } 
}

这将猎取名为g_MessageCategoryTable的变量,查找其初始化表达式并婚配此表达式的一切子项。而每一个子表达式对应于音讯种别表的一行。 关于每一行,它接纳第二列(因为getChild谓词的参数是零索引的,以是运用getChild(1)),每一个列都是对敕令处置惩罚顺序表的援用,并婚配援用的变量。 在上面的示例中是g_CommandHandlers_Baseg_CommandHandlers_App0

我们运用相似的要领界说了音讯处置惩罚函数集:

class MessageHandlerFunction extends Function { 
  Expr tableEntry; 

  MessageHandlerFunction() { 
    exists(CommandHandlerTable table |
      tableEntry = table.getInitializer().getExpr().getAChild()
      )
    and this = tableEntry.getChild(2).(FunctionAccess).getTarget()
  }

  int getExpectedRequestLength() { 
    result = tableEntry.getChild(1).getValue().toInt() 
  } 
  …
}

此QL类运用成员变量tableEntry来生存一切用于处置惩罚顺序表中的一切行的敕令。如许就可以或许在特性谓词(MessageHandlerFunction(){...})和getExpectedRequestLength()并援用它,而不反复界说。

一切这些都映照到上面的代码构造,以下所示:

运用 Semmle QL 举行破绽搜刮 Part 2

每一个音讯处置惩罚函数都具有雷同的署名:

typedef unsigned char UINT8;
int ExampleMessageHandler(UINT8 *pRequest, int RequestLength, UINT8 *pResponse);

此处遵照一样平常形式,个中要求数据被强迫转换为透露表现音讯结构的构造,并并经由过程其字段接见:

int ExampleMessageHandler(UINT8 *pRequest, int RequestLength, UINT8 *pResponse)
{
    ExampleMessageRequest* pMsgReq = (ExampleMessageRequest *)pRequest;
    …

    someFunction(pMsgReq->aaa.bbb)

    …
}

在此剖析中,我们只对要求数据感兴趣。 我们在MessageHandlerFunction QL类中界说了两个分外的谓词来要求数据及其长度:

class MessageHandlerFunction extends Function {
  Expr tableEntry;
  …

  Parameter getRequestDataPointer() {
    result = this.getParameter(0)
  }

  Parameter getRequestLength() {
    result = this.getParameter(1)
  }
}

在这里我们笼统出音讯处置惩罚函数,因为它可以或许像任何其他QL类一样运用。 比方,此查询按其庞杂度的降序列出一切音讯处置惩罚函数:

from MessageHandlerFunction mhf
select
  mhf, 
  mhf.getADeclarationEntry().getCyclomaticComplexity() as cc
order by cc desc

数据流剖析

如今我们为不受信托的数据界说了一组进口点,下一步我们须要找到破绽运用的地位。为此我们须要经由过程代码库来跟踪此类数据的活动。 QL供应了一个功用强大的全局数据流库,它可以或许笼统出言语的特定细节。

DataFlow库带入查询局限,包罗:

import semmle.code.cpp.dataflow.DataFlow

它经由过程将DataFlow::Configuration设置为子类并掩盖其谓词来界说数据流,因为它运用于QL类DataFlow::Node以便透露表现数据可以或许流经的任何顺序伪像:

运用 Semmle QL 举行破绽搜刮 Part 2

大多数数据流查询以下所示:

class RequestDataFlowConfiguration extends DataFlow::Configuration { 
  RequestDataFlowConfiguration() { this = "RequestDataFlowConfiguration" } 

  override predicate isSource(DataFlow::Node source) { 
    …
  }

  override predicate isSink(DataFlow::Node sink) { 
    …
  }

  override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { 
    …
  }

  override predicate isBarrier(DataFlow::Node node) { 
    …
  }

}
from DataFlow::Node source, DataFlow::Node sink 
where any(RequestDataFlowConfiguration c).hasFlow(source, sink) 
select 
  "Data flow from $@ to $@", 
  source, sink

请注意,在QL数据流库实行过程当中,除搜检函数当地的数据流以外还将包罗流经函数挪用参数的数据。 这是我们的平安检察的一个基本功用。只管下面议论的破绽代码形式在简朴的示例函数中显现以便于演示,但在我们的目的的现实源代码中,大多数效果都有逾越多个庞杂函数的数据流。

内存平安破绽

因为此组件是纯C代码,我们起首决议搜刮与内存平安相干的代码形式。

全国大学生信息安全竞赛—区块链题目分析

一、前言 前几天全国大学生信息安全竞赛初赛如期进行,在这次比赛中也看到了区块链题目的身影。所以我将题目拿来进行分析,并为后续的比赛赛题提供一些分析思路。 由于本次比赛我并没有参加,所以我并没有Flag等相关信息,但是我拿到了比赛中的相关文件以及合约地址并在此基础上进行的详细分析,希望能帮助到进行研究的同学。 二、题目分析 拿到题目后,我们只得到了两个内容,一个是合约的地址,一个是broken.so。 pragma solidity ^0.4.24;

contract DaysBank {
mapping(address => uint) public balanceOf;
mapping(address =

引发这类毛病的一个罕见原因是数组索引不实行界限搜检。 零丁搜刮此形式将供应很大的资助,这些效果极可以或许不是平安破绽。 因而,在这类情况下,我们正在寻觅数据流中的接收器是数组索引表达式,数据源是音讯处置惩罚顺序函数的要求数据,而且在由相干界限搜检珍爱的数据流节点上存在停滞。

比方,我们想要找到婚配代码的数据流,以下所示:

int ExampleMessageHandler(UINT8 *pRequest(1:source), int RequestLength, UINT8 *pResponse)
{
    ExampleMessageRequest* pMsgReq(3) = (ExampleMessageRequest *) pRequest(2);
    int index1(6) = pMsgReq(4)->index1(5);
    pTable1[index1(7:sink)].field1 = pMsgReq->value1;
}

但我们也愿望消除代码的数据流,以下所示:

int ExampleMessageHandler(UINT8 *pRequest(1:source), int RequestLength, UINT8 *pResponse)
{
    ExampleMessageRequest* pMsgReq(3) = (ExampleMessageRequest *) pRequest(2);
    int index2(6) = pMsgReq(4)->index2(5);
    if (index2 >= 0 && index2 < PTABLE_SIZE)
    {
        pTable2[index2].field1 = pMsgReq->value2;
    }
}

运用前面议论的MessageHandlerFunction类界说源代码,我们可以或许运用ArrayExprgetArrayOffset谓词来界说适宜的接收器:

override predicate isSource(DataFlow::Node source) {
    any(MessageHandlerFunction mhf).getRequestDataPointer() = source.asParameter()
  }

  override predicate isSink(DataFlow::Node sink) { 
    exists(ArrayExpr ae | ae.getArrayOffset() = sink.asExpr())  
  }

默许情况下,DataFlow库仅保存每一个节点的值,比方函数挪用参数,赋值表达式等。 然则我们还须要数据从要求数据指针流向它所投射到的构造的字段。 我们运用以下操纵:

override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2)
  {
    // any terminal field access on request packet
    //   e.g. in expression a->b.c the data flows from a to c
    exists(Expr e, FieldAccess fa |  
      node1.asExpr() = e and node2.asExpr() = fa |  
      fa.getQualifier*() = e and not (fa.getParent() instanceof FieldAccess)
    )
  }

要运用界限搜检消除数据,我们须要在掌握流图中较早的在某些前提语句中运用变量或字段,并安排一些屏蔽。

override predicate isBarrier(DataFlow::Node node) { 
    exists(ConditionalStmt condstmt |  
      // dataflow node variable is used in expression of conditional statement
      //   this includes fields (because FieldAccess extends VariableAccess)
      node.asExpr().(VariableAccess).getTarget().getAnAccess()
                                          = condstmt.getControllingExpr().getAChild*()
      // and that statement precedes the dataflow node in the control flow graph
      and condstmt.getASuccessor+() = node.asExpr()
      // and the dataflow node itself not part of the conditional statement expression
      and not (node.asExpr() = cs.getControllingExpr().getAChild*())
    ) 
  }

将此运用于上述两个示例,每一个节点的数据流将是:

运用 Semmle QL 举行破绽搜刮 Part 2

在我们的代码库中,此查询在15个音讯处置惩罚顺序函数中共定位了18个破绽,这是一组进击者所掌握的越界读写操纵。

我们运用了相似的剖析来查找函数挪用的参数,其实不考证音讯要求数据。 起首,我们界说了一个QL类来界说函数调的用和参数,包罗挪用memcpy的巨细参数和相似的函数_fmemcpy,和CalculateChecksum的长度参数。 CalculateChecksum是一个特定的代码库函数,它将返回缓冲区的CRC32局部,而且可以或许被用作信息泄漏。个中音讯处置惩罚函数将此值复制到其相应缓冲区中。

class ArgumentMustBeCheckedFunctionCall extends FunctionCall {
  int argToCheck;

  ArgumentMustBeCheckedFunctionCall() {
    ( this.getTarget().hasName("memcpy")            and argToCheck = 2 ) or
    ( this.getTarget().hasName("_fmemcpy")          and argToCheck = 2 ) or
    ( this.getTarget().hasName("CalculateChecksum") and argToCheck = 1 )
  }
  Expr getArgumentToCheck() { result = this.getArgument(argToCheck) }
}

接下来,我们修正了上一个查询的接收器以婚配ArgumentMustBeCheckedFunctionCall而不是数组索引:

override predicate isSink(DataFlow::Node sink) {
    // sink node is an argument to a function call that must be checked first
    exists (ArgumentMustBeCheckedFunctionCall fc | 
              fc.getArgumentToCheck() = sink.asExpr())
  }

这个查询暴露出别的17个破绽,个中大多数是进击者掌握的超越界限读取(我们厥后证着实相应音讯中已披露了这些破绽),个中一个超越界限写入。

污点跟踪

在上面的查询中,我们掩盖了DataFlow库的isAdditionalFlowStep谓词以确保数据流向指向构造的指针,该构造的字段将作为数据流图中的节点被增加。 在默许情况下,数据流剖析仅包罗数据值连结稳定的门路,但我们愿望跟踪可以或许受影响的表达式鸠合。 也就是说,我们界说了一组被不受信托的数据污染表达式。

QL包罗一个内置库,可以或许运用更通用的要领来举行污点跟踪。 在DataFlow库之上开辟,它会掩盖isAdditionalFlowStep,并为值修正表达式供应更雄厚的划定规矩集。 这就是TaintTracking库,它以相似于DataFlow的体式格局被导入:

import semmle.code.cpp.dataflow.TaintTracking

它的运用体式格局与数据库险些雷同,只是要扩大的QL类是TaintTracking::Configuration,这些设置装备摆设谓词:

运用 Semmle QL 举行破绽搜刮 Part 2

我们从新运行了先前的查询,删除isAdditionalFlowStep而且将isBarrier重命名为isSanitizer。 正如预期的那样,它返回了上面提到的一切效果,但也在数组索引中发明了一些分外的整数下溢缺点。 比方:

int ExampleMessageHandler(UINT8 *pRequest(1:source), int RequestLength, UINT8 *pResponse)
{
    ExampleMessageRequest* pMsgReq(3) = (ExampleMessageRequest *) pRequest(2);
    int index1(6) = pMsgReq(4)->index1(5);
    pTable1[(index1(7) - 2)(8:sink)].field1 = pMsgReq->value1;
}

关于每一个破绽范例的内部申报,我们将这些与初期查询效果离开举行分类。 这包罗运用SubExpr QL类对接收器举行简朴修正:

override predicate isSink(DataFlow::Node sink) {
    // this sink is the left operand of a subtraction expression,
    //   which is part of an array offset expression, e.g. x in a[x - 1]
    exists(ArrayExpr ae, SubExpr s | sink.asExpr() instanceof FieldAccess
      and ae.getArrayOffset().getAChild*() = s
      and s.getLeftOperand().getAChild*() = sink.asExpr())
  }

这也暴露了个中2个音讯处置惩罚函数中的别的3个破绽。

查找门路遍历破绽

为了找到潜伏的门路来遍历破绽,我们运用QL实验辨认文件所翻开函数中的音讯处置惩罚函数。

我们此次运用了分歧的要领举行污点跟踪。此处我们界说了一些分外的污点步调,这些步调将流经种种字符串处置惩罚C库函数:

predicate isTaintedString(Expr expSrc, Expr expDest) {
  exists(FunctionCall fc, Function f |
    expSrc = fc.getArgument(1) and 
    expDest = fc.getArgument(0) and
    f = fc.getTarget() and (
      f.hasName("memcpy") or 
      f.hasName("_fmemcpy") or 
      f.hasName("memmove") or 
      f.hasName("strcpy") or 
      f.hasName("strncpy") or
      f.hasName("strcat") or
      f.hasName("strncat")
      )
  )
  or exists(FunctionCall fc, Function f, int n |
    expSrc = fc.getArgument(n) and 
    expDest = fc.getArgument(0) and
    f = fc.getTarget() and (
      (f.hasName("sprintf") and n >= 1) or 
      (f.hasName("snprintf") and n >= 2)
    )
  )
}
…

  override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
    isTaintedString(node1.asExpr(), node2.asExpr())
  }

此处我们将接收器界说为文件翻开功用的门路参数:

class FileOpenFunction extends Function {
  FileOpenFunction() {
    this.hasName("fopen") or this.hasName("open")
  }
  int getPathParameter() { result = 0 } // filename parameter index
}

…

  override predicate isSink(DataFlow::Node sink) {
    exists(FunctionCall fc, FileOpenFunction fof |
      fc.getTarget() = fof and fc.getArgument(fof.getPathParameter()) = sink.asExpr())
  }

在我们处理下一个消除数据考证流程的题目之前,我们对目的装备的事情形式举行了开端检察,我们发明其效果正如我们之前的查询的那样,查询基础没有返回任何内容。

因为没有搜检的数据流门路,我们没法翻开查询函数挪用以搜刮音讯处置惩罚函数和挪用文件翻开函数之间的任何门路,不包罗path参数为常量的挪用:

// this recursive predicate defines a function call graph
predicate mayCallFunction(Function caller, FunctionCall fc) {
  fc.getEnclosingFunction() = caller or mayCallFunction(fc.getTarget(), fc)
}

from MessageHandlerFunction mhf, FunctionCall fc, FileOpenFunction fof
where mayCallFunction(mhf, fc)
  and fc.getTarget() = fof
  and not fc.getArgument(fof.getPathParameter()).isConstant()
select 
  mhf, "$@ may have a path to $@",
  mhf, mhf.toString(),
  fc, fc.toString()

这个查询供应了5个效果,从中我们发明了2个门路遍历破绽,一个写入文件,另一个读取文件,二者都有进击者供应的门路。 事实证明,污点跟踪没有符号这些,因为它须要发送两个零丁的音讯范例:第一个设置文件名,第二个读取或写入具有该称号的文件的数据。 然则QL充足天真,可以或许供应另一种探究门路。

结论

在Microsoft,我们接纳深度防备要领来珍爱云并珍爱客户的数据平安。 个中一个重要局部是对Azure内部进击面举行周全的平安性检察。 在这个嵌入式装备的源代码检察中,我们运用Semmle QL的静态剖析手艺来查找基于音讯的治理协定中的破绽。 这在种种bug类中发明了统共33个易受进击的音讯处置惩罚顺序。 运用QL使我们可以或许自动实行完整手动代码检察的反复局部,同时可以或许接纳新型手腕举行探测。


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明运用 Semmle QL 举行破绽搜刮 Part 2
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

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

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