CBC位反转攻击

前段时间学习了CBC模式的加密,针对其中的位反转攻击,许多比赛的题目中也都有所体现,从一篇博客中找到了一个题目,想自己实现一下。
CBC字节翻转攻击
拿到题目后,我将源码修改了一下,主要是能够在本地建立服务端,与同在一个子网的主机进行交互。
点击下载源码
源码
提取码:did3
将代码在本地端用python编辑器运行,客户端使用 (nc 服务端IP 20001)链接即可。
如果在自己本地运行,就是自己的IP。

简单叙述一下CBC位反转攻击漏洞的来源:


解密

这是CBC模式解密的图解,从图中我们可以看出,我们解密第一段密文(A)的时候,使用Key进行解密 ,然后与初始化向量IV进行异或(XOR)运算,得到明文1(Plaintext_1)。
第二段明文(Plaintext_2)则是将Ciphertext_2用Key解密后(得到 B )与上一段密文分组 A 进行异或运算得到 C 。以此类推。

我们可以看到,解密时后一段的明文是受前一段密文影响的,所谓的位反转攻击就是通过修改前一段的密文,来达到解密时,篡改了后一段明文的一种攻击方式。

举个栗子:Eve想要篡改的是将 C 变成 M 。
我们知道 C = A ^ B
则 B = A ^ C
如果将 A 替换成(A ^ C ),则C = A ^ B = A ^ C ^ A ^ C = 0
那么再为C异或一次M,就是我们想要的明文分组了。

我们来看看源码当中的漏洞出现在什么地方。


def mkprofile(email,client_socket):
    if ((";" in email)):
        return -1
    prefix = "comment1=wowsuch%20CBC;userdata="
    suffix = ";coment2=%20suchsafe%20very%20encryptwowww"
    ptxt = prefix + email + suffix
    #client_socket.send ("����"+encrypt_cbc(KEY, IV, ptxt))
    return encrypt_cbc(KEY, IV, ptxt,client_socket)


def parse_profile(data,client_socket):
    print data,'break 3'
    ptxt = decrypt_cbc(KEY, IV, data.encode('hex'),client_socket)  # ����
    print data, 'break 4'
    ptxt = ptxt.replace(" ", "")  # ���ܺ�ȥ���ո�
    print data,'break 5'
    #client_socket.send(bytes(ptxt))
    if ";admin=true" in ptxt:
        client_socket.send(bytes(FLAG))
        #print FLAG
        return 1
    else:
        client_socket.send(bytes("you are stupid"))
        return 0


def dataReceived(data,client_socket):
    if (data.startswith("getapikey:")):
        data = data[10:]
        resp = mkprofile(data,client_socket)
        if (resp == -1):
            client_socket.send(bytes("No Cheating!\n"))
        else:
            client_socket.send(bytes(resp))
    # Decrypt Ciphertext and "parse" into Profile
    elif (data.startswith("getflag:")):
        client_socket.send(bytes("Parsing Profile...\n"))
        data=data.strip()
        data = data[8:].decode('hex')
        if (parse_profile(data,client_socket) == 1):
            client_socket.send(bytes(FLAG))
        else:
            client_socket.send(bytes("[BLACKBOX] You are a normal user.\n"))
    else:
        client_socket.send(bytes("\nyou should be admin"))


def connectionMade(self,client_socket):
    self.key = os.urandom(16)
    self.iv = os.urandom(16)
    self.client_socket.send_docs()

def body(client_socket,i):
    while True:
        print('start ',i,'thread')
        try:
            client_socket.send(bytes("\nplease input string:\n"))
            data = str(client_socket.recv(1024).decode('utf-8'))
            client_socket.send(bytes("\ncipher:" + mkprofile(data,client_socket) + "\n"))
            dataReceived(data,client_socket)
        except:
            break

server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server_socket.bind(('',20001))
server_socket.listen(128)

i=0
while True:
    try:
        client_socket, client_address = server_socket.accept()
        thread.start_new_thread(body,(client_socket,i))
        i=i+1
    except:
        print 'connect fail'

首先主体是body中的函数,表述了建立连接之后会做的一系列事情:

def body(client_socket,i):
    while True:
        print('start ',i,'thread')
        try:
            client_socket.send(bytes("\nplease input string:\n"))
            data = str(client_socket.recv(1024).decode('utf-8'))
            client_socket.send(bytes("\ncipher:" + mkprofile(data,client_socket) + "\n"))
            dataReceived(data,client_socket)
        except:
            break

在接受了客户端的输入之后,会通过dataReceived()函数,根据输入的不同,走不同的路径。
仔细研究dataReceived()和parse_profile()函数可以发现,我们获取flag的条件是

if (parse_profile(data,client_socket) == 1):
            client_socket.send(bytes(FLAG))
 if ";admin=true" in ptxt:
        client_socket.send(bytes(FLAG))

也就是说,用户的输入要带有";admin=true",这样,用户的输入被服务端接受后进行加密,解密出来的明文才会带有";admin=true",但是尝试着做题的朋友们肯定发现,客户端是不能直接输入";admin=true"的。

那么怎么破解呢,漏洞就在dataReceived()函数中:
与将用户的输入先加密再解密不同,当用户的输入以"getflag:"为开头的时候,在dataReceived()函数中调用的是parse_profile()函数,直接进行解密操作,这样我们就可以精心构造一个密文,让它以"getflag:"作为开头,解密之后就带有";admin=true"就可以得到flag了。

CBC加密模式每组16个字节,我们要修改的密文在第三个分组,通过前面的表述,得知我们需要改第二个分组。


20170811165053720.png

我们先输入"*admin=true"( * 也可以是其他的字符,只要将后面的是admin=true即可),得到一串密文,将其作为data,也就是我们准备开始构造密文的雏形,只要将它对应 * 的一位在解密时解密出" ; "即可。
利用上面提到的方法构造,如下:

# coding=UTF-8
data = 'cb16a54c2fad7eb698eb620e66bd642daed5230138e49c75fd4e12ba0ffbaef38e8082ded7cfb240d086dae2ba1bd32f90d1f5085311101fa437a29c98d2672ba5e0125b8ad88af53ade51adc8ed299468c490b03df1ce5b8bf633201830693d'
data = data.strip()
data = data.decode("hex")
data = list(data)
print data
data[16] = chr(ord("*") ^ ord(";") ^ ord(data[16]))
print "异或",data
data = "".join(data)
print data
data = data.encode("hex")
print data

将得到的data前面加上"getflag:"作为输入,即可得到flag。


image.png

后记:
关于出题过程当中出现的问题:

  1. 建立客户端与服务器之间的交互。
    一开始参考了网上关于socket的使用方法,写进代码之后发现服务端只能与单个客
    户端通信,不满足实验要求,然后在教员和同学的帮助下,使用thread,启用多个线程,每有一个客户端连接进来,都会启动一个新的线程,这样就解决了服务端与客户端的交互问题。
  2. 全局变量在各个用户端的数据混乱
    由于一开始的代码中client_socket是全局变量,导致各个客户端在client_socket.sent
    发送的时候数据会混乱。解决问题时,所有含有client_socket变量的函数,都将它作为参数传进函数。
  3. 程序在某个地方卡住,后面的信息打印不出来。
    解决:在服务端程序卡住的代码附近写一些打印函数,先定位到data = data[8:].decode(‘hex’)。


    image.png

在这行代码前后,检查data的类型以及长度,发现长度有问题。没有去掉字符串末尾的空格或者换行符。
加上data=data.strip()。解决了此问题。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343