[NPUCTF2020]webdog

image-20241114192253842

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php 
error_reporting(0);
include('config.php'); # $key,$flag
define("METHOD", "aes-128-cbc"); //定义加密方式
define("SECRET_KEY", $key); //定义密钥
define("IV","6666666666666666"); //定义初始向量 16个6
define("BR",'<br>');
if(!isset($_GET['source']))header('location:./index.php?source=1');


#var_dump($GLOBALS); //听说你想看这个?
function aes_encrypt($iv,$data)
{
echo "--------encrypt---------".BR;
echo 'IV:'.$iv.BR;
return base64_encode(openssl_encrypt($data, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv)).BR;
}
function aes_decrypt($iv,$data)
{
return openssl_decrypt(base64_decode($data),METHOD,SECRET_KEY,OPENSSL_RAW_DATA,$iv) or die('False');
}
if($_GET['method']=='encrypt')
{
$iv = IV;
$data = $flag;
echo aes_encrypt($iv,$data);
} else if($_GET['method']=="decrypt")
{
$iv = @$_POST['iv'];
$data = @$_POST['data'];
echo aes_decrypt($iv,$data);
}
echo "我摊牌了,就是懒得写前端".BR;

if($_GET['source']==1)highlight_file(__FILE__);
?>

Padding oracle attack 与CBC翻转字节攻击

异或(XOR)

何为异或? 异或是一种运算方法,简要概括就是同假异真

代码语言:javascript

复制

1
2
3
4
5
6
7
8
9
10
11
12
true xor true = false; true xor false = true;

在二进制中:
011 xor 110 = 101; 二进制数每位数分别进行xor运算
上面算式的运算步骤是这样的 0 xor 1 = 1;1 xor 1 = 0;1 xor 0=1
然后把运算结果写在一起就是101

在更高的进制中:
以十进制为例: 65 xor 42 = 107; 原理就是把十进制数字先变为二进制数进行异或,再将异或得到的二进制数结果变为十进制数

在字符中:
对字符进行异或运算其实就是对字符的ascii码进行异或计算,计算得到的结果视作新的一个ascii码再将其转换为字符。

xor还有一个性质,就是 已知

1
a xor b = c 那么 b xor c =a; a xor c = b.

即满足异或运算里只需知道任意两个数就能得到另一个数。

CBC模式

CBC是一种加密模型,采用的是分组链接模式,把明文分为一组一组的进行加密

image-20241114193437702

上图为CBC加密流程,最开始一个特别别分组IV(初始向量)去和第一段密文XOR,得到的结果呗密钥加密,加密得到该组的密文,同时这个密文会充当最开始的特别分组IV的作用去参与下一组的加密,以此类推。

CBC的每一个分组的加密结果都可以影响到下一个分组的加密结果,使原本独立的分组密码加密过程形成迭代,这可以强化加密算法的“敏感性”,即实现所谓的“雪崩效应”,在香浓理论中这就是”扰乱原则“。

CBC只是一种模式,他经常把aes或des作为加密使用的算法,DES分组长度式八字节而AES分组长度式十六字节。

接下来就是CBC解密

image-20241114194056628

顺序依旧是从左到右,第一组密文被KEY解密后与IV xor得到第一组明文,同时第一组密文参与下一组的解密充当IV的作用

PKCS #5

竟然CBC模式涉及到分组,那么就会出现最后一组字节没有被占满的情况。 比如原本一个分组是8字节,我们有15个字节的明文需要被加密,此时最后一个分组就不会被占满(还差一个字节占满),那么这个时候要怎么办呢?

这时候就需要对最后一个分组进行填充,将其填充满。 对于采用des算法加密的内容,填充规则是PKC #5,而AES是 PKC #7. 这两者唯一区别是 PKCS #5填充是八字节分组而PKCS #7是十六字节 ,还记得上面我们说过的 DES分组长度是八字节而AES分组长度是十六字节 吗?就是这个分组字节数影响了填充方式。

那么具体是怎么填充的呢,我们以PKC #5为例

image-20241114194320377

当最后一组还剩n个字节未被填充时,就会填充n个 0xn字符上去. 上图是PKCS #5,其实PKCS #7和PKCS #5原理是一样的,不过是分组字节数大了点罢了(0~16)

就是如果是alsdawgfqa这个按照4个一组的话就留了qa两个,在qa后加上\x02\x02,若是删除a,只有q,那就是\x03\x03\x03

Padding oracle attack 攻击场景举例

首先我们假设一个场景,从而引出这个攻击。

假设我们有一个任意文件包含场景

代码语言:javascript

复制

1
url?file=/etc/passwd

黑客们看见了就会很轻而易举的去包含想要的文件。管理者发现了这个问题,对file参数采用了CBC加密,即当提交请求时,file参数的值是被加密的,然后服务器用算法解密得到其想包含的文件,然后返回给客户端。

代码语言:javascript

复制

1
url?file=e28b2e3c972edab8 其中前8位数是IV,后八位数是密文。(这里是我瞎写的密文= =,你只需要理解到这里是一个CBC加密后的密文就行了)

那么如何去实现我们的任意文件包含呢? padding oracle attack 出现了。

攻击原理

假设我们向刚刚那个任意文件包含的提交了一段密文。服务器就会尝试解密,就会出现三种结果。

1.密文不能正常解密,这种原因是在于最后一组的填充字节出现了错误 2.密文能正常解密但解密出来的文件路径不存在 3.密文能正常解密且能成功包含

其中第1种情况和2.3种情况网页返回的内容肯定是不同的。 比如说第一种情况可能就直接返回500了,2.3可能就是302跳转啥啥的,通过这个网页返回的信息,我们就有了可乘之机。

我们先通过一个图感受一下第一组的解密流程

image-20241114200918054

好的好的,感受了这个解密流程后,我们来说说攻击的事。 如果我们得到了 Intermediary Value(中间值),并且可以手动修改IV,那么我们岂不是可以构造任意Decrypted Value(明文)了? 所以padding oracle attack 的核心就是去获得中间值。 那么我们怎么去获取呢?

还记得刚刚提到的3种情况吗?我们可以通过修改IV,通过判断网页返回内容来判断中间值,具体做法如下:

我们先把IV全部设置为0x00,然后修改IV的最后一个数,当其与中间值XOR后的值为0x01则此时解密就会成功,若不是0x01解密就会失败,网页会返回不同的内容,以此来判断何时解密成功。然后把解密成功时的IV的最后一位数与0x01进行异或计算,即可得到中间值的最后一位

image-20241114200941307

然后我们把IV最后一位数设置为能和中间值最后一位数异或后值为0x02的数,穷举IV倒数第二个数看看哪个数能和中间值倒数第二个数异或运算后值为0x02,然后我们就可得到中间值倒数第二个数,以此类推可以获得第一组的中间值(有点绕) 然后破解到了中间值我们再用最开始的IV(不是我们后面构造的IV)去和中间值异或就得到明文了 当然,你也可以再次构造IV,从而构造解密出来的字符(通过中间值与IV异或)

image-20241114201047952

先获取密文:

1
ly7auKVQCZWum/W/4osuPA==

然后对其进行base64解密,发现其刚好是有16个字节,那么我们可以直接穷举IV得到中间值,然后凭此与初始IV(16个6)进行异或得到明文$flag.

话不多说,爆破中间值的脚本安排上。(借鉴了一个大佬的WP)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import requests
import base64
import time
Intermediary=""
url="http://de1650aa-2b24-40e0-bb51-736ff5d38269.node3.buuoj.cn//index.php?source=1&method=decrypt"
iv=""
hexs=""
IV="6666666666666666"
def xor(a,b):
return "".join([chr(ord(a[i])^ord(b[i])) for i in range(len(a))])

for step in range(1,17):
padding=chr(step)*(step-1)
print("第%s轮"%step)
for i in range(0,256):
iv=chr(0)*(16-step)+chr(i)+xor(Intermediary,padding)
post={
"iv":iv,
"data":"ly7auKVQCZWum/W/4osuPA=="
}
r=requests.post(url=url,data=post,proxies={"http":"http://127.0.0.1:8080"})
time.sleep(0.1)
print(r.text+"第%s轮i=%s "%(step,i))
if "False" != r.text:
Intermediary=xor(chr(i),chr(step))+Intermediary
print(Intermediary)
break
for k in range(len(Intermediary)):
hexs="%"+str(ord(Intermediary[k]))+hexs
print(hexs)
print(xor(Intermediary,IV))

爆出了中间值以及$flag明文

image-20241114201206936

CBC翻转字节攻击

在对CBC模式加密的数据进行解密时,若iv可控,则可以任意控制解密后的内容。 CBC翻转字节攻击不同于padding oracle attack,后者的核心是IV可控情况下获取中间值,从而可以获得明文或者任意控制密文解密后的数据。 而CBC翻转字节攻击的核心思想就不是获取中间值了,而是在IV可控的情况下,通过算法缺陷来直接控制密文解密后的数据。

现在假定有中间值A,明文B1,IV C1

那么就有

代码语言:javascript

复制

1
A^B1=c1

一点错误都没有对吧。 现在又假定有中间值A,我们想要解密出的明文B2和与之对应的IV C2 那么就有

代码语言:javascript

复制

1
2
3
4
5
6
7
8
A^B2=C2
结合以上两个式子,有
A=B1^C1=B2^C2
于是有
B2=B1^C1^C2
C2=B1^B2^C1
若我们已知B1,C1,且C2可控,那么B2即可控
既满足刚刚我们说的,通过算法缺陷来直接控制密文解密后的数据。

FlagIsHere.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
ryino46QYgekMUTu0JFPMQ==
<?php
#error_reporting(0);
include('config.php'); //$fl4g
define("METHOD", "aes-128-cbc");
define("SECRET_KEY", "6666666");
session_start();

function get_iv(){ //生成随机初始向量IV
$random_iv='';
for($i=0;$i<16;$i++){
$random_iv.=chr(rand(1,255));
}
return $random_iv;
}

$lalala = 'piapiapiapia';

if(!isset($_SESSION['Identity'])){
$_SESSION['iv'] = get_iv();

$_SESSION['Identity'] = base64_encode(openssl_encrypt($lalala, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $_SESSION['iv']));
}
echo base64_encode($_SESSION['iv'])."<br>";

if(isset($_POST['iv'])){
$tmp_id = openssl_decrypt(base64_decode($_SESSION['Identity']), METHOD, SECRET_KEY, OPENSSL_RAW_DATA, base64_decode($_POST['iv']));
echo $tmp_id."<br>";
if($tmp_id ==='weber')die($fl4g);
}

highlight_file(__FILE__);
?>

此时我们已知初始IV,初始密文,且IV可控,那么CBC翻转字节攻击条件成立,可以攻击。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
这里贴一个别人写的python2 CBC字节反转攻击脚本
自己拿python3写了半天都没写出很好的效果...
import base64
def bxor(b1, b2): # use xor for bytes
parts = []
for b1, b2 in zip(b1, b2):
parts.append(bytes([b1 ^ b2]))
return b''.join(parts)
iv = base64.b64decode("h34HL5RbMPw8oTaQ+P58nw==")
text = b"piapiapiapia\x04\x04\x04\x04"
result = b"weber\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
middle = bxor(iv,text)
iv = bxor(middle,result)
print(base64.b64encode(iv))

把跑出来的结果POST过去,就会得到下一步了..

直接有了链接,这个是buu的话,直接下载附件就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

public class HelloWorld {
public HelloWorld() {
}

public static void main(String[] var0) {
System.out.println("众所周知,你是一名WEB选手,掌握javaweb也是一项必备技能,那么逆向个java应该不过分吧?");
byte[] var10000 = new byte[]{102, 108, 97, 103, 123, 119, 101, 54, 95, 52, 111, 103, 95, 49, 115, 95, 101, 52, 115, 121, 103, 48, 105, 110, 103, 125};
}
}

获取了这个class文件,有一些字节东西可以查看

1
2
3
4
a = bytearray(
[102, 108, 97, 103, 123, 119, 101, 54, 95, 52, 111, 103, 95, 49, 115, 95, 101, 52, 115, 121, 103, 48, 105, 110, 103, 125]
)
print(a.decode())

image-20241114204159168

获取flag

参考链接:https://cloud.tencent.com/developer/article/1942522

参考链接:https://blog.csdn.net/weixin_43610673/article/details/105898440