溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何在通過TCP通信的位置無關(guān)代碼中實(shí)現(xiàn)數(shù)據(jù)加密

發(fā)布時(shí)間:2021-12-28 10:31:18 來源:億速云 閱讀:168 作者:小新 欄目:數(shù)據(jù)安全

這篇文章給大家分享的是有關(guān)如何在通過TCP通信的位置無關(guān)代碼中實(shí)現(xiàn)數(shù)據(jù)加密的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過來看看吧。

前言

如何在通過TCP通信的位置無關(guān)代碼(PIC)中實(shí)現(xiàn)數(shù)據(jù)加密。

我將以Linux下的同步Shell作為演示樣例

協(xié)議和代碼庫

當(dāng)我們?cè)谒伎技用軈f(xié)議時(shí),第一個(gè)想到的很可能是安全傳輸層協(xié)議(TLS),因?yàn)樗轻槍?duì)Web安全的工業(yè)級(jí)標(biāo)準(zhǔn)。有的人可能還會(huì)想到SSH或IPSec等等,但是考慮到這些協(xié)議所采用的底層算法,它們其實(shí)都不適用于資源受限環(huán)境。而類似SHA-2和分組密碼(例如Blowfish)這樣加密哈希函數(shù)也并不是為類似RFID芯片這樣的占用資源較少的電子設(shè)備設(shè)計(jì)的。

在2018年4月份,NIST曾為物聯(lián)網(wǎng)行業(yè)的輕量級(jí)加密算法推行過一個(gè)標(biāo)準(zhǔn)化進(jìn)程,整個(gè)過程需要好幾年的時(shí)間才可以完成,但毫無疑問的是,整個(gè)行業(yè)并不會(huì)一直等待,因?yàn)檫@樣會(huì)導(dǎo)致不安全的產(chǎn)品暴露在互聯(lián)網(wǎng)中。某些密碼學(xué)家選擇采取主動(dòng)的方式,通過自己的努力將他們?cè)O(shè)計(jì)的協(xié)議采用到這些低資源消耗的設(shè)備上,其中有兩個(gè)典型的算法就是BLINKER和STROBE,而相應(yīng)的適用于資源受限環(huán)境的代碼庫有LibHydrogen和MonoCypher。

分組密碼

分組密碼有很多種,但AES 128可能是目前最適合對(duì)在線流量進(jìn)行加密的算法了,下面給出的是我們對(duì)不同種類分組密碼的測(cè)試結(jié)果:

如何在通過TCP通信的位置無關(guān)代碼中實(shí)現(xiàn)數(shù)據(jù)加密

雖然這些加密算法都非常優(yōu)秀,但是他們?nèi)孕枰愃朴?jì)數(shù)器(CTR)和基于認(rèn)證的加密模塊,其中最適合消息認(rèn)證碼(MAC)的加密算法就是LightMAC了,因?yàn)樗趯?shí)現(xiàn)加密的過程中使用的是相同的分組密碼。

流密碼

另外兩種針對(duì)認(rèn)證加密的熱門算法(AES-GCM的替換)就是ChaCha20和Poly1305了,但是ChaCha20采用的是200字節(jié),而Poly1305為330字節(jié)。雖然跟HMAC-SHA2相比,Poly1305已經(jīng)壓縮得非常小了,但仍然占用資源過多。

置換函數(shù)

如果你花了很多時(shí)間去測(cè)試各種加密算法的話,你最終會(huì)發(fā)現(xiàn)在構(gòu)造流密碼、分組密碼、加密認(rèn)證模型、加密哈希函數(shù)和隨機(jī)數(shù)生成器時(shí),你需要的僅僅只是一個(gè)置換函數(shù)。下面這個(gè)表格給出的是我們針對(duì)三種函數(shù)的測(cè)試結(jié)果:

如何在通過TCP通信的位置無關(guān)代碼中實(shí)現(xiàn)數(shù)據(jù)加密

這里我們選擇使用Gimli,因?yàn)樗加觅Y源最少,并且可以用來構(gòu)造針對(duì)通信流量的加密算法。

異或密碼

接下來,我們實(shí)現(xiàn)一個(gè)針對(duì)數(shù)據(jù)流的簡(jiǎn)單異或操作(Just For Fun?。?。下面的截圖中顯示的是一臺(tái)Windows虛擬機(jī)發(fā)送給Linux虛擬機(jī)的部分命令,其中Linux平臺(tái)運(yùn)行的Shellcode是沒有采用任何加密的。

如何在通過TCP通信的位置無關(guān)代碼中實(shí)現(xiàn)數(shù)據(jù)加密

捕捉到兩臺(tái)主機(jī)間的通信數(shù)據(jù)之后,我們可以看到如下所示的TCP流數(shù)據(jù):

如何在通過TCP通信的位置無關(guān)代碼中實(shí)現(xiàn)數(shù)據(jù)加密

給Shellcode x86匯編代碼中添加部分命令后,我們就可以進(jìn)行8位異或運(yùn)算了:

;      ; read(r, buf, BUFSIZ, 0);      xor   esi, esi          ; esi = 0      mov   ecx, edi          ; ecx = buf      cdq                      ; edx = 0      mov   dl, BUFSIZ        ; edx = BUFSIZ      push  SYS_read          ; eax = SYS_read      pop   eax      int   0x80           ; encrypt/decrypt buffer      pushad      xchg  eax, ecxxor_loop:      xor   byte[eax+ecx-1], XOR_KEY      loop  xor_loop      popad           ; write(w, buf, len);      xchg  eax, edx          ; edx = len      mov   al, SYS_write      pop   ebx               ; s or in[1]      int   0x80      jmp   poll_wait

通過在新的會(huì)話中執(zhí)行相同的命令,通信數(shù)據(jù)將無法直接可讀,我這里使用了haxdump來查看發(fā)送的命令以及接收到的結(jié)果:

如何在通過TCP通信的位置無關(guān)代碼中實(shí)現(xiàn)數(shù)據(jù)加密

當(dāng)然了,長(zhǎng)度為8位的密鑰是無法有效阻止攻擊者恢復(fù)出通信明文的,下圖給出的是Cyberchef爆破密鑰的過程:

如何在通過TCP通信的位置無關(guān)代碼中實(shí)現(xiàn)數(shù)據(jù)加密

Speck和LightMAC

一開始,我使用的是下面這段代碼來對(duì)數(shù)據(jù)包的加密進(jìn)行驗(yàn)證,它使用了Encrypt-then-MAC (EtM),而且這種方法比其他的方法要更安全,比如說MAC-then-Encrypt (MtE) 或Encrypt-and-MAC(E&M):

bits32   %defineSPECK_RNDS    27%defineN              8%defineK             16 ;*****************************************;Light MAC parameters based on SPECK64-128;; N =64-bits; K =128-bits;%defineCOUNTER_LENGTH N/2  ; should be <= N/2%defineBLOCK_LENGTH   N  ; equal to N%defineTAG_LENGTH     N  ; >= 64-bits && <= N%defineBC_KEY_LENGTH  K  ; K %defineENCRYPT_BLK speck_encrypt%defineGET_MAC lightmac%defineLIGHTMAC_KEY_LENGTH BC_KEY_LENGTH*2 ; K*2   %definek0 edi   %definek1 ebp   %definek2 ecx   %definek3 esi %definex0 ebx   %definex1 edx ; esi= IN data; ebp= IN key speck_encrypt:        pushad       push   esi            ; save M           lodsd                  ; x0 = x->w[0]      xchg   eax, x0      lodsd                  ; x1 = x->w[1]      xchg   eax, x1       mov    esi, ebp       ; esi = key      lodsd      xchg   eax, k0        ; k0 = key[0]      lodsd      xchg   eax, k1        ; k1 = key[1]      lodsd      xchg   eax, k2        ; k2 = key[2]      lodsd      xchg   eax, k3        ; k3 = key[3]         xor    eax, eax       ; i = 0spk_el:      ; x0 = (ROTR32(x0, 8) + x1) ^ k0;      ror    x0, 8      add    x0, x1      xor    x0, k0      ; x1 = ROTL32(x1, 3) ^ x0;      rol    x1, 3      xor    x1, x0      ; k1 = (ROTR32(k1, 8) + k0) ^ i;      ror    k1, 8      add    k1, k0      xor    k1, eax      ; k0 = ROTL32(k0, 3) ^ k1;      rol    k0, 3      xor    k0, k1         xchg   k3, k2      xchg   k3, k1      ; i++      inc    eax      cmp    al, SPECK_RNDS         jnz    spk_el           pop    edi         xchg   eax, x0        ; x->w[0] = x0      stosd      xchg   eax, x1        ; x->w[1] = x1      stosd      popad      ret ; edx= IN len; ebx= IN msg; ebp= IN key; edi= OUT tag     lightmac:      pushad      mov     ecx, edx      xor     edx, edx      add     ebp, BLOCK_LENGTH + BC_KEY_LENGTH           pushad                 ; allocate N-bytes for M      ; zero initialize T      mov    [edi+0], edx   ; t->w[0] = 0;      mov    [edi+4], edx   ; t->w[1] = 0;      ; while we have msg datalmx_l0:      mov    esi, esp       ; esi = M      jecxz  lmx_l2         ; exit loop ifmsglen == 0lmx_l1:      ; add byte to M      mov    al, [ebx]      ; al = *data++      inc    ebx      mov    [esi+edx+COUNTER_LENGTH], al                inc    edx            ; idx++      ; M filled?      cmp    dl, BLOCK_LENGTH - COUNTER_LENGTH      ; --msglen      loopne lmx_l1      jne    lmx_l2      ; add S counter in big endian format      inc    dword[esp+_edx]; ctr++      mov    eax, [esp+_edx]      ; reset index     cdq                    ; idx = 0      bswap  eax            ; m.ctr =SWAP32(ctr)      mov    [esi], eax      ; encrypt M with E using K1      call   ENCRYPT_BLK      ; update T      lodsd                  ; t->w[0] ^= m.w[0];      xor    [edi+0], eax           lodsd                  ; t->w[1] ^= m.w[1];      xor    [edi+4], eax              jmp    lmx_l0         ; keep goinglmx_l2:      ; add the end bit      mov    byte[esi+edx+COUNTER_LENGTH], 0x80      xchg   esi, edi       ; swap T and Mlmx_l3:      ; update T with any msg dataremaining         mov    al, [edi+edx+COUNTER_LENGTH]      xor    [esi+edx], al      dec    edx      jns    lmx_l3      ; advance key to K2      add    ebp, BC_KEY_LENGTH      ; encrypt T with E using K2      call   ENCRYPT_BLK      popad                  ; release memory for M      popad                  ; restore registers      ret ; IN:ebp = global memory, edi = msg, ecx = enc flag, edx = msglen;OUT: -1 or length of data encrypted/decryptedencrypt:      push   -1      pop    eax            ; set return valueto -1      pushad      lea    ebp, [ebp+@ctx] ; ebp crypto ctx      mov    ebx, edi       ; ebx = msg           pushad                 ; allocate 8-bytes fortag+strm      mov    edi, esp       ; edi = tag      ; if (enc) {      ;  verify tag + decrypt      jecxz  enc_l0      ; msglen -= TAG_LENGTH;      sub    edx, TAG_LENGTH      jle    enc_l5         ; return -1 if msglen <= 0      mov    [esp+_edx], edx      ; GET_MAC(ctx, msg, msglen, mac);      call   GET_MAC      ; memcmp(mac, &msg[msglen],TAG_LENGTH)      lea    esi, [ebx+edx] ; esi = &msg[msglen]       cmpsd      jnz    enc_l5         ; not equal? return-1      cmpsd      jnz    enc_l5         ; ditto      ; MACs are equal      ; zero the MAC      xor    eax, eax      mov    [esi-4], eax      mov    [esi-8], eaxenc_l0:      mov    edi, esp      test   edx, edx       ; exit if (msglen== 0)      jz     enc_lx      ; memcpy (strm, ctx->e_ctr,BLOCK_LENGTH);      mov    esi, [esp+_ebp]; esi = ctx->e_ctr      push   edi      movsd      movsd      mov    ebp, esi      pop    esi           ; ENCRYPT_BLK(ctx->e_key, &strm);      call   ENCRYPT_BLK      mov    cl, BLOCK_LENGTH      ; r=(len > BLOCK_LENGTH) ?BLOCK_LENGTH : len;enc_l2:      lodsb                  ; al = *strm++      xor    [ebx], al      ; *msg ^= al      inc    ebx            ; msg++      dec    edx      loopnz enc_l2         ; while (!ZF&& --ecx)      mov    cl, BLOCK_LENGTH     enc_l3:                      ; do {      ; update counter      mov    ebp, [esp+_ebp]      inc    byte[ebp+ecx-1]         loopz  enc_l3         ; } while (ZF&& --ecx)      jmp    enc_l0enc_lx:      ; encrypting? add MAC of ciphertext      dec    dword[esp+_ecx]      mov    edx, [esp+_edx]      jz     enc_l4      mov    edi, ebx      mov    ebx, [esp+_ebx]      mov    ebp, [esp+_ebp]      ; GET_MAC(ctx, buf, buflen, msg);      call   GET_MAC      ; msglen += TAG_LENGTH;      add    edx, TAG_LENGTHenc_l4:      ; return msglen;      mov    [esp+32+_eax], edx           enc_l5:           popad      popad      ret

需要注意的是,這里還得用到一個(gè)協(xié)議,接收方在對(duì)數(shù)據(jù)有效性進(jìn)行驗(yàn)證之前需要知道發(fā)送方到底發(fā)送了多少數(shù)據(jù)過來,因此加密長(zhǎng)度需要首先發(fā)送,接下來才是加密數(shù)據(jù)。但是請(qǐng)等一下,這里明明應(yīng)該是Shellcode,為什么現(xiàn)在搞得那么復(fù)雜呢?試一下RC4?不,請(qǐng)大家往下看!

Gimli

為了使用Gimli來代替RC4,我編寫了下面這段代碼,這里的置換函數(shù)本質(zhì)上就是Gimli:

#defineR(v,n)(((v)>>(n))|((v)<<(32-(n))))#defineF(n)for(i=0;i<n;i++)#defineX(a,b)(t)=(s[a]),(s[a])=(s[b]),(s[b])=(t) voidpermute(void*p){  uint32_t i,r,t,x,y,z,*s=p;   for(r=24;r>0;--r){    F(4)      x=R(s[i],24),      y=R(s[4+i],9),      z=s[8+i],        s[8+i]=x^(z+z)^((y&z)*4),      s[4+i]=y^x^((x|z)*2),     s[i]=z^y^((x&y)*8);    t=r&amp3;       if(!t)      X(0,1),X(2,3),      *s^=0x9e377900|r;      if(t==2)X(0,2),X(1,3);  }} typedefstruct _crypt_ctx {    uint32_t idx;    int     fdr, fdw;    uint8_t s[48];    uint8_t buf[BUFSIZ];}crypt_ctx; uint8_tgf_mul(uint8_t x) {    return (x << 1) ^ ((x >> 7) *0x1b);} //initialize crypto contextvoidinit_crypt(crypt_ctx *c, int r, int w, void *key) {    int i;       c->fdr = r; c->fdw = w;       for(i=0;i<48;i++) {      c->s[i] = ((uint8_t*)key)[i % 16] ^gf_mul(i);    }    permute(c->s);    c->idx = 0;} //encrypt or decrypt buffervoidcrypt(crypt_ctx *c) {    int i, len;     // read from socket or stdout    len = read(c->fdr, c->buf, BUFSIZ);       // encrypt/decrypt    for(i=0;i<len;i++) {      if(c->idx >= 32) {        permute(c->s);        c->idx = 0;      }      c->buf[i] ^= c->s[c->idx++];    }    // write to socket or stdin    write(c->fdw, c->buf, len);}

在Linux Shell中使用這段代碼之前,我們需要聲明兩個(gè)單獨(dú)的加密上下文來處理輸入、輸出和128位的靜態(tài)密鑰:

//using a static 128-bit key    crypt_ctx          *c, c1, c2;       // echo -n top_secret_key | openssl md5-binary -out key.bin    // xxd -i key.bin       uint8_t key[] = {      0x4f, 0xef, 0x5a, 0xcc, 0x15, 0x78, 0xf6,0x01,      0xee, 0xa1, 0x4e, 0x24, 0xf1, 0xac, 0xf9,0x49 };

在進(jìn)入主輸出循環(huán)之前,我們還需要對(duì)每一個(gè)上下文初始化文件讀取和寫入描述符,這樣可以減少代碼的行數(shù):

//        // c1 is for reading from socket andwriting to stdin        init_crypt(&c1, s, in[1], key);               // c2 is for reading from stdout andwriting to socket        init_crypt(&c2, out[0], s, key);               // now loop until user exits or someother error        for (;;) {          r = epoll_wait(efd, &evts, 1,-1);                           // error? bail out                    if (r<=0) break;                   // not input? bail out          if (!(evts.events & EPOLLIN))break;           fd = evts.data.fd;                   c = (fd == s) ? &c1 : &ampc2;                   crypt(c);            }

感謝各位的閱讀!關(guān)于“如何在通過TCP通信的位置無關(guān)代碼中實(shí)現(xiàn)數(shù)據(jù)加密”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,讓大家可以學(xué)到更多知識(shí),如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI