幽灵的例子

时间:2018-01-06 18:03:55

标签: security spectre

在幽灵paper中,有一个利用出界数组访问的例子(第1.2节)。代码是

//...
   //Java IO File imports
   import java.io.BufferedReader;
   import java.io.InputStreamReader;
   import java.net.URL;
   import java.net.URLConnection;
   //Java SQL imports
   import java.sql.Connection;
   import java.sql.DriverManager;
   import java.sql.PreparedStatement;
   import java.sql.ResultSet;
   import java.sql.SQLException;
   import java.sql.Statement; 


   // ...
    // Button code
    public void doBrowserReadFromTwitterPHP() {
        // ...
        // Entering try catch
        try {
            // ...
            // Connect to DB2 to access Twitter table(s)
            Connection connection = DB2TWConnector.getConnection();
            // ...
            // make available CSV in URL
            URL urlCSV = new URL(
                    "http://www.yourweburl.com/AppDevFolder/resttwitterpubpostcsv.php");
            // ...
            // establish connection to file in URL
            URLConnection urlConn = urlCSV.openConnection();

            // ...
            InputStreamReader inputCSV = new InputStreamReader(
                    ((URLConnection) urlConn).getInputStream());
            // ...
            BufferedReader br = new BufferedReader(inputCSV);
            // ...
            // Declare String to hold file to Split from URL
            String line;
            String RoleNameVal = RoleNameValue.toString();
            String UserNameVal = UserNameValue.toString();
            String PageIDVal = PageID.toString();
            // ...
            // Read file accordingly
            while ((line = br.readLine()) != null) {
                // ...
                // Split file based on Delimiter in question "MyStrToMyDotoboseSectoid"
                String[] values = line.split(" MyStrToMyDotoboseSectoid "); // separator
                // ...
                // Declare and plug values obtained from Split
                String strPostID = values[0];
                String strPostName = values[1];
                String strPostMessage = values[2];
                String strPostDate = values[3];
                String strPostURL = values[4];
                String strPostStamp = values[5];

                // ...
                // Plug in App generated Info

                String strRoleID = RoleNameVal.trim();
                String strUserName = UserNameVal.trim();
                String strPageID = PageIDVal.trim();

                //DEBUG Purposes, comment out in prod
                System.out.println("strPostID = " +strPostID);
                System.out.println("strPostName = " +strPostName);
                System.out.println("strPostMessage = " +strPostMessage);
                System.out.println("strPostDate = " +strPostDate);
                System.out.println("strPostURL = " +strPostURL);
                System.out.println("strPostStamp = " +strPostStamp);
                System.out.println("strRoleID = " +strRoleID);
                System.out.println("strUserName = " +strUserName);
                System.out.println("strPageID = " +strPageID);
                // ...
                System.out.println("Entering DB2 query...");
                // ...
                PreparedStatement prep = connection
                        .prepareStatement("insert into DB2ADMIN.TWITTER_WEB_POST values(?,?,?,?,?,?,?,?,?)");
                // ... ?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?
                System.out.println("Loading values values to Columns...");

                // ...
                // make avail expected value
                prep.setString(1, strPostID);
                prep.setString(2, strPostDate);
                prep.setString(3, strPostName);
                prep.setString(4, strPostURL);
                prep.setString(5, strPostMessage);
                prep.setString(6, strPostStamp);
                prep.setString(7, strRoleID);
                prep.setString(8, strUserName);
                prep.setString(9, strPageID);

                connection.setAutoCommit(false);
                prep.execute();
                connection.setAutoCommit(true);
                // ...
                System.out.println("DB2 Twitter values added...");
            }
            // clean stuff up
            br.close();
            connection.close();
        } catch (Exception e) {
            SQLException e2 = ((SQLException) e).getNextException();
            String more = "";
            if (e2 != null)
                more = " : " + e2.getMessage();
            try {
                throw new SQLException("Connecting to DB, using: "
                        + UserNameValue + " account: " + e.getMessage() + more);
            } catch (SQLException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
        }
    }

该过程是使用一些有效的if (x < array1_size) y = array2[ array1[x] * 256 ]; 值训练正确的路径。然后给出无效的x值,同时假设x未被缓存。由于分支预测器认为条件为真,它将推测性地获取array2的偏移量。

现在,问题就在这里。在推测性执行中,它必须获取arra1_size,其中x是恶意的并且它是超出约束的。所以,array1[x]实际上是无效的!然后是什么攻击?!没有提取有效数据!

任何人都可以为我解释一下吗?什么被误解了?

3 个答案:

答案 0 :(得分:4)

  

所以,array1 [x]实际上是无效的!然后是什么攻击?!没有提取有效数据!

这是攻击的主要内容。索引(即x)可能很大,因此我们可以访问我们无法访问的数据。

例如,如果我们的代码在JavaScript沙箱或Java虚拟机中,我们将能够访问沙盒/虚拟机之外的数据。

更多的是,推测性执行可能会访问内核页面,即我们没有权限访问的页面。那是熔毁。

以下是基于Spectre的Meltdown概念验证,只需99行,您可能会更容易理解:

https://github.com/berestovskyy/spectre-meltdown

答案 1 :(得分:1)

Spectre(与Metldown不同)由于CPU处理分支预测而起作用。从同一篇论文中你可以参考

  

[2.3]推测执行要求处理器猜测分支指令的可能结果。更好的预测通过增加可成功提交的推测性执行操作的数量来提高性能。
(...)
  要预测是否采用条件分支,   处理器维护最近分支结果的记录。

然后

  

[4]代码片段以x的边界检查开始,这对安全性至关重要。特别是,此检查可防止处理器读取array1之外的敏感内存。否则,越界输入x可能会触发异常,或者可能导致处理器通过提供x = (address of a secret byte to read)−(base address of array1)来访问敏感内存。

但是论文继续并解释了为什么这可能会起作用,触发异常:

  

不幸的是,在推测执行期间,边界检查的条件分支可能遵循不正确的路径。例如,假设攻击者导致代码运行:

     

x的值被恶意选择(并且超出范围),以便array1[x]解析为受害者内存中某处的秘密字节 k ;

     

array1大小和array2不存在于处理器的缓存中,但 k 已缓存;和

     

•之前的操作收到有效的x值,导致分支预测器假设if可能为真。

最后,在x值被评估为过高之后,CPU将有效地绕过if主体,并且不会退出检索到的y值。但是缓存状态已经改变,这就是攻击发生的地方:

  

处理器意识到它的推测执行是错误的,并且重新调整其寄存器状态。但是,在实际处理器上,来自array2的推测性读取会以特定于地址的方式影响缓存状态,其中地址取决于 k

在推测代码执行期间未经授权访问array1[x],因此不会引发异常,因为CPU&#34;知道&#34;如果来自前面的分支的条件恰好是假的,则该推测执行的结果将不会被退出。

(与执行的用户代码访问未授权区域时触发异常的Meltdown不同 - Meltdown利用退出异常所花费的时间与预先执行的访问之后的少数乱序指令之间的竞争条件(并且不会退休))。

答案 2 :(得分:0)

我的理解(简化)(可能是错的):

当x高于处理器将加载数据的边界时 别的地方。 (有点像缓冲区溢出)

例如,当数据保存时如下:

1&lt; - Start Array

2

3&lt; - 开始秘密数据

4

x大于它可以读取的界限,例如3(在论文中,这可能是一切,它的名字是k)。然后3被缓存

3用于加载第二个数组中的数据。处理器意识到错误并从缓存中删除它(3 / k)。

然后,Attaker可以用不同的方法来修改这个3(或纸k)。例如,尝试一个数字并测量加载时间,然后重复从开头的所有内容

(sry for bad english)