压在透明的玻璃上c-国产精品国产一级A片精品免费-国产精品视频网-成人黄网站18秘 免费看|www.tcsft.com

針對過時的RNG隨機數(shù)發(fā)生器的有效狀態(tài)恢復攻擊

“Practical state recovery attacks against legacy RNG implementations”由Shaanan N. Cohney, Matthew D. Green, Nadia Heninger三位研究員發(fā)表于CCS2018會議上。論文中,作者對數(shù)百種經(jīng)FIPS 140-2認證的應(yīng)用ANSI X9.31隨機數(shù)發(fā)生器的公開產(chǎn)品進行了系統(tǒng)研究,發(fā)現(xiàn)其中12個產(chǎn)品中使用了靜態(tài)硬編碼密鑰,攻擊者可從源代碼或二進制文件中獲取該密鑰。 為了證明這種攻擊的實用性,作者對應(yīng)用FortiOS v4的 FortiGate VPN網(wǎng)關(guān)實施完全被動解密攻擊,可在幾秒鐘內(nèi)恢復私鑰。 研究者使用主動掃描在Internet上測量此漏洞的普遍程度,并展示 狀態(tài)恢復和完全私鑰恢復普遍存在。 作者的工作突出顯示出驗證和認證過程未能在多大程度上提供適度的安全保障。論文中,作者并沒有對固件逆向、/dev/uramdom實現(xiàn)、及攻擊代碼實現(xiàn)等具體細節(jié)做詳細闡述。筆者復現(xiàn)了隨機數(shù)生成器代碼及部分攻擊過程,并將擴展介紹該攻擊的技術(shù)細節(jié)。

一、ANSI X9.17/31隨機數(shù)生成標準

隨機數(shù)生成是加密系統(tǒng)的重要組成部分。近年來,已發(fā)現(xiàn)許多密碼系統(tǒng)的隨機數(shù)生成器存在缺陷或被惡意植入后門。例如,Edward Snowden泄漏的文件表明NIST Dual EC DRBG標準可能設(shè)計有后門。2015年,Juniper公司透露他們的ScreenOS系列VPN設(shè)備已被修改為包含一組惡意的雙EC參數(shù),可導致VPN會話被動解密。

ANSI X9.17“金融機構(gòu)密鑰管理(批發(fā))”標準由ANSI-American National Standards Institute(美國國家標準學會)于1985年首次發(fā)布,為金融行業(yè)的加密密鑰生成和分發(fā)定義了一個自愿的互操作性標準。 該標準在附錄C中包括偽隨機數(shù)發(fā)生器(PRG),作為生成密鑰素材的建議方法。 此生成器使用分組密碼(在原始描述中為DES)從當前狀態(tài)生成輸出,并使用當前時間更新狀態(tài)。

在接下來的三十年中,相同的PRG設(shè)計出現(xiàn)在美國政府的加密標準中,偶爾會更新新的分組密碼。 1992年,ANSI X9.17-1985標準的一個子集作為FIPS標準FIPS-171被采用。FIPS-171規(guī)定“只有NIST認可的密鑰生成算法(例如,ANSI X9.17附錄C中定義的技術(shù))才能使用。 1994年采用的FIPS 140-1規(guī)定模塊應(yīng)使用FIPS認可的密鑰生成算法; FIPS 186-1是1998年采用的DSA標準的原始版本,它將X9.17 PRG列為生成私鑰的批準方法。1998年的ANSI X9.31標準(作為X9.17 PRG的變體)使用雙密鑰3DES作為分組密碼;此變體作為批準的隨機數(shù)生成器包含在其他標準中,例如2004年的FIPS 186-2。NIST使用三密鑰的3DES和AES作為分組密碼[39]發(fā)布了此設(shè)計的擴展,正式包含在FIPS中140-2 2005年批準的隨機數(shù)生成算法列表。

edeX(Y)表示通過DEA(Data Encryption Algorithm)算法,應(yīng)用密鑰K加密,其中*K保密,但ANSI X9.31 PRG設(shè)計的NIST文檔沒有指定如何生成密碼密鑰;

V是64比特種子,同樣保密;

DT是日期/時間向量,每次調(diào)用更新;

I為中間值;

64比特R生成方法如下:

I=ede*K(DT)
R=ede*K(I^V)    #R通過級連生成連續(xù)的隨機數(shù)
V=ede*K(R^I)    #下一輪V生成方法

 

二、隨機數(shù)狀態(tài)恢復攻擊

隨機數(shù)生成算法詳細描述如下:

K是在初始化時以某種方式生成的對稱加密算法(如3DES、AES)加密密鑰。隨機數(shù)迭代 生成過程如下:

Ti = EK(current timestamp)
output[i] = EK(Ti ⊕ seed[i])
seed[i + 1] = EK(Ti ⊕ output[i])

直接密碼分析攻擊這個生成器需要對 AES(或者正在使用其它分組密碼)。

當K不保密時,隨機數(shù)發(fā)生器就變得十分脆弱。已知K的攻擊者可以使用兩個連續(xù)的輸出塊并猜測時間戳來恢復當前狀態(tài)。單個輸出塊不會唯一地標識狀態(tài),但兩個塊幾乎肯定會。中間相遇攻擊算法如下:

seed[i + 1] = EK(output[i] ⊕ Ti)
seed[i + 1] = DK(output[i + 1]) ⊕ Ti+1

攻擊者嘗試Ti的所有可能值,并形成一個可能的種子[i + 1]值的排序列表。然后他嘗試Ti + 1的所有可能值,并形成另一個可能的種子[i + 1]值的排序列表。正確的種子[i + 1]值是兩個列表中出現(xiàn)的值。

如果只是大致知道時間戳,可以在一定范圍內(nèi)暴破它們,直到我們找到一對產(chǎn)生相等或者應(yīng)用中間相遇的攻擊。 如果只知道分組的部分值,則可以重新排列加密和解密,并驗證塊的已知部分的相等性。 一旦知道時間戳T1和T2,下一個種子就是:

seed[i+2] = EK(output[i + 1]  ⊕ Ti+1) 
#通過猜測下一個current timestamp,驗證output[i+2],確定確定的隨機數(shù)
Ti+2 = EK(current timestamp)
output[i+2] = EK(Ti+2  ⊕ seed[i+2])

其中Ti+2由下一時刻的系統(tǒng)時間唯一確定,可通過有限窮盡,驗證隨機數(shù)生成的正確性。

三、隨機數(shù)生成算法實現(xiàn)與攻擊驗證

#include <openssl/des.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include <sys/time.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>

#define FIPS_RAND_SIZE_T size_t
#define SEED_SIZE    8

static unsigned char seed[SEED_SIZE];
static FIPS_RAND_SIZE_T n_seed;
static FIPS_RAND_SIZE_T o_seed;
static DES_cblock key1;
static DES_cblock key2;
static DES_key_schedule ks1,ks2;
static int key_set;
static int key_init;
static int test_mode;
static unsigned char test_faketime[8];
static int second;

void FIPS_set_prng_key(const unsigned char k1[8],const unsigned char k2[8]);
void FIPS_rand_seed(const void *buf, FIPS_RAND_SIZE_T num);
static void FIPS_rand_cleanup(void);
static int FIPS_rand_bytes(unsigned char *buf, FIPS_RAND_SIZE_T num);
static void dump(const unsigned char *b,int n);

void FIPS_test_mode(int test,const unsigned char faketime[8])
{
    test_mode=test;
    if(!test_mode)
    return;
    memcpy(test_faketime,faketime,sizeof test_faketime);
}

void FIPS_set_prng_key(const unsigned char k1[8],const unsigned char k2[8])
{
    memcpy(&key1,k1,sizeof key1);
    memcpy(&key2,k2,sizeof key2);
    key_set=1;
    second=0;
}

// struct timeval {
//     time_t      tv_sec;      seconds 
//     suseconds_t tv_usec;    /* microseconds */
// };

static void FIPS_gettime(unsigned char buf[8])
{
    if(test_mode)
    {
        memcpy(buf,test_faketime,sizeof test_faketime);
        return;
    }
    struct timeval tv;

    gettimeofday(&tv,NULL);
    buf[0] = (unsigned char) (tv.tv_sec & 0xff);
    buf[1] = (unsigned char) ((tv.tv_sec >> 8) & 0xff);
    buf[2] = (unsigned char) ((tv.tv_sec >> 16) & 0xff);
    buf[3] = (unsigned char) ((tv.tv_sec >> 24) & 0xff);
    buf[4] = (unsigned char) (tv.tv_usec & 0xff);
    buf[5] = (unsigned char) ((tv.tv_usec >> 8) & 0xff);
    buf[6] = (unsigned char) ((tv.tv_usec >> 16) & 0xff);
    buf[7] = (unsigned char) ((tv.tv_usec >> 24) & 0xff);
}

static void FIPS_rand_encrypt(unsigned char *out,const unsigned char *in)
{
    DES_ecb2_encrypt(in,out,&ks1,&ks2,1);
}

static void FIPS_rand_cleanup(void)
{
    OPENSSL_cleanse(seed,sizeof seed);
    n_seed=0;
    o_seed=0;
    key_init=0;
}

void FIPS_rand_seed(const void *buf_, FIPS_RAND_SIZE_T num)
{
    const char *buf=buf_;
    FIPS_RAND_SIZE_T n;

    /* If the key hasn't been set, we can't seed! */
    if(!key_set)
    return;

    if(!key_init)
    {
        key_init=1;
        DES_set_key(&key1,&ks1);
        DES_set_key(&key2,&ks2);
    }

    /*
     * This algorithm only uses 64 bits of seed, so ensure that we use
     * the most recent 64 bits.
     */
    for(n=0 ; n < num ; )
    {
        FIPS_RAND_SIZE_T t=num-n;

        if(o_seed+t > sizeof seed)
            t=sizeof seed-o_seed;
        memcpy(seed+o_seed,buf+n,t);
        n+=t;
        o_seed+=t;
        if(o_seed == sizeof seed)
            o_seed=0;
        if(n_seed < sizeof seed)
            n_seed+=t;
    }
}

static int FIPS_rand_bytes(unsigned char *buf,FIPS_RAND_SIZE_T num)
{
    FIPS_RAND_SIZE_T n;
    unsigned char timeseed[8];
    unsigned char intermediate[SEED_SIZE];
    unsigned char output[SEED_SIZE];
    static unsigned char previous[SEED_SIZE];

    if(n_seed < sizeof seed)
    {
        printf("n_seed<sizeof(seed)!n");
        return 0;
    }

    for(n=0 ; n < num ; )
    {
        unsigned char t[SEED_SIZE];
        FIPS_RAND_SIZE_T l;

        /* ANS X9.31 A.2.4:    I = ede*K(DT)
               timeseed == DT
               intermediate == I
        */
        FIPS_gettime(timeseed);

        printf("time: ");
        dump(timeseed,8);
        putchar('t');
        printf("seed1: ");
        dump(seed,8);
        putchar('t');

        FIPS_rand_encrypt(intermediate,timeseed);

        printf("I: ");
        dump(intermediate,8);
        putchar('t');

        /* ANS X9.31 A.2.4:     R = ede*K(I^V)
               intermediate == I
               seed == V
               output == R
        */
        for(l=0 ; l < sizeof t ; ++l)
            t[l]=intermediate[l]^seed[l];
        FIPS_rand_encrypt(output,t);

        printf("rand: ");
        dump(output,8);
        putchar('t');

        /* ANS X9.31 A.2.4:     V = ede*K(R^I)
               output == R
               intermediate == I
               seed == V
        */
        for(l=0 ; l < sizeof t ; ++l)
            t[l]=output[l]^intermediate[l];
        FIPS_rand_encrypt(seed,t);

        printf("seed2: ");
        dump(seed,8);
        putchar('n');

        if(second && !memcmp(output,previous,sizeof previous))
        {
            printf("output is the same with the previous!n");
            return 0;
        }
        memcpy(previous,output,sizeof previous);
        second=1;

        /* Successive values of R may be concatenated to produce a
           pseudo random number of the desired length */ 
        l=SEED_SIZE < num-n ? SEED_SIZE : num-n;
        memcpy(buf+n,output,l);
        n+=l;
    }
    return 1;
}

typedef struct
{
    DES_cblock keys[2];
    const unsigned char time[8];
    const unsigned char seed[8];
    const unsigned char block1[8];
    const unsigned char block100[8];
} PRNGtest;

/* FIXME: these test vectors are made up! */
static PRNGtest t1=
    {
    { { 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07 },  //key
      { 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f },
    },
    { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },  //fake_time
    { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },  //seed
    { 0x33,0xc3,0xdf,0xfe,0x60,0x60,0x49,0x9e },
    { 0xcd,0x2b,0x41,0xaf,0x80,0x51,0x37,0xd8 }
    };
static PRNGtest t2=
    {
    { { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff },
      { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff } },
    { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff },
    { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff },
    { 0x65,0xf1,0xa4,0x07,0x42,0x38,0xd5,0x25 },
    { 0xbb,0x75,0x84,0x20,0x7a,0x44,0xf0,0xa0 }
    };

static void dump(const unsigned char *b,int n)
{
    while(n-- > 0)
    {
        printf(" %02x",*b++);
    }
}

static void compare(const unsigned char *result,const unsigned char *expected, int n)
{
    int i;

    for(i=0 ; i < n ; ++i)
        if(result[i] != expected[i])
        {
            puts("Random test failed, got:");
            dump(result,8);
            puts("n               expected:");
            dump(expected,8);
            putchar('n');
            exit(1);
        }
}

static void run_test(const PRNGtest *t)
{
    unsigned char buf[8];
    int n;

    FIPS_set_prng_key(t->keys[0],t->keys[1]);
    FIPS_test_mode(1,t->time);
    FIPS_rand_seed(t->seed,sizeof t->seed);

    if(FIPS_rand_bytes(buf,8) <= 0)
    {
        printf("FIPS_rand_bytes error!n");
        exit(2);
    }
    compare(buf,t->block1,8);
    for(n=0 ; n < 99 ; ++n)
    if(FIPS_rand_bytes(buf,8) <= 0)
    {
        printf("FIPS_rand_bytes error!n");
        exit(2);
    }
    compare(buf,t->block100,8);
    FIPS_test_mode(0,NULL);
    //FIPS_rand_cleanup();
}

void gen_rand(const PRNGtest *t)
{
   unsigned char buf[8];
    int n;

    FIPS_set_prng_key(t->keys[0],t->keys[1]);
    FIPS_rand_seed(t->seed,sizeof t->seed);

    for(n=0 ; n < 8 ; ++n)
    {
        if(FIPS_rand_bytes(buf,8) <= 0)
        {
            printf("FIPS_rand_bytes error!n");
            exit(2);
        }
    }
}

int main()
{
    // test where the code runs as expected
    //time is fixed = fake_time; encryption is fixed
    run_test(&t1);
    run_test(&t2);
    //encryption is fixed; time is current time
    gen_rand(&t1);
    FIPS_rand_cleanup();    
}

代碼采用C語言編寫,8組測試輸出結(jié)果如下:

time:  0d 08 71 5c 97 11 0c 00  seed1:  00 00 00 00 00 00 00 00 I:  bc 8a 0e 0a 20 5f 7e d8     rand:  34 b5 11 d5 bf 60 bc be  seed2:  f8 22 91 7a 9b a0 77 8e
time:  0d 08 71 5c e4 11 0c 00  seed1:  f8 22 91 7a 9b a0 77 8e I:  46 2d 1b 9f dc 05 6d 68     rand:  fa 45 71 c0 54 86 43 d6  seed2:  fa 22 29 55 fb fc 41 7e
time:  0d 08 71 5c ef 11 0c 00  seed1:  fa 22 29 55 fb fc 41 7e I:  bf c0 f2 6e 71 f1 82 c6     rand:  cd 5a a2 0a 47 77 31 28  seed2:  e4 fb 5a 3d 8e 9c ad c3
time:  0d 08 71 5c 12 12 0c 00  seed1:  e4 fb 5a 3d 8e 9c ad c3 I:  96 21 5f 5e b5 7b 26 4c     rand:  1a 51 52 70 54 fc 3c fd  seed2:  14 58 9b ba 46 db 10 5e
time:  0d 08 71 5c 1b 12 0c 00  seed1:  14 58 9b ba 46 db 10 5e I:  57 cc aa 31 27 0b 2d c1     rand:  43 13 3a 1f c5 3f c2 13  seed2:  50 68 a1 83 8d 62 6c 66
time:  0d 08 71 5c 22 12 0c 00  seed1:  50 68 a1 83 8d 62 6c 66 I:  98 86 5e 21 28 a4 49 1b     rand:  ac 5d c6 12 6f 74 be c9  seed2:  b9 66 32 e0 19 aa 09 a6
time:  0d 08 71 5c 2a 12 0c 00  seed1:  b9 66 32 e0 19 aa 09 a6 I:  ea dc 46 98 0f 49 bc 72     rand:  32 e0 53 ec b9 3d 36 0c  seed2:  44 66 1e ca 58 e5 2c 20
time:  0d 08 71 5c 31 12 0c 00  seed1:  44 66 1e ca 58 e5 2c 20 I:  f4 ec 47 d5 a3 48 41 f0     rand:  00 00 e2 58 e2 34 2f cb  seed2:  37 49 0d 63 08 b1 18 0b

時間共64位,小端存儲,多次調(diào)用僅在微秒時間內(nèi)發(fā)生變化。seed2和下一輪seed1相同。

攻擊驗證算法代碼如下:

#include <openssl/des.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include <sys/time.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>

static DES_cblock key1;
static DES_cblock key2;
static DES_key_schedule ks1,ks2;

DES_cblock keys[2]=
{
    { 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07 }, 
    { 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f }
};

void dump(const unsigned char *b,int n);
void FIPS_set_prng_key(const unsigned char k1[8],const unsigned char k2[8]);
void xor_vectors(unsigned char *in1, unsigned char *in2,
            unsigned char *out, unsigned int size);
static void FIPS_rand_encrypt(unsigned char *out,const unsigned char *in);
static void FIPS_rand_decrypt(unsigned char *out,const unsigned char *in);
int compare_seed(unsigned char *rand1 ,unsigned char *time_buf1, unsigned char *rand2, unsigned char *time_buf2);


void dump(const unsigned char *b,int n)
{
    while(n-- > 0)
    {
        printf(" %02x",*b++);
    }
    putchar('n');
}

void FIPS_set_prng_key(const unsigned char k1[8],const unsigned char k2[8])
{
    memcpy(&key1,k1,sizeof key1);
    memcpy(&key2,k2,sizeof key2);
    DES_set_key(&key1,&ks1);
    DES_set_key(&key2,&ks2);
}

void xor_vectors(unsigned char *in1, unsigned char *in2,
            unsigned char *out, unsigned int size)
{
    int i;

    for (i = 0; i < size; i++)
        out[i] = in1[i] ^ in2[i];
}

static void FIPS_rand_encrypt(unsigned char *out,const unsigned char *in)
{
    DES_ecb2_encrypt(in,out,&ks1,&ks2,1);
}

static void FIPS_rand_decrypt(unsigned char *out,const unsigned char *in)
{
    DES_ecb2_encrypt(in,out,&ks1,&ks2,0);
}

int compare_seed(unsigned char *rand1 ,unsigned char *time_buf1, unsigned char *rand2, unsigned char *time_buf2)
{
    unsigned char in1[8], out2[8];
    unsigned char seed1[8], seed2[8];
    unsigned char T1[8], T2[8];
    int i;

    /*
    Ti=Ek(time_buf_i)
    seed[i+1]=Ek(rand[i]^Ti)
    */
    FIPS_set_prng_key(keys[0],keys[1]);
    FIPS_rand_encrypt(T1,time_buf1);
    xor_vectors(rand1,T1,in1,8);
    FIPS_rand_encrypt(seed1,in1);
    dump(seed1,8);

    /*
    Ti+1=Ek(time_buf_i+1)
    seed[i+1]=Dk(rand[i+1])^Ti+1
    */
    FIPS_rand_encrypt(T2,time_buf2);
    FIPS_rand_decrypt(out2,rand2);
    xor_vectors(out2,T2,seed2,8);
    dump(seed2,8);

    if(memcmp(seed1,seed2,8)==0)
        return 1;
    return 0;
}

int main()
{
    unsigned char rand1[]={0xfa,0x45,0x71,0xc0,0x54,0x86,0x43,0xd6};
    unsigned char time_buf1[]={0x0d,0x08,0x71,0x5c,0xe4,0x11,0x0c,0x00};
    unsigned char rand2[]={0xcd,0x5a,0xa2,0x0a,0x47,0x77,0x31,0x28};
    unsigned char time_buf2[]={0x0d,0x08,0x71,0x5c,0xef,0x11,0x0c,0x00};

    int ret=compare_seed(rand1,time_buf1,rand2,time_buf2);
    if(ret)
        printf("mached!n");
    else
        printf("mismached!n");
}

利用測試輸出的連續(xù)兩組隨機數(shù)及確定的時間,驗證攻擊算法的正確性。實驗表明,驗證算法正確。

四、存在隨機數(shù)漏洞的產(chǎn)品攻擊

X9.31隨機數(shù)發(fā)生器的NIST設(shè)計描述沒有規(guī)定如何生成或存儲分組密碼密鑰。 但是,希望獲得FIPS認證的供應(yīng)商需要制作詳細的公共“安全政策”文檔,描述其加密實施和密鑰管理程序。 論文對針對X9.31 PRG認證的產(chǎn)品的安全策略進行了系統(tǒng)研究,以了解有多少供應(yīng)商公開記錄了潛在的硬編碼密鑰漏洞。 作者從NIST網(wǎng)站獲得了認證設(shè)備清單。統(tǒng)計結(jié)果如下:

不安全設(shè)備的文檔表明AES密鑰靜態(tài)存儲在固件或閃存中,并在運行時加載到PRG中。共有12家供應(yīng)商,涉及40個產(chǎn)品線。其中包括Cisco、Fortinet等大廠商。

FortiOS 4.3的FIPS認證表明X9.31密鑰是“在模塊外部生成的”。作者對兩個版本的FortiOS進行了逆向工程,發(fā)現(xiàn)他們使用相同的硬編碼密鑰進行X9.31實現(xiàn),然后將其用作操作系統(tǒng)的隨機數(shù)生成器。

FortiOSv4是Fortigate網(wǎng)絡(luò)設(shè)備的嵌入式操作系統(tǒng)。兩個鏡像分別來自FortiGate 100D防火墻的固件和運行相同版本操作系統(tǒng)的“虛擬設(shè)備”(VM)FortiOS是一種GNU/Linux變種,具有定制的shell,其內(nèi)核模塊實現(xiàn)了硬件接口和加密功能。 內(nèi)核是Linux 2.4.37。通過binwalk即可實現(xiàn)固件鏡像解壓,并對其操作系統(tǒng)加載。FortiOS通過導出Linux字符設(shè)備模塊,在內(nèi)核中實現(xiàn)X9.31隨機數(shù)發(fā)生器。 在引導時,init進程加載模塊并將/dev/urandom替換為與X9.31字符設(shè)備對應(yīng)的文件系統(tǒng)節(jié)點。PRG實現(xiàn)使用對do_gettimeofday()的調(diào)用生成時間戳,并生成包含64位時間到最接近的微秒的struct timeval。 此結(jié)構(gòu)將兩次復制到緩沖區(qū)中,以形成X9.31生成器的完整128位時間戳。作者對提供X9.31實現(xiàn)的內(nèi)核模塊進行了逆向工程,并找到了用于PRG的硬編碼AES密鑰。

(一)HTTPS攻擊

FortiOS v4使用OpenSSL實現(xiàn)TLS。 初始化庫時,它將隨機數(shù)生成方法設(shè)置為系統(tǒng)PRG,即X9.31實現(xiàn)。

TLS服務(wù)器hello random包含一個四字節(jié)時間戳,后跟兩個X9.31 PRG輸出的原始塊,截斷為28字節(jié),允許狀態(tài)恢復攻擊。 但是,TLS DH密鑰交換實現(xiàn)方式為臨時靜態(tài)Diffie-Hellman,每次重啟后生成直至關(guān)機,不容易對服務(wù)器直接密鑰恢復攻擊。

(二)IPSec攻擊

IKE守護程序基于raccoon2項目偵察,使用GNU MP庫編譯。守護進程使用的所有隨機數(shù)都是通過/dev/urandom生成,因此使用X9.31模塊。

在IKEv1實現(xiàn)中,PRG輸出的第一個分組用于生成IKEv1 cookie,方法是將其與IP地址,端口,內(nèi)存地址以及時間一起散列,以秒為單位。在IKEv2實現(xiàn)中,SPI字段(相當于IKEv1 cookie)是PRG輸出的八個原始字節(jié)。在IKEv1和IKEv2中,下一個PRG輸出分組用于生成握手Random,其長度為16個字節(jié)。對于與1024位Oakley Group 2 prime進行Diffie-Hellman密鑰交換的情況,F(xiàn)ortiOS v4使用來自PRG的兩個連續(xù)塊生成指數(shù)。在虛擬設(shè)備的實現(xiàn)中,隨機字節(jié)直接讀入Diffie-Hellman指數(shù)而不進行修改。

四、Fortinet特定版本產(chǎn)品的在線探測

作者使用ZMap在在互聯(lián)網(wǎng)空間內(nèi)掃描TCP 443端口(HTTPS)和 UDP 500端口(IKE)。通過IKE協(xié)議中的Vendor ID信息,即可確定VPN類型。HTTPS掃描中,一方面可通過證書common name字段判斷設(shè)備廠商信息。進一步可通過ETAG判斷設(shè)備指紋信息。方程式組織泄露的文件中注明了ETAG及其對應(yīng)版本的詳細信息,可作為版本探測的參考。而在現(xiàn)實攻擊中,還可通過telnet和SSH協(xié)議的flag信息判斷Fortinet的產(chǎn)品。

Fortigate防火墻證書信息

方程式組織泄露文件EGBL.config

下面重點對ETAG的作用進闡述。

HTTP協(xié)議規(guī)格說明定義ETag為“被請求變量的實體值”。另一種說法是,ETag是一個可以與Web資源關(guān)聯(lián)的記號(token)。典型的Web資源可以一個Web頁,但也可能是JSON或XML文檔。服務(wù)器單獨負責判斷記號是什么及其含義,并在HTTP響應(yīng)頭中將其傳送到客戶端,以下是服務(wù)器端返回的格式:ETag:"50b1c1d4f775c61:df3"。客戶端的查詢更新格式是這樣的:If-None-Match : W / "50b1c1d4f775c61:df3"如果ETag沒改變,則返回狀態(tài)304然后不返回,這也和Last-Modified一樣。測試Etag主要在斷點下載時比較有用。利用ETAG,可以作為服務(wù)器版本的特定指紋信息。針對FortiGate防火墻掃描返回的信息如下。

原文鏈接:https://www.anquanke.com/post/id/172076

上一篇:你需要的都幫你整理好了!RSAC主題演講資料下載(含155份PPT)

下一篇:RSAC啟示:資產(chǎn)管理的正確“打開方式”