如何在通过TCP通信的位置无关代码中实现数据加密

如何在通过TCP通信的位置无关代码中实现数据加密

这篇文章给大家分享的是有关如何在通过TCP通信的位置无关代码中实现数据加密的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。

前言

如何在通过TCP通信的位置无关代码(PIC)中实现数据加密。

我将以Linux下的同步Shell作为演示样例

协议和代码库

当我们在思考加密协议时,第一个想到的很可能是安全传输层协议(TLS),因为它是针对Web安全的工业级标准。有的人可能还会想到SSH或IPSec等等,但是考虑到这些协议所采用的底层算法,它们其实都不适用于资源受限环境。而类似SHA-2和分组密码(例如Blowfish)这样加密哈希函数也并不是为类似RFID芯片这样的占用资源较少的电子设备设计的。

在2018年4月份,NIST曾为物联网行业的轻量级加密算法推行过一个标准化进程,整个过程需要好几年的时间才可以完成,但毫无疑问的是,整个行业并不会一直等待,因为这样会导致不安全的产品暴露在互联网中。某些密码学家选择采取主动的方式,通过自己的努力将他们设计的协议采用到这些低资源消耗的设备上,其中有两个典型的算法就是BLINKER和STROBE,而相应的适用于资源受限环境的代码库有LibHydrogen和MonoCypher。

分组密码

分组密码有很多种,但AES 128可能是目前最适合对在线流量进行加密的算法了,下面给出的是我们对不同种类分组密码的测试结果:

虽然这些加密算法都非常优秀,但是他们仍需要类似计数器(CTR)和基于认证的加密模块,其中最适合消息认证码(MAC)的加密算法就是LightMAC了,因为它在实现加密的过程中使用的是相同的分组密码。

流密码

另外两种针对认证加密的热门算法(AES-GCM的替换)就是ChaCha20和Poly1305了,但是ChaCha20采用的是200字节,而Poly1305为330字节。虽然跟HMAC-SHA2相比,Poly1305已经压缩得非常小了,但仍然占用资源过多。

置换函数

如果你花了很多时间去测试各种加密算法的话,你最终会发现在构造流密码、分组密码、加密认证模型、加密哈希函数和随机数生成器时,你需要的仅仅只是一个置换函数。下面这个表格给出的是我们针对三种函数的测试结果:

这里我们选择使用Gimli,因为它占用资源最少,并且可以用来构造针对通信流量的加密算法。

异或密码

接下来,我们实现一个针对数据流的简单异或操作(Just For Fun!)。下面的截图中显示的是一台Windows虚拟机发送给Linux虚拟机的部分命令,其中Linux平台运行的Shellcode是没有采用任何加密的。

捕捉到两台主机间的通信数据之后,我们可以看到如下所示的TCP流数据:

给Shellcode x86汇编代码中添加部分命令后,我们就可以进行8位异或运算了:

;;read(r,buf,BUFSIZ,0);xoresi,esi;esi=0movecx,edi;ecx=bufcdq;edx=0movdl,BUFSIZ;edx=BUFSIZpushSYS_read;eax=SYS_readpopeaxint0x80;encrypt/decryptbufferpushadxchgeax,ecxxor_loop:xorbyte[eax+ecx-1],XOR_KEYloopxor_looppopad;write(w,buf,len);xchgeax,edx;edx=lenmoval,SYS_writepopebx;sorin[1]int0x80jmppoll_wait

通过在新的会话中执行相同的命令,通信数据将无法直接可读,我这里使用了haxdump来查看发送的命令以及接收到的结果:

当然了,长度为8位的密钥是无法有效阻止攻击者恢复出通信明文的,下图给出的是Cyberchef爆破密钥的过程:

Speck和LightMAC

一开始,我使用的是下面这段代码来对数据包的加密进行验证,它使用了Encrypt-then-MAC (EtM),而且这种方法比其他的方法要更安全,比如说MAC-then-Encrypt (MtE) 或Encrypt-and-MAC(E&M):

bits32%defineSPECK_RNDS27%defineN8%defineK16;*****************************************;LightMACparametersbasedonSPECK64-128;;N=64-bits;K=128-bits;%defineCOUNTER_LENGTHN/2;shouldbe<=N/2%defineBLOCK_LENGTHN;equaltoN%defineTAG_LENGTHN;>=64-bits&&<=N%defineBC_KEY_LENGTHK;K%defineENCRYPT_BLKspeck_encrypt%defineGET_MAClightmac%defineLIGHTMAC_KEY_LENGTHBC_KEY_LENGTH*2;K*2%definek0edi%definek1ebp%definek2ecx%definek3esi%definex0ebx%definex1edx;esi=INdata;ebp=INkeyspeck_encrypt:pushadpushesi;saveMlodsd;x0=x->w[0]xchgeax,x0lodsd;x1=x->w[1]xchgeax,x1movesi,ebp;esi=keylodsdxchgeax,k0;k0=key[0]lodsdxchgeax,k1;k1=key[1]lodsdxchgeax,k2;k2=key[2]lodsdxchgeax,k3;k3=key[3]xoreax,eax;i=0spk_el:;x0=(ROTR32(x0,8)+x1)^k0;rorx0,8addx0,x1xorx0,k0;x1=ROTL32(x1,3)^x0;rolx1,3xorx1,x0;k1=(ROTR32(k1,8)+k0)^i;rork1,8addk1,k0xork1,eax;k0=ROTL32(k0,3)^k1;rolk0,3xork0,k1xchgk3,k2xchgk3,k1;i++inceaxcmpal,SPECK_RNDSjnzspk_elpopedixchgeax,x0;x->w[0]=x0stosdxchgeax,x1;x->w[1]=x1stosdpopadret;edx=INlen;ebx=INmsg;ebp=INkey;edi=OUTtaglightmac:pushadmovecx,edxxoredx,edxaddebp,BLOCK_LENGTH+BC_KEY_LENGTHpushad;allocateN-bytesforM;zeroinitializeTmov[edi+0],edx;t->w[0]=0;mov[edi+4],edx;t->w[1]=0;;whilewehavemsgdatalmx_l0:movesi,esp;esi=Mjecxzlmx_l2;exitloopifmsglen==0lmx_l1:;addbytetoMmoval,[ebx];al=*data++incebxmov[esi+edx+COUNTER_LENGTH],alincedx;idx++;Mfilled?cmpdl,BLOCK_LENGTH-COUNTER_LENGTH;--msglenloopnelmx_l1jnelmx_l2;addScounterinbigendianformatincdword[esp+_edx];ctr++moveax,[esp+_edx];resetindexcdq;idx=0bswapeax;m.ctr=SWAP32(ctr)mov[esi],eax;encryptMwithEusingK1callENCRYPT_BLK;updateTlodsd;t->w[0]^=m.w[0];xor[edi+0],eaxlodsd;t->w[1]^=m.w[1];xor[edi+4],eaxjmplmx_l0;keepgoinglmx_l2:;addtheendbitmovbyte[esi+edx+COUNTER_LENGTH],0x80xchgesi,edi;swapTandMlmx_l3:;updateTwithanymsgdataremainingmoval,[edi+edx+COUNTER_LENGTH]xor[esi+edx],aldecedxjnslmx_l3;advancekeytoK2addebp,BC_KEY_LENGTH;encryptTwithEusingK2callENCRYPT_BLKpopad;releasememoryforMpopad;restoreregistersret;IN:ebp=globalmemory,edi=msg,ecx=encflag,edx=msglen;OUT:-1orlengthofdataencrypted/decryptedencrypt:push-1popeax;setreturnvalueto-1pushadleaebp,[ebp+@ctx];ebpcryptoctxmovebx,edi;ebx=msgpushad;allocate8-bytesfortag+strmmovedi,esp;edi=tag;if(enc){;verifytag+decryptjecxzenc_l0;msglen-=TAG_LENGTH;subedx,TAG_LENGTHjleenc_l5;return-1ifmsglen<=0mov[esp+_edx],edx;GET_MAC(ctx,msg,msglen,mac);callGET_MAC;memcmp(mac,&msg[msglen],TAG_LENGTH)leaesi,[ebx+edx];esi=&msg[msglen]cmpsdjnzenc_l5;notequal?return-1cmpsdjnzenc_l5;ditto;MACsareequal;zerotheMACxoreax,eaxmov[esi-4],eaxmov[esi-8],eaxenc_l0:movedi,esptestedx,edx;exitif(msglen==0)jzenc_lx;memcpy(strm,ctx->e_ctr,BLOCK_LENGTH);movesi,[esp+_ebp];esi=ctx->e_ctrpushedimovsdmovsdmovebp,esipopesi;ENCRYPT_BLK(ctx->e_key,&strm);callENCRYPT_BLKmovcl,BLOCK_LENGTH;r=(len>BLOCK_LENGTH)?BLOCK_LENGTH:len;enc_l2:lodsb;al=*strm++xor[ebx],al;*msg^=alincebx;msg++decedxloopnzenc_l2;while(!ZF&&--ecx)movcl,BLOCK_LENGTHenc_l3:;do{;updatecountermovebp,[esp+_ebp]incbyte[ebp+ecx-1]loopzenc_l3;}while(ZF&&--ecx)jmpenc_l0enc_lx:;encrypting?addMACofciphertextdecdword[esp+_ecx]movedx,[esp+_edx]jzenc_l4movedi,ebxmovebx,[esp+_ebx]movebp,[esp+_ebp];GET_MAC(ctx,buf,buflen,msg);callGET_MAC;msglen+=TAG_LENGTH;addedx,TAG_LENGTHenc_l4:;returnmsglen;mov[esp+32+_eax],edxenc_l5:popadpopadret

需要注意的是,这里还得用到一个协议,接收方在对数据有效性进行验证之前需要知道发送方到底发送了多少数据过来,因此加密长度需要首先发送,接下来才是加密数据。但是请等一下,这里明明应该是Shellcode,为什么现在搞得那么复杂呢?试一下RC4?不,请大家往下看!

Gimli

为了使用Gimli来代替RC4,我编写了下面这段代码,这里的置换函数本质上就是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_ti,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_tidx;intfdr,fdw;uint8_ts[48];uint8_tbuf[BUFSIZ];}crypt_ctx;uint8_tgf_mul(uint8_tx){return(x<<1)^((x>>7)*0x1b);}//initializecryptocontextvoidinit_crypt(crypt_ctx*c,intr,intw,void*key){inti;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;}//encryptordecryptbuffervoidcrypt(crypt_ctx*c){inti,len;//readfromsocketorstdoutlen=read(c->fdr,c->buf,BUFSIZ);//encrypt/decryptfor(i=0;i<len;i++){if(c->idx>=32){permute(c->s);c->idx=0;}c->buf[i]^=c->s[c->idx++];}//writetosocketorstdinwrite(c->fdw,c->buf,len);}

在Linux Shell中使用这段代码之前,我们需要声明两个单独的加密上下文来处理输入、输出和128位的静态密钥:

//usingastatic128-bitkeycrypt_ctx*c,c1,c2;//echo-ntop_secret_key|opensslmd5-binary-outkey.bin//xxd-ikey.binuint8_tkey[]={0x4f,0xef,0x5a,0xcc,0x15,0x78,0xf6,0x01,0xee,0xa1,0x4e,0x24,0xf1,0xac,0xf9,0x49};

在进入主输出循环之前,我们还需要对每一个上下文初始化文件读取和写入描述符,这样可以减少代码的行数:

////c1isforreadingfromsocketandwritingtostdininit_crypt(&c1,s,in[1],key);//c2isforreadingfromstdoutandwritingtosocketinit_crypt(&c2,out[0],s,key);//nowloopuntiluserexitsorsomeothererrorfor(;;){r=epoll_wait(efd,&evts,1,-1);//error?bailoutif(r<=0)break;//notinput?bailoutif(!(evts.events&EPOLLIN))break;fd=evts.data.fd;c=(fd==s)?&c1:&ampc2;crypt(c);}

感谢各位的阅读!关于“如何在通过TCP通信的位置无关代码中实现数据加密”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

发布于 2021-12-28 21:02:24
收藏
分享
海报
0 条评论
54
上一篇:SAML漏洞的示例分析 下一篇:Crutch是一款什么软件
目录

    0 条评论

    本站已关闭游客评论,请登录或者注册后再评论吧~

    忘记密码?

    图形验证码