type
status
date
slug
summary
tags
category
icon
password

命令执行

web29 /*过滤flag*/

web30 /*过滤system*/

web31 /*绕过过滤*/

web32/web33/web34/web35/web36 /*过滤括号、分号等等(;*/

web37/web38/web39 /*文件包含,过滤php、flag*/

web40 /*无参数RCE*/

notion image
过滤的括号是中文括号,这是个坑

web41 /*无字母数字RCE、或运算绕过*/

notion image
 
过滤了^和~,但没过滤|,可以用或运算绕过

web42/web43/web44 /*绕过拼接字符串*/

notion image
  • web43将cat改为tac或nl
  • web44在web43基础上把flag.php改为fla*

web45 /*空格绕过1.0*/

notion image
在上面三题的基础上多过滤了个空格

web46/web47/web48 /* 过滤*和$ */

notion image

web49/web50/web51 /*空格绕过2.0*/

notion image
多过滤了个百分号%,也就是说空格不能通过%09绕过了
这题的空格绕过可以用<或者<>,但是上面的空格绕过1.0的不能用,可能也被过滤了只是没告诉我们
包括这题过滤的*字符,原本想用通配符?来绕过的,但是也是没用,应该也被过滤了
linux 适用空格绕过:
<、<>、%20(space)、%09(tab)、$IFS$9、${IFS}、$IFS、{cat,/etc/passwd}、%0a(回车)

web52 /*空格绕过*/

将<>过滤了,但是能用${IFS}绕过空格
执行成功了,但f12发现flag不在这。

web53 /*echo调用system函数*/

notion image
  • echo原本是输出命令,但是可以执行system函数

web54 /*重命名mv绕过*/

简要:过滤了cat、tac、nl、cp、flag、%、*等并且不能用引号绕过

web55/web56 /*文件上传+通配符绕过无字母数字*/

  • 题web56还过滤了数字,base64用不了,只能用法二
法一:
法二:
文件上传
思路:
  • 通过写的html脚本文件将.php文件上传到靶机里,默认的文件路径名是/tmp/phpXXXXXX(后面六位随机)。
  • 然后用通配符来替代字母,把文件路径名变成/???/????????[@-[]解释一下:最后一位不用通配符而是用“匹配”来替换,因为这个路径下有很多符合/???/?????????的文件,但它们都是小写字母,而我们上传的文件路径名后六位它是大小写随机共存的,"@"是"A"的前一位,"["是"Z"的后一位,因为字母被过滤了,所以用[@-[]而不是[A-Z]
实操:
  • 新建一个html文件
  • 双击后上传shell.php,文件内容就是要执行的命令ls。记得抓包放到Repeater里方便更改命令
  • 传递参数?c=.%20/???/????????[@-[] 前面的"."作用是执行文件,%20只是一个空格
notion image
tips:只有它随机生成的文件路径名的最后一位是大写才能有回显,这是它的唯一特征,没有回显多Send几次就有了
  • 找到flag.php,修改命令cat flag.php再Send
notion image

web57 /*括号构造数字*/

前提:$~和括号没有被过滤
简要:过滤了字母、数字、%,并提示flag在36.php,得知要拼凑出数字36
思路:没有过滤~、$和括号,利用括号的数学运算构造出36
原理:
综上,36的取反就是-37,就可以这样构造:

web58/web59/web60/web61/web62/web63/web64/web65

/*命令执行1.0*/

notion image
给的提示很有限,第一反应是传system命令,但执行后发现被禁用了,只能一个一个试

web66/web67/web68/web69/web70

/*命令执行1.1*/

到这题才禁用了show_source(),上面的五个payload只剩两个

web71 /*exit()阻断语句执行*/

简要:
  • 禁用了哪些函数没有明说出来(就爆了个highlight_file函数),可以肯定的是前面题禁用过的这也不能用。
  • 出来eval($c)后门还多了一些东西:就算不去代码审计它,正常执行后也能大概知道,它是把回显的字母和数字用"?"替换了,在多传个exit()就可以阻止它执行后面的代码了

web72 /*绕过 open_basedir*/

给的代码和上题一样,但是经过尝试发现这题只能够在当前目录下ls和cat
原因是它设置了open_basedir
💡
open_basedir是php.ini中的一个配置选项,它可将用户访问文件的活动范围限制在指定的区域,假设open_basedir=/home/wwwroot/home/web1/:/tmp/,那么通过web1访问服务器的用户就无法获取服务器上除了/home/wwwroot/home/web1/和/tmp/这两个目录以外的文件。注意用open_basedir指定的限制实际上是前缀,而不是目录名。举例来说: 若"open_basedir = /dir/user", 那么目录 "/dir/user" 和 "/dir/user1"都是可以访问的。所以如果要将访问限制在仅为指定的目录,请用斜线结束路径名。
思路:也就是说它设置了open_basedir限制了我们的访问区域,可以参考别人的文章来学习一下怎么绕过
这里记录两种方法来绕过open_basedir
#glob://伪协议绕过open_basedir
但是读取文件的话学到另一种方法:uaf绕过open_basedir,给出脚本:
执行命令在最后一行,也可以访问其他目录绕过open_basedir。然后将其url编码后传进c就可以了
注意:POST传参太长用bp稳定些,用hackbar没有回显
notion image
找到flag0.txt
修改命令cat flag0.txt,最终payload:
notion image

web73

web74

这题scandir()用不了

web75/web76 /*PDO访问数据库*/

还是猜测跟web72一样open_basedir
#用glob://伪协议成功访问根目录找到flag36txt,题web76是flag36d.txt
  • 但是用uaf绕过open_basedir执行命令时却发现被禁用了
notion image
  • 查看别人wp,找到另一种方法:
通过PDO(PHP操作多种数据库的统一接口)对数据库进行访问,执行SQL语句得到flag。
  • 猜解出数据库用户名和密码,连接数据库
notion image
发现名为ctftraining的数据库名,应该就是flag36.txt所在的数据库
然后用load_file函数读取flag文件即可获取flag信息
代码审计: 首先连接数据库,然后使用foreach遍历query()函数(作用:发送一条MYSQL查询)返回的结果,并且输出结果,再下面的就是输出报错信息了。
notion image

web77 /*php7.4+的FFI特性*/

  • 尝试沿用前面的思路,连接数据库然后用load_file来读取flag,但是发现连接不上数据库。
  • 题目有提示php7.4,查看wp得知这题使用php7.4的FFI特性来实现调用system函数。
(PHP 7 >= 7.4.0, PHP 8)FFI::cdef — Creates a new FFI objectFFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。
notion image
发现两个flag文件
  • 看别的wp说是 因为权限不足,列出根目录的文件权限信息,发现该文件对于其他用户并没有读的权限,而我们执行命令的用户为www-data,因此无法直接使用cat读取文件内容。
notion image
  • 猜测readflag是可执行文件,尝试后发现果然可行
notion image
 

文件包含

web78 #input、filter伪协议

notion image
  • 爆出flag.php和index.php
接着cat没有回显,f12也没有什么东西,那就用filter伪协议爆出源码,拿到flag

web79 #data伪协议

题目描述:
notion image
解题:

web80 #大小写PHP绕过 #data禁用

题目描述:
notion image
解题:
  • 大小写绕过——禁用php
  • data被禁用就真的用不了了,大小写也不能绕过
  • 因为被禁用了php,大小写绕过也不能用fliter伪协议
另一种方法是用包含日志文件getshell
notion image
然后再用蚁剑连接即可

web81 #包含日志文件getshell

题目描述:
notion image
解题:冒号也被过滤了,只能包含日志文件来getshell了
notion image
最后再用蚁剑连接成功getshell

web82/web83/web84/web85/web86

🥚session.upload_progress🥚禁用"."

题目描述:
notion image
解题:因为禁用了点,上一题用日志文件getshell的方法不适用了(access.log)
session会话文件
默认路径:
linux: /tmp 或 /var/lib/php/sessionWindows:C:\WINDOWS\Temp
该功能是在php5.4添加的,首先先了解下php.ini以下的几个默认选项
  • enable = on表示upload_progress功能开始,也意味着当浏览器向服务器上传一个文件时,php将会把此次文件上传的详细信息(如上传时间、上传进度等)存储在session当中 ;
  • cleanup = on表示当文件上传结束后,php将会立即清空对应session文件中的内容,这个选项非常重要;
  • name当它出现在表单中,php将会报告上传进度,最大的好处是,它的值可控;
  • prefix+name将表示为session中的键名;
  • 另外还有一个session配置中的重要选项:session.use_strict_mode=off这个选项默认值为off,表示我们对Cookie中sessionid可控。
过程分析
如果session.auto_start=on,则php会在接收请求的时候会自动初始化Session,不再需要执行session_start()。但默认情况下,这个选项都是关闭的。但session还有一个默认选项,session.use_strict_mode默认值为0。此时用户是可以自己定义Session ID的。比如,我们在Cookie里设置PHPSESSID=rikka,PHP将会在服务器上创建一个文件/tmp/sess_rikka。即使此时用户没有初始化Session,PHP也会自动初始化Session。 并产生一个键值,这个键值有ini.get(“session.upload_progress.prefix”)+由我们构造的session.upload_progress.name值组成,最后被写入sess_文件里;
简而言之,我们自定义的PHPSESSID的值会变成文件名,比如定义PHPSESSID:rikka,文件名即为/tmp/sess_rikka,而PHP_SESSION_UPLOAD_PROGRESS的值即为该文件的内容
上传成功后,就会在session['upload_progress_123']存储一些本次上传的相关信息
但是由于cleanup=on,会导致文件上传后,session文件的内容立即清空。此时我们得利用条件竞争,在session文件的内容被清空前进行文件包含
接着就是抄作业了,下面两个脚本分别跑命令和上传文件

web87 #filter伪协议绕过死亡exit

利用filter伪协议绕过死亡exit

什么是死亡exit

死亡exit指的是在进行写入PHP文件操作时,执行了以下函数:
file_put_contents($content, '<?php exit();' . $content);
亦或者
file_put_contents($content, '<?php exit();?>' . $content);
这样,当你插入一句话木马时,文件的内容是这样子的:
<?php exit();?><?php @eval($_POST['snakin']);?>
这样即使插入了一句话木马,在被使用的时候也无法被执行。这样的死亡exit通常存在于缓存、配置文件等等不允许用户直接访问的文件当中。

base64decode绕过

利用filter协议来绕过,看下这样的代码:
<?php$content = '<?php exit; ?>';$content .= $_POST['txt'];file_put_contents($_POST['filename'], $content);
当用户通过POST方式提交一个数据时,会与死亡exit进行拼接,从而避免提交的数据被执行。
然而这里可以利用php://filter的base64-decode方法,将$content解码,利用php base64_decode函数特性去除死亡exit。
base64编码中只包含64个可打印字符,当PHP遇到不可解码的字符时,会选择性的跳过,这个时候base64就相当于以下的过程:
<?php$_GET['txt'] = preg_replace('|[^a-z0-9A-Z+/]|s', '', $_GET['txt']);base64_decode($_GET['txt']);
所以,当$content 包含 <?php exit; ?>时,解码过程会先去除识别不了的字符,< ; ? >和空格等都将被去除,于是剩下的字符就只有phpexit以及我们传入的字符了。由于base64是4个byte一组,再添加一个字符例如添加字符’a’后,将’phpexita’当做两组base64进行解码,也就绕过这个死亡exit了。
这个时候后面再加上编码后的一句话木马,就可以getshell了。
文章出处:
题目描述:
notion image
解题:
抄作业,直接跑脚本:

web88 #无=的base64#data伪协议

题目描述:
notion image
解题:
  • 用data伪协议,传base64编码后的命令
  • base64编码不能有空格和加号,解决办法——调整要编码的命令的空格数量即可
 

PHP特性

web89 #intval()#正则匹配绕过

题目描述:
notion image
解题:
intval()函数返回参数的整数部分,传入的参数如果是数组会返回1
preg_match()函数传入的参数是数组则返回0
 

web90 #intval()绕过

web91 #回车%0a绕过正则匹配

题目描述:
notion image
解题:正则匹配的修饰符/m表示多行匹配,/i表示不区分大小写
可以先用回车符%0a,既能进入第一个if,也能绕过第二个if
 

web92/web93/web94/web95 #intval()

题目描述:
notion image
  • 题Web93禁用了字母
  • 题Web94if(!strpos($num, "0")){die("no no no!");}要求num的值里有"0",但不能是首位
  • 题Web95禁用了点".",则不能用浮点绕过了
解题:

web96 #传路径

题目描述:
notion image
解题:
传什么文件就显示什么文件,但不能等于‘flag.php’。那就传绝对路径或者当前路径
 

web97 #MD5

题目描述:
notion image
解题:
 

web98 #三元运算符

题目描述:
notion image
解题:
  • 对代码的理解是:GET随便传个参数,使得 $_GET=&$_POST,然后再POST传HTTP_FLAG=flag。然后因为$flag不是文件,highlight_file函数会报错,然后将它回显出来
  • 经过尝试确实可行,但是发现不用GET传参也是可以的。
  • 而中间两行代码我也不知道有什么用
  • 看了别人的wp,别人也没有解释什么,感觉这题....
 

web99 #in_array弱比较

题目描述:
notion image
解题:
  • 代码理解:allow为一个数组,里面由循环几百个随机数组成,范围由1到若干。只有当我们GET传的参数n在这个allow数组里时才会把我们POST传的参数content写进n文件里。
  • 这里有个漏洞就是in_array,它没有设置第三个参数的情况下默认为true,此时对于前面两个参数就是弱比较(==),因此就可以设置n=1.php,然后运气好的话直接把传给content的一句话木马给写进1.php。
  • 运气不好的情况下可能不能一次成功写进去,多试几次就可以了。成功率应该挺高的,毕竟由几百个allow的值。
  • 最后用蚁剑连接成功就拿到flag了
 

web100 #构造get_class_vars

题目描述:
notion image
解题:
  • 代码理解:提示flag在ctfshow这个类里,然后再最内层eval语句内已经拼接了ctfshow,那么就只要v2=var_dump(get_class_vars和$v3=);就可以了。
  • 再看看前面的条件,乍一看还以为三个参数都必须是数字,但有个“冷知识”:“=”的优先级比”and”高!也就是说只需要v1是数字就可以了,它会直接赋值给v0。
  • 而内层的两个if只是规定了v2和v3,这两个正则匹配对赋值没有什么影响
最后回显flag
notion image
把0x2d换成-再套个flag格式ctfshow{}就可以了

web101 #反射函数包含类#35位flag

题目描述:
notion image
解题:对比上一题,它过滤了下划线,但是没有过滤空格,可以构造反射函数包含类
notion image
拿到flag以为结束了(0x2d换成-),但是提交发现不成功。
看别人的wp说是flag一般是uuid(36位),而这里是35位,最后一位手动爆破出来。

web102/web103

#call_user_func()#hex2bin#filter写文件

题目描述:
notion image
解题:
代码审计:
  • v2要求为纯数字
  • v1设为一个函数名,且v2的除了前两位剩下的字符串作为v1函数的参数
  • 将v1函数运行的结果($str)写进v3
思路:
  • 因为是file_put_contents,所以可以用filter伪协议将$str写进shell.php文件
  • 想办法让$str变成一条命令(base64)以执行:v2是数字,要利用call_user_func将v2转化成命令字符串
  • 将v2转化成字符串的方法就要利用v1函数,这里用的方法:
把写入命令的base64字符串的十六进制,赋值给v2绕过is_numeric();
接着利用hex2bin()函数将十六进制的v2转化成字符串并成功写入shell.php
上面的思路就将两个参数v1和v3确定下来了。这个v2是数字,前两位任意数字即可绕过substr。
$str是shell.php的源码,就是php代码,要控制代码的base64编码的十六进制形式是纯数字,这个比较难把控,尽量越短越好,传一句话木马就不现实了(不可能没有其他字母)。自己写php代码来找"纯数字代码"
notion image
=的十六进制数是3d,不要=传入v2发现可以
最后的payload:
写入成功!
notion image
最后访问shell.php,查看源码拿到flag
notion image

web104

web105

题目描述:
notion image
解题:
代码审计:
  • 两层foreach的作用时限制了get传参的键名不能有errorpost传参的键值不能有flag
  • 绕过foreach里的if的话就$$key=$$value,例如如果GET传参suces=flag,则会变成$suces=$flag
  • 在不传任何参数的情况下,弹出$error是因为此时没有POST传flag,那么和它原本的$flag就不==,
$_POST['flag']==$flag值为0,改if语句值就为1。
思路:error和suces的值是可以修改的,代码里的两个die说明了有两个解题的办法,将二者之一改为flag然后die出来就可以了。
方法一:
因为要die出$error,那就需要把error值改为flag,从而$error=$flag。但是两个foreach就决定了这两个不能放在同一种传参方式里面,但是可以中间加个任意变量(键error不能GET那就POST,值flag不能POST那就GET)
方法二:
因为要die出$suces,那就需要把suces值改为flag,从而$suces=$flag。这时需要绕过die($error);
解决办法也很简单:POST传递个flag就好了,不需要赋值。这样$_POST['flag']==$flag就等价于$flag==$flag

web106 #sha1

web107 #parse_str()

题目描述:
解题:
parse_str($a,$b) 在数组$b中存储$a

web108 #%00截断匹配ereg

题目描述:
notion image
解题:
代码审计:
  • 匹配参数c必须是纯字母(可用截断符%00绕过)
  • 值为877(十六进制0x36d)
  • strrev()反转字符串(778=>877)
 
A Secret红日内网渗透靶场(一)